@kne/fastify-app-manager 0.1.2 → 0.1.4
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 +4 -2
- package/libs/utils/entryInject.js +2 -1
- package/libs/utils/zip.js +7 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -23,6 +23,8 @@ npm i --save @kne/fastify-app-manager
|
|
|
23
23
|
适用场景:内网/堡垒机上的业务应用集中托管;不负责多机编排、硬磁盘配额或 WebSocket 升级。
|
|
24
24
|
|
|
25
25
|
> **关键设计**:子应用前端通过部署时注入的 `runtimePublicUrl` / `runtimeApiUrl` 感知对外前缀;网关对路径模式剥前缀反代,并改写 Location / Cookie / 文本 body 兜底。Scheme B(子进程挂 `APP_BASE_PATH`)已弃用。
|
|
26
|
+
>
|
|
27
|
+
> **存储隔离**:同时注入 `window.__LOCAL_STORAGE_PREFIX="<应用名>"`,`@kne/local-storage`(≥0.1.2,`@kne/token-storage` 底层依赖)读到后自动给 key 加 `<应用名>:` 前缀,避免同源挂载的子应用与宿主、其它子应用互相覆盖登录 token 等本地存储。
|
|
26
28
|
|
|
27
29
|
#### 核心架构与流程
|
|
28
30
|
|
|
@@ -115,7 +117,7 @@ deploy(versionId)
|
|
|
115
117
|
|
|
116
118
|
| 特性 | 说明 |
|
|
117
119
|
|------|------|
|
|
118
|
-
| 版本上传 | Zip Slip
|
|
120
|
+
| 版本上传 | Zip Slip 防护、条目数/体积上限、解压丢弃任意层级 `node_modules`、Fullstack 包格式校验、server 生产依赖安装 |
|
|
119
121
|
| 短部署 | `deploy` / `start` / `restart` 立即返回 `deploying`,后台按 PM2 真实状态 + healthCheck 写回 `running`/`error`/`stopped` |
|
|
120
122
|
| 生命周期 | `start` / `stop` / `restart` / `remove`;父应用重启后异步 `syncAllStatuses` 对齐 PM2 |
|
|
121
123
|
| 双通道访问 | 自定义域名透明反代 + `/app/{name}` 剥前缀反代 |
|
|
@@ -194,7 +196,7 @@ await fastify.listen({ port: 3000 });
|
|
|
194
196
|
| `healthCheckTimeoutMs` | number | 否 | `30000` | 探测总超时(毫秒) |
|
|
195
197
|
| `healthCheckIntervalMs` | number | 否 | `1000` | 探测轮询间隔(毫秒) |
|
|
196
198
|
| `maxZipSize` | number | 否 | `209715200` | zip 最大字节(200MB);同步 multipart 限制 |
|
|
197
|
-
| `maxZipEntries` | number | 否 | `20000` | zip
|
|
199
|
+
| `maxZipEntries` | number | 否 | `20000` | zip 最大条目数(不含被丢弃的 `node_modules` 条目) |
|
|
198
200
|
| `npmInstallTimeoutMs` | number | 否 | `600000` | `server` 目录 `npm install` 超时 |
|
|
199
201
|
| `sseReplayLines` | number | 否 | `100` | SSE 连接时回放最近行数 |
|
|
200
202
|
| `sseHeartbeatMs` | number | 否 | `15000` | SSE 心跳间隔 |
|
|
@@ -60,7 +60,8 @@ const injectEntryHtml = async ({ buildDir, appName, publicUrl, apiUrl }) => {
|
|
|
60
60
|
`window.runtimeAppName=${JSON.stringify(appName || '')};` +
|
|
61
61
|
`window.runtimePublicUrl=${JSON.stringify(normalizedPublic)};` +
|
|
62
62
|
`window.runtimeApiUrl=${JSON.stringify(normalizedApi)};` +
|
|
63
|
-
`window.__webpack_public_path__=${JSON.stringify(publicPath)}
|
|
63
|
+
`window.__webpack_public_path__=${JSON.stringify(publicPath)};` +
|
|
64
|
+
`window.__LOCAL_STORAGE_PREFIX=${JSON.stringify(appName || '')};`;
|
|
64
65
|
doc.head.insertBefore(scriptEl, doc.head.firstChild);
|
|
65
66
|
|
|
66
67
|
await fs.writeFile(filePath, dom.serialize());
|
package/libs/utils/zip.js
CHANGED
|
@@ -7,6 +7,10 @@ const isPathInside = (parent, child) => {
|
|
|
7
7
|
return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
|
|
8
8
|
};
|
|
9
9
|
|
|
10
|
+
const normalizeEntryName = name => name.replace(/^[/\\]+/, '').replace(/\\/g, '/');
|
|
11
|
+
|
|
12
|
+
const isNodeModulesEntry = name => normalizeEntryName(name).split('/').includes('node_modules');
|
|
13
|
+
|
|
10
14
|
const extractZipSafe = async (zipBuffer, destDir, { maxZipSize, maxZipEntries }) => {
|
|
11
15
|
if (!Buffer.isBuffer(zipBuffer)) {
|
|
12
16
|
throw new Error('zip content must be a Buffer');
|
|
@@ -17,13 +21,14 @@ const extractZipSafe = async (zipBuffer, destDir, { maxZipSize, maxZipEntries })
|
|
|
17
21
|
|
|
18
22
|
await fs.ensureDir(destDir);
|
|
19
23
|
const zip = new AdmZip(zipBuffer);
|
|
20
|
-
|
|
24
|
+
// Dependencies are reinstalled after extraction, so bundled node_modules are dropped before the entry limit applies.
|
|
25
|
+
const entries = zip.getEntries().filter(entry => !isNodeModulesEntry(entry.entryName));
|
|
21
26
|
if (entries.length > maxZipEntries) {
|
|
22
27
|
throw new Error(`zip has too many entries (${entries.length} > ${maxZipEntries})`);
|
|
23
28
|
}
|
|
24
29
|
|
|
25
30
|
for (const entry of entries) {
|
|
26
|
-
const entryName = entry.entryName
|
|
31
|
+
const entryName = normalizeEntryName(entry.entryName);
|
|
27
32
|
if (!entryName || entryName.includes('\0')) {
|
|
28
33
|
throw new Error('invalid zip entry name');
|
|
29
34
|
}
|