nuwax-file-server 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.md +340 -0
  2. package/dist/cli.js +2 -0
  3. package/dist/config/index.js +1 -0
  4. package/dist/config/swagger/buildDocs.js +1 -0
  5. package/dist/config/swagger/codeDocs.js +1 -0
  6. package/dist/config/swagger/projectDocs.js +1 -0
  7. package/dist/config/swagger.js +1 -0
  8. package/dist/env.development +85 -0
  9. package/dist/routes/buildRoutes.js +1 -0
  10. package/dist/routes/codeRoutes.js +1 -0
  11. package/dist/routes/computerRoutes.js +1 -0
  12. package/dist/routes/projectRoutes.js +1 -0
  13. package/dist/routes/router.js +1 -0
  14. package/dist/scheduler/pnpmPruneScheduler.js +2 -0
  15. package/dist/server.js +1 -0
  16. package/dist/service/codeService.js +4 -0
  17. package/dist/service/projectService.js +1 -0
  18. package/dist/utils/build/buildProjectUtils.js +4 -0
  19. package/dist/utils/build/keepAliveDevUtils.js +1 -0
  20. package/dist/utils/build/processManager.js +9 -0
  21. package/dist/utils/build/restartDevUtils.js +1 -0
  22. package/dist/utils/build/startDevUtils.js +1 -0
  23. package/dist/utils/build/stopDevUtils.js +2 -0
  24. package/dist/utils/build/syntaxCheckUtils.js +7 -0
  25. package/dist/utils/buildArg/extraArgsUtils.js +1 -0
  26. package/dist/utils/buildArg/portPool.js +1 -0
  27. package/dist/utils/buildArg/portUtils.js +8 -0
  28. package/dist/utils/buildDependency/dependencyManager.js +17 -0
  29. package/dist/utils/buildJudge/aliveJudgeUtils.js +1 -0
  30. package/dist/utils/buildJudge/restartJudgeUtils.js +1 -0
  31. package/dist/utils/buildPermission/permissionManager.js +5 -0
  32. package/dist/utils/common/npmrcUtils.js +7 -0
  33. package/dist/utils/common/sensitiveUtils.js +1 -0
  34. package/dist/utils/common/zipUtils.js +1 -0
  35. package/dist/utils/computer/computerFileUtils.js +1 -0
  36. package/dist/utils/computer/computerUtils.js +1 -0
  37. package/dist/utils/envUtils.js +5 -0
  38. package/dist/utils/error/buildErrorParser.js +23 -0
  39. package/dist/utils/error/errorCodes.js +1 -0
  40. package/dist/utils/error/errorHandler.js +1 -0
  41. package/dist/utils/log/getDevLogUtils.js +2 -0
  42. package/dist/utils/log/logCacheManager.js +2 -0
  43. package/dist/utils/log/logUtils.js +2 -0
  44. package/dist/utils/project/backupUtils.js +1 -0
  45. package/dist/utils/project/copyProjectUtils.js +1 -0
  46. package/dist/utils/project/frameworkDetectorUtils.js +1 -0
  47. package/dist/utils/project/getContentUtils.js +1 -0
  48. package/dist/utils/project/initProjectCleanupUtils.js +1 -0
  49. package/dist/utils/project/uploadAttachmentFileUtils.js +1 -0
  50. package/dist/utils/serviceManager.js +3 -0
  51. package/package.json +93 -0
package/README.md ADDED
@@ -0,0 +1,340 @@
1
+ # nuwax-file-server
2
+
3
+ 跨平台的文件服务部署工具,支持 Windows、Linux、macOS 操作系统。
4
+
5
+ ## 功能特性
6
+
7
+ - **CLI 命令行操作**: 支持 start / stop / restart / status 命令
8
+ - **跨平台支持**: Windows、Linux、macOS 完美兼容
9
+ - **环境变量配置**: 支持通过环境变量和命令行参数配置
10
+ - **健康检查端点**: 提供 /health 接口用于服务检活
11
+ - **PID 文件管理**: 自动管理服务进程 ID
12
+
13
+ ## 安装部署
14
+
15
+ ### 本地开发安装
16
+
17
+ ```bash
18
+ # 克隆项目
19
+ git clone <repository-url>
20
+ cd subapp-deployer
21
+
22
+ # 安装依赖
23
+ npm install
24
+
25
+ # 本地运行(开发模式)
26
+ npm run dev
27
+
28
+ # 本地运行(生产模式)
29
+ npm run prod
30
+ ```
31
+
32
+ ### 全局安装 CLI 工具
33
+
34
+ ```bash
35
+ # 在项目根目录执行
36
+ npm install -g .
37
+
38
+ # 然后可以在任何位置使用
39
+ nuwax-file-server --help
40
+ ```
41
+
42
+ ### 系统要求
43
+
44
+ - Node.js >= 22.0.0(ES Module 原生支持)
45
+ - zip/unzip 工具(用于项目打包)
46
+ - pnpm(推荐)或 npm/yarn
47
+
48
+ ## CLI 命令
49
+
50
+ nuwax-file-server 提供以下 CLI 命令:
51
+
52
+ ### 基本命令
53
+
54
+ ```bash
55
+ # 启动服务(默认使用 env.production 配置)
56
+ nuwax-file-server start
57
+
58
+ # 启动服务(指定环境)
59
+ nuwax-file-server start --env development
60
+ nuwax-file-server start --env production
61
+ nuwax-file-server start --env test
62
+
63
+ # 停止服务
64
+ nuwax-file-server stop
65
+
66
+ # 强制停止服务
67
+ nuwax-file-server stop --force
68
+
69
+ # 重启服务
70
+ nuwax-file-server restart
71
+
72
+ # 查看服务状态
73
+ nuwax-file-server status
74
+ ```
75
+
76
+ ### 高级选项
77
+
78
+ ```bash
79
+ # 指定端口启动
80
+ nuwax-file-server start --port 8080
81
+
82
+ # 指定配置文件
83
+ nuwax-file-server start --config /path/to/config.json
84
+
85
+ # 组合使用
86
+ nuwax-file-server start --env development --port 3000
87
+ ```
88
+
89
+ ### 使用 npm scripts
90
+
91
+ ```bash
92
+ # 启动服务
93
+ npm run cli:start
94
+ npm run cli:start:dev # 开发环境
95
+ npm run cli:start:prod # 生产环境
96
+ npm run cli:start:test # 测试环境
97
+
98
+ # 停止服务
99
+ npm run cli:stop
100
+
101
+ # 重启服务
102
+ npm run cli:restart
103
+
104
+ # 查看状态
105
+ npm run cli:status
106
+ ```
107
+
108
+ ## 环境变量配置
109
+
110
+ 完整的环境变量配置说明请参考:[环境变量配置文档](./docs/ENV.md)
111
+
112
+ ### 快速使用示例
113
+
114
+ ```bash
115
+ # 使用 env.production 默认配置(推荐)
116
+ nuwax-file-server start --env production --port 60000
117
+
118
+ # 自定义路径配置(根据实际需求删减)
119
+ nuwax-file-server start --env production --port 60000 \
120
+ PROJECT_SOURCE_DIR=/data/projects \
121
+ DIST_TARGET_DIR=/var/www/html \
122
+ UPLOAD_PROJECT_DIR=/data/uploads
123
+ ```
124
+
125
+ ### 核心路径变量
126
+
127
+ | 环境变量 | 说明 |
128
+ | ------------------------ | ------------------------------ |
129
+ | `INIT_PROJECT_DIR` | 初始化工程目录 |
130
+ | `UPLOAD_PROJECT_DIR` | 上传的项目压缩包路径 |
131
+ | `PROJECT_SOURCE_DIR` | 项目源文件路径 |
132
+ | `DIST_TARGET_DIR` | 构建产物目标目录(nginx 加载) |
133
+ | `LOG_BASE_DIR` | 日志基础目录 |
134
+ | `COMPUTER_WORKSPACE_DIR` | computer 工作目录 |
135
+ | `COMPUTER_LOG_DIR` | computer 日志目录 |
136
+
137
+ 完整配置项和场景示例请参阅:[环境变量配置文档](./docs/ENV.md)
138
+
139
+ ### 命令行覆盖
140
+
141
+ ```bash
142
+ # 端口优先级: 命令行 > 环境变量 > 默认值
143
+ nuwax-file-server start --env production --port 8080
144
+ ```
145
+
146
+ ## 健康检查端点
147
+
148
+ 服务提供 `/health` 端点用于健康检查和监控。
149
+
150
+ ### 请求
151
+
152
+ ```bash
153
+ curl http://localhost:60000/health
154
+ ```
155
+
156
+ ### 响应
157
+
158
+ ```json
159
+ {
160
+ "status": "ok",
161
+ "timestamp": 1738600000000,
162
+ "uptime": 3600,
163
+ "version": "1.0.0",
164
+ "platform": "darwin",
165
+ "nodeVersion": "v22.0.0",
166
+ "pid": 12345,
167
+ "memory": {
168
+ "heapUsed": 25.5,
169
+ "heapTotal": 50.0,
170
+ "rss": 100.0,
171
+ "external": 5.0
172
+ },
173
+ "env": "production"
174
+ }
175
+ ```
176
+
177
+ ### 响应字段说明
178
+
179
+ | 字段 | 类型 | 说明 |
180
+ | ----------- | ------ | --------------------------------- |
181
+ | status | string | 服务状态,ok 表示正常 |
182
+ | timestamp | number | 当前时间戳(毫秒) |
183
+ | uptime | number | 服务运行时间(秒) |
184
+ | version | string | 服务版本号 |
185
+ | platform | string | 操作系统平台 (darwin/linux/win32) |
186
+ | nodeVersion | string | Node.js 版本 |
187
+ | pid | number | 进程 ID |
188
+ | memory | object | 内存使用情况(MB) |
189
+ | env | string | 当前环境 |
190
+
191
+ ## 跨平台说明
192
+
193
+ ### Windows
194
+
195
+ - PID 文件存储在 `%TEMP%\nuwax-file-server\`
196
+ - 进程停止使用 `taskkill /F /PID` 命令
197
+ - 路径分隔符使用反斜杠 `\`
198
+
199
+ ### Linux/macOS
200
+
201
+ - PID 文件存储在 `/tmp/nuwax-file-server/`
202
+ - 进程停止使用 kill 信号 (SIGTERM/SIGKILL)
203
+ - 路径分隔符使用正斜杠 `/`
204
+
205
+ ### 通用
206
+
207
+ - 所有路径使用 `path.join()` 处理
208
+ - 使用 `os.tmpdir()` 获取临时目录
209
+ - 使用 `cross-spawn` 执行 shell 命令
210
+ - 使用 `tree-kill` 杀死进程树
211
+
212
+ ## pnpm 磁盘空间优化
213
+
214
+ 本项目自动为所有创建、上传或复制的项目注入优化的 `.npmrc` 配置文件,以优化 pnpm 的磁盘空间使用。
215
+
216
+ ### 自动优化
217
+
218
+ 在以下操作中,系统会自动为项目创建 `.npmrc` 配置文件:
219
+
220
+ - **创建项目** (`/create-project`)
221
+ - **上传项目** (`/upload-project`)
222
+ - **复制项目** (`/copy-project`)
223
+
224
+ ### 检查磁盘使用
225
+
226
+ ```bash
227
+ # 自动检测当前环境配置
228
+ npm run pnpm:check
229
+
230
+ # 或指定特定环境
231
+ npm run pnpm:check:dev # 开发环境
232
+ npm run pnpm:check:prod # 生产环境
233
+ npm run pnpm:check:test # 测试环境
234
+
235
+ # 或手动指定目录
236
+ bash scripts/pnpm-check.sh /path/to/projects
237
+ ```
238
+
239
+ ### 清理未使用的包
240
+
241
+ ```bash
242
+ # 立即清理
243
+ npm run pnpm:prune
244
+
245
+ # 查看日志的清理(推荐)
246
+ npm run pnpm:prune:log
247
+ ```
248
+
249
+ ### 定时清理(自动集成)
250
+
251
+ 定时任务已集成到主应用,随应用启动自动运行,通过环境变量配置:
252
+
253
+ ```yaml
254
+ # docker-compose.yml 或 .env 文件
255
+ environment:
256
+ PNPM_PRUNE_ENABLED: "true" # 启用定时清理(默认 true)
257
+ PNPM_PRUNE_SCHEDULE: "0 2 * * 0" # 每周日凌晨 2 点
258
+ PNPM_PRUNE_TIMEZONE: "Asia/Shanghai" # 时区(默认 Asia/Shanghai)
259
+ PNPM_PRUNE_RUN_ON_START: "false" # 启动时立即执行(默认 false)
260
+ ```
261
+
262
+ **常用时间配置:**
263
+
264
+ ```bash
265
+ "0 2 * * 0" # 每周日凌晨 2 点
266
+ "0 3 * * *" # 每天凌晨 3 点
267
+ "0 2 1 * *" # 每月 1 号凌晨 2 点
268
+ "0 */6 * * *" # 每 6 小时
269
+ ```
270
+
271
+ ### 预期效果
272
+
273
+ - 磁盘空间节省 50-70%(多项目共享依赖)
274
+ - 安装速度提升(使用国内镜像)
275
+ - 完全自动化,无需手动配置
276
+ - 定期清理,保持 store 整洁
277
+
278
+ ## 故障排除
279
+
280
+ ### 服务无法启动
281
+
282
+ 1. 检查端口是否被占用
283
+ 2. 检查日志文件权限
284
+ 3. 检查环境配置文件是否存在
285
+
286
+ ```bash
287
+ # 查看详细日志
288
+ nuwax-file-server start --env development
289
+ ```
290
+
291
+ ### 服务无法停止
292
+
293
+ ```bash
294
+ # 使用强制停止
295
+ nuwax-file-server stop --force
296
+
297
+ # 手动查找并停止进程
298
+ ps aux | grep nuwax-file-server
299
+ kill -9 <pid>
300
+ ```
301
+
302
+ ### 健康检查失败
303
+
304
+ 1. 检查服务是否正在运行
305
+ 2. 检查端口是否正确
306
+ 3. 检查防火墙设置
307
+
308
+ ```bash
309
+ # 查看服务状态
310
+ nuwax-file-server status
311
+
312
+ # 测试健康检查
313
+ curl http://localhost:60000/health
314
+ ```
315
+
316
+ ## 开发指南
317
+
318
+ ### 添加新命令
319
+
320
+ 在 `src/cli.js` 中使用 commander 定义新命令:
321
+
322
+ ```javascript
323
+ program
324
+ .command("newcommand")
325
+ .description("新命令描述")
326
+ .option("--option", "选项描述")
327
+ .action((options) => {
328
+ // 命令逻辑
329
+ });
330
+ ```
331
+
332
+ ### 添加新配置项
333
+
334
+ 1. 在 `env.development` / `env.production` / `env.test` 中添加环境变量
335
+ 2. 在 `config/index.js` 中读取配置
336
+ 3. 在文档中说明
337
+
338
+ ## 许可证
339
+
340
+ ISC
package/dist/cli.js ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import{Command as O}from"commander";import v from"path";import S from"os";import g from"fs-extra";import{spawn as y}from"cross-spawn";import{fileURLToPath as k}from"url";import{createRequire as N}from"module";var T=v.dirname(k(import.meta.url)),B=N(import.meta.url),P="1.0.1",c=new O,f={name:"nuwax-file-server",pidDir:v.join(S.tmpdir(),"nuwax-file-server"),logDir:v.join(S.tmpdir(),"nuwax-file-server","logs"),pidFile:v.join(S.tmpdir(),"nuwax-file-server","server.pid")},d={reset:"\x1B[0m",red:"\x1B[31m",green:"\x1B[32m",yellow:"\x1B[33m",blue:"\x1B[34m",cyan:"\x1B[36m"};function p(o,e="reset"){console.log(`${d[e]}${o}${d.reset}`)}function a(o){console.error(`${d.red}ERROR: ${o}${d.reset}`)}function l(o){console.log(`${d.green}${o}${d.reset}`)}function s(o){console.log(`${d.blue}${o}${d.reset}`)}function I(){return f.pidFile}function D(){try{let o=I();if(g.existsSync(o)){let e=g.readFileSync(o,"utf8");return JSON.parse(e)}}catch{}return null}function b(o){let e=v.dirname(I());g.ensureDirSync(e),g.writeFileSync(I(),JSON.stringify(o,null,2))}function h(){let o=I();g.existsSync(o)&&g.removeSync(o)}function x(o){try{return process.kill(o,0),!0}catch(e){return e.code==="ESRCH"||e.code==="EPERM",!1}}async function R(o,e=!1){return new Promise(n=>{if(process.platform==="win32"){let r=e?["/F","/PID",String(o)]:["/PID",String(o)],t=y("taskkill",r,{stdio:"pipe"});t.on("error",u=>{a(`\u505C\u6B62\u8FDB\u7A0B\u5931\u8D25: ${u.message}`),n(!1)}),t.on("close",u=>{u===0?(l(`\u8FDB\u7A0B ${o} \u5DF2\u505C\u6B62`),n(!0)):e?y("taskkill",["/F","/PID",String(o)],{stdio:"pipe"}).on("close",$=>{n($===0)}):n(!1)})}else try{process.kill(-o,e?"SIGKILL":"SIGTERM"),l(`\u8FDB\u7A0B\u7EC4 ${o} \u5DF2\u505C\u6B62`),n(!0)}catch(r){if(r.code==="ESRCH")l(`\u8FDB\u7A0B ${o} \u4E0D\u5B58\u5728\uFF0C\u89C6\u4E3A\u5DF2\u505C\u6B62`),n(!0);else try{process.kill(o,e?"SIGKILL":"SIGTERM"),l(`\u8FDB\u7A0B ${o} \u5DF2\u505C\u6B62`),n(!0)}catch(t){t.code==="ESRCH"?(l(`\u8FDB\u7A0B ${o} \u4E0D\u5B58\u5728\uFF0C\u89C6\u4E3A\u5DF2\u505C\u6B62`),n(!0)):(a(`\u505C\u6B62\u8FDB\u7A0B\u5931\u8D25: ${t.message}`),n(!1))}}})}async function _(o){let{env:e,port:n,config:w}=o;s(`\u542F\u52A8 ${f.name} \u670D\u52A1...`);let r=D();r&&x(r.pid)&&(a(`\u670D\u52A1\u5DF2\u5728\u8FD0\u884C\u4E2D (PID: ${r.pid})`),s("\u8BF7\u4F7F\u7528 'nuwax-file-server stop' \u505C\u6B62\u73B0\u6709\u670D\u52A1\u540E\u518D\u8BD5"),process.exit(1));let t=e||"production";process.env.NODE_ENV=t,s(`\u4F7F\u7528\u73AF\u5883: ${t}`),n&&(process.env.PORT=n,s(`\u4F7F\u7528\u7AEF\u53E3: ${n}`)),w&&(process.env.CONFIG_FILE=w,s(`\u4F7F\u7528\u914D\u7F6E\u6587\u4EF6: ${w}`)),g.ensureDirSync(f.logDir);let u=v.join(T,"..","scripts","start-cli.js"),i=y("node",[u],{env:{...process.env,NODE_ENV:t},stdio:["pipe","pipe","pipe"],detached:!0,cwd:process.cwd(),windowsHide:!0});i.stdout.on("data",m=>{process.stdout.write(m)}),i.stderr.on("data",m=>{process.stderr.write(m)}),i.on("error",m=>{a(`\u542F\u52A8\u670D\u52A1\u5931\u8D25: ${m.message}`),process.exit(1)}),await new Promise(m=>setTimeout(m,2e3)),x(i.pid)||(a("\u670D\u52A1\u542F\u52A8\u5931\u8D25"),process.exit(1));let $={pid:i.pid,startedAt:new Date().toISOString(),env:e||"production",port:n||process.env.PORT||"60000",version:P,platform:process.platform};b($),i.unref(),l(`\u670D\u52A1\u5DF2\u542F\u52A8 (PID: ${i.pid})`),p(`\u670D\u52A1\u8FD0\u884C\u5728: http://localhost:${$.port}`,"cyan"),p(`\u73AF\u5883: ${$.env}`,"cyan"),p(`\u5E73\u53F0: ${$.platform}`,"cyan"),p(`PID \u6587\u4EF6: ${I()}`,"cyan")}async function F(o){let{force:e}=o;s(`\u505C\u6B62 ${f.name} \u670D\u52A1...`);let n=D();n||(a("\u672A\u627E\u5230\u8FD0\u884C\u4E2D\u7684\u670D\u52A1"),s("\u670D\u52A1\u53EF\u80FD\u672A\u542F\u52A8\u6216 PID \u6587\u4EF6\u5DF2\u4E22\u5931"),process.exit(0)),x(n.pid)||(s("\u670D\u52A1\u8FDB\u7A0B\u5DF2\u505C\u6B62\uFF0C\u6E05\u7406 PID \u6587\u4EF6..."),h(),l("\u670D\u52A1\u5DF2\u505C\u6B62"),process.exit(0)),await R(n.pid,e)&&(await new Promise(t=>setTimeout(t,1e3)),x(n.pid)||(h(),l("\u670D\u52A1\u5DF2\u505C\u6B62"),process.exit(0))),e&&(a("\u5F3A\u5236\u505C\u6B62\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u505C\u6B62\u8FDB\u7A0B"),process.exit(1)),s("\u5C1D\u8BD5\u5F3A\u5236\u505C\u6B62..."),await R(n.pid,!0)&&(await new Promise(t=>setTimeout(t,1e3)),x(n.pid)||(h(),l("\u670D\u52A1\u5DF2\u5F3A\u5236\u505C\u6B62"),process.exit(0))),a("\u505C\u6B62\u670D\u52A1\u5931\u8D25"),process.exit(1)}async function C(o){p(`\u91CD\u542F ${f.name} \u670D\u52A1...`,"yellow"),s("\u505C\u6B62\u73B0\u6709\u670D\u52A1...");try{await F({force:!1})}catch{s("\u670D\u52A1\u672A\u8FD0\u884C\u6216\u5DF2\u505C\u6B62")}await new Promise(e=>setTimeout(e,2e3)),s("\u542F\u52A8\u670D\u52A1..."),await _(o),l("\u670D\u52A1\u5DF2\u91CD\u542F")}function L(){s(`${f.name} \u670D\u52A1\u72B6\u6001:`);let o=D();o||(p("\u670D\u52A1\u672A\u8FD0\u884C","yellow"),process.exit(0));let e=x(o.pid);if(console.log(""),console.log(` \u670D\u52A1\u540D\u79F0: ${f.name}`),console.log(` \u8FD0\u884C\u72B6\u6001: ${e?"\u8FD0\u884C\u4E2D":"\u5DF2\u505C\u6B62"}`),console.log(` \u8FDB\u7A0B ID: ${o.pid}`),console.log(` \u73AF\u5883: ${o.env||"production"}`),console.log(` \u7AEF\u53E3: ${o.port||"60000"}`),console.log(` \u7248\u672C: ${o.version||P}`),console.log(` \u5E73\u53F0: ${o.platform||process.platform}`),console.log(` \u542F\u52A8\u65F6\u95F4: ${o.startedAt||"\u672A\u77E5"}`),o.startedAt){let n=new Date(o.startedAt),r=Math.floor((new Date-n)/1e3),t=Math.floor(r/3600),u=Math.floor(r%3600/60),i=r%60;console.log(` \u8FD0\u884C\u65F6\u95F4: ${t}\u5C0F\u65F6 ${u}\u5206 ${i}\u79D2`)}console.log(` PID \u6587\u4EF6: ${I()}`),console.log(""),e?p("\u670D\u52A1\u8FD0\u884C\u6B63\u5E38","green"):(p("\u8B66\u544A: \u670D\u52A1\u8FDB\u7A0B\u4E0D\u5B58\u5728\uFF0C\u4F46 PID \u6587\u4EF6\u4ECD\u5B58\u5728","yellow"),s("\u5EFA\u8BAE\u6267\u884C stop \u547D\u4EE4\u6E05\u7406"))}function E(){console.log(""),console.log(` ${f.name} v${P}`),console.log(""),console.log(" \u7528\u6CD5:"),console.log(" nuwax-file-server <\u547D\u4EE4> [\u9009\u9879]"),console.log(""),console.log(" \u547D\u4EE4:"),console.log(" start \u542F\u52A8\u670D\u52A1"),console.log(" stop \u505C\u6B62\u670D\u52A1"),console.log(" restart \u91CD\u542F\u670D\u52A1"),console.log(" status \u67E5\u770B\u670D\u52A1\u72B6\u6001"),console.log(" help \u663E\u793A\u5E2E\u52A9\u4FE1\u606F"),console.log(""),console.log(" \u9009\u9879:"),console.log(" --env \u6307\u5B9A\u73AF\u5883 (development|production|test)"),console.log(" --port \u6307\u5B9A\u7AEF\u53E3\u53F7"),console.log(" --config \u6307\u5B9A\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84"),console.log(" --force \u5F3A\u5236\u505C\u6B62\u670D\u52A1 (\u4EC5 stop \u547D\u4EE4)"),console.log(" --help \u663E\u793A\u5E2E\u52A9\u4FE1\u606F"),console.log(" --version \u663E\u793A\u7248\u672C\u4FE1\u606F"),console.log(""),console.log(" \u793A\u4F8B:"),console.log(" nuwax-file-server start"),console.log(" nuwax-file-server start --env development"),console.log(" nuwax-file-server start --port 8080"),console.log(" nuwax-file-server stop --force"),console.log(" nuwax-file-server restart --env production"),console.log("")}function j(){c.name("nuwax-file-server"),c.version(P),c.description("\u8DE8\u5E73\u53F0\u7684\u6587\u4EF6\u670D\u52A1\u90E8\u7F72\u5DE5\u5177"),c.command("start").description("\u542F\u52A8\u670D\u52A1").option("--env <environment>","\u6307\u5B9A\u73AF\u5883 (development|production|test)","production").option("--port <port>","\u6307\u5B9A\u7AEF\u53E3\u53F7").option("--config <path>","\u6307\u5B9A\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action(_),c.command("stop").description("\u505C\u6B62\u670D\u52A1").option("--force","\u5F3A\u5236\u505C\u6B62\u670D\u52A1").action(F),c.command("restart").description("\u91CD\u542F\u670D\u52A1").option("--env <environment>","\u6307\u5B9A\u73AF\u5883 (development|production|test)","production").option("--port <port>","\u6307\u5B9A\u7AEF\u53E3\u53F7").option("--config <path>","\u6307\u5B9A\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action(C),c.command("status").description("\u67E5\u770B\u670D\u52A1\u72B6\u6001").action(L),c.command("help").description("\u663E\u793A\u5E2E\u52A9\u4FE1\u606F").action(()=>{E()}),c.parse(process.argv),c.args.length===0&&E()}j();
@@ -0,0 +1 @@
1
+ import _ from"path";import r from"fs";import o from"os";import{fileURLToPath as L}from"url";import I from"dotenv";const T=_.dirname(L(import.meta.url));I.config();function O(){const E=process.env.LOG_BASE_DIR,e=_.join(o.tmpdir(),"nuwax-file-server","project_logs");if(!E)return e;try{return r.mkdirSync(E,{recursive:!0}),E}catch{try{r.mkdirSync(e,{recursive:!0})}catch{}return e}}const n=process.env.NODE_ENV||"development";function R(E){const e=_.join(T,"..",`env.${E}`);if(r.existsSync(e))I.config({path:e}),console.log(`\u5DF2\u52A0\u8F7D\u73AF\u5883\u914D\u7F6E\u6587\u4EF6: env.${E}`);else{const s=`\u73AF\u5883\u914D\u7F6E\u6587\u4EF6 env.${E} \u4E0D\u5B58\u5728\uFF0C\u8BF7\u521B\u5EFA\u5BF9\u5E94\u7684\u73AF\u5883\u914D\u7F6E\u6587\u4EF6\u540E\u91CD\u8BD5`;throw console.error(s),new Error(s)}}R(n);const p={NODE_ENV:n,PORT:parseInt(process.env.PORT),INIT_PROJECT_NAME:process.env.INIT_PROJECT_NAME,INIT_PROJECT_DIR:process.env.INIT_PROJECT_DIR,PROJECT_SOURCE_DIR:process.env.PROJECT_SOURCE_DIR,DIST_TARGET_DIR:process.env.DIST_TARGET_DIR,UPLOAD_PROJECT_DIR:process.env.UPLOAD_PROJECT_DIR,MAX_BUILD_CONCURRENCY:process.env.MAX_BUILD_CONCURRENCY?parseInt(process.env.MAX_BUILD_CONCURRENCY,10):void 0,MAX_INLINE_FILE_SIZE_BYTES:process.env.MAX_INLINE_FILE_SIZE_BYTES?parseInt(process.env.MAX_INLINE_FILE_SIZE_BYTES,10):void 0,UPLOAD_MAX_FILE_SIZE_BYTES:process.env.UPLOAD_MAX_FILE_SIZE_BYTES?parseInt(process.env.UPLOAD_MAX_FILE_SIZE_BYTES,10):void 0,UPLOAD_ALLOWED_EXTENSIONS:process.env.UPLOAD_ALLOWED_EXTENSIONS?process.env.UPLOAD_ALLOWED_EXTENSIONS.split(",").map(E=>E.trim().toLowerCase()).filter(Boolean):[],UPLOAD_SINGLE_FILE_SIZE_BYTES:process.env.UPLOAD_SINGLE_FILE_SIZE_BYTES?parseInt(process.env.UPLOAD_SINGLE_FILE_SIZE_BYTES,10):void 0,REQUEST_BODY_LIMIT:process.env.REQUEST_BODY_LIMIT,TRAVERSE_EXCLUDE_DIRS:process.env.TRAVERSE_EXCLUDE_DIRS?process.env.TRAVERSE_EXCLUDE_DIRS.split(",").map(E=>E.trim()).filter(Boolean):[],BACKUP_TRAVERSE_EXCLUDE_FILES:process.env.BACKUP_TRAVERSE_EXCLUDE_FILES?process.env.BACKUP_TRAVERSE_EXCLUDE_FILES.split(",").map(E=>E.trim()).filter(Boolean):[],CONTENT_TRAVERSE_EXCLUDE_FILES:process.env.CONTENT_TRAVERSE_EXCLUDE_FILES?process.env.CONTENT_TRAVERSE_EXCLUDE_FILES.split(",").map(E=>E.trim()).filter(Boolean):[],INLINE_IMAGE_EXTENSIONS:process.env.INLINE_IMAGE_EXTENSIONS?process.env.INLINE_IMAGE_EXTENSIONS.split(",").map(E=>E.trim().toLowerCase()).filter(Boolean):[],TOP_LEVEL_NOISE_PATTERNS:process.env.TOP_LEVEL_NOISE_PATTERNS?process.env.TOP_LEVEL_NOISE_PATTERNS.split(",").map(E=>E.trim()).filter(Boolean):[],LOG_BASE_DIR:O(),LOG_LEVEL:process.env.LOG_LEVEL?process.env.LOG_LEVEL.toLowerCase():void 0,LOG_PREFIX_API:process.env.LOG_PREFIX_API,LOG_PREFIX_BUILD:process.env.LOG_PREFIX_BUILD,LOG_CONSOLE_ENABLED:typeof process.env.LOG_CONSOLE_ENABLED=="string"?process.env.LOG_CONSOLE_ENABLED.toLowerCase()==="true":void 0,LOG_CACHE_ENABLED:typeof process.env.LOG_CACHE_ENABLED=="string"?process.env.LOG_CACHE_ENABLED.toLowerCase()==="true":void 0,LOG_CACHE_DURATION:process.env.LOG_CACHE_DURATION?parseInt(process.env.LOG_CACHE_DURATION,10):void 0,LOG_CACHE_MAX_ENTRIES:process.env.LOG_CACHE_MAX_ENTRIES?parseInt(process.env.LOG_CACHE_MAX_ENTRIES,10):void 0,LOG_CACHE_MAX_FILE_SIZE:process.env.LOG_CACHE_MAX_FILE_SIZE?parseInt(process.env.LOG_CACHE_MAX_FILE_SIZE,10):void 0,DEV_SERVER_PORT_TIMEOUT:process.env.DEV_SERVER_PORT_TIMEOUT?parseInt(process.env.DEV_SERVER_PORT_TIMEOUT,10):void 0,DEV_SERVER_STOP_TIMEOUT:process.env.DEV_SERVER_STOP_TIMEOUT?parseInt(process.env.DEV_SERVER_STOP_TIMEOUT,10):void 0,DEV_SERVER_STOP_CHECK_INTERVAL:process.env.DEV_SERVER_STOP_CHECK_INTERVAL?parseInt(process.env.DEV_SERVER_STOP_CHECK_INTERVAL,10):void 0,DEV_SERVER_STOP_MAX_ATTEMPTS:process.env.DEV_SERVER_STOP_MAX_ATTEMPTS?parseInt(process.env.DEV_SERVER_STOP_MAX_ATTEMPTS,10):void 0,COMPUTER_WORKSPACE_DIR:process.env.COMPUTER_WORKSPACE_DIR,COMPUTER_LOG_DIR:process.env.COMPUTER_LOG_DIR,CLI_SERVICE_NAME:"nuwax-file-server",CLI_PID_DIR:process.env.CLI_PID_DIR||(process.platform==="win32"?_.join(process.env.TEMP||"","nuwax-file-server"):_.join("/tmp","nuwax-file-server")),CLI_PID_FILE:"server.pid",CLI_STOP_TIMEOUT:process.env.CLI_STOP_TIMEOUT?parseInt(process.env.CLI_STOP_TIMEOUT,10):3e4,CLI_CHECK_INTERVAL:process.env.CLI_CHECK_INTERVAL?parseInt(process.env.CLI_CHECK_INTERVAL,10):500,CLI_LOG_DIR:process.env.CLI_LOG_DIR||(process.platform==="win32"?_.join(process.env.TEMP||"","nuwax-file-server","logs"):_.join("/tmp","nuwax-file-server","logs")),CLI_IS_WINDOWS:process.platform==="win32"};export default p;
@@ -0,0 +1 @@
1
+ export default{};
@@ -0,0 +1 @@
1
+ export default{};
@@ -0,0 +1 @@
1
+ export default{};
@@ -0,0 +1 @@
1
+ import t from"swagger-jsdoc";import e from"../../config/index.js";const s={definition:{openapi:"3.0.0",info:{title:"Application Builder API",version:"1.0.0",description:"\u5E94\u7528\u6784\u5EFA\u7CFB\u7EDF API \u6587\u6863",contact:{name:"API Support"}},servers:[{url:`http://localhost:${e.PORT}`,description:`${e.NODE_ENV} \u73AF\u5883`}],tags:[{name:"Build",description:"\u6784\u5EFA\u76F8\u5173\u63A5\u53E3"},{name:"Project",description:"\u9879\u76EE\u7BA1\u7406\u63A5\u53E3"},{name:"Code",description:"\u4EE3\u7801\u63D0\u4EA4\u63A5\u53E3"}],components:{schemas:{Error:{type:"object",properties:{success:{type:"boolean",example:!1},error:{type:"object",properties:{type:{type:"string",example:"VALIDATION_ERROR"},message:{type:"string",example:"\u9879\u76EEID\u4E0D\u80FD\u4E3A\u7A7A"},timestamp:{type:"string",format:"date-time"},requestId:{type:"string"},details:{type:"object"}}}}},Success:{type:"object",properties:{success:{type:"boolean",example:!0},message:{type:"string"}}}}}},apis:["./src/config/swagger/*.js"]},o=t(s);export default o;
@@ -0,0 +1,85 @@
1
+ # 开发环境配置
2
+ NODE_ENV=development
3
+ PORT=60000
4
+
5
+ # 日志配置
6
+ LOG_BASE_DIR=/Users/leilinzhou/Work/custom_page/project_logs
7
+ LOG_LEVEL=debug
8
+ LOG_CONSOLE_ENABLED=true
9
+ LOG_PREFIX_API=api
10
+ LOG_PREFIX_BUILD=build
11
+
12
+ # 系统内置初始化工程
13
+ INIT_PROJECT_NAME=react-vite-template
14
+ INIT_PROJECT_DIR=/Users/leilinzhou/Work/custom_page/project_init
15
+
16
+ #上传的项目压缩包所在路径
17
+ UPLOAD_PROJECT_DIR=/Users/leilinzhou/Work/custom_page/project_zips
18
+
19
+ #项目源文件所在路径
20
+ PROJECT_SOURCE_DIR=/Users/leilinzhou/Work/custom_page/project_workspace
21
+
22
+ #dist拷贝到的目标目录,nginx加载
23
+ DIST_TARGET_DIR=/Users/leilinzhou/Work/custom_page/project_nginx
24
+
25
+ #build最大并发数
26
+ MAX_BUILD_CONCURRENCY=20
27
+
28
+ #单文件大小限制,如果超大就不返回内容(用于判断查询文件内容时是否超大;1M)
29
+ MAX_INLINE_FILE_SIZE_BYTES=1048576
30
+
31
+ # 上传限制(用户上传压缩包;1000M)
32
+ UPLOAD_MAX_FILE_SIZE_BYTES=1048576000
33
+ UPLOAD_ALLOWED_EXTENSIONS=.zip
34
+
35
+ # 单文件上传大小限制(用于上传工程单文件;1000M)
36
+ UPLOAD_SINGLE_FILE_SIZE_BYTES=1048576000
37
+
38
+ # 请求体大小限制(Express 格式)
39
+ REQUEST_BODY_LIMIT=2000mb
40
+
41
+ # 目录遍历排除(返回内容、归档等)
42
+ TRAVERSE_EXCLUDE_DIRS=dist,node_modules,.pnpm-store,__MACOSX,.attachments
43
+
44
+ # 归档遍历排除文件
45
+ BACKUP_TRAVERSE_EXCLUDE_FILES=pnpm-lock.yaml,yarn.lock,package-lock.json
46
+
47
+ # 返回项目内容遍历排除文件
48
+ CONTENT_TRAVERSE_EXCLUDE_FILES=AGENT.md,AGENTS.md,CLAUDE.md,pnpm-lock.yaml,yarn.lock,package-lock.json
49
+
50
+ # 允许内联返回内容的图片扩展名
51
+ INLINE_IMAGE_EXTENSIONS=.png,.jpg,.jpeg,.gif,.bmp,.svg,.ico,.webp,.avif
52
+
53
+ # 顶层噪声条目(用于解压后扁平化判断)
54
+ TOP_LEVEL_NOISE_PATTERNS=__MACOSX,Thumbs.db,node_modules,.pnpm-store,.attachments
55
+
56
+ # 开发服务器配置
57
+ # 从日志中解析实际端口,最多等待5秒
58
+ DEV_SERVER_PORT_TIMEOUT=5000
59
+ # 等待进程真正停止,最多等待5秒
60
+ DEV_SERVER_STOP_TIMEOUT=5000
61
+ # 等待进程真正停止,每100ms检查一次
62
+ DEV_SERVER_STOP_CHECK_INTERVAL=100
63
+ # 等待进程真正停止,最大重试次数
64
+ DEV_SERVER_STOP_MAX_ATTEMPTS=50
65
+
66
+ # pnpm清理配置
67
+ PNPM_PRUNE_ENABLED=true
68
+ PNPM_PRUNE_SCHEDULE=0 2 * * *
69
+ PNPM_PRUNE_TIMEZONE=Asia/Shanghai
70
+ PNPM_PRUNE_RUN_ON_START=false
71
+
72
+ # 日志缓存配置
73
+ # 是否启用日志缓存
74
+ LOG_CACHE_ENABLED=true
75
+ # 缓存过期时间(毫秒)默认3分钟
76
+ LOG_CACHE_DURATION=180000
77
+ # 最大缓存项目数量
78
+ LOG_CACHE_MAX_ENTRIES=100
79
+ # 最大缓存文件大小(字节)默认2MB,超过此大小的文件不缓存
80
+ LOG_CACHE_MAX_FILE_SIZE=2097152
81
+
82
+ # computer 工作目录
83
+ COMPUTER_WORKSPACE_DIR=/Users/leilinzhou/Work/computer-project-workspace
84
+ # computer 日志目录
85
+ COMPUTER_LOG_DIR=/Users/leilinzhou/Work/custom_page/computer_logs
@@ -0,0 +1 @@
1
+ import l from"express";import{startDevServer as h}from"../utils/build/startDevUtils.js";import{restartDevServer as u}from"../utils/build/restartDevUtils.js";import{keepAliveDevServer as m}from"../utils/build/keepAliveDevUtils.js";import{stopDevServer as f}from"../utils/build/stopDevUtils.js";import{buildProject as g}from"../utils/build/buildProjectUtils.js";import{listRunningProcesses as w}from"../utils/build/processManager.js";import I from"../utils/error/buildErrorParser.js";import{getDevLog as j}from"../utils/log/getDevLogUtils.js";import c from"../utils/log/logCacheManager.js";import y from"../utils/buildArg/portPool.js";import{ValidationError as s,asyncHandler as i}from"../utils/error/errorHandler.js";const p=l.Router(),v=[{path:"/start-dev",method:"get",handler:i(async(t,r)=>{const e=t.query.projectId;if(!e)throw new s("\u9879\u76EEID\u4E0D\u80FD\u4E3A\u7A7A",{field:"projectId"});t.setTimeout(6e5),r.setTimeout(6e5);const o=await h(t,String(e));r.json(o)})},{path:"/keep-alive",method:"get",handler:i(async(t,r)=>{const e=t.query.projectId,o=t.query.pid,n=t.query.port,a=t.query.basePath;if(!e)throw new s("\u9879\u76EEID\u4E0D\u80FD\u4E3A\u7A7A",{field:"projectId"});if(!o)throw new s("\u8FDB\u7A0BID\u4E0D\u80FD\u4E3A\u7A7A",{field:"pid"});if(!n)throw new s("\u7AEF\u53E3\u53F7\u4E0D\u80FD\u4E3A\u7A7A",{field:"port"});if(!a)throw new s("basePath\u4E0D\u80FD\u4E3A\u7A7A",{field:"basePath"});const d=await m(t,String(e),o,n,a);r.json(d)})},{path:"/build",method:"get",handler:i(async(t,r)=>{const e=t.query.projectId;if(!e)throw new s("\u9879\u76EEID\u4E0D\u80FD\u4E3A\u7A7A",{field:"projectId"});t.setTimeout(6e5),r.setTimeout(6e5);const o=await g(t,String(e));r.json(o)})},{path:"/stop-dev",method:"get",handler:i(async(t,r)=>{const e=t.query.projectId,o=t.query.pid;if(!e)throw new s("\u9879\u76EEID\u4E0D\u80FD\u4E3A\u7A7A",{field:"projectId"});if(!o)throw new s("\u8FDB\u7A0BID\u4E0D\u80FD\u4E3A\u7A7A",{field:"pid"});const n=await f(t,String(e),o,{strict:!0});r.json(n)})},{path:"/restart-dev",method:"get",handler:i(async(t,r)=>{const e=t.query.projectId;if(!e)throw new s("\u9879\u76EEID\u4E0D\u80FD\u4E3A\u7A7A",{field:"projectId"});t.setTimeout(6e5),r.setTimeout(6e5);const o=await u(t,String(e));r.json(o)})},{path:"/list-dev",method:"get",handler:(t,r)=>{const o={success:!0,list:w()};r.json(o)}},{path:"/parse-build-error",method:"post",handler:i(async(t,r)=>{const{projectId:e,errorMessage:o}=t.body;if(!e)throw new s("\u9879\u76EEID\u4E0D\u80FD\u4E3A\u7A7A",{field:"projectId"});if(!o)throw new s("\u9519\u8BEF\u6D88\u606F\u4E0D\u80FD\u4E3A\u7A7A",{field:"errorMessage"});const a=new I().parseBuildError(o,String(e));r.json({success:!0,message:a})})},{path:"/get-dev-log",method:"get",handler:i(async(t,r)=>{const e=t.query.projectId,o=t.query.startIndex,n=t.query.logType||"temp";if(!e)throw new s("\u9879\u76EEID\u4E0D\u80FD\u4E3A\u7A7A",{field:"projectId"});const a=o?parseInt(o,10):1;if(isNaN(a)||a<1)throw new s("\u8D77\u59CB\u884C\u53F7\u5FC5\u987B\u662F\u6B63\u6574\u6570\uFF08\u4ECE1\u5F00\u59CB\uFF09",{field:"startIndex",value:o});const d=await j(String(e),a,n);r.json(d)})},{path:"/get-log-cache-stats",method:"get",handler:(t,r)=>{const e=c.getStats();r.json({success:!0,message:"\u83B7\u53D6\u65E5\u5FD7\u7F13\u5B58\u7EDF\u8BA1\u6210\u529F",stats:e})}},{path:"/clear-all-log-cache",method:"get",handler:(t,r)=>{c.clear(),r.json({success:!0,message:"\u6240\u6709\u65E5\u5FD7\u7F13\u5B58\u5DF2\u6E05\u7406"})}},{path:"/port-pool-status",method:"get",handler:(t,r)=>{const e=y.getStatus();r.json({success:!0,message:"\u83B7\u53D6\u7AEF\u53E3\u6C60\u72B6\u6001\u6210\u529F",...e})}}];v.forEach(t=>{p[t.method](t.path,t.handler)});export default p;
@@ -0,0 +1 @@
1
+ import g from"express";import p from"multer";import{asyncHandler as c,ValidationError as s}from"../utils/error/errorHandler.js";import m from"../service/codeService.js";import{log as d}from"../utils/log/logUtils.js";import w from"../../config/index.js";const h=g.Router(),y=p({storage:p.memoryStorage(),limits:{fileSize:w.UPLOAD_SINGLE_FILE_SIZE_BYTES}}),I=[{path:"/specified-files-update",method:"post",handler:c(async(o,a)=>{const{projectId:t,codeVersion:r,files:e}=o.body||{};d(t,"INFO","\u90E8\u5206\u6587\u4EF6\u66F4\u65B0",{projectId:t,codeVersion:r,filesCount:e?e.length:0}),e&&Array.isArray(e)&&e.forEach(i=>{if(i&&typeof i.contents=="string"&&i.contents)try{i.contents=decodeURIComponent(i.contents)}catch(f){d(t,"WARN","\u89E3\u7801\u6587\u4EF6\u5185\u5BB9\u5931\u8D25",{fileName:i.path,error:f.message})}});const n=await m.specifiedFilesUpdate(String(t),String(r),e,o);a.status(200).json(n)})},{path:"/all-files-update",method:"post",handler:c(async(o,a)=>{const{projectId:t,codeVersion:r,files:e,basePath:n,pid:i}=o.body||{};if(d(t,"INFO","\u63D0\u4EA4\u6587\u4EF6",{projectId:t,codeVersion:r,basePath:n,pid:i}),!t)throw new s("\u9879\u76EEID\u4E0D\u80FD\u4E3A\u7A7A",{field:"projectId"});if(r==null)throw new s("codeVersion\u4E0D\u80FD\u4E3A\u7A7A",{field:"codeVersion"});if(!Array.isArray(e))throw new s("files\u5FC5\u987B\u662F\u6570\u7EC4",{field:"files"});e&&Array.isArray(e)&&e.forEach(l=>{if(l&&typeof l.contents=="string"&&l.contents)try{l.contents=decodeURIComponent(l.contents)}catch(u){d(t,"WARN","\u89E3\u7801\u6587\u4EF6\u5185\u5BB9\u5931\u8D25",{fileName:l.name,error:u.message})}});const f=await m.allFilesUpdate(String(t),String(r),e,o);a.status(200).json(f)})},{path:"/upload-single-file",method:"post",middleware:y.single("file"),handler:c(async(o,a)=>{const{projectId:t,codeVersion:r,filePath:e}=o.body||{},n=o.file;if(d(t,"INFO","\u4E0A\u4F20\u5355\u4E2A\u6587\u4EF6",{projectId:t,codeVersion:r,filePath:e}),!t)throw new s("\u9879\u76EEID\u4E0D\u80FD\u4E3A\u7A7A",{field:"projectId"});if(r==null)throw new s("codeVersion\u4E0D\u80FD\u4E3A\u7A7A",{field:"codeVersion"});if(!n)throw new s("\u6587\u4EF6\u4E0D\u80FD\u4E3A\u7A7A",{field:"file"});if(!e||typeof e!="string")throw new s("\u6587\u4EF6\u8DEF\u5F84\u4E0D\u80FD\u4E3A\u7A7A",{field:"filePath"});d(t,"INFO","\u63A5\u6536\u5230\u7684\u6587\u4EF6\u4FE1\u606F",{originalname:n.originalname,mimetype:n.mimetype,size:n.size,bufferLength:n.buffer?n.buffer.length:0,bufferIsBuffer:Buffer.isBuffer(n.buffer)});const i={buffer:n.buffer,originalname:n.originalname,mimetype:n.mimetype,size:n.size},f=await m.uploadSingleFile(String(t),String(r),i,e,o);a.status(200).json(f)})},{path:"/rollback-version",method:"post",handler:c(async(o,a)=>{const{projectId:t,codeVersion:r,rollbackTo:e}=o.body||{};if(d(t,"INFO","\u56DE\u6EDA\u7248\u672C",{projectId:t,codeVersion:r,rollbackTo:e}),!t)throw new s("\u9879\u76EEID\u4E0D\u80FD\u4E3A\u7A7A",{field:"projectId"});if(r==null)throw new s("codeVersion\u4E0D\u80FD\u4E3A\u7A7A",{field:"codeVersion"});if(e==null)throw new s("rollbackTo\u4E0D\u80FD\u4E3A\u7A7A",{field:"rollbackTo"});const n=await m.rollbackVersion(String(t),String(r),String(e),o);a.status(200).json(n)})}];I.forEach(o=>{o.middleware?h[o.method](o.path,o.middleware,o.handler):h[o.method](o.path,o.handler)});export default h;
@@ -0,0 +1 @@
1
+ import $ from"express";import g from"multer";import d from"fs";import y from"path";import{ValidationError as E,asyncHandler as f}from"../utils/error/errorHandler.js";import{log as m}from"../utils/log/logUtils.js";import w from"../../config/index.js";import{createWorkspace as S}from"../utils/computer/computerUtils.js";import{getFileList as A,updateFiles as z,uploadFile as N,uploadFiles as P,downloadAllFiles as R}from"../utils/computer/computerFileUtils.js";const h=$.Router(),I=g({storage:g.diskStorage({destination:(e,a,s)=>{try{const t=w.COMPUTER_WORKSPACE_DIR;if(!t)return s(new Error("COMPUTER_WORKSPACE_DIR \u672A\u914D\u7F6E\uFF0C\u65E0\u6CD5\u786E\u5B9A\u4E0A\u4F20\u4E34\u65F6\u76EE\u5F55"));const o=e.body?.userId||"unknown",n=e.body?.cId||"unknown",i=y.join(t,String(o),String(n),".tmp");d.existsSync(i)||d.mkdirSync(i,{recursive:!0}),s(null,i)}catch(t){s(t)}},filename:(e,a,s)=>{const t=y.extname(a.originalname)||".zip",o=y.basename(a.originalname,t),n=`${Date.now()}_${Math.round(Math.random()*1e6)}`;s(null,`${o}_${n}${t}`)}}),limits:{fileSize:w.UPLOAD_MAX_FILE_SIZE_BYTES}});function b(e,a,s,t){if(e&&e.name==="MulterError"||e&&(e.name==="ValidationError"||e instanceof E))return t(e);t(e)}const C=[{path:"/create-workspace",method:"post",middleware:I.single("file"),handler:f(async(e,a)=>{const{userId:s,cId:t}=e.body||{},o=e.file||null,n=`computer:${s}:${t}`;m(n,"INFO","\u521B\u5EFA\u5DE5\u4F5C\u7A7A\u95F4\u8BF7\u6C42",{userId:s,cId:t,hasFile:!!o,fileName:o?.originalname,fileSize:o?.size});const i=await S(s,t,o);a.status(200).json({success:!0,...i})})},{path:"/get-file-list",method:"get",handler:f(async(e,a)=>{const{userId:s,cId:t,proxyPath:o}=e.query,n=await A(s,t,o);a.status(200).json({success:!0,...n})})},{path:"/files-update",method:"post",handler:f(async(e,a)=>{const{userId:s,cId:t,files:o}=e.body||{},n=`computer:${s}:${t}`;m(n,"INFO","\u6587\u4EF6\u66F4\u65B0",{userId:s,cId:t,filesCount:o?o.length:0}),o&&Array.isArray(o)&&o.forEach(r=>{if(r&&typeof r.contents=="string"&&r.contents)try{r.contents=decodeURIComponent(r.contents)}catch(l){m(n,"WARN","\u89E3\u7801\u6587\u4EF6\u5185\u5BB9\u5931\u8D25",{fileName:r.name,error:l.message})}});const i=await z(s,t,o);a.status(200).json(i)})},{path:"/upload-file",method:"post",middleware:I.single("file"),handler:f(async(e,a)=>{const{userId:s,cId:t,filePath:o}=e.body||{},n=e.file,i=`computer:${s}:${t}`;m(i,"INFO","\u4E0A\u4F20\u5355\u4E2A\u6587\u4EF6",{userId:s,cId:t,filePath:o});const l={buffer:await d.promises.readFile(n.path),originalname:n.originalname,mimetype:n.mimetype,size:n.size};try{const u=await N(s,t,l,o);a.status(200).json(u)}finally{d.existsSync(n.path)&&await d.promises.unlink(n.path)}})},{path:"/upload-files",method:"post",middleware:I.array("files"),handler:f(async(e,a)=>{const{userId:s,cId:t,filePaths:o}=e.body||{},n=e.files||[],i=`computer:${s}:${t}`,r=Array.isArray(o)?o:typeof o=="string"?[o]:o;m(i,"INFO","\u6279\u91CF\u4E0A\u4F20\u6587\u4EF6\u8BF7\u6C42",{userId:s,cId:t,filesCount:n.length,filePathsCount:Array.isArray(r)?r.length:0});const l=[],u=[];try{for(const c of n){u.push(c.path);const F=await d.promises.readFile(c.path);l.push({buffer:F,originalname:c.originalname,mimetype:c.mimetype,size:c.size})}const p=await P(s,t,l,r);a.status(200).json(p)}finally{for(const p of u)if(d.existsSync(p))try{await d.promises.unlink(p)}catch(c){m(i,"WARN","\u6E05\u7406\u4E34\u65F6\u6587\u4EF6\u5931\u8D25",{tempPath:p,error:c.message})}}})},{path:"/download-all-files",method:"get",handler:f(async(e,a)=>{const{userId:s,cId:t}=e.query||{},o=`computer:${s}:${t}`;m(o,"INFO","\u4E0B\u8F7D\u5168\u90E8\u6587\u4EF6\u8BF7\u6C42",{userId:s,cId:t});const{archive:n,zipFileName:i}=await R(s,t);a.setHeader("Content-Type","application/zip");const r=encodeURIComponent(i);a.setHeader("Content-Disposition",`attachment; filename="${r}"; filename*=UTF-8''${r}`),n.on("error",l=>{a.destroy(l)}),n.pipe(a),n.finalize()})}];C.forEach(e=>{if(e.middleware)h[e.method](e.path,e.middleware,e.handler);else if(e.customHandler){const a=[];e.handler&&a.push(e.handler),e.decodeMiddleware&&a.push(e.decodeMiddleware),a.push(b),a.push(e.customHandler),h[e.method](e.path,...a)}else h[e.method](e.path,e.handler)});export default h;
@@ -0,0 +1 @@
1
+ import y from"express";import g from"multer";import f from"path";import c from"fs";import S from"iconv-lite";import p from"../service/projectService.js";import{getProjectContent as E,getProjectContentByVersion as I}from"../utils/project/getContentUtils.js";import{uploadAttachmentFile as P}from"../utils/project/uploadAttachmentFileUtils.js";import{copyProject as x}from"../utils/project/copyProjectUtils.js";import m from"../../config/index.js";import{log as u}from"../utils/log/logUtils.js";import{ValidationError as r,SystemError as D,asyncHandler as d}from"../utils/error/errorHandler.js";const w=y.Router(),R=g.diskStorage({destination:function(e,n,t){const o=f.join(m.UPLOAD_PROJECT_DIR,"temp");c.existsSync(o)||c.mkdirSync(o,{recursive:!0}),t(null,o)},filename:function(e,n,t){const o=Date.now()+"-"+Math.round(Math.random()*1e6);t(null,n.fieldname+"-"+o+f.extname(n.originalname))}}),O=g({storage:R,fileFilter:function(e,n,t){const o=f.extname(n.originalname).toLowerCase();m.UPLOAD_ALLOWED_EXTENSIONS.includes(o)?t(null,!0):t(new r("\u6587\u4EF6\u7C7B\u578B\u4E0D\u88AB\u5141\u8BB8",{fileExtension:o,allowedExtensions:m.UPLOAD_ALLOWED_EXTENSIONS}),!1)},limits:{fileSize:m.UPLOAD_MAX_FILE_SIZE_BYTES}}),_=g.diskStorage({destination:function(e,n,t){const o=f.join(m.UPLOAD_PROJECT_DIR,"temp");c.existsSync(o)||c.mkdirSync(o,{recursive:!0}),t(null,o)},filename:function(e,n,t){const o=Date.now()+"-"+Math.round(Math.random()*1e6);t(null,n.fieldname+"-"+o+f.extname(n.originalname))}}),j=[".pdf",".doc",".docx",".xls",".xlsx",".ppt",".pptx",".txt",".md",".csv",".json",".xml",".png",".jpg",".jpeg",".gif",".bmp",".svg",".ico",".webp",".avif",".zip",".rar",".7z",".tar",".gz",".mp4",".avi",".mov",".wmv",".flv",".mp3",".wav",".ogg",".m4a"],z=g({storage:_,fileFilter:function(e,n,t){const o=f.extname(n.originalname).toLowerCase();j.includes(o)?t(null,!0):t(new r("\u9644\u4EF6\u6587\u4EF6\u7C7B\u578B\u4E0D\u88AB\u5141\u8BB8",{fileExtension:o,allowedExtensions:j}),!1)},limits:{fileSize:m.UPLOAD_MAX_FILE_SIZE_BYTES}});function A(e,n,t){if(e.file&&e.file.originalname)try{const o=e.file.originalname,i=S.decode(Buffer.from(e.file.originalname,"latin1"),"utf8");e.file.originalname=i,u("system","INFO","\u6587\u4EF6\u540D\u89E3\u7801\u6210\u529F",{before:o,after:i})}catch(o){u("system","WARN","\u6587\u4EF6\u540D\u89E3\u7801\u5931\u8D25",{originalName:e.file.originalname,error:o.message})}t()}const L=[{path:"/create-project",method:"post",handler:d(async(e,n)=>{const{projectId:t}=e.body;if(!t)throw new r("\u9879\u76EEID\u4E0D\u80FD\u4E3A\u7A7A",{field:"projectId"});const o=await p.createProject(String(t));n.status(200).json(o)})},{path:"/upload-project",method:"post",handler:O.single("file"),customHandler:d(async(e,n)=>{const{projectId:t,codeVersion:o,pid:i,basePath:s}=e.body;if(!t)throw new r("\u9879\u76EEID\u4E0D\u80FD\u4E3A\u7A7A",{field:"projectId"});if(!o)throw new r("\u4EE3\u7801\u7248\u672C\u4E0D\u80FD\u4E3A\u7A7A",{field:"codeVersion"});if(!e.file)throw new r("\u8BF7\u4E0A\u4F20\u538B\u7F29\u5305\u6587\u4EF6",{field:"zipFile"});const a=await p.handleFileUpload(String(t),o,e.file);e.file.path=a.filePath;try{const l=await p.uploadProject(String(t),e.file.path,e,o,i,s);n.status(200).json(l)}catch(l){try{await p.cleanupProjectDirectory(String(t))}catch(h){u(t,"ERROR","\u8DEF\u7531\u5C42\u6E05\u7406\u9879\u76EE\u76EE\u5F55\u5931\u8D25",{projectId:t,error:h.message})}if(e.file&&c.existsSync(e.file.path))try{c.unlinkSync(e.file.path)}catch(h){u(t,"ERROR","\u6E05\u7406\u4E0A\u4F20\u6587\u4EF6\u5931\u8D25",{projectId:t,error:h.message})}throw l}})},{path:"/get-project-content",method:"get",handler:d(async(e,n)=>{const{projectId:t,command:o,proxyPath:i}=e.query;if(!t)throw new r("\u9879\u76EEID\u4E0D\u80FD\u4E3A\u7A7A",{field:"projectId"});const s=f.join(m.PROJECT_SOURCE_DIR,t);if(!c.existsSync(s))throw new r("\u9879\u76EE\u4E0D\u5B58\u5728",{field:"projectId"});try{const a=await E(s,o,i);n.status(200).json({success:!0,...a})}catch(a){const l=a?.message||"\u67E5\u8BE2\u5931\u8D25";n.status(500).json({success:!1,message:l})}})},{path:"/get-project-content-by-version",method:"get",handler:d(async(e,n)=>{const{projectId:t,codeVersion:o,command:i,proxyPath:s}=e.query;if(!t)throw new r("\u9879\u76EEID\u4E0D\u80FD\u4E3A\u7A7A",{field:"projectId"});if(!o)throw new r("\u4EE3\u7801\u7248\u672C\u4E0D\u80FD\u4E3A\u7A7A",{field:"codeVersion"});try{const a=await I(String(t),o,i,s);n.status(200).json({success:!0,...a})}catch(a){const l=a?.message||"\u67E5\u8BE2\u5931\u8D25";n.status(500).json({success:!1,message:l})}})},{path:"/backup-current-version",method:"post",handler:d(async(e,n)=>{const{projectId:t,codeVersion:o}=e.body,i=await p.backupCurrentVersion(String(t),o);n.status(200).json({success:!0,...i})})},{path:"/export-project",method:"post",handler:d(async(e,n)=>{const{projectId:t,codeVersion:o,exportType:i,config:s}=e.body,a=await p.exportProject(String(t),o,i,s);if(!c.existsSync(a.zipPath))throw new D("\u5BFC\u51FA\u7684zip\u6587\u4EF6\u4E0D\u5B58\u5728",{zipPath:a.zipPath});const l=f.basename(a.zipPath);n.setHeader("Content-Type","application/zip"),n.setHeader("Content-Disposition",`attachment; filename="${l}"`),n.sendFile(a.zipPath,h=>{h?(u(t,"ERROR","\u53D1\u9001zip\u6587\u4EF6\u5931\u8D25",{projectId:t,error:h.message}),n.headersSent||n.status(500).json({success:!1,message:"\u6587\u4EF6\u53D1\u9001\u5931\u8D25"})):u(t,"INFO","zip\u6587\u4EF6\u53D1\u9001\u6210\u529F",{projectId:t,zipPath:a.zipPath})})})},{path:"/delete-project",method:"get",handler:d(async(e,n)=>{const{projectId:t,pid:o}=e.query;if(!t)throw new r("\u9879\u76EEID\u4E0D\u80FD\u4E3A\u7A7A",{field:"projectId"});const i=await p.deleteProject(String(t),o,e);n.status(200).json(i)})},{path:"/upload-attachment-file",method:"post",handler:z.single("file"),decodeMiddleware:A,customHandler:d(async(e,n)=>{const{projectId:t,fileName:o}=e.body;if(!t)throw new r("\u9879\u76EEID\u4E0D\u80FD\u4E3A\u7A7A",{field:"projectId"});if(!e.file)throw new r("\u8BF7\u4E0A\u4F20\u6587\u4EF6",{field:"file"});try{const i=await P(String(t),e.file,o);n.status(200).json({success:!0,...i})}catch(i){if(e.file&&c.existsSync(e.file.path))try{c.unlinkSync(e.file.path)}catch(s){u(t,"ERROR","\u6E05\u7406\u4E0A\u4F20\u6587\u4EF6\u5931\u8D25",{projectId:t,error:s.message})}throw i}})},{path:"/copy-project",method:"post",handler:d(async(e,n)=>{const{sourceProjectId:t,targetProjectId:o}=e.body;if(!t)throw new r("\u6E90\u9879\u76EEID\u4E0D\u80FD\u4E3A\u7A7A",{field:"sourceProjectId"});if(!o)throw new r("\u76EE\u6807\u9879\u76EEID\u4E0D\u80FD\u4E3A\u7A7A",{field:"targetProjectId"});const i=await x(String(t),String(o));n.status(200).json(i)})}];function N(e,n,t,o){if(e instanceof g.MulterError||e.name==="ValidationError"||e instanceof r)return o(e);o(e)}L.forEach(e=>{if(e.customHandler){const n=[];e.handler&&n.push(e.handler),e.decodeMiddleware&&n.push(e.decodeMiddleware),n.push(N),n.push(e.customHandler),w[e.method](e.path,...n)}else w[e.method](e.path,e.handler)});export default w;
@@ -0,0 +1 @@
1
+ import i from"express";import c from"./buildRoutes.js";import u from"./projectRoutes.js";import l from"./codeRoutes.js";import d from"./computerRoutes.js";import s from"path";import h from"fs";import{fileURLToPath as f}from"url";const g=s.dirname(f(import.meta.url));let a="1.0.0";try{const o=s.resolve(g,"..","..","package.json");a=JSON.parse(h.readFileSync(o,"utf8")).version||"1.0.0"}catch{}const e=i.Router();e.get("/",(o,r)=>{r.send("Hello")}),e.get("/health",(o,r)=>{const p=Math.floor(process.uptime()),t=process.memoryUsage(),m={heapUsed:Math.round(t.heapUsed/1024/1024*100)/100,heapTotal:Math.round(t.heapTotal/1024/1024*100)/100,rss:Math.round(t.rss/1024/1024*100)/100,external:Math.round(t.external/1024/1024*100)/100},n={status:"ok",timestamp:Date.now(),uptime:p,version:a,platform:process.platform,nodeVersion:process.version,pid:process.pid,memory:m,env:process.env.NODE_ENV||"unknown"};r.json(n)}),e.use("/api/build",c),e.use("/api/project",u),e.use("/api/project",l),e.use("/api/computer",d);export default e;
@@ -0,0 +1,2 @@
1
+ import o from"node-cron";import{exec as a}from"child_process";import"fs";import"path";import{log as e}from"../utils/log/logUtils.js";class h{constructor(t={}){this.config={enabled:process.env.PNPM_PRUNE_ENABLED!=="false",schedule:process.env.PNPM_PRUNE_SCHEDULE||"0 2 * * 0",timezone:process.env.PNPM_PRUNE_TIMEZONE||"Asia/Shanghai",runOnStart:process.env.PNPM_PRUNE_RUN_ON_START==="true",...t},this.task=null,this.isRunning=!1}start(){if(!this.config.enabled){e("scheduler","INFO","pnpm prune \u5B9A\u65F6\u4EFB\u52A1\u5DF2\u7981\u7528\uFF08PNPM_PRUNE_ENABLED=false\uFF09");return}if(!o.validate(this.config.schedule)){e("scheduler","ERROR",`\u65E0\u6548\u7684 cron \u8868\u8FBE\u5F0F: ${this.config.schedule}`);return}e("scheduler","INFO","pnpm prune \u5B9A\u65F6\u4EFB\u52A1\u5DF2\u542F\u52A8",{schedule:this.config.schedule,timezone:this.config.timezone}),this.task=o.schedule(this.config.schedule,()=>{this.executePrune()},{scheduled:!0,timezone:this.config.timezone}),this.config.runOnStart&&(e("scheduler","INFO","\u542F\u52A8\u65F6\u7ACB\u5373\u6267\u884C\u4E00\u6B21 pnpm prune"),setTimeout(()=>{this.executePrune()},5e3))}stop(){this.task&&(this.task.stop(),e("scheduler","INFO","pnpm prune \u5B9A\u65F6\u4EFB\u52A1\u5DF2\u505C\u6B62"))}async executePrune(){if(this.isRunning){e("scheduler","WARN","pnpm prune \u6B63\u5728\u6267\u884C\u4E2D\uFF0C\u8DF3\u8FC7\u672C\u6B21\u8C03\u5EA6");return}this.isRunning=!0,e("scheduler","INFO","===================================="),e("scheduler","INFO","\u5F00\u59CB\u6267\u884C pnpm store prune"),e("scheduler","INFO","====================================");try{const t=await this.getStoreStatus();t&&e("scheduler","INFO","\u6E05\u7406\u524D\u72B6\u6001",t);const s=await this.runCommand("pnpm store prune");if(s.success){e("scheduler","INFO","\u2705 pnpm store prune \u6267\u884C\u6210\u529F"),s.stdout&&e("scheduler","INFO",s.stdout);const r=await this.getStoreStatus();r&&e("scheduler","INFO","\u6E05\u7406\u540E\u72B6\u6001",r)}else e("scheduler","ERROR","\u274C pnpm store prune \u6267\u884C\u5931\u8D25",{error:s.error})}catch(t){e("scheduler","ERROR","pnpm prune \u6267\u884C\u5F02\u5E38",{error:t.message})}finally{this.isRunning=!1,e("scheduler","INFO","===================================="),e("scheduler","INFO","pnpm store prune \u6267\u884C\u5B8C\u6210"),e("scheduler","INFO",`====================================
2
+ `)}}async getStoreStatus(){try{const t=await this.runCommand("pnpm store path");if(!t.success)return null;const s=t.stdout.trim(),r=await this.runCommand(`du -sh "${s}"`),c=r.success?r.stdout.trim().split(" ")[0]:"unknown";return{path:s,size:c}}catch{return null}}runCommand(t){return new Promise(s=>{a(t,{maxBuffer:10*1024*1024,env:process.env},(r,c,l)=>{s(r?{success:!1,error:r.message,stderr:l}:{success:!0,stdout:c})})})}getNextRun(){return{schedule:this.config.schedule,timezone:this.config.timezone}}}let u=null;function i(n){return u||(u=new h(n)),u}function p(n){const t=i(n);return t.start(),t}function d(){u&&u.stop()}async function m(n={}){await i(n).executePrune()}export{h as PnpmPruneScheduler,i as getScheduler,p as startScheduler,d as stopScheduler,m as executePruneManually};
package/dist/server.js ADDED
@@ -0,0 +1 @@
1
+ import O from"express";import"json-bigint";import m from"swagger-ui-express";import E from"./config/swagger.js";import s from"../config/index.js";import{log as c,logger as I}from"./utils/log/logUtils.js";import S from"./utils/log/logCacheManager.js";import{errorHandler as P,notFoundHandler as R}from"./utils/error/errorHandler.js";import w from"./routes/router.js";import{cleanupInitProjectOnStartup as y}from"./utils/project/initProjectCleanupUtils.js";import{startScheduler as N,stopScheduler as D}from"./scheduler/pnpmPruneScheduler.js";import T from"path";const o=O();o.use(O.json({limit:s.REQUEST_BODY_LIMIT,reviver:(t,e)=>typeof e=="number"&&!Number.isSafeInteger(e)?e.toString():e})),o.use(O.urlencoded({extended:!0,limit:s.REQUEST_BODY_LIMIT})),o.use(I);const C=t=>{let e=t;try{for(;;){const n=decodeURIComponent(e);if(n===e)break;e=n}}catch{}return e};o.use("/api/page/static/:projectId",(t,e,n)=>{const{projectId:l}=t.params;let i=t.path||"/";if(!l||i==="/")return e.status(404).send("Not Found");const r=t.headers.origin,a=r||"*";if(e.header("Access-Control-Allow-Origin",a),e.header("Access-Control-Allow-Methods","HEAD,GET,POST,PUT,DELETE,OPTIONS"),e.header("Access-Control-Allow-Headers","Origin, X-Requested-With, Content-Type, Accept, Authorization, Cache-Control, Fragment"),e.header("Access-Control-Expose-Headers","Content-Type"),r&&(e.header("Access-Control-Allow-Credentials","true"),e.header("Vary","Origin")),t.method==="OPTIONS")return e.sendStatus(200);i=i.replace(/^\/+/,"");const p=C(i),A=T.join(s.PROJECT_SOURCE_DIR,l,p),d={"Access-Control-Allow-Origin":a,"Access-Control-Allow-Methods":"HEAD,GET,POST,PUT,DELETE,OPTIONS","Access-Control-Allow-Headers":"Origin, X-Requested-With, Content-Type, Accept, Authorization, Cache-Control, Fragment"};return r&&(d["Access-Control-Allow-Credentials"]="true",d.Vary="Origin"),e.sendFile(A,{dotfiles:"ignore",headers:d},u=>{if(u)return n()})}),o.use("/api/computer/static/:userId/:cId",(t,e,n)=>{const{userId:l,cId:i}=t.params;let r=t.path||"/";if(!l||!i||r==="/")return e.status(404).send("Not Found");const a=t.headers.origin,p=a||"*";if(e.header("Access-Control-Allow-Origin",p),e.header("Access-Control-Allow-Methods","HEAD,GET,POST,PUT,DELETE,OPTIONS"),e.header("Access-Control-Allow-Headers","Origin, X-Requested-With, Content-Type, Accept, Authorization, Cache-Control"),e.header("Access-Control-Expose-Headers","Content-Type"),a&&(e.header("Access-Control-Allow-Credentials","true"),e.header("Vary","Origin")),t.method==="OPTIONS")return e.sendStatus(200);r=r.replace(/^\/+/,"");const A=C(r),d=T.join(s.COMPUTER_WORKSPACE_DIR,l,i,A),u={"Access-Control-Allow-Origin":p,"Access-Control-Allow-Methods":"HEAD,GET,POST,PUT,DELETE,OPTIONS","Access-Control-Allow-Headers":"Origin, X-Requested-With, Content-Type, Accept, Authorization, Cache-Control"};return a&&(u["Access-Control-Allow-Credentials"]="true",u.Vary="Origin"),e.sendFile(d,{dotfiles:"ignore",headers:u},g=>{if(g)return n()})}),o.use("/api-docs",m.serve,m.setup(E,{customCss:".swagger-ui .topbar { display: none }",customSiteTitle:"Subapp Deployer API \u6587\u6863"})),o.use(w),o.use(R),o.use(P);const h=o.listen(s.PORT,async()=>{c("default","INFO",`Server is running on port ${s.PORT} (${s.NODE_ENV} mode)`),await y(s);try{N()}catch(t){c("default","ERROR",`pnpm prune \u5B9A\u65F6\u4EFB\u52A1\u542F\u52A8\u5931\u8D25: ${t.message}`)}});h.timeout=6e5,h.keepAliveTimeout=61e4,h.headersTimeout=62e4;const f=t=>{c("default","INFO",`\u6536\u5230 ${t} \u4FE1\u53F7\uFF0C\u51C6\u5907\u4F18\u96C5\u9000\u51FA...`),D();try{S.destroy(),c("default","INFO","\u65E5\u5FD7\u7F13\u5B58\u7BA1\u7406\u5668\u5DF2\u6E05\u7406")}catch(e){c("default","ERROR",`\u6E05\u7406\u65E5\u5FD7\u7F13\u5B58\u7BA1\u7406\u5668\u5931\u8D25: ${e.message}`)}h.close(()=>{c("default","INFO","\u670D\u52A1\u5668\u5DF2\u5173\u95ED"),process.exit(0)}),setTimeout(()=>{c("default","ERROR","\u5F3A\u5236\u9000\u51FA\uFF08\u8D85\u65F6\uFF09"),process.exit(1)},3e4)};process.on("SIGTERM",()=>f("SIGTERM")),process.on("SIGINT",()=>f("SIGINT"));
@@ -0,0 +1,4 @@
1
+ import a from"path";import o from"fs";import F from"../../config/index.js";import{restartDevServer as $}from"../utils/build/restartDevUtils.js";import{log as t}from"../utils/log/logUtils.js";import{ValidationError as m,SystemError as v,ResourceError as S}from"../utils/error/errorHandler.js";import"../utils/common/sensitiveUtils.js";import{backupProjectToZip as C,restoreProjectFromZip as D,pruneMissingFiles as A,removeEmptyDirectories as z}from"../utils/project/backupUtils.js";import"../utils/common/zipUtils.js";import"../utils/buildJudge/restartJudgeUtils.js";function V(e,b){const f=e.split(/\r?\n/),g=b.split(/\r?\n/),u=f.length,s=g.length,h=Math.min(u,s);let r=0;for(let n=0;n<h;n++)f[n]!==g[n]&&(f[n]=g[n],r++);if(u>s)for(let n=u-1;n>=s;n--)f.splice(n,1),r++;if(s>u)for(let n=u;n<s;n++)f.push(g[n]),r++;const c=e.includes(`\r
2
+ `)?`\r
3
+ `:`
4
+ `;return{finalContent:f.join(c),changesCount:r}}function I(e,b){return e===b?{finalContent:e,changesCount:0}:{finalContent:b,changesCount:-1}}async function T(e,b,f,g){if(!e)throw new m("\u9879\u76EEID\u4E0D\u80FD\u4E3A\u7A7A",{field:"projectId"});if(b==null)throw new m("codeVersion\u4E0D\u80FD\u4E3A\u7A7A",{field:"codeVersion"});const u=Number(b);if(!Number.isFinite(u))throw new m("codeVersion\u5FC5\u987B\u662F\u6570\u5B57",{field:"codeVersion"});if(!Array.isArray(f))throw new m("files\u5FC5\u987B\u662F\u6570\u7EC4",{field:"files"});for(let r=0;r<f.length;r++){const c=f[r];if(!c||typeof c.operation!="string")throw new m(`files[${r}].operation \u4E0D\u80FD\u4E3A\u7A7A`,{field:`files[${r}].operation`});if(!c.name||typeof c.name!="string")throw new m(`files[${r}].name \u4E0D\u80FD\u4E3A\u7A7A`,{field:`files[${r}].name`});const i=c.operation.toLowerCase();if(!["create","delete","rename","modify"].includes(i))throw new m(`files[${r}].operation \u5FC5\u987B\u662F create\u3001delete\u3001rename \u6216 modify \u4E4B\u4E00`,{field:`files[${r}].operation`});if(i==="rename"&&!c.renameFrom)throw new m(`files[${r}].renameFrom \u4E0D\u80FD\u4E3A\u7A7A\uFF08\u91CD\u547D\u540D\u64CD\u4F5C\u9700\u8981\uFF09`,{field:`files[${r}].renameFrom`});if(i==="modify"&&typeof c.contents!="string")throw new m(`files[${r}].contents \u5FC5\u987B\u662F\u5B57\u7B26\u4E32\uFF08\u4FEE\u6539\u64CD\u4F5C\u9700\u8981\uFF09`,{field:`files[${r}].contents`})}const s=a.join(F.PROJECT_SOURCE_DIR,e);if(!o.existsSync(s))throw t(e,"ERROR","\u9879\u76EE\u4E0D\u5B58\u5728",{projectId:e,projectPath:s}),new S("\u9879\u76EE\u4E0D\u5B58\u5728",{projectId:e});let h="";try{const r=a.join(F.UPLOAD_PROJECT_DIR,e);o.existsSync(r)||o.mkdirSync(r,{recursive:!0});const c=`${e}-v${u}.zip`;h=a.join(r,c),await C(e,s,h),t(e,"INFO","\u9879\u76EE\u5DF2\u5907\u4EFD",{projectId:e,zipPath:h});try{for(const i of f){const n=i.operation.toLowerCase(),w=i.name,l=a.normalize(w).replace(/^[\/\\]+/,""),R=a.join(s,l),O=a.resolve(R),y=a.resolve(s);if(!O.startsWith(y+a.sep)&&O!==y){t(e,"WARN","\u6587\u4EF6\u8DEF\u5F84\u4E0D\u5B89\u5168\uFF0C\u8DF3\u8FC7",{filePath:l,resolvedPath:O});continue}switch(n){case"create":{await o.promises.mkdir(a.dirname(R),{recursive:!0});const P=i.contents||"";await o.promises.writeFile(R,P,"utf8"),t(e,"INFO","\u6587\u4EF6\u521B\u5EFA\u6210\u529F",{filePath:l});break}case"delete":{o.existsSync(R)?(await o.promises.unlink(R),t(e,"INFO","\u6587\u4EF6\u5220\u9664\u6210\u529F",{filePath:l})):t(e,"WARN","\u8981\u5220\u9664\u7684\u6587\u4EF6\u4E0D\u5B58\u5728",{filePath:l});break}case"rename":{const P=i.renameFrom;if(!P||typeof P!="string"){t(e,"WARN","\u91CD\u547D\u540D\u64CD\u4F5C\u7F3A\u5C11 renameFrom",{filePath:l});break}const E=a.normalize(P).replace(/^[\/\\]+/,""),k=a.join(s,E),N=a.resolve(k);if(!N.startsWith(y+a.sep)&&N!==y){t(e,"WARN","\u91CD\u547D\u540D\u6E90\u8DEF\u5F84\u4E0D\u5B89\u5168\uFF0C\u8DF3\u8FC7",{renameFrom:E,resolvedPath:N});break}o.existsSync(k)?(await o.promises.mkdir(a.dirname(R),{recursive:!0}),await o.promises.rename(k,R),t(e,"INFO","\u6587\u4EF6\u91CD\u547D\u540D\u6210\u529F",{oldPath:E,newPath:l})):t(e,"WARN","\u8981\u91CD\u547D\u540D\u7684\u6587\u4EF6\u4E0D\u5B58\u5728",{renameFrom:E});break}case"modify":{if(!o.existsSync(R)){t(e,"WARN","\u8981\u4FEE\u6539\u7684\u6587\u4EF6\u4E0D\u5B58\u5728",{filePath:l});break}const P=await o.promises.readFile(R,"utf8"),E=typeof i.contents=="string"?i.contents:"",{finalContent:k,changesCount:N}=V(P,E);if(N===0){t(e,"INFO","\u6587\u4EF6\u5185\u5BB9\u65E0\u53D8\u5316\uFF0C\u8DF3\u8FC7\u5199\u5165",{filePath:l});break}await o.promises.writeFile(R,k,"utf8"),t(e,"INFO","\u6587\u4EF6\u4FEE\u6539\u6210\u529F",{filePath:l,changesCount:N});break}default:{t(e,"WARN","\u4E0D\u652F\u6301\u7684\u64CD\u4F5C\u7C7B\u578B",{operation:n,filePath:l});break}}}try{await z(s,F.TRAVERSE_EXCLUDE_DIRS||[])}catch(i){t(e,"WARN","\u6E05\u7406\u7A7A\u76EE\u5F55\u5931\u8D25",{projectId:e,error:i&&i.message})}return t(e,"INFO","\u90E8\u5206\u6587\u4EF6\u66F4\u65B0\u6210\u529F",{projectId:e,filesCount:f.length}),{success:!0,message:"\u90E8\u5206\u6587\u4EF6\u66F4\u65B0\u6210\u529F",projectId:e,filesCount:f.length}}catch(i){throw t(e,"ERROR","\u5904\u7406\u6587\u4EF6\u64CD\u4F5C\u5931\u8D25",{projectId:e,error:i&&i.message}),i}}catch(r){throw r.isOperational?r:new v("\u5907\u4EFD\u9879\u76EE\u5931\u8D25",{projectId:e,originalError:r&&r.message})}}async function p(e,b,f,g){if(!e)throw new m("\u9879\u76EEID\u4E0D\u80FD\u4E3A\u7A7A",{field:"projectId"});const u=Number(b);if(!Number.isFinite(u))throw new m("codeVersion\u5FC5\u987B\u662F\u6570\u5B57",{field:"codeVersion"});if(!Array.isArray(f))throw new m("files\u5FC5\u987B\u662F\u6570\u7EC4",{field:"files"});const s=a.join(F.PROJECT_SOURCE_DIR,e);if(!o.existsSync(s))throw t(e,"ERROR","\u9879\u76EE\u4E0D\u5B58\u5728",{projectId:e,projectPath:s}),new S("\u9879\u76EE\u4E0D\u5B58\u5728",{projectId:e});let h="";try{const r=a.join(F.UPLOAD_PROJECT_DIR,e);o.existsSync(r)||o.mkdirSync(r,{recursive:!0});const c=`${e}-v${u}.zip`;h=a.join(r,c),await C(e,s,h);try{for(const i of f){if(!i||typeof i.name!="string")continue;const n=a.join(s,i.name);if(i.renameFrom&&typeof i.renameFrom=="string"){const P=a.join(s,i.renameFrom);if(o.existsSync(P)){await o.promises.mkdir(a.dirname(n),{recursive:!0}),await o.promises.rename(P,n),t(e,"INFO","\u6587\u4EF6\u91CD\u547D\u540D\u6210\u529F",{projectId:e,oldPath:i.renameFrom,newPath:i.name});continue}}const w=i.binary===!0,l=i.binary===!1,R=!!i.sizeExceeded,O=typeof i.contents=="string"&&i.contents.length>0;if(w){if(o.existsSync(n)){t(e,"INFO","\u4E8C\u8FDB\u5236\u6587\u4EF6\u5DF2\u5B58\u5728\uFF0C\u8DF3\u8FC7\u5199\u5165",{filePath:i.name});continue}if(O)try{await o.promises.mkdir(a.dirname(n),{recursive:!0});const P=Buffer.from(i.contents,"base64");await o.promises.writeFile(n,P),t(e,"INFO","\u4E8C\u8FDB\u5236\u6587\u4EF6\u5199\u5165\u6210\u529F",{filePath:i.name})}catch(P){t(e,"ERROR","\u4E8C\u8FDB\u5236\u6587\u4EF6\u5199\u5165\u5931\u8D25",{filePath:i.name,error:P&&P.message})}else t(e,"WARN","\u4E8C\u8FDB\u5236\u6587\u4EF6\u4E0D\u5B58\u5728\u4E14\u65E0\u5185\u5BB9\uFF0C\u8DF3\u8FC7",{filePath:i.name});continue}l&&(!R||R&&O)&&(await o.promises.mkdir(a.dirname(n),{recursive:!0}),await o.promises.writeFile(n,i.contents||"","utf8"))}}catch(i){throw t(e,"ERROR","\u5199\u5165\u6587\u4EF6\u5931\u8D25",{projectId:e,error:i&&i.message}),i}try{const i=new Set(f.filter(n=>n&&typeof n.name=="string").map(n=>a.normalize(n.name)));await A(s,i,F.TRAVERSE_EXCLUDE_DIRS||[]),await z(s,F.TRAVERSE_EXCLUDE_DIRS||[])}catch(i){throw t(e,"ERROR","\u6E05\u7406\u7F3A\u5931\u6587\u4EF6\u5931\u8D25\uFF0C\u5F00\u59CB\u56DE\u6EDA",{projectId:e,error:i&&i.message}),i}return t(e,"INFO","\u6587\u4EF6\u63D0\u4EA4\u6210\u529F",{projectId:e,filesCount:f.length}),{success:!0,message:"\u6587\u4EF6\u63D0\u4EA4\u6210\u529F",projectId:e,restarted:!1}}catch(r){throw r.isOperational?r:new v("\u5907\u4EFD\u65E7\u7248\u672C\u5931\u8D25",{projectId:e,originalError:r&&r.message})}}async function x(e,b,f,g,u){if(!e)throw new m("\u9879\u76EEID\u4E0D\u80FD\u4E3A\u7A7A",{field:"projectId"});const s=Number(b);if(!Number.isFinite(s))throw new m("codeVersion\u5FC5\u987B\u662F\u6570\u5B57",{field:"codeVersion"});if(!f)throw new m("\u6587\u4EF6\u4E0D\u80FD\u4E3A\u7A7A",{field:"file"});if(!g||typeof g!="string")throw new m("\u6587\u4EF6\u8DEF\u5F84\u4E0D\u80FD\u4E3A\u7A7A",{field:"filePath"});const h=a.join(F.PROJECT_SOURCE_DIR,e);if(!o.existsSync(h))throw t(e,"ERROR","\u9879\u76EE\u4E0D\u5B58\u5728",{projectId:e,projectPath:h}),new S("\u9879\u76EE\u4E0D\u5B58\u5728",{projectId:e});const r=a.normalize(g).replace(/^[\/\\]+/,""),c=a.join(h,r),i=a.resolve(c),n=a.resolve(h);if(!i.startsWith(n))throw new m("\u6587\u4EF6\u8DEF\u5F84\u4E0D\u5B89\u5168\uFF0C\u4E0D\u80FD\u8D85\u51FA\u9879\u76EE\u76EE\u5F55",{field:"filePath",providedPath:g,resolvedPath:i});let w="";try{const l=a.join(F.UPLOAD_PROJECT_DIR,e);o.existsSync(l)||o.mkdirSync(l,{recursive:!0});const R=`${e}-v${s}.zip`;w=a.join(l,R),await C(e,h,w),t(e,"INFO",`\u9879\u76EE\u5DF2\u5907\u4EFD: ${w}`,{projectId:e,zipPath:w});try{if(await o.promises.mkdir(a.dirname(c),{recursive:!0}),!f.buffer)throw new m("\u6587\u4EF6\u5185\u5BB9\u683C\u5F0F\u4E0D\u6B63\u786E\uFF0C\u7F3A\u5C11buffer",{field:"file"});if(t(e,"INFO","\u51C6\u5907\u5199\u5165\u6587\u4EF6",{targetPath:c,bufferLength:f.buffer.length,expectedSize:f.size,bufferIsBuffer:Buffer.isBuffer(f.buffer),sizeMatch:f.buffer.length===f.size}),await o.promises.writeFile(c,f.buffer),t(e,"INFO","\u6587\u4EF6\u4E0A\u4F20\u6210\u529F",{projectId:e,filePath:r,targetPath:i,fileSize:f.buffer?f.buffer.length:0}),!1)try{const y=await $(u,e);return t(e,"INFO","\u91CD\u542F\u5F00\u53D1\u670D\u52A1\u5668\u6210\u529F",{projectId:e,pid:y.pid,port:y.port}),{success:!0,message:"\u6587\u4EF6\u4E0A\u4F20\u5E76\u91CD\u542F\u5F00\u53D1\u670D\u52A1\u5668\u6210\u529F",projectId:e,filePath:r,targetPath:i,fileSize:f.buffer?f.buffer.length:0,pid:y.pid,port:y.port,restarted:!0}}catch(y){t(e,"ERROR","\u91CD\u542F\u5F00\u53D1\u670D\u52A1\u5668\u5931\u8D25",{projectId:e,filePath:r,error:y&&y.message})}else return t(e,"INFO","\u6587\u4EF6\u4FEE\u6539\u4E0D\u9700\u8981\u91CD\u542F\u5F00\u53D1\u670D\u52A1\u5668",{projectId:e,filePath:r}),{success:!0,message:"\u6587\u4EF6\u4E0A\u4F20\u6210\u529F\uFF0C\u65E0\u9700\u91CD\u542F\u5F00\u53D1\u670D\u52A1\u5668",projectId:e,restarted:!1}}catch(O){throw t(e,"ERROR","\u5199\u5165\u6587\u4EF6\u5931\u8D25",{projectId:e,filePath:r,error:O&&O.message}),O}}catch(l){throw l.isOperational?l:new v("\u5907\u4EFD\u9879\u76EE\u5931\u8D25",{projectId:e,filePath:r,originalError:l&&l.message})}}async function _(e,b,f,g){if(!e)throw new m("\u9879\u76EEID\u4E0D\u80FD\u4E3A\u7A7A",{field:"projectId"});const u=Number(b);if(!Number.isFinite(u))throw new m("codeVersion\u5FC5\u987B\u662F\u6570\u5B57",{field:"codeVersion"});const s=Number(f);if(!Number.isFinite(s))throw new m("rollbackTo\u5FC5\u987B\u662F\u6570\u5B57",{field:"rollbackTo"});if(s<0)throw new m("rollbackTo\u4E0D\u80FD\u5C0F\u4E8E0",{field:"rollbackTo"});if(s>=u)throw new m("rollbackTo\u5FC5\u987B\u5C0F\u4E8E\u5F53\u524DcodeVersion",{field:"rollbackTo"});const h=a.join(F.PROJECT_SOURCE_DIR,e);if(!o.existsSync(h))throw t(e,"ERROR","\u9879\u76EE\u4E0D\u5B58\u5728",{projectId:e,projectPath:h}),new S("\u9879\u76EE\u4E0D\u5B58\u5728",{projectId:e});const r=a.join(F.UPLOAD_PROJECT_DIR,e),c=`${e}-v${s}.zip`,i=a.join(r,c);if(!o.existsSync(i))throw t(e,"ERROR","\u56DE\u6EDA\u7248\u672C\u5907\u4EFD\u6587\u4EF6\u4E0D\u5B58\u5728",{projectId:e,rollbackTo:s,zipPath:i}),new S("\u56DE\u6EDA\u7248\u672C\u5907\u4EFD\u6587\u4EF6\u4E0D\u5B58\u5728",{projectId:e,rollbackTo:s});let n="";try{o.existsSync(r)||o.mkdirSync(r,{recursive:!0});const w=`${e}-v${u}.zip`;return n=a.join(r,w),o.existsSync(n)?t(e,"INFO","\u5F53\u524D\u7248\u672C\u5907\u4EFD\u5DF2\u5B58\u5728\uFF0C\u8DF3\u8FC7\u5907\u4EFD",{projectId:e,zipPath:n}):(await C(e,h,n),t(e,"INFO","\u5F53\u524D\u7248\u672C\u5DF2\u5907\u4EFD",{projectId:e,zipPath:n})),await D(e,h,i),t(e,"INFO","\u9879\u76EE\u56DE\u6EDA\u6210\u529F",{projectId:e,newVersion:u,toVersion:s,rollbackZipPath:i}),{success:!0,message:"\u9879\u76EE\u56DE\u6EDA\u6210\u529F",newVersion:u,rollbackTo:s}}catch(w){if(t(e,"ERROR","\u56DE\u6EDA\u9879\u76EE\u5931\u8D25",{projectId:e,rollbackTo:s,error:w&&w.message}),n&&o.existsSync(n))try{t(e,"INFO","\u56DE\u6EDA\u5931\u8D25\uFF0C\u5C1D\u8BD5\u6062\u590D\u5F53\u524D\u7248\u672C",{projectId:e,backupPath:n}),await D(e,h,n),t(e,"INFO","\u5DF2\u6062\u590D\u5F53\u524D\u7248\u672C",{projectId:e})}catch(l){t(e,"ERROR","\u6062\u590D\u5F53\u524D\u7248\u672C\u5931\u8D25",{projectId:e,error:l&&l.message})}throw w.isOperational?w:new v("\u56DE\u6EDA\u9879\u76EE\u5931\u8D25",{projectId:e,rollbackTo:s,originalError:w&&w.message})}}export{T as specifiedFilesUpdate,p as allFilesUpdate,x as uploadSingleFile,_ as rollbackVersion};export default{specifiedFilesUpdate:T,allFilesUpdate:p,uploadSingleFile:x,rollbackVersion:_};
@@ -0,0 +1 @@
1
+ import{log as i}from"../utils/log/logUtils.js";import h from"../../config/index.js";import m from"path";import n from"fs";import{extractZip as D}from"../utils/common/zipUtils.js";import"../utils/build/startDevUtils.js";import"../utils/build/restartDevUtils.js";import{stopDevServer as P}from"../utils/build/stopDevUtils.js";import{ValidationError as O,BusinessError as J,SystemError as E,FileError as A,ResourceError as N}from"../utils/error/errorHandler.js";import{sanitizeSensitivePaths as F}from"../utils/common/sensitiveUtils.js";import{removeNodeModules as z}from"../utils/buildDependency/dependencyManager.js";import{backupProjectToZip as U,copyDirectoryFiltered as L}from"../utils/project/backupUtils.js";import{createPnpmNpmrc as S}from"../utils/common/npmrcUtils.js";async function _(e){if(!e)throw new O("\u9879\u76EEID\u4E0D\u80FD\u4E3A\u7A7A",{field:"projectId"});const r=h.PROJECT_SOURCE_DIR,s=m.join(r,e);if(n.existsSync(s))throw new J(`\u9879\u76EE\u76EE\u5F55 ${e} \u5DF2\u5B58\u5728`,{projectId:e,projectPath:s});try{n.mkdirSync(s,{recursive:!0}),i(e,"INFO",`\u9879\u76EE\u76EE\u5F55\u521B\u5EFA\u6210\u529F: ${s}`,{projectId:e});const t=h.INIT_PROJECT_DIR,a=m.join(t,`${h.INIT_PROJECT_NAME}.zip`),u=m.join(t,h.INIT_PROJECT_NAME);if(!n.existsSync(u)){if(!n.existsSync(a))throw i(e,"ERROR",`\u521D\u59CB\u5316\u6A21\u677F\u4E0D\u5B58\u5728: ${a}`,{projectId:e,templateZipPath:a}),new N("\u521D\u59CB\u5316\u6A21\u677F\u4E0D\u5B58\u5728",{});if(i(e,"INFO",`\u6A21\u677F\u76EE\u5F55\u4E0D\u5B58\u5728\uFF0C\u5F00\u59CB\u89E3\u538B\u6A21\u677F: ${a}`,{projectId:e,templateZipPath:a}),await D(a,u),i(e,"INFO","\u6A21\u677F\u89E3\u538B\u5B8C\u6210",{projectId:e}),!n.existsSync(u))throw new E("\u6A21\u677F\u89E3\u538B\u540E\u76EE\u5F55\u4ECD\u4E0D\u5B58\u5728",{})}const w=await n.promises.readdir(u,{withFileTypes:!0});for(const f of w){const o=m.join(u,f.name),l=m.join(s,f.name);f.isDirectory()?(await n.promises.mkdir(l,{recursive:!0}),await L(o,l)):f.isFile()&&(await n.promises.mkdir(m.dirname(l),{recursive:!0}),await n.promises.copyFile(o,l))}return i(e,"INFO",`\u9879\u76EE ${e} \u521D\u59CB\u5316\u6210\u529F`,{projectId:e}),await S(s,e),{success:!0,message:`\u9879\u76EE ${e} \u521B\u5EFA\u6210\u529F`,projectPath:s}}catch(t){throw i(e,"ERROR",`\u9879\u76EE ${e} \u521D\u59CB\u5316\u5931\u8D25: ${t.message}`,{projectId:e}),new E(`\u9879\u76EE ${e} \u521D\u59CB\u5316\u5931\u8D25: ${t.message}`,{projectId:e,projectPath:s,originalError:t.message})}}async function W(e){const r=await n.promises.readdir(e,{withFileTypes:!0}),s=h.TOP_LEVEL_NOISE_PATTERNS,t=r.filter(a=>{const u=a.name;return u.startsWith(".")?!1:!s.some(w=>w.endsWith("*")?u.startsWith(w.slice(0,-1)):u===w)});if(t.length===1&&t[0].isDirectory()){const a=m.join(e,t[0].name),u=m.join(e,"..",`temp_${Date.now()}`);await n.promises.rename(a,u);const w=await n.promises.readdir(u);for(const f of w){const o=m.join(u,f),l=m.join(e,f);await n.promises.rename(o,l)}await n.promises.rmdir(u)}}async function $(e){if(!e)throw new O("\u9879\u76EEID\u4E0D\u80FD\u4E3A\u7A7A",{field:"projectId"});const r=m.join(h.PROJECT_SOURCE_DIR,e);if(n.existsSync(r))try{i(e,"INFO",`\u5F00\u59CB\u6E05\u7406\u9879\u76EE\u76EE\u5F55: ${r}`,{projectId:e}),await n.promises.rm(r,{recursive:!0,force:!0}),i(e,"INFO",`\u9879\u76EE\u76EE\u5F55\u6E05\u7406\u5B8C\u6210: ${r}`,{projectId:e})}catch(s){throw i(e,"ERROR",`\u6E05\u7406\u9879\u76EE\u76EE\u5F55\u5931\u8D25: ${s.message}`,{projectId:e,projectPath:r,originalError:s.message}),new E(`\u6E05\u7406\u9879\u76EE\u76EE\u5F55\u5931\u8D25: ${s.message}`,{projectId:e,projectPath:r,originalError:s.message})}else i(e,"INFO",`\u9879\u76EE\u76EE\u5F55\u4E0D\u5B58\u5728\uFF0C\u65E0\u9700\u6E05\u7406: ${r}`,{projectId:e})}function Z(e){if(!n.existsSync(e))return!0;try{return n.readdirSync(e).filter(t=>!t.startsWith(".")&&t!=="node_modules").length===0}catch(r){const s=m.basename(e);return i(s,"ERROR",`\u68C0\u67E5\u76EE\u5F55\u662F\u5426\u4E3A\u7A7A\u5931\u8D25: ${r.message}`,{dirPath:e}),!0}}async function v(e,r,s,t,a,u){const w=h.PROJECT_SOURCE_DIR,f=m.join(w,e);try{if(Z(f))i(e,"INFO","\u9879\u76EE\u76EE\u5F55\u4E3A\u7A7A\uFF0C\u76F4\u63A5\u90E8\u7F72\u65B0\u9879\u76EE",{projectId:e});else{i(e,"INFO","\u9879\u76EE\u76EE\u5F55\u975E\u7A7A\uFF0C\u5F00\u59CB\u5907\u4EFD\u5F53\u524D\u7248\u672C",{projectId:e});const l=parseInt(t)-1,y=m.join(h.UPLOAD_PROJECT_DIR,e),c=m.join(y,`${e}-v${l}.zip`);if(n.existsSync(c))i(e,"INFO",`\u5907\u4EFD\u6587\u4EF6\u5DF2\u5B58\u5728\uFF0C\u8DF3\u8FC7\u5907\u4EFD: ${c}`,{projectId:e});else try{await g(e,l),i(e,"INFO",`\u5F53\u524D\u7248\u672C\u5DF2\u5907\u4EFD: ${c}`,{projectId:e})}catch(R){throw i(e,"ERROR",`\u5907\u4EFD\u5F53\u524D\u7248\u672C\u5931\u8D25: ${R.message}`,{projectId:e}),new E(`\u5907\u4EFD\u5F53\u524D\u7248\u672C\u5931\u8D25: ${R.message}`,{projectId:e,originalError:R.message})}if(a&&!isNaN(Number(a))){const R=Number(a);i(e,"INFO",`\u6B63\u5728\u505C\u6B62\u65E7\u7248\u672Cdev\u670D\u52A1\u5668\uFF0CPID: ${R}`,{projectId:e});try{await P(s,e,R,{strict:!0}),i(e,"INFO","\u65E7\u7248\u672Cdev\u670D\u52A1\u5668\u5DF2\u505C\u6B62",{projectId:e})}catch(k){i(e,"WARN",`\u505C\u6B62\u65E7\u7248\u672Cdev\u670D\u52A1\u5668\u5931\u8D25: ${k.message}`,{projectId:e,pid:R})}}n.existsSync(f)&&(i(e,"INFO",`\u6B63\u5728\u6E05\u7A7A\u9879\u76EE\u76EE\u5F55: ${f}`,{projectId:e}),await n.promises.rm(f,{recursive:!0,force:!0}))}return n.mkdirSync(f,{recursive:!0}),i(e,"INFO",`\u9879\u76EE\u76EE\u5F55\u521B\u5EFA\u6210\u529F: ${f}`,{projectId:e}),i(e,"INFO","\u5F00\u59CB\u89E3\u538B\u538B\u7F29\u5305",{projectId:e}),await D(r,f),i(e,"INFO","\u538B\u7F29\u5305\u89E3\u538B\u5B8C\u6210",{projectId:e}),i(e,"INFO","\u68C0\u67E5\u5E76\u5904\u7406\u9876\u5C42\u6587\u4EF6\u5939",{projectId:e}),await W(f),i(e,"INFO","\u68C0\u67E5\u5E76\u5220\u9664 node_modules \u6587\u4EF6\u5939",{projectId:e}),await z(f),await S(f,e),{success:!0,message:`\u9879\u76EE ${e} \u4E0A\u4F20\u6210\u529F`,projectId:e,codeVersion:t}}catch(o){i(e,"ERROR",`\u4E0A\u4F20\u9879\u76EE\u5931\u8D25: ${o.message}`,{projectId:e});try{await $(e),i(e,"INFO","\u4E0A\u4F20\u5931\u8D25\uFF0C\u9879\u76EE\u76EE\u5F55\u5DF2\u6E05\u7406",{projectId:e})}catch(l){i(e,"ERROR",`\u6E05\u7406\u9879\u76EE\u76EE\u5F55\u5931\u8D25: ${l.message}`,{projectId:e,originalError:l.message})}throw o.isOperational?o:new E(`\u4E0A\u4F20\u9879\u76EE\u5931\u8D25: ${o.message}`,{projectId:e,projectPath:f,zipFilePath:r,originalError:o.message})}}async function g(e,r){if(!e)throw new O("\u9879\u76EEID\u4E0D\u80FD\u4E3A\u7A7A",{field:"projectId"});if(r==null)throw new O("codeVersion\u4E0D\u80FD\u4E3A\u7A7A",{field:"codeVersion"});const s=Number(r);if(!Number.isFinite(s))throw new O("codeVersion\u5FC5\u987B\u662F\u6570\u5B57",{field:"codeVersion"});const t=m.join(h.PROJECT_SOURCE_DIR,e);if(!n.existsSync(t))throw new N("\u9879\u76EE\u4E0D\u5B58\u5728",{projectId:e});const a=m.join(h.UPLOAD_PROJECT_DIR,e);n.existsSync(a)||n.mkdirSync(a,{recursive:!0});const u=`${e}-v${s}.zip`,w=m.join(a,u);return await U(e,t,w)}async function T(e,r,s){if(!e)throw new O("\u9879\u76EEID\u4E0D\u80FD\u4E3A\u7A7A",{field:"projectId"});if(!r)throw new O("\u4EE3\u7801\u7248\u672C\u4E0D\u80FD\u4E3A\u7A7A",{field:"codeVersion"});if(!s)throw new O("\u8BF7\u4E0A\u4F20\u538B\u7F29\u5305\u6587\u4EF6",{field:"zipFile"});const t=m.join(h.UPLOAD_PROJECT_DIR,e);n.existsSync(t)||n.mkdirSync(t,{recursive:!0});const a=s.path,u=m.join(t,`${e}-v${r}.zip`);try{return n.renameSync(a,u),i(e,"INFO","\u6587\u4EF6\u4FDD\u5B58\u6210\u529F",{projectId:e,codeVersion:r,filePath:u}),{success:!0,filePath:u}}catch(w){if(i(e,"ERROR","\u79FB\u52A8\u6587\u4EF6\u5931\u8D25",{projectId:e,codeVersion:r,error:w.message}),n.existsSync(a))try{n.unlinkSync(a)}catch(f){i(e,"ERROR","\u6E05\u7406\u4E34\u65F6\u6587\u4EF6\u5931\u8D25",{projectId:e,error:f.message})}throw new E("\u6587\u4EF6\u4FDD\u5B58\u5931\u8D25",{projectId:e,codeVersion:r,originalError:w.message})}}async function C(e,r,s){if(!e)throw new O("\u9879\u76EEID\u4E0D\u80FD\u4E3A\u7A7A",{field:"projectId"});const t=null;try{if(r&&!isNaN(Number(r))){const o=Number(r);i(t,"INFO",`[delete-project] \u6B63\u5728\u505C\u6B62\u5F00\u53D1\u670D\u52A1\u5668\uFF0CPID: ${o}`,{projectId:e,pid:o});try{await P(s,e,o,{strict:!0}),i(t,"INFO","[delete-project] \u5F00\u53D1\u670D\u52A1\u5668\u5DF2\u505C\u6B62",{projectId:e})}catch(l){i(t,"WARN",`[delete-project] \u505C\u6B62\u5F00\u53D1\u670D\u52A1\u5668\u5931\u8D25: ${l.message}`,{projectId:e,pid:o})}}const a=[m.join(h.UPLOAD_PROJECT_DIR,e),m.join(h.PROJECT_SOURCE_DIR,e),m.join(h.DIST_TARGET_DIR,e),m.join(h.LOG_BASE_DIR,e)],u=[],w=[];for(const o of a)if(n.existsSync(o))try{await n.promises.rm(o,{recursive:!0,force:!0}),u.push(o),i(t,"INFO",`[delete-project] \u76EE\u5F55\u5220\u9664\u6210\u529F: ${o}`,{projectId:e})}catch(l){w.push({path:o,error:l.message}),i(t,"ERROR",`[delete-project] \u76EE\u5F55\u5220\u9664\u5931\u8D25: ${o}`,{projectId:e,error:l.message})}else i(t,"INFO",`[delete-project] \u76EE\u5F55\u4E0D\u5B58\u5728\uFF0C\u8DF3\u8FC7\u5220\u9664: ${o}`,{projectId:e});const f={success:!0,message:`\u9879\u76EE ${e} \u5220\u9664\u5B8C\u6210`,projectId:e,deletedDirectories:u,failedDirectories:w};return w.length>0&&(f.message+=`\uFF0C\u4F46\u6709 ${w.length} \u4E2A\u76EE\u5F55\u5220\u9664\u5931\u8D25`,i(t,"WARN","[delete-project] \u90E8\u5206\u76EE\u5F55\u5220\u9664\u5931\u8D25",{projectId:e,failedDirs:w})),i(t,"INFO",`[delete-project] \u9879\u76EE\u5220\u9664\u5B8C\u6210: ${e}`,{projectId:e}),f}catch(a){throw i(t,"ERROR",`[delete-project] \u5220\u9664\u9879\u76EE\u5931\u8D25: ${a.message}`,{projectId:e,originalError:a.message}),a.isOperational?a:new E(`\u5220\u9664\u9879\u76EE\u5931\u8D25: ${a.message}`,{projectId:e,originalError:a.message})}}async function x(e,r,s,t){if(!e)throw new O("\u9879\u76EEID\u4E0D\u80FD\u4E3A\u7A7A",{field:"projectId"});if(r==null)throw new O("codeVersion\u4E0D\u80FD\u4E3A\u7A7A",{field:"codeVersion"});const a=Number(r);if(!Number.isFinite(a))throw new O("codeVersion\u5FC5\u987B\u662F\u6570\u5B57",{field:"codeVersion"});const u=m.join(h.PROJECT_SOURCE_DIR,e);if(!n.existsSync(u))throw new N("\u9879\u76EE\u4E0D\u5B58\u5728",{projectId:e});const w=m.join(h.UPLOAD_PROJECT_DIR,e),f=`${e}-v${a}.zip`,o=m.join(w,f);if(s!=="LATEST"){if(n.existsSync(o))return i(e,"INFO",`\u4F7F\u7528\u5DF2\u5B58\u5728\u7684\u5BFC\u51FA\u6587\u4EF6: ${o}`,{projectId:e,zipPath:o}),{success:!0,projectId:e,zipPath:o};throw new N(`\u6307\u5B9A\u7248\u672C\u7684zip\u5305\u4E0D\u5B58\u5728: ${o}`,{projectId:e,zipPath:o})}const l=m.join(u,"cpage_config.json");let y=!1;try{if(t)try{const R=JSON.stringify(t,null,2);await n.promises.writeFile(l,R,"utf8"),y=!0,i(e,"INFO",`\u5DF2\u521B\u5EFA\u914D\u7F6E\u6587\u4EF6: ${l}`,{projectId:e,configFilePath:l})}catch(R){throw i(e,"ERROR",`\u521B\u5EFA\u914D\u7F6E\u6587\u4EF6\u5931\u8D25: ${R.message}`,{projectId:e,error:R.message}),new A("\u521B\u5EFA\u914D\u7F6E\u6587\u4EF6\u5931\u8D25",{projectId:e,configFilePath:l,originalError:R.message})}const c=await g(e,r);return i(e,"INFO",`\u9879\u76EE\u5DF2\u5BFC\u51FA: ${c}`,{projectId:e,zipPath:c}),{success:!0,projectId:e,zipPath:c}}catch(c){throw c.isOperational?c:new E("\u5BFC\u51FA\u9879\u76EE\u5931\u8D25",{projectId:e,originalError:c&&c.message?F(c.message):c&&c.message})}finally{if(y&&n.existsSync(l))try{await n.promises.unlink(l),i(e,"INFO",`\u5DF2\u5220\u9664\u4E34\u65F6\u914D\u7F6E\u6587\u4EF6: ${l}`,{projectId:e,configFilePath:l})}catch(c){i(e,"WARN",`\u5220\u9664\u4E34\u65F6\u914D\u7F6E\u6587\u4EF6\u5931\u8D25: ${c.message}`,{projectId:e,error:c.message})}}}async function b(e,r){if(!e)throw new O("\u9879\u76EEID\u4E0D\u80FD\u4E3A\u7A7A",{field:"projectId"});if(r==null)throw new O("codeVersion\u4E0D\u80FD\u4E3A\u7A7A",{field:"codeVersion"});try{const s=await g(e,r);return i(e,"INFO",`\u5F53\u524D\u7248\u672C\u5DF2\u5907\u4EFD: ${s}`,{projectId:e,zipPath:s}),{success:!0,projectId:e,zipPath:s}}catch(s){throw s.isOperational?s:new E("\u5907\u4EFD\u5F53\u524D\u7248\u672C\u5931\u8D25",{projectId:e,originalError:s&&s.message?F(s.message):s&&s.message})}}export{_ as createProject,v as uploadProject,b as backupCurrentVersion,x as exportProject,g as backupProjectOfVersion,$ as cleanupProjectDirectory,T as handleFileUpload,C as deleteProject};export default{createProject:_,uploadProject:v,backupCurrentVersion:b,exportProject:x,backupProjectOfVersion:g,cleanupProjectDirectory:$,handleFileUpload:T,deleteProject:C};
@@ -0,0 +1,4 @@
1
+ import{exec as d}from"child_process";import y from"path";import a from"fs";import{log as g,logBuild as w}from"../log/logUtils.js";import p from"../error/buildErrorParser.js";import b from"../../../config/index.js";import{BusinessError as N,SystemError as x,FileError as A,ResourceError as T}from"../error/errorHandler.js";import{installDependencies as k}from"../buildDependency/dependencyManager.js";const U=b.PROJECT_SOURCE_DIR,W=b.DIST_TARGET_DIR,F=new Set;let O=0;async function B({req:t,projectPath:s,projectId:r,outStream:o}){try{const l=y.join(s,"dist"),u=W,m=y.join(u,r,"dist");if(!a.existsSync(l)){const e=`\u672A\u627E\u5230dist\u76EE\u5F55: ${l}`;g(r,"WARN",e,{projectId:r}),o&&o.write(`${e}
2
+ `);return}if(a.existsSync(u)||a.mkdirSync(u,{recursive:!0}),a.existsSync(m)&&await a.promises.rm(m,{recursive:!0,force:!0}),a.promises.cp)await a.promises.cp(l,m,{recursive:!0});else{const e=(i,c)=>{if(a.statSync(i).isDirectory()){a.existsSync(c)||a.mkdirSync(c,{recursive:!0});for(const f of a.readdirSync(i))e(y.join(i,f),y.join(c,f))}else a.copyFileSync(i,c)};e(l,m)}const n=`dist\u76EE\u5F55\u5DF2\u62F7\u8D1D\u5230: ${m}`;g(r,"INFO",n,{projectId:r}),o&&o.write(`${n}
3
+ `)}catch(l){const u=`\u62F7\u8D1Ddist\u76EE\u5F55\u5931\u8D25: ${l.message}`;throw g(r,"ERROR",u,{projectId:r}),o&&o.write(`${u}
4
+ `),l}}function D(t,s,r,o=[]){return new Promise((l,u)=>{const m=Array.isArray(o)&&o.length>0?" -- "+o.map(e=>String(e)).join(" "):"",n=`cd ${s} && pnpm run ${r}${m}`;w(t,"INFO","\u6267\u884C\u547D\u4EE4",{command:n});try{g(t,"INFO","\u6267\u884C\u6784\u5EFA\u811A\u672C",{command:n,cwd:s})}catch{}d(n,{env:process.env,maxBuffer:10*1024*1024},(e,i,c)=>{if(e){w(t,"ERROR","\u6267\u884C\u9519\u8BEF",{error:e.message,stderr:c});const R=new p,f=c||e.message,h=R.parseBuildError(f,t),S=new x(h,{originalError:e.message,command:n});return u(S)}w(t,"INFO","\u811A\u672C\u6267\u884C\u5B8C\u6210",{stdout:i}),l(i)})})}async function J(t,s){const r=y.join(U,s),o=y.join(r,"package.json");if(!a.existsSync(o))throw new T("\u9879\u76EE\u7F3A\u5C11package.json\u6587\u4EF6",{projectId:s,projectPath:r});let u;try{u=JSON.parse(a.readFileSync(o,"utf8"))}catch(i){throw new A("package.json\u6587\u4EF6\u683C\u5F0F\u9519\u8BEF",{projectId:s,jsonFilePath:o,originalError:i.message})}const n=u.scripts.build;if(!n)throw g(s,"WARN","\u9879\u76EE\u7F3A\u5C11build\u811A\u672C",{projectId:s,requestId:t.requestId}),new N("\u9879\u76EE\u7F3A\u5C11build\u811A\u672C",{projectId:s});if(F.has(s))throw new N("\u8BE5\u9879\u76EE\u6B63\u5728\u6784\u5EFA\u4E2D",{projectId:s});const e=Number.isFinite(b.MAX_BUILD_CONCURRENCY)?b.MAX_BUILD_CONCURRENCY:20;if(O>=e)throw new N("\u5E76\u53D1\u5DF2\u6EE1\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5",{currentBuilds:O,maxConcurrency:e});F.add(s),O+=1;try{let i="";t&&t.query&&typeof t.query.basePath=="string"&&(i=t.query.basePath),i&&(i.startsWith("/")||(i="/"+i),i.endsWith("/")||(i=i+"/"));const c=[];if(typeof n=="string"&&n.includes("vite")&&i&&c.push("--base",i),typeof n=="string"&&n.includes("vite")&&c.push("--debug"),g(s,"INFO","\u5F00\u59CB\u5B89\u88C5\u4F9D\u8D56",{projectId:s}),await k(t,s,r),typeof n=="string"&&n.includes("vite")){const R=["exec","vite","build",...c,"--debug"],f=`cd ${r} && pnpm ${R.join(" ")}`;w(s,"INFO","\u6267\u884C\u547D\u4EE4(\u76F4\u63A5vite)",{command:f});try{g(s,"INFO","\u6267\u884C\u6784\u5EFA\u811A\u672C(\u76F4\u63A5vite)",{command:f,cwd:r})}catch{}await new Promise((h,S)=>{d(f,(E,v,P)=>{if(E){w(s,"ERROR","\u6267\u884C\u9519\u8BEF",{error:E.message,stderr:P});const $=new p,C=P||E.message,_=$.parseBuildError(C,s),M=new x(_,{originalError:E.message,command:f});return S(M)}w(s,"INFO","\u811A\u672C\u6267\u884C\u5B8C\u6210",{stdout:v}),h(v)})})}else g(s,"INFO","\u5F00\u59CB\u540C\u6B65\u6267\u884C build \u811A\u672C",{projectId:s}),await D(s,r,"build",c);return await B({req:t,projectPath:r,projectId:s}),{success:!0,message:"\u6784\u5EFA\u5B8C\u6210",projectId:s}}finally{F.delete(s),O-=1}}export{J as buildProject,B as copyBuildOutputToTarget,D as runBuildScript};