nuwax-file-server 1.2.3 → 1.2.5

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 (44) hide show
  1. package/README.md +131 -151
  2. package/dist/appConfig/index.js +1 -1
  3. package/dist/cli.js +1 -1
  4. package/dist/config/swagger.js +1 -1
  5. package/dist/routes/buildRoutes.js +1 -1
  6. package/dist/routes/codeRoutes.js +1 -1
  7. package/dist/routes/computerRoutes.js +1 -1
  8. package/dist/routes/projectRoutes.js +1 -1
  9. package/dist/scheduler/pnpmPruneScheduler.js +2 -2
  10. package/dist/server.js +1 -1
  11. package/dist/service/codeService.js +2 -2
  12. package/dist/service/projectService.js +1 -1
  13. package/dist/utils/build/buildProjectUtils.js +4 -4
  14. package/dist/utils/build/keepAliveDevUtils.js +1 -1
  15. package/dist/utils/build/processManager.js +9 -9
  16. package/dist/utils/build/restartDevUtils.js +1 -1
  17. package/dist/utils/build/startDevUtils.js +1 -1
  18. package/dist/utils/build/stopDevUtils.js +2 -2
  19. package/dist/utils/build/syntaxCheckUtils.js +7 -7
  20. package/dist/utils/buildArg/extraArgsUtils.js +1 -1
  21. package/dist/utils/buildArg/portPool.js +1 -1
  22. package/dist/utils/buildArg/portUtils.js +7 -7
  23. package/dist/utils/buildDependency/dependencyManager.js +17 -17
  24. package/dist/utils/buildJudge/aliveJudgeUtils.js +1 -1
  25. package/dist/utils/buildJudge/restartJudgeUtils.js +1 -1
  26. package/dist/utils/buildPermission/permissionManager.js +3 -3
  27. package/dist/utils/common/npmrcUtils.js +3 -3
  28. package/dist/utils/common/zipUtils.js +1 -1
  29. package/dist/utils/computer/computerFileUtils.js +1 -1
  30. package/dist/utils/computer/computerUtils.js +1 -1
  31. package/dist/utils/envUtils.js +3 -3
  32. package/dist/utils/error/buildErrorParser.js +18 -18
  33. package/dist/utils/error/errorHandler.js +1 -1
  34. package/dist/utils/log/getDevLogUtils.js +2 -2
  35. package/dist/utils/log/logCacheManager.js +2 -2
  36. package/dist/utils/log/logUtils.js +2 -2
  37. package/dist/utils/project/backupUtils.js +1 -1
  38. package/dist/utils/project/copyProjectUtils.js +1 -1
  39. package/dist/utils/project/frameworkDetectorUtils.js +1 -1
  40. package/dist/utils/project/getContentUtils.js +1 -1
  41. package/dist/utils/project/initProjectCleanupUtils.js +1 -1
  42. package/dist/utils/project/uploadAttachmentFileUtils.js +1 -1
  43. package/dist/utils/serviceManager.js +3 -3
  44. package/package.json +2 -2
package/README.md CHANGED
@@ -1,159 +1,151 @@
1
1
  # nuwax-file-server
2
2
 
3
- 跨平台的文件服务部署工具,支持 WindowsLinuxmacOS 操作系统。
3
+ Cross-platform file service deployment tooling for Windows, Linux, and macOS.
4
4
 
5
- ## 功能特性
5
+ ## Features
6
6
 
7
- - **CLI 命令行操作**: 支持 start / stop / restart / status 命令
8
- - **跨平台支持**: WindowsLinuxmacOS 完美兼容
9
- - **环境变量配置**: 支持通过环境变量和命令行参数配置
10
- - **健康检查端点**: 提供 /health 接口用于服务检活
11
- - **PID 文件管理**: 自动管理服务进程 ID
7
+ - **CLI**: `start`, `stop`, `restart`, `status`
8
+ - **Cross-platform**: Windows, Linux, and macOS
9
+ - **Configuration**: Environment variables and CLI flags
10
+ - **Health endpoint**: `GET /health` for liveness checks
11
+ - **PID file**: Tracks and manages the server process
12
12
 
13
- ## 安装部署
13
+ ## Installation
14
14
 
15
- ### 本地开发安装
15
+ ### Local development
16
16
 
17
17
  ```bash
18
- # 克隆项目
19
18
  git clone <repository-url>
20
19
  cd nuwax-file-server
21
20
 
22
- # 安装依赖
23
21
  npm install
24
22
 
25
- # 本地运行(开发模式)
23
+ # Development mode
26
24
  npm run dev
27
25
 
28
- # 本地运行(生产模式)
26
+ # Production mode (local)
29
27
  npm run prod
30
28
  ```
31
29
 
32
- ### 全局安装 CLI 工具
30
+ ### Global CLI install
33
31
 
34
32
  ```bash
35
- # 在项目根目录执行
33
+ # From the project root
36
34
  npm install -g .
37
35
 
38
- # 然后可以在任何位置使用
36
+ # Then run from anywhere
39
37
  nuwax-file-server --help
40
38
  ```
41
39
 
42
- ### 系统要求
40
+ ### Requirements
43
41
 
44
- - Node.js >= 22.0.0ES Module 原生支持)
45
- - zip/unzip 工具(用于项目打包)
46
- - pnpm(推荐)或 npm/yarn
42
+ - Node.js >= 22.0.0 (native ES modules)
43
+ - zip/unzip (for project archives)
44
+ - pnpm (recommended) or npm/yarn
47
45
 
48
- ## CLI 命令
46
+ ## CLI
49
47
 
50
- nuwax-file-server 提供以下 CLI 命令:
48
+ Available commands:
51
49
 
52
- ### 基本命令
50
+ ### Basics
53
51
 
54
52
  ```bash
55
- # 启动服务(默认使用 env.production 配置)
53
+ # Start (defaults to env.production)
56
54
  nuwax-file-server start
57
55
 
58
- # 启动服务(指定环境)
56
+ # Start with a specific env
59
57
  nuwax-file-server start --env development
60
58
  nuwax-file-server start --env production
61
59
  nuwax-file-server start --env test
62
60
 
63
- # 停止服务
61
+ # Stop
64
62
  nuwax-file-server stop
65
63
 
66
- # 强制停止服务
64
+ # Force stop
67
65
  nuwax-file-server stop --force
68
66
 
69
- # 重启服务
67
+ # Restart
70
68
  nuwax-file-server restart
71
69
 
72
- # 查看服务状态
70
+ # Status
73
71
  nuwax-file-server status
74
72
  ```
75
73
 
76
- ### 高级选项
74
+ ### Advanced
77
75
 
78
76
  ```bash
79
- # 指定端口启动
77
+ # Custom port
80
78
  nuwax-file-server start --port 8080
81
79
 
82
- # 指定配置文件
80
+ # Custom config file
83
81
  nuwax-file-server start --config /path/to/config.json
84
82
 
85
- # 组合使用
83
+ # Combined
86
84
  nuwax-file-server start --env development --port 3000
87
85
  ```
88
86
 
89
- ### 使用 npm scripts
87
+ ### npm scripts
90
88
 
91
89
  ```bash
92
- # 启动服务
93
90
  npm run cli:start
94
- npm run cli:start:dev # 开发环境
95
- npm run cli:start:prod # 生产环境
96
- npm run cli:start:test # 测试环境
91
+ npm run cli:start:dev # development
92
+ npm run cli:start:prod # production
93
+ npm run cli:start:test # test
97
94
 
98
- # 停止服务
99
95
  npm run cli:stop
100
-
101
- # 重启服务
102
96
  npm run cli:restart
103
-
104
- # 查看状态
105
97
  npm run cli:status
106
98
  ```
107
99
 
108
- ## 环境变量配置
100
+ ## Environment variables
109
101
 
110
- 完整的环境变量配置说明请参考:[环境变量配置文档](./docs/ENV.md)
102
+ Full reference: [Environment variables](./docs/ENV.md)
111
103
 
112
- ### 快速使用示例
104
+ ### Quick example
113
105
 
114
106
  ```bash
115
- # 使用 env.production 默认配置(推荐)
107
+ # Defaults from env.production (typical)
116
108
  nuwax-file-server start --env production --port 60000
117
109
 
118
- # 自定义路径配置(根据实际需求删减)
110
+ # Override paths (trim to what you need)
119
111
  nuwax-file-server start --env production --port 60000 \
120
112
  PROJECT_SOURCE_DIR=/data/projects \
121
113
  DIST_TARGET_DIR=/var/www/html \
122
114
  UPLOAD_PROJECT_DIR=/data/uploads
123
115
  ```
124
116
 
125
- ### 核心路径变量
117
+ ### Core path variables
126
118
 
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 日志目录 |
119
+ | Variable | Purpose |
120
+ | -------- | ------- |
121
+ | `INIT_PROJECT_DIR` | Scaffold / init project directory |
122
+ | `UPLOAD_PROJECT_DIR` | Uploaded project archives |
123
+ | `PROJECT_SOURCE_DIR` | Project source tree |
124
+ | `DIST_TARGET_DIR` | Build output (e.g. nginx root) |
125
+ | `LOG_BASE_DIR` | Log directory root |
126
+ | `COMPUTER_WORKSPACE_DIR` | “Computer” workspace |
127
+ | `COMPUTER_LOG_DIR` | “Computer” logs |
136
128
 
137
- 完整配置项和场景示例请参阅:[环境变量配置文档](./docs/ENV.md)
129
+ More options and scenarios: [Environment variables](./docs/ENV.md)
138
130
 
139
- ### 命令行覆盖
131
+ ### CLI precedence
140
132
 
141
133
  ```bash
142
- # 端口优先级: 命令行 > 环境变量 > 默认值
134
+ # Port: CLI > env > default
143
135
  nuwax-file-server start --env production --port 8080
144
136
  ```
145
137
 
146
- ## 健康检查端点
138
+ ## Health check
147
139
 
148
- 服务提供 `/health` 端点用于健康检查和监控。
140
+ The server exposes `GET /health` for monitoring and probes.
149
141
 
150
- ### 请求
142
+ ### Request
151
143
 
152
144
  ```bash
153
145
  curl http://localhost:60000/health
154
146
  ```
155
147
 
156
- ### 响应
148
+ ### Example response
157
149
 
158
150
  ```json
159
151
  {
@@ -174,167 +166,155 @@ curl http://localhost:60000/health
174
166
  }
175
167
  ```
176
168
 
177
- ### 响应字段说明
169
+ ### Response fields
178
170
 
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 | 当前环境 |
171
+ | Field | Type | Description |
172
+ | ----- | ---- | ----------- |
173
+ | status | string | `"ok"` when healthy |
174
+ | timestamp | number | Unix time (ms) |
175
+ | uptime | number | Uptime in seconds |
176
+ | version | string | Server version |
177
+ | platform | string | `darwin` / `linux` / `win32` |
178
+ | nodeVersion | string | Node.js version |
179
+ | pid | number | Process ID |
180
+ | memory | object | Memory usage (MB) |
181
+ | env | string | Active environment name |
190
182
 
191
- ## 跨平台说明
183
+ ## Cross-platform notes
192
184
 
193
185
  ### Windows
194
186
 
195
- - PID 文件存储在 `%TEMP%\nuwax-file-server\`
196
- - 进程停止使用 `taskkill /F /PID` 命令
197
- - 路径分隔符使用反斜杠 `\`
187
+ - PID file under `%TEMP%\nuwax-file-server\`
188
+ - Stop uses `taskkill /F /PID`
189
+ - Paths use backslashes `\`
198
190
 
199
- ### Linux/macOS
191
+ ### Linux / macOS
200
192
 
201
- - PID 文件存储在 `/tmp/nuwax-file-server/`
202
- - 进程停止使用 kill 信号 (SIGTERM/SIGKILL)
203
- - 路径分隔符使用正斜杠 `/`
193
+ - PID file under `/tmp/nuwax-file-server/`
194
+ - Stop uses signals (SIGTERM / SIGKILL)
195
+ - Paths use `/`
204
196
 
205
- ### 通用
197
+ ### General
206
198
 
207
- - 所有路径使用 `path.join()` 处理
208
- - 使用 `os.tmpdir()` 获取临时目录
209
- - 使用 `cross-spawn` 执行 shell 命令
210
- - 使用 `tree-kill` 杀死进程树
199
+ - Paths built with `path.join()`
200
+ - Temp dir from `os.tmpdir()`
201
+ - Shell commands via `cross-spawn`
202
+ - Process trees via `tree-kill`
211
203
 
212
- ## pnpm 磁盘空间优化
204
+ ## pnpm disk usage
213
205
 
214
- 本项目自动为所有创建、上传或复制的项目注入优化的 `.npmrc` 配置文件,以优化 pnpm 的磁盘空间使用。
206
+ Created, uploaded, or copied projects get an optimized `.npmrc` to reduce pnpm disk footprint.
215
207
 
216
- ### 自动优化
208
+ ### Automatic injection
217
209
 
218
- 在以下操作中,系统会自动为项目创建 `.npmrc` 配置文件:
210
+ `.npmrc` is applied when:
219
211
 
220
- - **创建项目** (`/create-project`)
221
- - **上传项目** (`/upload-project`)
222
- - **复制项目** (`/copy-project`)
212
+ - **Create project** (`/create-project`)
213
+ - **Upload project** (`/upload-project`)
214
+ - **Copy project** (`/copy-project`)
223
215
 
224
- ### 检查磁盘使用
216
+ ### Inspect disk usage
225
217
 
226
218
  ```bash
227
- # 自动检测当前环境配置
228
219
  npm run pnpm:check
229
220
 
230
- # 或指定特定环境
231
- npm run pnpm:check:dev # 开发环境
232
- npm run pnpm:check:prod # 生产环境
233
- npm run pnpm:check:test # 测试环境
221
+ npm run pnpm:check:dev # development
222
+ npm run pnpm:check:prod # production
223
+ npm run pnpm:check:test # test
234
224
 
235
- # 或手动指定目录
236
225
  bash scripts/pnpm-check.sh /path/to/projects
237
226
  ```
238
227
 
239
- ### 清理未使用的包
228
+ ### Prune unused packages
240
229
 
241
230
  ```bash
242
- # 立即清理
243
231
  npm run pnpm:prune
244
232
 
245
- # 查看日志的清理(推荐)
246
233
  npm run pnpm:prune:log
247
234
  ```
248
235
 
249
- ### 定时清理(自动集成)
236
+ ### Scheduled prune (built-in)
250
237
 
251
- 定时任务已集成到主应用,随应用启动自动运行,通过环境变量配置:
238
+ The app can run a cron-style prune job. Configure with env vars, for example:
252
239
 
253
240
  ```yaml
254
- # docker-compose.yml .env 文件
241
+ # docker-compose.yml or .env
255
242
  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)
243
+ PNPM_PRUNE_ENABLED: "true"
244
+ PNPM_PRUNE_SCHEDULE: "0 2 * * 0"
245
+ PNPM_PRUNE_TIMEZONE: "Asia/Shanghai"
246
+ PNPM_PRUNE_RUN_ON_START: "false"
260
247
  ```
261
248
 
262
- **常用时间配置:**
249
+ **Example schedules:**
263
250
 
264
251
  ```bash
265
- "0 2 * * 0" # 每周日凌晨 2 点
266
- "0 3 * * *" # 每天凌晨 3 点
267
- "0 2 1 * *" # 每月 1 号凌晨 2 点
268
- "0 */6 * * *" # 6 小时
252
+ "0 2 * * 0" # Sunday 02:00
253
+ "0 3 * * *" # Daily 03:00
254
+ "0 2 1 * *" # 1st of month 02:00
255
+ "0 */6 * * *" # Every 6 hours
269
256
  ```
270
257
 
271
- ### 预期效果
258
+ ### Expected benefits
272
259
 
273
- - 磁盘空间节省 50-70%(多项目共享依赖)
274
- - 安装速度提升(使用国内镜像)
275
- - 完全自动化,无需手动配置
276
- - 定期清理,保持 store 整洁
260
+ - Lower disk use when many projects share dependencies (often a large share of savings vs naive installs)
261
+ - Faster installs when using a nearby registry mirror
262
+ - Automated `.npmrc`; periodic store maintenance when enabled
277
263
 
278
- ## 故障排除
264
+ ## Troubleshooting
279
265
 
280
- ### 服务无法启动
266
+ ### Server will not start
281
267
 
282
- 1. 检查端口是否被占用
283
- 2. 检查日志文件权限
284
- 3. 检查环境配置文件是否存在
268
+ 1. Check the port is free
269
+ 2. Check log directory permissions
270
+ 3. Confirm env files exist
285
271
 
286
272
  ```bash
287
- # 查看详细日志
288
273
  nuwax-file-server start --env development
289
274
  ```
290
275
 
291
- ### 服务无法停止
276
+ ### Server will not stop
292
277
 
293
278
  ```bash
294
- # 使用强制停止
295
279
  nuwax-file-server stop --force
296
280
 
297
- # 手动查找并停止进程
298
281
  ps aux | grep nuwax-file-server
299
282
  kill -9 <pid>
300
283
  ```
301
284
 
302
- ### 健康检查失败
285
+ ### Health check fails
303
286
 
304
- 1. 检查服务是否正在运行
305
- 2. 检查端口是否正确
306
- 3. 检查防火墙设置
287
+ 1. Confirm the process is running
288
+ 2. Confirm host/port
289
+ 3. Check firewall rules
307
290
 
308
291
  ```bash
309
- # 查看服务状态
310
292
  nuwax-file-server status
311
-
312
- # 测试健康检查
313
293
  curl http://localhost:60000/health
314
294
  ```
315
295
 
316
- ## 开发指南
296
+ ## Development
317
297
 
318
- ### 添加新命令
298
+ ### Adding a command
319
299
 
320
- `src/cli.js` 中使用 commander 定义新命令:
300
+ In `src/cli.js`, use Commander:
321
301
 
322
302
  ```javascript
323
303
  program
324
304
  .command("newcommand")
325
- .description("新命令描述")
326
- .option("--option", "选项描述")
305
+ .description("Description of the new command")
306
+ .option("--option", "Option description")
327
307
  .action((options) => {
328
- // 命令逻辑
308
+ // handler
329
309
  });
330
310
  ```
331
311
 
332
- ### 添加新配置项
312
+ ### Adding a setting
333
313
 
334
- 1. `env.development` / `env.production` / `env.test` 中添加环境变量
335
- 2. `config/index.js` 中读取配置
336
- 3. 在文档中说明
314
+ 1. Add variables to `src/env.development` / `src/env.production` / `src/env.test`
315
+ 2. Wire them through `src/appConfig/index.js` as needed
316
+ 3. Document the change
337
317
 
338
- ## 许可证
318
+ ## License
339
319
 
340
320
  ISC
@@ -1 +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;
1
+ import E from"path";import n from"fs";import o from"os";import{fileURLToPath as L}from"url";import r from"dotenv";const T=E.dirname(L(import.meta.url));r.config();function O(){const e=process.env.LOG_BASE_DIR,_=E.join(o.tmpdir(),"nuwax-file-server","project_logs");if(!e)return _;try{return n.mkdirSync(e,{recursive:!0}),e}catch{try{n.mkdirSync(_,{recursive:!0})}catch{}return _}}const I=process.env.NODE_ENV||"development";function R(e){const _=E.join(T,"..",`env.${e}`);if(n.existsSync(_))r.config({path:_}),console.log(`Environment configuration file env.${e} loaded`);else{const s=`Environment configuration file env.${e} does not exist, please create the corresponding environment configuration file and try again`;throw console.error(s),new Error(s)}}R(I);const p={NODE_ENV:I,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"?E.join(process.env.TEMP||"","nuwax-file-server"):E.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"?E.join(process.env.TEMP||"","nuwax-file-server","logs"):E.join("/tmp","nuwax-file-server","logs")),CLI_IS_WINDOWS:process.platform==="win32"};export default p;
package/dist/cli.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{Command as N}from"commander";import g from"path";import y from"os";import u from"fs-extra";import{spawn as D}from"cross-spawn";import{fileURLToPath as T}from"url";import{createRequire as b}from"module";var L=g.dirname(T(import.meta.url)),K=b(import.meta.url),E="1.2.3",l=new N,$={name:"nuwax-file-server",pidDir:g.join(y.tmpdir(),"nuwax-file-server"),logDir:g.join(y.tmpdir(),"nuwax-file-server","logs"),pidFile:g.join(y.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 a(e,o="reset"){console.log(`${d[o]}${e}${d.reset}`)}function f(e){console.error(`${d.red}ERROR: ${e}${d.reset}`)}function r(e){console.log(`${d.green}${e}${d.reset}`)}function s(e){console.log(`${d.blue}${e}${d.reset}`)}function h(){return $.pidFile}function R(){try{let e=h();if(u.existsSync(e)){let o=u.readFileSync(e,"utf8");return JSON.parse(o)}}catch{}return null}function j(e){let o=g.dirname(h());u.ensureDirSync(o),u.writeFileSync(h(),JSON.stringify(e,null,2))}function x(){let e=h();u.existsSync(e)&&u.removeSync(e)}function v(e){try{return process.kill(e,0),!0}catch(o){return o.code==="ESRCH"||o.code==="EPERM",!1}}async function k(e,o=!1){return new Promise(t=>{if(process.platform==="win32"){let i=o?["/F","/PID",String(e)]:["/PID",String(e)],n=D("taskkill",i,{stdio:"pipe"});n.on("error",c=>{f(`\u505C\u6B62\u8FDB\u7A0B\u5931\u8D25: ${c.message}`),t(!1)}),n.on("close",c=>{c===0?(r(`\u8FDB\u7A0B ${e} \u5DF2\u505C\u6B62`),t(!0)):o?D("taskkill",["/F","/PID",String(e)],{stdio:"pipe"}).on("close",m=>{t(m===0)}):t(!1)})}else try{process.kill(-e,o?"SIGKILL":"SIGTERM"),r(`\u8FDB\u7A0B\u7EC4 ${e} \u5DF2\u505C\u6B62`),t(!0)}catch(i){if(i.code==="ESRCH")r(`\u8FDB\u7A0B ${e} \u4E0D\u5B58\u5728\uFF0C\u89C6\u4E3A\u5DF2\u505C\u6B62`),t(!0);else try{process.kill(e,o?"SIGKILL":"SIGTERM"),r(`\u8FDB\u7A0B ${e} \u5DF2\u505C\u6B62`),t(!0)}catch(n){n.code==="ESRCH"?(r(`\u8FDB\u7A0B ${e} \u4E0D\u5B58\u5728\uFF0C\u89C6\u4E3A\u5DF2\u505C\u6B62`),t(!0)):(f(`\u505C\u6B62\u8FDB\u7A0B\u5931\u8D25: ${n.message}`),t(!1))}}})}async function F(e){let{env:o,port:t,config:w}=e,i=new Set(["env","port","config","force","help","version"]);process.argv.slice(2).forEach(p=>{if(p.startsWith("--")&&p.includes("=")){let O=p.indexOf("="),P=p.slice(2,O);if(!i.has(P)){let _=p.slice(O+1);process.env[P]=_,s(`CLI \u53C2\u6570\u8986\u76D6\u73AF\u5883\u53D8\u91CF: ${P}=${_}`)}}}),s(`\u542F\u52A8 ${$.name} \u670D\u52A1...`);let n=R();n&&v(n.pid)&&(f(`\u670D\u52A1\u5DF2\u5728\u8FD0\u884C\u4E2D (PID: ${n.pid})`),s("\u8BF7\u4F7F\u7528 'nuwax-file-server stop' \u505C\u6B62\u73B0\u6709\u670D\u52A1\u540E\u518D\u8BD5"),process.exit(1));let c=o||"production";process.env.NODE_ENV=c,s(`\u4F7F\u7528\u73AF\u5883: ${c}`),t&&(process.env.PORT=t,s(`\u4F7F\u7528\u7AEF\u53E3: ${t}`)),w&&(process.env.CONFIG_FILE=w,s(`\u4F7F\u7528\u914D\u7F6E\u6587\u4EF6: ${w}`)),u.ensureDirSync($.logDir);let I=g.join(L,"server.js"),m=D("node",[I],{env:{...process.env,NODE_ENV:c},stdio:"inherit",detached:!0,cwd:process.cwd(),windowsHide:!0});m.on("error",p=>{f(`\u542F\u52A8\u670D\u52A1\u5931\u8D25: ${p.message}`),process.exit(1)}),await new Promise(p=>setTimeout(p,2e3)),v(m.pid)||(f("\u670D\u52A1\u542F\u52A8\u5931\u8D25"),process.exit(1));let S={pid:m.pid,startedAt:new Date().toISOString(),env:o||"production",port:t||process.env.PORT||"60000",version:E,platform:process.platform};j(S),m.unref(),r(`\u670D\u52A1\u5DF2\u542F\u52A8 (PID: ${m.pid})`),a(`\u670D\u52A1\u8FD0\u884C\u5728: http://localhost:${S.port}`,"cyan"),a(`\u73AF\u5883: ${S.env}`,"cyan"),a(`\u5E73\u53F0: ${S.platform}`,"cyan"),a(`PID \u6587\u4EF6: ${h()}`,"cyan")}async function C(e){let{force:o}=e;s(`\u505C\u6B62 ${$.name} \u670D\u52A1...`);let t=R();t||(f("\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)),v(t.pid)||(s("\u670D\u52A1\u8FDB\u7A0B\u5DF2\u505C\u6B62\uFF0C\u6E05\u7406 PID \u6587\u4EF6..."),x(),r("\u670D\u52A1\u5DF2\u505C\u6B62"),process.exit(0)),await k(t.pid,o)&&(await new Promise(n=>setTimeout(n,1e3)),v(t.pid)||(x(),r("\u670D\u52A1\u5DF2\u505C\u6B62"),process.exit(0))),o&&(f("\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 k(t.pid,!0)&&(await new Promise(n=>setTimeout(n,1e3)),v(t.pid)||(x(),r("\u670D\u52A1\u5DF2\u5F3A\u5236\u505C\u6B62"),process.exit(0))),f("\u505C\u6B62\u670D\u52A1\u5931\u8D25"),process.exit(1)}async function A(e){a(`\u91CD\u542F ${$.name} \u670D\u52A1...`,"yellow"),s("\u505C\u6B62\u73B0\u6709\u670D\u52A1...");try{await C({force:!1})}catch{s("\u670D\u52A1\u672A\u8FD0\u884C\u6216\u5DF2\u505C\u6B62")}await new Promise(o=>setTimeout(o,2e3)),s("\u542F\u52A8\u670D\u52A1..."),await F(e),r("\u670D\u52A1\u5DF2\u91CD\u542F")}function G(){s(`${$.name} \u670D\u52A1\u72B6\u6001:`);let e=R();e||(a("\u670D\u52A1\u672A\u8FD0\u884C","yellow"),process.exit(0));let o=v(e.pid);if(console.log(""),console.log(` \u670D\u52A1\u540D\u79F0: ${$.name}`),console.log(` \u8FD0\u884C\u72B6\u6001: ${o?"\u8FD0\u884C\u4E2D":"\u5DF2\u505C\u6B62"}`),console.log(` \u8FDB\u7A0B ID: ${e.pid}`),console.log(` \u73AF\u5883: ${e.env||"production"}`),console.log(` \u7AEF\u53E3: ${e.port||"60000"}`),console.log(` \u7248\u672C: ${e.version||E}`),console.log(` \u5E73\u53F0: ${e.platform||process.platform}`),console.log(` \u542F\u52A8\u65F6\u95F4: ${e.startedAt||"\u672A\u77E5"}`),e.startedAt){let t=new Date(e.startedAt),i=Math.floor((new Date-t)/1e3),n=Math.floor(i/3600),c=Math.floor(i%3600/60),I=i%60;console.log(` \u8FD0\u884C\u65F6\u95F4: ${n}\u5C0F\u65F6 ${c}\u5206 ${I}\u79D2`)}console.log(` PID \u6587\u4EF6: ${h()}`),console.log(""),o?a("\u670D\u52A1\u8FD0\u884C\u6B63\u5E38","green"):(a("\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 H(){l.name("nuwax-file-server").description("\u8DE8\u5E73\u53F0\u6587\u4EF6\u670D\u52A1\u90E8\u7F72\u5DE5\u5177\uFF0C\u652F\u6301 start/stop/restart/status").version(E,"-v, --version","\u663E\u793A\u7248\u672C\u53F7").helpOption("-h, --help","\u663E\u793A\u5E2E\u52A9\u4FE1\u606F"),l.command("start").allowUnknownOption().description("\u542F\u52A8\u670D\u52A1").option("--env <environment>","\u73AF\u5883: development|production|test","production").option("--port <port>","\u670D\u52A1\u7AEF\u53E3\u53F7").option("--config <path>","\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action(F),l.command("stop").description("\u505C\u6B62\u670D\u52A1").option("--force","\u5F3A\u5236\u505C\u6B62").action(C),l.command("restart").allowUnknownOption().description("\u91CD\u542F\u670D\u52A1").option("--env <environment>","\u73AF\u5883: development|production|test","production").option("--port <port>","\u670D\u52A1\u7AEF\u53E3\u53F7").option("--config <path>","\u81EA\u5B9A\u4E49\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").action(A),l.command("status").description("\u67E5\u770B\u670D\u52A1\u72B6\u6001").action(G),l.command("help").description("\u663E\u793A\u5E2E\u52A9\u4FE1\u606F").action(()=>{l.outputHelp()}),l.parse(process.argv),process.argv.slice(2).length||(l.outputHelp(),process.exit(0))}H();
2
+ import{Command as C}from"commander";import S from"path";import x from"os";import m from"fs-extra";import{spawn as D}from"cross-spawn";import{fileURLToPath as T}from"url";import{createRequire as N}from"module";var U=S.dirname(T(import.meta.url)),K=N(import.meta.url),R="1.2.5",a=new C,v={name:"nuwax-file-server",pidDir:S.join(x.tmpdir(),"nuwax-file-server"),logDir:S.join(x.tmpdir(),"nuwax-file-server","logs"),pidFile:S.join(x.tmpdir(),"nuwax-file-server","server.pid")},f={reset:"\x1B[0m",red:"\x1B[31m",green:"\x1B[32m",yellow:"\x1B[33m",blue:"\x1B[34m",cyan:"\x1B[36m"};function l(e,o="reset"){console.log(`${f[o]}${e}${f.reset}`)}function d(e){console.error(`${f.red}ERROR: ${e}${f.reset}`)}function r(e){console.log(`${f.green}${e}${f.reset}`)}function n(e){console.log(`${f.blue}${e}${f.reset}`)}function $(){return v.pidFile}function E(){try{let e=$();if(m.existsSync(e)){let o=m.readFileSync(e,"utf8");return JSON.parse(o)}}catch{}return null}function L(e){let o=S.dirname($());m.ensureDirSync(o),m.writeFileSync($(),JSON.stringify(e,null,2))}function I(){let e=$();m.existsSync(e)&&m.removeSync(e)}function h(e){try{return process.kill(e,0),!0}catch(o){return o.code==="ESRCH"||o.code==="EPERM",!1}}async function _(e,o=!1){return new Promise(t=>{if(process.platform==="win32"){let i=o?["/F","/PID",String(e)]:["/PID",String(e)],s=D("taskkill",i,{stdio:"pipe"});s.on("error",c=>{d(`Stop process failed: ${c.message}`),t(!1)}),s.on("close",c=>{c===0?(r(`Process ${e} stopped`),t(!0)):o?D("taskkill",["/F","/PID",String(e)],{stdio:"pipe"}).on("close",u=>{t(u===0)}):t(!1)})}else try{process.kill(-e,o?"SIGKILL":"SIGTERM"),r(`Process group ${e} stopped`),t(!0)}catch(i){if(i.code==="ESRCH")r(`Process ${e} does not exist, already stopped`),t(!0);else try{process.kill(e,o?"SIGKILL":"SIGTERM"),r(`Process ${e} stopped`),t(!0)}catch(s){s.code==="ESRCH"?(r(`Process ${e} does not exist, already stopped`),t(!0)):(d(`Stop process failed: ${s.message}`),t(!1))}}})}async function F(e){let{env:o,port:t,config:g}=e,i=new Set(["env","port","config","force","help","version"]);process.argv.slice(2).forEach(p=>{if(p.startsWith("--")&&p.includes("=")){let b=p.indexOf("="),P=p.slice(2,b);if(!i.has(P)){let O=p.slice(b+1);process.env[P]=O,n(`CLI parameter overrides environment variable: ${P}=${O}`)}}}),n(`Start ${v.name} service...`);let s=E();s&&h(s.pid)&&(d(`Service is already running (PID: ${s.pid})`),n("Please use 'nuwax-file-server stop' to stop the existing service before trying again"),process.exit(1));let c=o||"production";process.env.NODE_ENV=c,n(`Use environment: ${c}`),t&&(process.env.PORT=t,n(`Use port: ${t}`)),g&&(process.env.CONFIG_FILE=g,n(`Use configuration file: ${g}`)),m.ensureDirSync(v.logDir);let w=S.join(U,"server.js"),u=D("node",[w],{env:{...process.env,NODE_ENV:c},stdio:"inherit",detached:!0,cwd:process.cwd(),windowsHide:!0});u.on("error",p=>{d(`Start service failed: ${p.message}`),process.exit(1)}),await new Promise(p=>setTimeout(p,2e3)),h(u.pid)||(d("Start service failed"),process.exit(1));let y={pid:u.pid,startedAt:new Date().toISOString(),env:o||"production",port:t||process.env.PORT||"60000",version:R,platform:process.platform};L(y),u.unref(),r(`Service started (PID: ${u.pid})`),l(`Service running on: http://localhost:${y.port}`,"cyan"),l(`Environment: ${y.env}`,"cyan"),l(`Platform: ${y.platform}`,"cyan"),l(`PID file: ${$()}`,"cyan")}async function k(e){let{force:o}=e;n(`Stop ${v.name} service...`);let t=E();t||(d("Service not found"),n("Service may not be running or PID file has been lost"),process.exit(0)),h(t.pid)||(n("Service process has stopped, clean PID file..."),I(),r("Service has stopped"),process.exit(0)),await _(t.pid,o)&&(await new Promise(s=>setTimeout(s,1e3)),h(t.pid)||(I(),r("Service has stopped"),process.exit(0))),o&&(d("Force stop failed, please stop the process manually"),process.exit(1)),n("Try to force stop..."),await _(t.pid,!0)&&(await new Promise(s=>setTimeout(s,1e3)),h(t.pid)||(I(),r("Service has been forced stopped"),process.exit(0))),d("Stop service failed"),process.exit(1)}async function V(e){l(`Restart ${v.name} service...`,"yellow"),n("Stop existing service...");try{await k({force:!1})}catch{n("Service not running or has stopped")}await new Promise(o=>setTimeout(o,2e3)),n("Start service..."),await F(e),r("Service has been restarted")}function j(){n(`${v.name} service status:`);let e=E();e||(l("Service not running","yellow"),process.exit(0));let o=h(e.pid);if(console.log(""),console.log(` Service name: ${v.name}`),console.log(` Running status: ${o?"Running":"Stopped"}`),console.log(` Process ID: ${e.pid}`),console.log(` Environment: ${e.env||"production"}`),console.log(` Port: ${e.port||"60000"}`),console.log(` Version: ${e.version||R}`),console.log(` Platform: ${e.platform||process.platform}`),console.log(` Started at: ${e.startedAt||"Unknown"}`),e.startedAt){let t=new Date(e.startedAt),i=Math.floor((new Date-t)/1e3),s=Math.floor(i/3600),c=Math.floor(i%3600/60),w=i%60;console.log(` Uptime: ${s} hours ${c} minutes ${w} seconds`)}console.log(` PID file: ${$()}`),console.log(""),o?l("Service running normally","green"):(l("Warning: Service process does not exist, but PID file still exists","yellow"),n("Suggest executing stop command to clean up"))}function A(){a.name("nuwax-file-server").description("Cross-platform file service deployment tool, supporting start/stop/restart/status").version(R,"-v, --version","Display version number").helpOption("-h, --help","Display help information"),a.command("start").allowUnknownOption().description("Start service").option("--env <environment>","\u73AF\u5883: development|production|test","production").option("--port <port>","Service port").option("--config <path>","Custom configuration file path").action(F),a.command("stop").description("Stop service").option("--force","Force stop").action(k),a.command("restart").allowUnknownOption().description("Restart service").option("--env <environment>","\u73AF\u5883: development|production|test","production").option("--port <port>","Service port").option("--config <path>","Custom configuration file path").action(V),a.command("status").description("View service status").action(j),a.command("help").description("Display help information").action(()=>{a.outputHelp()}),a.parse(process.argv),process.argv.slice(2).length||(a.outputHelp(),process.exit(0))}A();
@@ -1 +1 @@
1
- import t from"swagger-jsdoc";import e from"../appConfig/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;
1
+ import t from"swagger-jsdoc";import e from"../appConfig/index.js";const s={definition:{openapi:"3.0.0",info:{title:"Application Builder API",version:"1.0.0",description:"Application builder API documentation",contact:{name:"API Support"}},servers:[{url:`http://localhost:${e.PORT}`,description:`${e.NODE_ENV} environment`}],tags:[{name:"Build",description:"Build related interfaces"},{name:"Project",description:"Project management interfaces"},{name:"Code",description:"Code submission interfaces"}],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:"Project ID cannot be empty"},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;
@@ -1 +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;
1
+ import l from"express";import{startDevServer as m}from"../utils/build/startDevUtils.js";import{restartDevServer as h}from"../utils/build/restartDevUtils.js";import{keepAliveDevServer as u}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 j}from"../utils/build/processManager.js";import y from"../utils/error/buildErrorParser.js";import{getDevLog as w}from"../utils/log/getDevLogUtils.js";import d from"../utils/log/logCacheManager.js";import I from"../utils/buildArg/portPool.js";import{ValidationError as s,asyncHandler as c}from"../utils/error/errorHandler.js";const p=l.Router(),b=[{path:"/start-dev",method:"get",handler:c(async(t,r)=>{const e=t.query.projectId;if(!e)throw new s("Project ID cannot be empty",{field:"projectId"});t.setTimeout(6e5),r.setTimeout(6e5);const o=await m(t,String(e));r.json(o)})},{path:"/keep-alive",method:"get",handler:c(async(t,r)=>{const e=t.query.projectId,o=t.query.pid,a=t.query.port,n=t.query.basePath;if(!e)throw new s("Project ID cannot be empty",{field:"projectId"});if(!o)throw new s("Process ID cannot be empty",{field:"pid"});if(!a)throw new s("Port cannot be empty",{field:"port"});if(!n)throw new s("basePath cannot be empty",{field:"basePath"});const i=await u(t,String(e),o,a,n);r.json(i)})},{path:"/build",method:"get",handler:c(async(t,r)=>{const e=t.query.projectId;if(!e)throw new s("Project ID cannot be empty",{field:"projectId"});t.setTimeout(6e5),r.setTimeout(6e5);const o=await g(t,String(e));r.json(o)})},{path:"/stop-dev",method:"get",handler:c(async(t,r)=>{const e=t.query.projectId,o=t.query.pid;if(!e)throw new s("Project ID cannot be empty",{field:"projectId"});if(!o)throw new s("Process ID cannot be empty",{field:"pid"});const a=await f(t,String(e),o,{strict:!0});r.json(a)})},{path:"/restart-dev",method:"get",handler:c(async(t,r)=>{const e=t.query.projectId;if(!e)throw new s("Project ID cannot be empty",{field:"projectId"});t.setTimeout(6e5),r.setTimeout(6e5);const o=await h(t,String(e));r.json(o)})},{path:"/list-dev",method:"get",handler:(t,r)=>{const o={success:!0,list:j()};r.json(o)}},{path:"/parse-build-error",method:"post",handler:c(async(t,r)=>{const{projectId:e,errorMessage:o}=t.body;if(!e)throw new s("Project ID cannot be empty",{field:"projectId"});if(!o)throw new s("Error message cannot be empty",{field:"errorMessage"});const n=new y().parseBuildError(o,String(e));r.json({success:!0,message:n})})},{path:"/get-dev-log",method:"get",handler:c(async(t,r)=>{const e=t.query.projectId,o=t.query.startIndex,a=t.query.logType||"temp";if(!e)throw new s("Project ID cannot be empty",{field:"projectId"});const n=o?parseInt(o,10):1;if(isNaN(n)||n<1)throw new s("Start index must be a positive integer (starting from 1)",{field:"startIndex",value:o});const i=await w(String(e),n,a);r.json(i)})},{path:"/get-log-cache-stats",method:"get",handler:(t,r)=>{const e=d.getStats();r.json({success:!0,message:"Get log cache statistics successfully",stats:e})}},{path:"/clear-all-log-cache",method:"get",handler:(t,r)=>{d.clear(),r.json({success:!0,message:"All log caches have been cleared"})}},{path:"/port-pool-status",method:"get",handler:(t,r)=>{const e=I.getStatus();r.json({success:!0,message:"Get port pool status successfully",...e})}}];b.forEach(t=>{p[t.method](t.path,t.handler)});export default p;
@@ -1 +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"../appConfig/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;
1
+ import u from"express";import h from"multer";import{asyncHandler as c,ValidationError as a}from"../utils/error/errorHandler.js";import m from"../service/codeService.js";import{log as l}from"../utils/log/logUtils.js";import b from"../appConfig/index.js";const p=u.Router(),g=h({storage:h.memoryStorage(),limits:{fileSize:b.UPLOAD_SINGLE_FILE_SIZE_BYTES}}),w=[{path:"/specified-files-update",method:"post",handler:c(async(t,s)=>{const{projectId:o,codeVersion:n,files:e}=t.body||{};l(o,"INFO","Partial files update",{projectId:o,codeVersion:n,filesCount:e?e.length:0}),e&&Array.isArray(e)&&e.forEach(r=>{if(r&&typeof r.contents=="string"&&r.contents)try{r.contents=decodeURIComponent(r.contents)}catch(f){l(o,"WARN","Decode file content failed",{fileName:r.path,error:f.message})}});const i=await m.specifiedFilesUpdate(String(o),String(n),e,t);s.status(200).json(i)})},{path:"/all-files-update",method:"post",handler:c(async(t,s)=>{const{projectId:o,codeVersion:n,files:e,basePath:i,pid:r}=t.body||{};if(l(o,"INFO","Submit files",{projectId:o,codeVersion:n,basePath:i,pid:r}),!o)throw new a("Project ID cannot be empty",{field:"projectId"});if(n==null)throw new a("codeVersion cannot be empty",{field:"codeVersion"});if(!Array.isArray(e))throw new a("files must be an array",{field:"files"});e&&Array.isArray(e)&&e.forEach(d=>{if(d&&typeof d.contents=="string"&&d.contents)try{d.contents=decodeURIComponent(d.contents)}catch(y){l(o,"WARN","Decode file content failed",{fileName:d.name,error:y.message})}});const f=await m.allFilesUpdate(String(o),String(n),e,t);s.status(200).json(f)})},{path:"/upload-single-file",method:"post",middleware:g.single("file"),handler:c(async(t,s)=>{const{projectId:o,codeVersion:n,filePath:e}=t.body||{},i=t.file;if(l(o,"INFO","\u4E0A\u4F20\u5355\u4E2A\u6587\u4EF6",{projectId:o,codeVersion:n,filePath:e}),!o)throw new a("Project ID cannot be empty",{field:"projectId"});if(n==null)throw new a("codeVersion cannot be empty",{field:"codeVersion"});if(!i)throw new a("File cannot be empty",{field:"file"});if(!e||typeof e!="string")throw new a("File path cannot be empty",{field:"filePath"});l(o,"INFO","Received file information",{originalname:i.originalname,mimetype:i.mimetype,size:i.size,bufferLength:i.buffer?i.buffer.length:0,bufferIsBuffer:Buffer.isBuffer(i.buffer)});const r={buffer:i.buffer,originalname:i.originalname,mimetype:i.mimetype,size:i.size},f=await m.uploadSingleFile(String(o),String(n),r,e,t);s.status(200).json(f)})},{path:"/rollback-version",method:"post",handler:c(async(t,s)=>{const{projectId:o,codeVersion:n,rollbackTo:e}=t.body||{};if(l(o,"INFO","Rollback version",{projectId:o,codeVersion:n,rollbackTo:e}),!o)throw new a("Project ID cannot be empty",{field:"projectId"});if(n==null)throw new a("codeVersion cannot be empty",{field:"codeVersion"});if(e==null)throw new a("rollbackTo cannot be empty",{field:"rollbackTo"});const i=await m.rollbackVersion(String(o),String(n),String(e),t);s.status(200).json(i)})}];w.forEach(t=>{t.middleware?p[t.method](t.path,t.middleware,t.handler):p[t.method](t.path,t.handler)});export default p;
@@ -1 +1 @@
1
- import $ from"express";import g from"multer";import c from"fs";import I from"path";import{ValidationError as S,asyncHandler as f}from"../utils/error/errorHandler.js";import{log as d}from"../utils/log/logUtils.js";import w from"../appConfig/index.js";import{createWorkspace as z,pushSkillsToWorkspace as E}from"../utils/computer/computerUtils.js";import{getFileList as N,updateFiles as A,uploadFile as b,uploadFiles as k,downloadAllFiles as P}from"../utils/computer/computerFileUtils.js";const h=$.Router(),y=g({storage:g.diskStorage({destination:(e,a,o)=>{try{const t=w.COMPUTER_WORKSPACE_DIR;if(!t)return o(new Error("COMPUTER_WORKSPACE_DIR \u672A\u914D\u7F6E\uFF0C\u65E0\u6CD5\u786E\u5B9A\u4E0A\u4F20\u4E34\u65F6\u76EE\u5F55"));const s=e.body?.userId||"unknown",i=e.body?.cId||"unknown",n=I.join(t,String(s),String(i),".tmp");c.existsSync(n)||c.mkdirSync(n,{recursive:!0}),o(null,n)}catch(t){o(t)}},filename:(e,a,o)=>{const t=I.extname(a.originalname)||".zip",s=I.basename(a.originalname,t),i=`${Date.now()}_${Math.round(Math.random()*1e6)}`;o(null,`${s}_${i}${t}`)}}),limits:{fileSize:w.UPLOAD_MAX_FILE_SIZE_BYTES}});function R(e,a,o,t){if(e&&e.name==="MulterError"||e&&(e.name==="ValidationError"||e instanceof S))return t(e);t(e)}const C=[{path:"/create-workspace",method:"post",middleware:y.single("file"),handler:f(async(e,a)=>{const{userId:o,cId:t}=e.body||{},s=e.file||null,i=`computer:${o}:${t}`;d(i,"INFO","\u521B\u5EFA\u5DE5\u4F5C\u7A7A\u95F4\u8BF7\u6C42",{userId:o,cId:t,hasFile:!!s,fileName:s?.originalname,fileSize:s?.size});const n=await z(o,t,s);a.status(200).json({success:!0,...n})})},{path:"/push-skills-to-workspace",method:"post",middleware:y.single("file"),handler:f(async(e,a)=>{const{userId:o,cId:t}=e.body||{},s=e.file||null,i=`computer:${o}:${t}`;d(i,"INFO","\u63A8\u9001\u6280\u80FD\u5230\u5DE5\u4F5C\u7A7A\u95F4\u8BF7\u6C42",{userId:o,cId:t,hasFile:!!s,fileName:s?.originalname,fileSize:s?.size});const n=await E(o,t,s);a.status(200).json({success:!0,...n})})},{path:"/get-file-list",method:"get",handler:f(async(e,a)=>{const{userId:o,cId:t,proxyPath:s}=e.query,i=await N(o,t,s);a.status(200).json({success:!0,...i})})},{path:"/files-update",method:"post",handler:f(async(e,a)=>{const{userId:o,cId:t,files:s}=e.body||{},i=`computer:${o}:${t}`;d(i,"INFO","\u6587\u4EF6\u66F4\u65B0",{userId:o,cId:t,filesCount:s?s.length:0}),s&&Array.isArray(s)&&s.forEach(r=>{if(r&&typeof r.contents=="string"&&r.contents)try{r.contents=decodeURIComponent(r.contents)}catch(l){d(i,"WARN","\u89E3\u7801\u6587\u4EF6\u5185\u5BB9\u5931\u8D25",{fileName:r.name,error:l.message})}});const n=await A(o,t,s);a.status(200).json(n)})},{path:"/upload-file",method:"post",middleware:y.single("file"),handler:f(async(e,a)=>{const{userId:o,cId:t,filePath:s}=e.body||{},i=e.file,n=`computer:${o}:${t}`;d(n,"INFO","\u4E0A\u4F20\u5355\u4E2A\u6587\u4EF6",{userId:o,cId:t,filePath:s});const l={buffer:await c.promises.readFile(i.path),originalname:i.originalname,mimetype:i.mimetype,size:i.size};try{const u=await b(o,t,l,s);a.status(200).json(u)}finally{c.existsSync(i.path)&&await c.promises.unlink(i.path)}})},{path:"/upload-files",method:"post",middleware:y.array("files"),handler:f(async(e,a)=>{const{userId:o,cId:t,filePaths:s}=e.body||{},i=e.files||[],n=`computer:${o}:${t}`,r=Array.isArray(s)?s:typeof s=="string"?[s]:s;d(n,"INFO","\u6279\u91CF\u4E0A\u4F20\u6587\u4EF6\u8BF7\u6C42",{userId:o,cId:t,filesCount:i.length,filePathsCount:Array.isArray(r)?r.length:0});const l=[],u=[];try{for(const m of i){u.push(m.path);const F=await c.promises.readFile(m.path);l.push({buffer:F,originalname:m.originalname,mimetype:m.mimetype,size:m.size})}const p=await k(o,t,l,r);a.status(200).json(p)}finally{for(const p of u)if(c.existsSync(p))try{await c.promises.unlink(p)}catch(m){d(n,"WARN","\u6E05\u7406\u4E34\u65F6\u6587\u4EF6\u5931\u8D25",{tempPath:p,error:m.message})}}})},{path:"/download-all-files",method:"get",handler:f(async(e,a)=>{const{userId:o,cId:t}=e.query||{},s=`computer:${o}:${t}`;d(s,"INFO","\u4E0B\u8F7D\u5168\u90E8\u6587\u4EF6\u8BF7\u6C42",{userId:o,cId:t});const{archive:i,zipFileName:n}=await P(o,t);a.setHeader("Content-Type","application/zip");const r=encodeURIComponent(n);a.setHeader("Content-Disposition",`attachment; filename="${r}"; filename*=UTF-8''${r}`),i.on("error",l=>{a.destroy(l)}),i.pipe(a),i.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(R),a.push(e.customHandler),h[e.method](e.path,...a)}else h[e.method](e.path,e.handler)});export default h;
1
+ import $ from"express";import I from"multer";import d from"fs";import g from"path";import{ValidationError as S,asyncHandler as f}from"../utils/error/errorHandler.js";import{log as c}from"../utils/log/logUtils.js";import w from"../appConfig/index.js";import{createWorkspace as z,pushSkillsToWorkspace as E}from"../utils/computer/computerUtils.js";import{getFileList as N,updateFiles as k,uploadFile as A,uploadFiles as C,downloadAllFiles as b}from"../utils/computer/computerFileUtils.js";const h=$.Router(),y=I({storage:I.diskStorage({destination:(e,a,o)=>{try{const t=w.COMPUTER_WORKSPACE_DIR;if(!t)return o(new Error("COMPUTER_WORKSPACE_DIR is not configured, cannot determine upload temporary directory"));const s=e.body?.userId||"unknown",i=e.body?.cId||"unknown",n=g.join(t,String(s),String(i),".tmp");d.existsSync(n)||d.mkdirSync(n,{recursive:!0}),o(null,n)}catch(t){o(t)}},filename:(e,a,o)=>{const t=g.extname(a.originalname)||".zip",s=g.basename(a.originalname,t),i=`${Date.now()}_${Math.round(Math.random()*1e6)}`;o(null,`${s}_${i}${t}`)}}),limits:{fileSize:w.UPLOAD_MAX_FILE_SIZE_BYTES}});function P(e,a,o,t){if(e&&e.name==="MulterError"||e&&(e.name==="ValidationError"||e instanceof S))return t(e);t(e)}const R=[{path:"/create-workspace",method:"post",middleware:y.single("file"),handler:f(async(e,a)=>{const{userId:o,cId:t}=e.body||{},s=e.file||null,i=`computer:${o}:${t}`;c(i,"INFO","Create workspace request",{userId:o,cId:t,hasFile:!!s,fileName:s?.originalname,fileSize:s?.size});const n=await z(o,t,s);a.status(200).json({success:!0,...n})})},{path:"/push-skills-to-workspace",method:"post",middleware:y.single("file"),handler:f(async(e,a)=>{const{userId:o,cId:t}=e.body||{},s=e.file||null,i=`computer:${o}:${t}`;c(i,"INFO","\u63A8\u9001\u6280\u80FD\u5230\u5DE5\u4F5C\u7A7A\u95F4\u8BF7\u6C42",{userId:o,cId:t,hasFile:!!s,fileName:s?.originalname,fileSize:s?.size});const n=await E(o,t,s);a.status(200).json({success:!0,...n})})},{path:"/get-file-list",method:"get",handler:f(async(e,a)=>{const{userId:o,cId:t,proxyPath:s}=e.query,i=await N(o,t,s);a.status(200).json({success:!0,...i})})},{path:"/files-update",method:"post",handler:f(async(e,a)=>{const{userId:o,cId:t,files:s}=e.body||{},i=`computer:${o}:${t}`;c(i,"INFO","Files update",{userId:o,cId:t,filesCount:s?s.length:0}),s&&Array.isArray(s)&&s.forEach(r=>{if(r&&typeof r.contents=="string"&&r.contents)try{r.contents=decodeURIComponent(r.contents)}catch(l){c(i,"WARN","Decode file content failed",{fileName:r.name,error:l.message})}});const n=await k(o,t,s);a.status(200).json(n)})},{path:"/upload-file",method:"post",middleware:y.single("file"),handler:f(async(e,a)=>{const{userId:o,cId:t,filePath:s}=e.body||{},i=e.file,n=`computer:${o}:${t}`;c(n,"INFO","Upload single file",{userId:o,cId:t,filePath:s});const l={buffer:await d.promises.readFile(i.path),originalname:i.originalname,mimetype:i.mimetype,size:i.size};try{const u=await A(o,t,l,s);a.status(200).json(u)}finally{d.existsSync(i.path)&&await d.promises.unlink(i.path)}})},{path:"/upload-files",method:"post",middleware:y.array("files"),handler:f(async(e,a)=>{const{userId:o,cId:t,filePaths:s}=e.body||{},i=e.files||[],n=`computer:${o}:${t}`,r=Array.isArray(s)?s:typeof s=="string"?[s]:s;c(n,"INFO","Batch upload files request",{userId:o,cId:t,filesCount:i.length,filePathsCount:Array.isArray(r)?r.length:0});const l=[],u=[];try{for(const m of i){u.push(m.path);const F=await d.promises.readFile(m.path);l.push({buffer:F,originalname:m.originalname,mimetype:m.mimetype,size:m.size})}const p=await C(o,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(m){c(n,"WARN","Clean temporary file failed",{tempPath:p,error:m.message})}}})},{path:"/download-all-files",method:"get",handler:f(async(e,a)=>{const{userId:o,cId:t}=e.query||{},s=`computer:${o}:${t}`;c(s,"INFO","Download all files request",{userId:o,cId:t});const{archive:i,zipFileName:n}=await b(o,t);a.setHeader("Content-Type","application/zip");const r=encodeURIComponent(n);a.setHeader("Content-Disposition",`attachment; filename="${r}"; filename*=UTF-8''${r}`),i.on("error",l=>{a.destroy(l)}),i.pipe(a),i.finalize()})}];R.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(P),a.push(e.customHandler),h[e.method](e.path,...a)}else h[e.method](e.path,e.handler)});export default h;