@kne/fastify-app-manager 0.1.15 → 0.1.17

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.
package/README.md CHANGED
@@ -428,7 +428,7 @@ await fastify.listen({ port: 3000 });
428
428
 
429
429
  ##### POST `{prefix}/app/deploy`
430
430
 
431
- 选择版本部署。版本目录不在磁盘时先经 `resolveVersionFile` 取回 zip 解压并 `npm install`(耗时,取不回返回 400 且不改应用状态),再以数据库中的 SQL 覆盖目录内 `.sql`;接着按版本的 `env.schema.json` 校验最终环境变量(含注入的变量),版本没有 schema 或变量不合规时返回 400,且不切换版本、不执行迁移、不启动进程;随后立即返回 `deploying`,后台做健康检查。进程启动后删除该应用其它有 `fileId` 的版本目录(只保留当前版本)。
431
+ 选择版本部署。同一应用同时只能有一个部署:已有部署未结束(含就绪检查阶段)时返回 409,不影响进行中的部署。版本目录不在磁盘时先经 `resolveVersionFile` 取回 zip 解压并 `npm install`(耗时,取不回返回 400 且不改应用状态),再以数据库中的 SQL 覆盖目录内 `.sql`;接着按版本的 `env.schema.json` 校验最终环境变量(含注入的变量),版本没有 schema 或变量不合规时返回 400,且不切换版本、不执行迁移、不启动进程;随后立即返回 `deploying`,后台做健康检查。进程启动后删除该应用其它有 `fileId` 的版本目录(只保留当前版本)。
432
432
 
433
433
  | 参数名 | 类型 | 必填 | 默认值 | 说明 |
434
434
  |--------|------|------|--------|------|
@@ -757,7 +757,7 @@ data: {"name":"demo","ts":1791456608533,...}
757
757
  | `pruneAllVersionArtifacts()` | 对所有应用执行 `pruneVersionArtifacts` |
758
758
  | `restoreMissingArtifacts()` | 当前版本目录(`artifactPath/server`)缺失时,按版本 `fileId` 经 `resolveVersionFile` 取回 zip 重建并恢复 `current` 链接(不跑 SQL 迁移);返回 `[{ name, version, action: 'restored' \| 'skipped' \| 'failed', reason? }]`,无 `fileId` 的记 `skipped` |
759
759
  | `listVersions({ name, perPage, currentPage })` | 版本列表 |
760
- | `deploy({ name, versionId, version, runMigration, migrations?, progressId? })` | 部署;版本目录缺失时先从宿主文件重建,启动后清理其它版本目录;立即 `deploying`,后台 `syncRealStatus`;各阶段进度写入 `getDeployProgress()` |
760
+ | `deploy({ name, versionId, version, runMigration, migrations?, progressId? })` | 部署;同一应用已有部署未结束时 409;版本目录缺失时先从宿主文件重建,启动后清理其它版本目录;立即 `deploying`,后台 `syncRealStatus`;各阶段进度写入 `getDeployProgress()` |
761
761
  | `getDeployProgress()` | 部署进度中心:`emitter`(事件名 `deploy:{name}`、日志 `deploy-log:{name}`)、`snapshot(name)` 返回最近一次部署 `{ progressId, status, events, ... }`、`active(name)` 进行中的部署 |
762
762
  | `deployList({ name, perPage?, currentPage? })` | 同 `GET .../app/deploy/list` |
763
763
  | `deployDetail({ name, progressId?, logLines? })` | 同 `GET .../app/deploy/detail` |
@@ -872,7 +872,7 @@ data: {"name":"demo","ts":1791456608533,...}
872
872
  | 应用不存在 | `/app/{name}` 下找不到应用时返回 404「应用不存在」页(带返回首页)或 JSON(`appStatus: 'missing'`);`pathPrefix` 之外以及未绑定的域名仍交给宿主路由 |
873
873
  | Location | 相对路径前缀补上 `/app/{name}` |
874
874
  | Set-Cookie | `Path=/` 改为 `Path=/app/{name}` |
875
- | Body | 对文本类 Content-Type,将 `"/static/`、`"/api/`、`"/account/` 等前缀改写;有 `content-encoding` 时跳过 |
875
+ | Body | 对文本类 Content-Type,将 `"/static/`、`"/api/`、`"/account/` 等前缀改写;有 `content-encoding` 时跳过;JS 脚本与 `text/event-stream`(SSE 不结束的流)原样流式透传 |
876
876
 
877
877
  ##### 日志切分与归档
878
878
 
@@ -1089,6 +1089,11 @@ module.exports = fp(async (fastify, options) => {
1089
1089
  httpError(404, 'version not found');
1090
1090
  }
1091
1091
 
1092
+ // 判断与登记之间不能有 await,否则并发请求会同时通过
1093
+ const running = deployProgress.active(app.name);
1094
+ if (running) {
1095
+ httpError(409, `app ${app.name} is deploying version ${running.version}`);
1096
+ }
1092
1097
  const progress = deployProgress.begin(app.name, progressId || crypto.randomUUID(), { versionId: String(ver.id), version: ver.version });
1093
1098
  let step = 'package';
1094
1099
  progress.report(step, 'running');
@@ -31,7 +31,10 @@ const TEXTUAL_TYPES = /text\/|json|xml|svg/i;
31
31
  // 前端脚本已按注入的 runtimePublicUrl / runtimeApiUrl 自行拼前缀(如 axios baseURL + '/api/...'),再改写会得到 /app/x/app/x/api
32
32
  const SCRIPT_TYPES = /javascript|ecmascript/i;
33
33
 
34
- const shouldRewriteBody = contentType => TEXTUAL_TYPES.test(contentType || '') && !SCRIPT_TYPES.test(contentType || '');
34
+ // SSE 是不结束的流,读完再改写会让客户端一直收不到响应
35
+ const STREAM_TYPES = /text\/event-stream/i;
36
+
37
+ const shouldRewriteBody = contentType => TEXTUAL_TYPES.test(contentType || '') && !SCRIPT_TYPES.test(contentType || '') && !STREAM_TYPES.test(contentType || '');
35
38
 
36
39
  const rewriteBody = (body, mountPrefix, prefixes = ['/static', '/api', '/account']) => {
37
40
  if (!body || !mountPrefix) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kne/fastify-app-manager",
3
- "version": "0.1.15",
3
+ "version": "0.1.17",
4
4
  "description": "Fastify 单机应用市场插件:版本上传、PM2 托管、路径/域名反代与日志 SSE",
5
5
  "main": "index.js",
6
6
  "scripts": {