-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathProjectController.ts
More file actions
110 lines (101 loc) · 3.51 KB
/
ProjectController.ts
File metadata and controls
110 lines (101 loc) · 3.51 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import {
HTTPController,
HTTPMethod,
HTTPMethodEnum,
HTTPParam,
HTTPQuery,
Inject,
HTTPContext,
} from '@eggjs/tegg';
import * as path from 'path';
import * as fs from 'fs/promises';
import archiver from 'archiver';
import { listFiles } from '../lib/utils';
import { PreviewService } from '../service/preview';
@HTTPController({ path: '/api/v1/projects' })
export class ProjectController {
@Inject()
private readonly previewService!: PreviewService;
// GET /api/v1/projects/:threadId/files
@HTTPMethod({ method: HTTPMethodEnum.GET, path: '/:threadId/files' })
async files(@HTTPParam({ name: 'threadId' }) threadId: string) {
const outputDir = `./output/${threadId}`;
try {
const allFiles = await listFiles(outputDir);
const fileTree = allFiles.map(f => path.relative(outputDir, f));
return { success: true, data: { threadId, files: fileTree } };
} catch {
return { success: true, data: { threadId, files: [] } };
}
}
// GET /api/v1/projects/:threadId/file?path=xxx
@HTTPMethod({ method: HTTPMethodEnum.GET, path: '/:threadId/file' })
async fileContent(
@HTTPParam({ name: 'threadId' }) threadId: string,
@HTTPQuery({ name: 'path' }) filePath: string,
) {
if (!filePath) {
return { success: false, error: 'path query parameter is required' };
}
const outputDir = `./output/${threadId}`;
const fullPath = path.resolve(outputDir, filePath);
if (!fullPath.startsWith(path.resolve(outputDir))) {
return { success: false, error: 'Access denied' };
}
try {
const content = await fs.readFile(fullPath, 'utf-8');
return { success: true, data: { path: filePath, content } };
} catch {
return { success: false, error: 'File not found' };
}
}
// GET /api/v1/projects/:threadId/download
@HTTPMethod({ method: HTTPMethodEnum.GET, path: '/:threadId/download' })
async download(
@HTTPParam({ name: 'threadId' }) threadId: string,
@HTTPContext() ctx: any,
) {
const outputDir = `./output/${threadId}`;
try {
await fs.access(outputDir);
} catch {
return { success: false, error: 'Project not found' };
}
ctx.res.writeHead(200, {
'Content-Type': 'application/zip',
'Content-Disposition': `attachment; filename="project-${threadId.slice(0, 8)}.zip"`,
});
ctx.respond = false;
const archive = archiver('zip', { zlib: { level: 9 } });
archive.pipe(ctx.res);
archive.directory(outputDir, `project-${threadId.slice(0, 8)}`);
await archive.finalize();
}
// POST /api/v1/projects/:threadId/preview
@HTTPMethod({ method: HTTPMethodEnum.POST, path: '/:threadId/preview' })
async startPreview(@HTTPParam({ name: 'threadId' }) threadId: string) {
const outputDir = `./output/${threadId}`;
try {
await fs.access(outputDir);
} catch {
return { success: false, error: 'Project not found' };
}
const info = await this.previewService.start(threadId, outputDir);
return {
success: true,
data: { status: info.status, port: info.port, error: info.error },
};
}
// GET /api/v1/projects/:threadId/preview
@HTTPMethod({ method: HTTPMethodEnum.GET, path: '/:threadId/preview' })
async previewStatus(@HTTPParam({ name: 'threadId' }) threadId: string) {
const info = this.previewService.getStatus(threadId);
if (!info) {
return { success: false, error: 'No preview found for this project' };
}
return {
success: true,
data: { status: info.status, port: info.port, error: info.error },
};
}
}