Skip to content

Commit ac64d52

Browse files
author
pengyu
committed
working with everything but preview and photourl broken
1 parent 1974769 commit ac64d52

File tree

9 files changed

+116
-94
lines changed

9 files changed

+116
-94
lines changed

backend/src/project/dto/project.input.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,9 @@ export class FetchPublicProjectsInputs {
124124

125125
@Field()
126126
size: number;
127+
128+
@Field()
129+
currentUserId: string;
127130
}
128131

129132
@InputType()

backend/src/project/project.resolver.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,14 @@ export class ProjectsResolver {
103103
const { buffer, mimetype } = await validateAndBufferFile(file);
104104

105105
// Call the service with the extracted buffer and mimetype
106-
return this.projectService.updateProjectPhotoUrl(
106+
const project1= await this.projectService.updateProjectPhotoUrl(
107107
userId,
108108
projectId,
109109
buffer,
110110
mimetype,
111111
);
112+
this.logger.debug('project1', project1.photoUrl);
113+
return project1;
112114
}
113115

114116
@Mutation(() => Project)
@@ -152,8 +154,10 @@ export class ProjectsResolver {
152154
*/
153155
@Query(() => [Project])
154156
async fetchPublicProjects(
157+
@GetUserIdFromToken() userId: string,
155158
@Args('input') input: FetchPublicProjectsInputs,
156159
): Promise<Project[]> {
160+
input.currentUserId = userId;
157161
return this.projectService.fetchPublicProjects(input);
158162
}
159163

backend/src/project/project.service.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -644,9 +644,8 @@ export class ProjectService {
644644
const limit = input.size > 50 ? 50 : input.size;
645645

646646
const whereCondition = {
647-
isPublic: true,
648647
isDeleted: false,
649-
photoUrl: Not(IsNull()),
648+
userId: Not(input.currentUserId), // Exclude current user's projects
650649
};
651650

652651
if (input.strategy === 'latest') {
@@ -808,7 +807,7 @@ export class ProjectService {
808807
this.logger.log(
809808
'check if the github project exist: ' + project.isSyncedWithGitHub,
810809
);
811-
// 2) Check users GitHub installation
810+
// 2) Check user's GitHub installation
812811
if (!user.githubInstallationId) {
813812
throw new Error('GitHub App not installed for this user');
814813
}
@@ -819,7 +818,7 @@ export class ProjectService {
819818
);
820819
const userOAuthToken = user.githubAccessToken;
821820

822-
// 4) Create the repo if the project doesnt have it yet
821+
// 4) Create the repo if the project doesn't have it yet
823822
if (!project.githubRepoName || !project.githubOwner) {
824823
// Use project.projectName or generate a safe name
825824

frontend/src/app/api/runProject/route.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -517,10 +517,12 @@ export async function GET(req: Request) {
517517
existingContainer.containerId
518518
);
519519
if (isRunning) {
520+
logger.info(`Using existing container with port: ${existingContainer.port}`);
520521
return NextResponse.json({
521522
message: 'Docker container already running',
522523
domain: existingContainer.domain,
523524
containerId: existingContainer.containerId,
525+
port: existingContainer.port,
524526
});
525527
} else {
526528
// Remove non-running container from state
@@ -547,12 +549,14 @@ export async function GET(req: Request) {
547549
processingRequests.add(projectPath);
548550

549551
try {
550-
const { domain, containerId } = await runDockerContainer(projectPath);
552+
const { domain, containerId, port } = await runDockerContainer(projectPath);
553+
logger.info(`Successfully started container on port: ${port}`);
551554

552555
return NextResponse.json({
553556
message: 'Docker container started',
554557
domain,
555558
containerId,
559+
port,
556560
});
557561
} catch (error: any) {
558562
logger.error(`Failed to start Docker container:`, error);

frontend/src/app/api/screenshot/route.ts

Lines changed: 83 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export async function GET(req: Request) {
2222
const { searchParams } = new URL(req.url);
2323
const url = searchParams.get('url');
2424
let page = null;
25+
const MAX_RETRIES = 3;
2526

2627
if (!url) {
2728
return NextResponse.json(
@@ -30,93 +31,112 @@ export async function GET(req: Request) {
3031
);
3132
}
3233

33-
try {
34-
// Get browser instance
35-
const browser = await getBrowser();
34+
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
35+
try {
36+
logger.info(`Screenshot attempt ${attempt + 1} for ${url}`);
37+
38+
// Get browser instance
39+
const browser = await getBrowser();
3640

37-
// Create a new page
38-
page = await browser.newPage();
41+
// Create a new page
42+
page = await browser.newPage();
3943

4044
// Set viewport to a reasonable size
4145
await page.setViewport({
4246
width: 1600,
4347
height: 900,
4448
});
4549

46-
// Navigate to URL with increased timeout and more reliable wait condition
47-
await page.goto(url, {
48-
waitUntil: 'domcontentloaded', // Less strict than networkidle0
49-
timeout: 60000, // Increased timeout to 60 seconds
50-
});
50+
// Navigate to URL with increased timeout and more reliable wait condition
51+
await page.goto(url, {
52+
waitUntil: 'networkidle2', // 更改为等待网络空闲状态,确保页面完全加载
53+
timeout: 90000, // 增加超时时间到90秒
54+
});
5155

52-
// 等待额外的时间让页面完全渲染
53-
await page.waitForTimeout(3000);
56+
// 等待额外的时间让页面完全渲染
57+
await page.waitForTimeout(8000); // 增加等待时间到8秒
5458

55-
// 尝试等待页面上的内容加载,如果失败也继续处理
56-
try {
57-
// 等待页面上可能存在的主要内容元素
58-
await Promise.race([
59-
page.waitForSelector('main', { timeout: 2000 }),
60-
page.waitForSelector('#root', { timeout: 2000 }),
61-
page.waitForSelector('.app', { timeout: 2000 }),
62-
page.waitForSelector('h1', { timeout: 2000 }),
63-
]);
64-
} catch (waitError) {
65-
// 忽略等待选择器的错误,继续截图
66-
logger.info('Unable to find common page elements, continuing with screenshot');
67-
}
59+
// 尝试等待页面上的内容加载,如果失败也继续处理
60+
try {
61+
// 等待页面上可能存在的主要内容元素
62+
await Promise.race([
63+
page.waitForSelector('main', { timeout: 5000 }),
64+
page.waitForSelector('#root', { timeout: 5000 }),
65+
page.waitForSelector('.app', { timeout: 5000 }),
66+
page.waitForSelector('h1', { timeout: 5000 }),
67+
page.waitForSelector('div', { timeout: 5000 }), // 添加更通用的选择器
68+
]);
69+
} catch (waitError) {
70+
// 忽略等待选择器的错误,继续截图
71+
logger.info('Unable to find common page elements, continuing with screenshot');
72+
}
6873

6974
// Take screenshot
7075
const screenshot = await page.screenshot({
7176
type: 'png',
7277
fullPage: true,
7378
});
7479

75-
// Always close the page when done
76-
if (page) {
77-
await page.close();
78-
}
79-
80-
// Return the screenshot as a PNG image
81-
return new Response(screenshot, {
82-
headers: {
83-
'Content-Type': 'image/png',
84-
'Cache-Control': 's-maxage=3600',
85-
},
86-
});
87-
} catch (error: any) {
88-
logger.error('Screenshot error:', error);
89-
90-
// Ensure page is closed even if an error occurs
91-
if (page) {
92-
try {
80+
// Always close the page when done
81+
if (page) {
9382
await page.close();
94-
} catch (closeError) {
95-
logger.error('Error closing page:', closeError);
9683
}
97-
}
9884

99-
// If browser seems to be in a bad state, recreate it
100-
if (
101-
error.message.includes('Target closed') ||
102-
error.message.includes('Protocol error') ||
103-
error.message.includes('Target.createTarget')
104-
) {
105-
try {
106-
if (browserInstance) {
107-
await browserInstance.close();
108-
browserInstance = null;
85+
// Return the screenshot as a PNG image
86+
return new Response(screenshot, {
87+
headers: {
88+
'Content-Type': 'image/png',
89+
'Cache-Control': 's-maxage=3600',
90+
},
91+
});
92+
} catch (error: any) {
93+
logger.error(`Screenshot error on attempt ${attempt + 1}:`, error);
94+
95+
// Ensure page is closed even if an error occurs
96+
if (page) {
97+
try {
98+
await page.close();
99+
} catch (closeError) {
100+
logger.error('Error closing page:', closeError);
109101
}
110-
} catch (closeBrowserError) {
111-
logger.error('Error closing browser:', closeBrowserError);
112102
}
113-
}
114103

115-
return NextResponse.json(
116-
{ error: error.message || 'Failed to capture screenshot' },
117-
{ status: 500 }
118-
);
104+
// If browser seems to be in a bad state, recreate it
105+
if (
106+
error.message.includes('Target closed') ||
107+
error.message.includes('Protocol error') ||
108+
error.message.includes('Target.createTarget')
109+
) {
110+
try {
111+
if (browserInstance) {
112+
await browserInstance.close();
113+
browserInstance = null;
114+
}
115+
} catch (closeBrowserError) {
116+
logger.error('Error closing browser:', closeBrowserError);
117+
}
118+
}
119+
120+
// 如果这不是最后一次尝试,则继续
121+
if (attempt < MAX_RETRIES - 1) {
122+
// 等待一会儿再重试
123+
await new Promise(resolve => setTimeout(resolve, 3000));
124+
continue;
125+
}
126+
127+
// 最后一次尝试失败
128+
return NextResponse.json(
129+
{ error: error.message || 'Failed to capture screenshot after multiple attempts' },
130+
{ status: 500 }
131+
);
132+
}
119133
}
134+
135+
// 如果重试都失败
136+
return NextResponse.json(
137+
{ error: 'Failed to capture screenshot after exhausting all retries' },
138+
{ status: 500 }
139+
);
120140
}
121141

122142
// Handle process termination to close browser

frontend/src/components/global-project-poller.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,17 @@ const GlobalToastListener = () => {
6565
if (project.id && project.projectPath) {
6666
logger.info(`Taking screenshot for project ${project.id}`);
6767
// 获取项目URL并进行截图
68-
const { domain } = await getWebUrl(project.projectPath);
69-
const baseUrl = `${URL_PROTOCOL_PREFIX}://${domain}`;
68+
const { domain, port } = await getWebUrl(project.projectPath);
69+
70+
// 使用端口直接访问
71+
let baseUrl;
72+
if (port) {
73+
baseUrl = `${URL_PROTOCOL_PREFIX}://localhost:${port}`;
74+
} else {
75+
baseUrl = `${URL_PROTOCOL_PREFIX}://${domain}`;
76+
}
77+
78+
logger.info(`Using URL for screenshot: ${baseUrl}`);
7079

7180
// 等待5秒钟让服务完全启动
7281
logger.info(`Waiting for service to fully start before taking screenshot for project ${project.id}`);

frontend/src/components/root/expand-card.tsx

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -33,26 +33,10 @@ export function ExpandableCard({ projects, isGenerating = false, onOpenChat }) {
3333
}, [active]);
3434

3535
const handleCardClick = async (project) => {
36-
if (isGenerating && onOpenChat) {
36+
if (onOpenChat) {
3737
onOpenChat();
3838
return;
3939
}
40-
41-
setActive(project);
42-
setIframeUrl('');
43-
if (cachedUrls.current.has(project.id)) {
44-
setIframeUrl(cachedUrls.current.get(project.id));
45-
return;
46-
}
47-
48-
try {
49-
const data = await getWebUrl(project.path);
50-
const url = `${URL_PROTOCOL_PREFIX}://${data.domain}`;
51-
cachedUrls.current.set(project.id, url);
52-
setIframeUrl(url);
53-
} catch (error) {
54-
logger.error('Error fetching project URL:', error);
55-
}
5640
};
5741

5842
return (
@@ -148,7 +132,7 @@ export function ExpandableCard({ projects, isGenerating = false, onOpenChat }) {
148132
className="absolute inset-0 bg-black/40 flex items-center justify-center"
149133
>
150134
<span className="text-white font-medium px-4 py-2 rounded-lg bg-white/20 backdrop-blur-sm">
151-
{isGenerating ? 'Open Chat' : 'View Project'}
135+
{isGenerating ? 'Open Chat' : 'Open Project'}
152136
</span>
153137
</motion.div>
154138
</motion.div>

frontend/src/components/root/projects-section.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export function ProjectsSection() {
3434
error: publicError,
3535
refetch: refetchPublic,
3636
} = useQuery(FETCH_PUBLIC_PROJECTS, {
37-
variables: { input: { size: 100, strategy: 'latest' } },
37+
variables: { input: { size: 100, strategy: 'latest', currentUserId: user?.id || '' } },
3838
fetchPolicy: 'network-only',
3939
});
4040

@@ -114,7 +114,7 @@ export function ProjectsSection() {
114114

115115
const displayProjects = view === 'my' ? mergedMyProjects : publicProjects;
116116

117-
const transformedProjects = displayProjects
117+
const transformedProjects = [...displayProjects]
118118
.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())
119119
.map((project) => ({
120120
id: project.id,
@@ -126,9 +126,7 @@ export function ProjectsSection() {
126126
: 'N/A',
127127
author: project.user?.username || user?.username || 'Unknown',
128128
forkNum: project.subNumber || 0,
129-
image: project.photoUrl || (project.projectPath
130-
? `https://picsum.photos/500/250?random=${project.id}`
131-
: null),
129+
image: project.photoUrl || null,
132130
}));
133131

134132
// 添加临时生成中的项目

frontend/src/graphql/schema.gql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ type EmailConfirmationResponse {
6969
}
7070

7171
input FetchPublicProjectsInputs {
72+
currentUserId: String!
7273
size: Float!
7374
strategy: String!
7475
}

0 commit comments

Comments
 (0)