Skip to content

Commit 4d055b0

Browse files
author
pengyu
committed
working with everything but preview and photourl broken
1 parent 36bd680 commit 4d055b0

File tree

9 files changed

+170
-128
lines changed

9 files changed

+170
-128
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: 114 additions & 76 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,130 @@ export async function GET(req: Request) {
3031
);
3132
}
3233

33-
try {
34-
// Get browser instance
35-
const browser = await getBrowser();
36-
37-
// Create a new page
38-
page = await browser.newPage();
39-
40-
// Set viewport to a reasonable size
41-
await page.setViewport({
42-
width: 1280,
43-
height: 720,
44-
});
45-
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-
});
51-
52-
// 等待额外的时间让页面完全渲染
53-
await page.waitForTimeout(3000);
54-
55-
// 尝试等待页面上的内容加载,如果失败也继续处理
34+
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
5635
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-
}
36+
logger.info(`Screenshot attempt ${attempt + 1} for ${url}`);
37+
38+
// Get browser instance
39+
const browser = await getBrowser();
40+
41+
// Create a new page
42+
page = await browser.newPage();
43+
44+
// Set viewport to a reasonable size
45+
await page.setViewport({
46+
width: 1280,
47+
height: 720,
48+
});
49+
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+
});
55+
56+
// 等待额外的时间让页面完全渲染
57+
await page.waitForTimeout(8000); // 增加等待时间到8秒
58+
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

69-
// Take screenshot
70-
const screenshot = await page.screenshot({
71-
type: 'png',
72-
fullPage: false,
73-
});
74+
// 检查页面是否有内容
75+
const contentCheck = await page.evaluate(() => {
76+
return {
77+
bodyContent: document.body.innerText.length,
78+
elements: document.querySelectorAll('*').length
79+
};
80+
});
81+
82+
logger.info(`Page content: ${contentCheck.bodyContent} chars, ${contentCheck.elements} elements`);
83+
84+
if (contentCheck.elements < 5) {
85+
logger.warn('Page seems empty, possibly not fully loaded');
86+
if (attempt < MAX_RETRIES - 1) {
87+
await page.close();
88+
continue; // 重试
89+
}
90+
}
7491

75-
// Always close the page when done
76-
if (page) {
77-
await page.close();
78-
}
92+
// Take screenshot with full page height
93+
const screenshot = await page.screenshot({
94+
type: 'png',
95+
fullPage: false,
96+
});
7997

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 {
98+
// Always close the page when done
99+
if (page) {
93100
await page.close();
94-
} catch (closeError) {
95-
logger.error('Error closing page:', closeError);
96101
}
97-
}
98102

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;
103+
// Return the screenshot as a PNG image
104+
return new Response(screenshot, {
105+
headers: {
106+
'Content-Type': 'image/png',
107+
'Cache-Control': 's-maxage=3600',
108+
},
109+
});
110+
} catch (error: any) {
111+
logger.error(`Screenshot error on attempt ${attempt + 1}:`, error);
112+
113+
// Ensure page is closed even if an error occurs
114+
if (page) {
115+
try {
116+
await page.close();
117+
} catch (closeError) {
118+
logger.error('Error closing page:', closeError);
109119
}
110-
} catch (closeBrowserError) {
111-
logger.error('Error closing browser:', closeBrowserError);
112120
}
113-
}
114121

115-
return NextResponse.json(
116-
{ error: error.message || 'Failed to capture screenshot' },
117-
{ status: 500 }
118-
);
122+
// If browser seems to be in a bad state, recreate it
123+
if (
124+
error.message.includes('Target closed') ||
125+
error.message.includes('Protocol error') ||
126+
error.message.includes('Target.createTarget')
127+
) {
128+
try {
129+
if (browserInstance) {
130+
await browserInstance.close();
131+
browserInstance = null;
132+
}
133+
} catch (closeBrowserError) {
134+
logger.error('Error closing browser:', closeBrowserError);
135+
}
136+
}
137+
138+
// 如果这不是最后一次尝试,则继续
139+
if (attempt < MAX_RETRIES - 1) {
140+
// 等待一会儿再重试
141+
await new Promise(resolve => setTimeout(resolve, 3000));
142+
continue;
143+
}
144+
145+
// 最后一次尝试失败
146+
return NextResponse.json(
147+
{ error: error.message || 'Failed to capture screenshot after multiple attempts' },
148+
{ status: 500 }
149+
);
150+
}
119151
}
152+
153+
// 如果重试都失败
154+
return NextResponse.json(
155+
{ error: 'Failed to capture screenshot after exhausting all retries' },
156+
{ status: 500 }
157+
);
120158
}
121159

122160
// 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}`);

0 commit comments

Comments
 (0)