@yinuo-ngm/server 1.0.16 → 1.0.18
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/lib/app.js +3 -15
- package/lib/common/editor.js +2 -2
- package/lib/env.d.ts +1 -0
- package/lib/env.js +1 -0
- package/lib/plugins/error-handler.plugin.d.ts +2 -2
- package/lib/plugins/error-handler.plugin.js +97 -93
- package/lib/plugins/ws/topics/nginx.ws.js +2 -1
- package/lib/plugins/ws/topics/svn.ws.d.ts +2 -1
- package/lib/plugins/ws/topics/svn.ws.js +3 -2
- package/lib/plugins/ws/topics/syslog.ws.d.ts +1 -1
- package/lib/plugins/ws/topics/task.ws.d.ts +2 -1
- package/lib/plugins/ws/topics/task.ws.js +2 -1
- package/lib/plugins/ws/ws.context.d.ts +1 -1
- package/lib/plugins/ws/ws.plugin.js +4 -3
- package/lib/plugins/ws/ws.router.d.ts +2 -2
- package/lib/plugins/ws/ws.router.js +8 -7
- package/lib/routes/api-client/collection.routes.js +8 -6
- package/lib/routes/api-client/env.routes.js +4 -3
- package/lib/routes/api-client/history.routes.js +3 -2
- package/lib/routes/api-client/hub-token.routes.js +11 -11
- package/lib/routes/api-client/request.routes.js +7 -6
- package/lib/routes/api-client/send.routes.js +3 -2
- package/lib/routes/config.routes.js +5 -5
- package/lib/routes/hub.routes.js +10 -10
- package/lib/routes/nginx/nginx-lifecycle.routes.js +4 -0
- package/lib/routes/nginx/nginx-route.context.js +23 -7
- package/lib/routes/nginx/nginx-server.routes.js +158 -6
- package/lib/routes/node-version.routes.js +7 -7
- package/lib/routes/project.routes.js +9 -9
- package/lib/routes/rss.routes.js +3 -3
- package/lib/routes/sprite-browse.routes.js +2 -1
- package/lib/routes/sprite.routes.js +3 -3
- package/lib/routes/static-files.routes.js +7 -7
- package/lib/routes/svn.routes.js +5 -3
- package/lib/routes/system.routes.js +20 -1
- package/lib/routes/task.routes.js +4 -4
- package/package.json +13 -6
- package/www/3rdpartylicenses.txt +52 -52
- package/www/browser/chunk-2L7NUOMX.js +2 -0
- package/www/browser/chunk-2NZJ7CN2.js +20 -0
- package/www/browser/{chunk-SPRWNZHF.js → chunk-3CM4SKDO.js} +2 -2
- package/www/browser/{chunk-EEVPZGEY.js → chunk-3I7BQCXS.js} +1 -1
- package/www/browser/{chunk-DXF7BVK5.js → chunk-3M56F2S2.js} +1 -1
- package/www/browser/chunk-3OHBKMAA.js +1 -0
- package/www/browser/{chunk-4KETC6EB.js → chunk-3XNNQFWR.js} +1 -1
- package/www/browser/chunk-4BRW6LCZ.js +4 -0
- package/www/browser/{chunk-HGONFYP6.js → chunk-4LBSLURA.js} +1 -1
- package/www/browser/{chunk-BYEU6KGP.js → chunk-4X42HB6N.js} +1 -1
- package/www/browser/chunk-5DYX4DUX.js +11 -0
- package/www/browser/chunk-6SYVDN5L.js +1 -0
- package/www/browser/{chunk-XJ5KZQNN.js → chunk-6YYNHZ2A.js} +1 -1
- package/www/browser/{chunk-G2W3B7TJ.js → chunk-75W3GVSO.js} +1 -1
- package/www/browser/{chunk-76TVIB33.js → chunk-7U44RF5F.js} +1 -1
- package/www/browser/{chunk-WD4IAQR3.js → chunk-AELTP6YN.js} +1 -1
- package/www/browser/chunk-AMXRL4GR.js +1 -0
- package/www/browser/chunk-AV2ZODEH.js +1 -0
- package/www/browser/chunk-AZ6SIMYH.js +1 -0
- package/www/browser/chunk-B3C35ET3.js +2 -0
- package/www/browser/chunk-BTQIUVTQ.js +1 -0
- package/www/browser/chunk-CN5J4WNO.js +1 -0
- package/www/browser/{chunk-N4LRZJBP.js → chunk-D2ODDESN.js} +1 -1
- package/www/browser/{chunk-O2EYEF7J.js → chunk-DE6E23ET.js} +1 -1
- package/www/browser/{chunk-SVQWPHF5.js → chunk-DIJPUYIA.js} +1 -1
- package/www/browser/{chunk-SYCNSLAW.js → chunk-DLGJD6YU.js} +4 -4
- package/www/browser/{chunk-JHMEKUZ5.js → chunk-EEDA5U4V.js} +1 -1
- package/www/browser/chunk-FK6Z4HLL.js +1 -0
- package/www/browser/chunk-FL6GDGHW.js +1 -0
- package/www/browser/{chunk-HRXCR3IN.js → chunk-FXCG34QS.js} +1 -1
- package/www/browser/{chunk-UJOHBN2Y.js → chunk-H2USFIYR.js} +1 -1
- package/www/browser/chunk-H5HGMOE6.js +1 -0
- package/www/browser/chunk-HB3HECPD.js +1 -0
- package/www/browser/{chunk-INL2PELS.js → chunk-HDNG236Q.js} +1 -1
- package/www/browser/{chunk-WF2QTF5L.js → chunk-HJTXXSMC.js} +1 -1
- package/www/browser/{chunk-LQ5OXSW7.js → chunk-HUMCWAKJ.js} +1 -1
- package/www/browser/chunk-IKB3EQCP.js +2 -0
- package/www/browser/{chunk-TMX5TTV3.js → chunk-K7PESFPY.js} +1 -1
- package/www/browser/{chunk-T3KK7ZXB.js → chunk-KRYMOHYF.js} +1 -1
- package/www/browser/{chunk-SQQNR223.js → chunk-M4QRBV3K.js} +1 -1
- package/www/browser/chunk-N2PELLMM.js +30 -0
- package/www/browser/{chunk-ACAZUX6C.js → chunk-ONXBYGIG.js} +1 -1
- package/www/browser/{chunk-JZULA5JV.js → chunk-OSBDR36P.js} +1 -1
- package/www/browser/chunk-OZCK4XVV.js +1 -0
- package/www/browser/{chunk-GDWS2L66.js → chunk-QJP5F735.js} +1 -1
- package/www/browser/{chunk-2X3MRS27.js → chunk-RGOYDY7H.js} +1 -1
- package/www/browser/{chunk-B6MBYCXI.js → chunk-UJKK4A7Y.js} +1 -1
- package/www/browser/{chunk-ZNTJRLVH.js → chunk-WD2EKZQC.js} +1 -1
- package/www/browser/{chunk-L5D75AMV.js → chunk-WUA5JFDD.js} +1 -1
- package/www/browser/chunk-XLFHB7RS.js +3 -0
- package/www/browser/{chunk-JU3TEDBV.js → chunk-YNW4HEJO.js} +1 -1
- package/www/browser/{chunk-RW2JPJV7.js → chunk-ZTDLWBW5.js} +1 -1
- package/www/browser/index.html +1 -1
- package/www/browser/main-N64WJCHX.js +34 -0
- package/www/browser/chunk-2BZRE4G7.js +0 -1
- package/www/browser/chunk-5T5KA5PG.js +0 -1
- package/www/browser/chunk-6CGHNKJI.js +0 -1
- package/www/browser/chunk-7J24TP36.js +0 -1
- package/www/browser/chunk-CZ5AZ3VW.js +0 -1
- package/www/browser/chunk-DW7F5PEA.js +0 -1
- package/www/browser/chunk-HHBPULJW.js +0 -2
- package/www/browser/chunk-KVFR7GFV.js +0 -20
- package/www/browser/chunk-L7TMCSHV.js +0 -1
- package/www/browser/chunk-MVQTKINJ.js +0 -1
- package/www/browser/chunk-OMDHJIUB.js +0 -1
- package/www/browser/chunk-OWUAAOHW.js +0 -2
- package/www/browser/chunk-PIK5YPIB.js +0 -1
- package/www/browser/chunk-RHLQRQDK.js +0 -9
- package/www/browser/chunk-S3SJ4VVM.js +0 -11
- package/www/browser/chunk-SIVPP74B.js +0 -0
- package/www/browser/chunk-UFY3FLDK.js +0 -1
- package/www/browser/chunk-UQGCUFNM.js +0 -1
- package/www/browser/chunk-UXXWRMM6.js +0 -1
- package/www/browser/chunk-UZRJGJTD.js +0 -2
- package/www/browser/chunk-WI67LAOV.js +0 -4
- package/www/browser/chunk-WNCM6QKB.js +0 -1
- package/www/browser/chunk-YETDFSQE.js +0 -1
- package/www/browser/chunk-YMTC5GZK.js +0 -1
- package/www/browser/chunk-YVZHJ76K.js +0 -1
- package/www/browser/main-6LN5C22E.js +0 -34
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.apiClientHubTokenRoutes = apiClientHubTokenRoutes;
|
|
4
|
-
const
|
|
4
|
+
const errors_1 = require("@yinuo-ngm/errors");
|
|
5
5
|
const api_1 = require("@yinuo-ngm/api");
|
|
6
6
|
const node_stream_1 = require("node:stream");
|
|
7
7
|
function normalizeNonEmptyString(value, field) {
|
|
8
8
|
if (typeof value !== "string" || value.trim() === "") {
|
|
9
|
-
throw new
|
|
9
|
+
throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.BAD_REQUEST, `${field} is required`);
|
|
10
10
|
}
|
|
11
11
|
return value.trim();
|
|
12
12
|
}
|
|
@@ -32,13 +32,13 @@ async function resolveHubTokenConfig(app, body, tokenType) {
|
|
|
32
32
|
return { baseUrl: inlineBaseUrl, token: inlineToken };
|
|
33
33
|
}
|
|
34
34
|
if (!body.projectId) {
|
|
35
|
-
throw new
|
|
35
|
+
throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.BAD_REQUEST, "projectId is required when baseUrl/token are not provided");
|
|
36
36
|
}
|
|
37
37
|
const project = await app.core.project.get(body.projectId);
|
|
38
38
|
const config = readHubTokenConfigFromProject(project);
|
|
39
39
|
const resolvedToken = tokenType === "personal" ? body.personalToken?.trim() || config.personalToken : config.token;
|
|
40
40
|
if (!config.baseUrl || !resolvedToken) {
|
|
41
|
-
throw new
|
|
41
|
+
throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.BAD_REQUEST, tokenType === "personal"
|
|
42
42
|
? "project hub-v2 config missing (NGM_HUB_V2_BASE_URL/NGM_HUB_V2_PERSONAL_TOKEN)"
|
|
43
43
|
: "project hub-v2 config missing (NGM_HUB_V2_BASE_URL/NGM_HUB_V2_TOKEN)");
|
|
44
44
|
}
|
|
@@ -59,7 +59,7 @@ async function apiClientHubTokenRoutes(fastify) {
|
|
|
59
59
|
const response = await requestHubApiRaw(baseUrl, "/api/token", token, "GET", normalizedPath);
|
|
60
60
|
if (!response.ok) {
|
|
61
61
|
const payload = await parseJson(response);
|
|
62
|
-
throw new
|
|
62
|
+
throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.BAD_REQUEST, payload?.message || `hub-v2 request failed (${response.status})`, {
|
|
63
63
|
status: response.status,
|
|
64
64
|
response: payload,
|
|
65
65
|
});
|
|
@@ -81,7 +81,7 @@ async function apiClientHubTokenRoutes(fastify) {
|
|
|
81
81
|
const response = await requestHubApiRaw(baseUrl, "/api/token", token, "GET", normalizedPath);
|
|
82
82
|
if (!response.ok) {
|
|
83
83
|
const payload = await parseJson(response);
|
|
84
|
-
throw new
|
|
84
|
+
throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.BAD_REQUEST, payload?.message || `hub-v2 request failed (${response.status})`, {
|
|
85
85
|
status: response.status,
|
|
86
86
|
response: payload,
|
|
87
87
|
});
|
|
@@ -103,7 +103,7 @@ async function apiClientHubTokenRoutes(fastify) {
|
|
|
103
103
|
const response = await requestHubApiRaw(baseUrl, "/api/token", token, "GET", normalizedPath);
|
|
104
104
|
if (!response.ok) {
|
|
105
105
|
const payload = await parseJson(response);
|
|
106
|
-
throw new
|
|
106
|
+
throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.BAD_REQUEST, payload?.message || `hub-v2 request failed (${response.status})`, {
|
|
107
107
|
status: response.status,
|
|
108
108
|
response: payload,
|
|
109
109
|
});
|
|
@@ -206,14 +206,14 @@ async function requestHubApi(baseUrl, apiPrefix, token, method, path, query, bod
|
|
|
206
206
|
});
|
|
207
207
|
const payload = await parseJson(response);
|
|
208
208
|
if (!response.ok) {
|
|
209
|
-
throw new
|
|
209
|
+
throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.BAD_REQUEST, payload?.message || `hub-v2 request failed (${response.status})`, {
|
|
210
210
|
status: response.status,
|
|
211
211
|
response: payload,
|
|
212
212
|
});
|
|
213
213
|
}
|
|
214
214
|
if (payload && typeof payload === "object" && "code" in payload) {
|
|
215
215
|
if (payload.code !== "OK") {
|
|
216
|
-
throw new
|
|
216
|
+
throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.BAD_REQUEST, payload.message || "hub-v2 response error", {
|
|
217
217
|
response: payload,
|
|
218
218
|
});
|
|
219
219
|
}
|
|
@@ -259,7 +259,7 @@ function assertPathProjectSegmentNotLocalProjectId(path, projectId) {
|
|
|
259
259
|
}
|
|
260
260
|
const projectSegment = (matched[1] ?? "").trim();
|
|
261
261
|
if (projectSegment && projectSegment === localProjectId) {
|
|
262
|
-
throw new
|
|
262
|
+
throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.BAD_REQUEST, "path must use projectKey (or business relative path), not local projectId");
|
|
263
263
|
}
|
|
264
264
|
}
|
|
265
265
|
function normalizeHubTokenPath(path, projectKey) {
|
|
@@ -271,7 +271,7 @@ function normalizeHubTokenPath(path, projectKey) {
|
|
|
271
271
|
return `/projects/${key}${rest}`;
|
|
272
272
|
}
|
|
273
273
|
if (!projectKey) {
|
|
274
|
-
throw new
|
|
274
|
+
throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.BAD_REQUEST, "projectKey is required when path does not include /projects/:projectKey");
|
|
275
275
|
}
|
|
276
276
|
return `/projects/${projectKey}${p}`;
|
|
277
277
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.apiClientRequestsRoutes = apiClientRequestsRoutes;
|
|
4
|
+
const errors_1 = require("@yinuo-ngm/errors");
|
|
4
5
|
function parseScope(q) {
|
|
5
6
|
const scope = (q?.scope ?? "project");
|
|
6
7
|
const projectId = q?.projectId;
|
|
7
8
|
if (scope === "project" && !projectId) {
|
|
8
|
-
throw new
|
|
9
|
+
throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.BAD_REQUEST, "projectId is required when scope=project");
|
|
9
10
|
}
|
|
10
11
|
return { scope, projectId };
|
|
11
12
|
}
|
|
@@ -30,9 +31,9 @@ async function apiClientRequestsRoutes(fastify) {
|
|
|
30
31
|
const body = req.body;
|
|
31
32
|
const scope = body?.scope ?? "project";
|
|
32
33
|
if (scope === "project" && !body.projectId)
|
|
33
|
-
throw new
|
|
34
|
+
throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.BAD_REQUEST, "projectId is required when scope=project");
|
|
34
35
|
if (!body?.request?.id)
|
|
35
|
-
throw new
|
|
36
|
+
throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.BAD_REQUEST, "request.id is required");
|
|
36
37
|
await api.saveRequest(body.request, scope, body.projectId);
|
|
37
38
|
return { id: body.request.id };
|
|
38
39
|
});
|
|
@@ -40,12 +41,12 @@ async function apiClientRequestsRoutes(fastify) {
|
|
|
40
41
|
const body = req.body;
|
|
41
42
|
const scope = body?.scope ?? "project";
|
|
42
43
|
if (scope === "project" && !body.projectId)
|
|
43
|
-
throw new
|
|
44
|
+
throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.BAD_REQUEST, "projectId is required when scope=project");
|
|
44
45
|
if (!body?.request?.id)
|
|
45
|
-
throw new
|
|
46
|
+
throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.BAD_REQUEST, "request.id is required");
|
|
46
47
|
const old = await api.getRequest(body.request.id, scope, body.projectId);
|
|
47
48
|
if (!old)
|
|
48
|
-
throw new
|
|
49
|
+
throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.NOT_FOUND, "request not found: " + body.request.id);
|
|
49
50
|
const updated = {
|
|
50
51
|
...old,
|
|
51
52
|
...body.request,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.apiClientSendRoutes = apiClientSendRoutes;
|
|
4
|
+
const errors_1 = require("@yinuo-ngm/errors");
|
|
4
5
|
const hub_cookie_jar_1 = require("./hub-cookie-jar");
|
|
5
6
|
async function apiClientSendRoutes(fastify) {
|
|
6
7
|
const api = fastify.api;
|
|
@@ -8,9 +9,9 @@ async function apiClientSendRoutes(fastify) {
|
|
|
8
9
|
const body = req.body;
|
|
9
10
|
const scope = body.scope ?? "project";
|
|
10
11
|
if (scope === "project" && !body.projectId)
|
|
11
|
-
throw new
|
|
12
|
+
throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.BAD_REQUEST, "projectId is required when scope=project");
|
|
12
13
|
if (!body.request && !body.requestId)
|
|
13
|
-
throw new
|
|
14
|
+
throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.BAD_REQUEST, "request or requestId is required");
|
|
14
15
|
const useCookieJar = body.useCookieJar !== false;
|
|
15
16
|
const sessionKey = body.sessionKey ?? `${scope}:${body.projectId ?? "global"}:${body.envId ?? "default"}`;
|
|
16
17
|
if (body.clearCookieJar) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.default = configRoutes;
|
|
4
|
-
const
|
|
4
|
+
const errors_1 = require("@yinuo-ngm/errors");
|
|
5
5
|
const editor_1 = require("../common/editor");
|
|
6
6
|
async function configRoutes(fastify) {
|
|
7
7
|
fastify.get("/catalog/:projectId", async (req) => {
|
|
@@ -17,7 +17,7 @@ async function configRoutes(fastify) {
|
|
|
17
17
|
const body = req.body;
|
|
18
18
|
const next = body?.raw ?? body?.data;
|
|
19
19
|
if (next === undefined) {
|
|
20
|
-
throw new
|
|
20
|
+
throw new errors_1.CoreError(errors_1.CoreErrorCodes.CONFIG_WRITE_FAILED, "missing body.raw or body.data", { projectId, docId });
|
|
21
21
|
}
|
|
22
22
|
return await fastify.core.config.writeDoc(projectId, docId, next);
|
|
23
23
|
});
|
|
@@ -29,7 +29,7 @@ async function configRoutes(fastify) {
|
|
|
29
29
|
return { ok: true };
|
|
30
30
|
}
|
|
31
31
|
catch (e) {
|
|
32
|
-
throw new
|
|
32
|
+
throw new errors_1.CoreError(errors_1.CoreErrorCodes.EDITOR_LAUNCH_FAILED, e?.message || "openInEditor failed");
|
|
33
33
|
}
|
|
34
34
|
});
|
|
35
35
|
fastify.get("/readSchema/:projectId/:domainId", async (req) => {
|
|
@@ -41,7 +41,7 @@ async function configRoutes(fastify) {
|
|
|
41
41
|
const body = req.body;
|
|
42
42
|
const vm = body?.vm;
|
|
43
43
|
if (vm === undefined) {
|
|
44
|
-
throw new
|
|
44
|
+
throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.BAD_REQUEST, "missing body.vm");
|
|
45
45
|
}
|
|
46
46
|
await fastify.core.config.writeDomainSchema(projectId, domainId, vm);
|
|
47
47
|
return {
|
|
@@ -58,7 +58,7 @@ async function configRoutes(fastify) {
|
|
|
58
58
|
const body = req.body;
|
|
59
59
|
const vm = body?.vm;
|
|
60
60
|
if (vm === undefined) {
|
|
61
|
-
throw new
|
|
61
|
+
throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.BAD_REQUEST, "missing body.vm");
|
|
62
62
|
}
|
|
63
63
|
return await fastify.core.config.diffDomainSchema(projectId, domainId, vm);
|
|
64
64
|
});
|
package/lib/routes/hub.routes.js
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.default = hubRoutes;
|
|
4
|
-
const
|
|
4
|
+
const errors_1 = require("@yinuo-ngm/errors");
|
|
5
5
|
const DEFAULT_HUB_PUBLIC_BASE_URL = "http://192.168.1.31:7070/api/public";
|
|
6
6
|
function normalizeText(value, maxLen, fieldName) {
|
|
7
7
|
if (value === undefined || value === null)
|
|
8
8
|
return undefined;
|
|
9
9
|
if (typeof value !== "string") {
|
|
10
|
-
throw new
|
|
10
|
+
throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.BAD_REQUEST, `${fieldName} must be string`);
|
|
11
11
|
}
|
|
12
12
|
const text = value.trim();
|
|
13
13
|
if (text.length > maxLen) {
|
|
14
|
-
throw new
|
|
14
|
+
throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.BAD_REQUEST, `${fieldName} length must <= ${maxLen}`);
|
|
15
15
|
}
|
|
16
16
|
return text;
|
|
17
17
|
}
|
|
18
18
|
function validateBody(input) {
|
|
19
19
|
if (!input || typeof input !== "object") {
|
|
20
|
-
throw new
|
|
20
|
+
throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.BAD_REQUEST, "invalid feedback payload");
|
|
21
21
|
}
|
|
22
22
|
const body = input;
|
|
23
23
|
const category = body.category;
|
|
@@ -29,13 +29,13 @@ function validateBody(input) {
|
|
|
29
29
|
const clientVersion = normalizeText(body.clientVersion, 60, "clientVersion");
|
|
30
30
|
const osInfo = normalizeText(body.osInfo, 200, "osInfo");
|
|
31
31
|
if (!category || !["bug", "suggestion", "feature", "other"].includes(category)) {
|
|
32
|
-
throw new
|
|
32
|
+
throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.BAD_REQUEST, "invalid feedback category");
|
|
33
33
|
}
|
|
34
34
|
if (!title) {
|
|
35
|
-
throw new
|
|
35
|
+
throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.BAD_REQUEST, "feedback title is required");
|
|
36
36
|
}
|
|
37
37
|
if (!content) {
|
|
38
|
-
throw new
|
|
38
|
+
throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.BAD_REQUEST, "feedback content is required");
|
|
39
39
|
}
|
|
40
40
|
return {
|
|
41
41
|
category,
|
|
@@ -82,7 +82,7 @@ async function hubRoutes(app) {
|
|
|
82
82
|
});
|
|
83
83
|
}
|
|
84
84
|
catch (error) {
|
|
85
|
-
throw new
|
|
85
|
+
throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.UNKNOWN_ERROR, "failed to connect hub service", { cause: error?.message || String(error), targetUrl });
|
|
86
86
|
}
|
|
87
87
|
let payload = null;
|
|
88
88
|
try {
|
|
@@ -93,10 +93,10 @@ async function hubRoutes(app) {
|
|
|
93
93
|
}
|
|
94
94
|
if (!response.ok) {
|
|
95
95
|
const message = payload?.message || `hub response error (${response.status})`;
|
|
96
|
-
throw new
|
|
96
|
+
throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.UNKNOWN_ERROR, message, { status: response.status, payload });
|
|
97
97
|
}
|
|
98
98
|
if (payload?.code !== "OK") {
|
|
99
|
-
throw new
|
|
99
|
+
throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.UNKNOWN_ERROR, payload?.message || "hub feedback submit failed", { payload });
|
|
100
100
|
}
|
|
101
101
|
return payload?.data ?? payload;
|
|
102
102
|
});
|
|
@@ -65,6 +65,10 @@ function registerNginxLifecycleRoutes(context) {
|
|
|
65
65
|
success: true,
|
|
66
66
|
});
|
|
67
67
|
});
|
|
68
|
+
fastify.get('/local-ip', async (_request, reply) => {
|
|
69
|
+
const result = await nginx.service.getLocalIp();
|
|
70
|
+
return reply.send(result);
|
|
71
|
+
});
|
|
68
72
|
fastify.post('/start', async (_request, reply) => {
|
|
69
73
|
const result = await nginx.service.start();
|
|
70
74
|
if (result.success) {
|
|
@@ -2,23 +2,39 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createNginxRouteContext = createNginxRouteContext;
|
|
4
4
|
exports.sendBadRequest = sendBadRequest;
|
|
5
|
+
const promises_1 = require("fs/promises");
|
|
5
6
|
const path_1 = require("path");
|
|
6
|
-
const
|
|
7
|
+
const errors_1 = require("@yinuo-ngm/errors");
|
|
7
8
|
function createNginxRouteContext(fastify) {
|
|
8
9
|
const nginx = fastify.nginx;
|
|
9
10
|
const normalizeFsPath = (filePath) => (0, path_1.resolve)(filePath).replace(/\\/g, '/').toLowerCase();
|
|
10
11
|
const ensureManageableConfigFile = async (rawPath) => {
|
|
11
12
|
const filePath = rawPath?.trim();
|
|
12
13
|
if (!filePath) {
|
|
13
|
-
throw new
|
|
14
|
+
throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.BAD_REQUEST, '配置文件路径不能为空');
|
|
14
15
|
}
|
|
15
16
|
const included = await nginx.config.getIncludedConfigs();
|
|
16
|
-
const includedSet = new Set(
|
|
17
|
-
const
|
|
17
|
+
const includedSet = new Set();
|
|
18
|
+
for (const item of included) {
|
|
19
|
+
try {
|
|
20
|
+
includedSet.add(normalizeFsPath(await (0, promises_1.realpath)(item)));
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
includedSet.add(normalizeFsPath((0, path_1.resolve)(item)));
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const resolvedTarget = (0, path_1.resolve)(filePath);
|
|
27
|
+
let normalizedTarget;
|
|
28
|
+
try {
|
|
29
|
+
normalizedTarget = normalizeFsPath(await (0, promises_1.realpath)(resolvedTarget));
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
normalizedTarget = normalizeFsPath(resolvedTarget);
|
|
33
|
+
}
|
|
18
34
|
if (!includedSet.has(normalizedTarget)) {
|
|
19
|
-
throw new
|
|
35
|
+
throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.OP_NOT_FOUND, '配置文件不在当前可管理列表中');
|
|
20
36
|
}
|
|
21
|
-
return
|
|
37
|
+
return resolvedTarget;
|
|
22
38
|
};
|
|
23
39
|
return {
|
|
24
40
|
fastify,
|
|
@@ -29,7 +45,7 @@ function createNginxRouteContext(fastify) {
|
|
|
29
45
|
}
|
|
30
46
|
function sendBadRequest(reply, error) {
|
|
31
47
|
const message = error instanceof Error ? error.message : String(error ?? '未知错误');
|
|
32
|
-
throw new
|
|
48
|
+
throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.BAD_REQUEST, message, {
|
|
33
49
|
route: reply.request?.url,
|
|
34
50
|
method: reply.request?.method,
|
|
35
51
|
});
|
|
@@ -1,16 +1,80 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.registerNginxServerRoutes = registerNginxServerRoutes;
|
|
4
|
-
const
|
|
4
|
+
const errors_1 = require("@yinuo-ngm/errors");
|
|
5
5
|
const nginx_route_context_1 = require("./nginx-route.context");
|
|
6
6
|
function registerNginxServerRoutes(context) {
|
|
7
7
|
const { fastify, nginx } = context;
|
|
8
|
+
const buildRuntimeStatus = (enabled, nginxRunning, pendingReload) => {
|
|
9
|
+
if (!enabled) {
|
|
10
|
+
return 'disabled';
|
|
11
|
+
}
|
|
12
|
+
if (nginxRunning === null) {
|
|
13
|
+
return 'unknown';
|
|
14
|
+
}
|
|
15
|
+
if (pendingReload) {
|
|
16
|
+
return 'pending';
|
|
17
|
+
}
|
|
18
|
+
return nginxRunning ? 'running' : 'stopped';
|
|
19
|
+
};
|
|
20
|
+
const parseIsoMs = (value) => {
|
|
21
|
+
const text = String(value || '').trim();
|
|
22
|
+
if (!text) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
const ms = Date.parse(text);
|
|
26
|
+
return Number.isFinite(ms) ? ms : null;
|
|
27
|
+
};
|
|
28
|
+
const isServerPendingReload = (server, nginxRunning, lastAppliedAt) => {
|
|
29
|
+
if (!server.enabled || !nginxRunning) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
if (lastAppliedAt === null) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
const changedAt = parseIsoMs(server.updatedAt) ?? parseIsoMs(server.createdAt);
|
|
36
|
+
if (changedAt === null) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
return changedAt > lastAppliedAt;
|
|
40
|
+
};
|
|
41
|
+
const resolveActor = (request) => {
|
|
42
|
+
const directHeader = request.headers['x-ngm-user'] || request.headers['x-user'] || request.headers['x-username'];
|
|
43
|
+
if (typeof directHeader === 'string' && directHeader.trim()) {
|
|
44
|
+
return directHeader.trim();
|
|
45
|
+
}
|
|
46
|
+
const user = request?.user;
|
|
47
|
+
if (typeof user?.name === 'string' && user.name.trim()) {
|
|
48
|
+
return user.name.trim();
|
|
49
|
+
}
|
|
50
|
+
if (typeof user?.username === 'string' && user.username.trim()) {
|
|
51
|
+
return user.username.trim();
|
|
52
|
+
}
|
|
53
|
+
if (typeof user?.id === 'string' && user.id.trim()) {
|
|
54
|
+
return user.id.trim();
|
|
55
|
+
}
|
|
56
|
+
return undefined;
|
|
57
|
+
};
|
|
8
58
|
fastify.get('/servers', async (_request, reply) => {
|
|
9
59
|
try {
|
|
10
60
|
const servers = await nginx.server.getAllServers();
|
|
61
|
+
let nginxRunning = null;
|
|
62
|
+
let lastAppliedAt = null;
|
|
63
|
+
try {
|
|
64
|
+
const status = await nginx.service.getStatus();
|
|
65
|
+
nginxRunning = Boolean(status.isRunning);
|
|
66
|
+
lastAppliedAt = nginx.service.getLastConfigAppliedAt();
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
nginxRunning = null;
|
|
70
|
+
}
|
|
71
|
+
const enriched = servers.map(server => ({
|
|
72
|
+
...server,
|
|
73
|
+
runtimeStatus: buildRuntimeStatus(Boolean(server.enabled), nginxRunning, isServerPendingReload(server, nginxRunning, lastAppliedAt)),
|
|
74
|
+
}));
|
|
11
75
|
return reply.send({
|
|
12
76
|
success: true,
|
|
13
|
-
servers,
|
|
77
|
+
servers: enriched,
|
|
14
78
|
});
|
|
15
79
|
}
|
|
16
80
|
catch (error) {
|
|
@@ -22,11 +86,25 @@ function registerNginxServerRoutes(context) {
|
|
|
22
86
|
try {
|
|
23
87
|
const server = await nginx.server.getServer(id);
|
|
24
88
|
if (!server) {
|
|
25
|
-
throw new
|
|
89
|
+
throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.NOT_FOUND, 'Server 不存在');
|
|
90
|
+
}
|
|
91
|
+
let nginxRunning = null;
|
|
92
|
+
let lastAppliedAt = null;
|
|
93
|
+
try {
|
|
94
|
+
const status = await nginx.service.getStatus();
|
|
95
|
+
nginxRunning = Boolean(status.isRunning);
|
|
96
|
+
lastAppliedAt = nginx.service.getLastConfigAppliedAt();
|
|
26
97
|
}
|
|
98
|
+
catch {
|
|
99
|
+
nginxRunning = null;
|
|
100
|
+
}
|
|
101
|
+
const enriched = {
|
|
102
|
+
...server,
|
|
103
|
+
runtimeStatus: buildRuntimeStatus(Boolean(server.enabled), nginxRunning, isServerPendingReload(server, nginxRunning, lastAppliedAt)),
|
|
104
|
+
};
|
|
27
105
|
return reply.send({
|
|
28
106
|
success: true,
|
|
29
|
-
server,
|
|
107
|
+
server: enriched,
|
|
30
108
|
});
|
|
31
109
|
}
|
|
32
110
|
catch (error) {
|
|
@@ -35,7 +113,11 @@ function registerNginxServerRoutes(context) {
|
|
|
35
113
|
});
|
|
36
114
|
fastify.post('/servers', async (request, reply) => {
|
|
37
115
|
try {
|
|
38
|
-
const
|
|
116
|
+
const actor = resolveActor(request);
|
|
117
|
+
const payload = actor
|
|
118
|
+
? { ...request.body, createdBy: request.body?.createdBy || actor }
|
|
119
|
+
: { ...request.body };
|
|
120
|
+
const server = await nginx.server.createServer(payload);
|
|
39
121
|
return reply.send({
|
|
40
122
|
success: true,
|
|
41
123
|
server,
|
|
@@ -61,9 +143,26 @@ function registerNginxServerRoutes(context) {
|
|
|
61
143
|
fastify.delete('/servers/:id', async (request, reply) => {
|
|
62
144
|
const { id } = request.params;
|
|
63
145
|
try {
|
|
64
|
-
await nginx.server.deleteServer(id);
|
|
146
|
+
const result = await nginx.server.deleteServer(id);
|
|
147
|
+
return reply.send({
|
|
148
|
+
success: true,
|
|
149
|
+
snapshotId: result.snapshotId,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
return (0, nginx_route_context_1.sendBadRequest)(reply, error);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
fastify.post('/servers/restore-deleted', async (request, reply) => {
|
|
157
|
+
try {
|
|
158
|
+
const snapshotId = String(request.body?.snapshotId || '').trim();
|
|
159
|
+
if (!snapshotId) {
|
|
160
|
+
throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.BAD_REQUEST, 'snapshotId 不能为空');
|
|
161
|
+
}
|
|
162
|
+
const server = await nginx.server.restoreDeletedServer(snapshotId);
|
|
65
163
|
return reply.send({
|
|
66
164
|
success: true,
|
|
165
|
+
server,
|
|
67
166
|
});
|
|
68
167
|
}
|
|
69
168
|
catch (error) {
|
|
@@ -94,4 +193,57 @@ function registerNginxServerRoutes(context) {
|
|
|
94
193
|
return (0, nginx_route_context_1.sendBadRequest)(reply, error);
|
|
95
194
|
}
|
|
96
195
|
});
|
|
196
|
+
fastify.post('/servers/import/parse', async (request, reply) => {
|
|
197
|
+
try {
|
|
198
|
+
const content = String(request.body?.content || '').trim();
|
|
199
|
+
if (!content) {
|
|
200
|
+
return reply.send({
|
|
201
|
+
success: true,
|
|
202
|
+
candidates: [],
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
const candidates = await nginx.server.parseImportCandidates(content);
|
|
206
|
+
return reply.send({
|
|
207
|
+
success: true,
|
|
208
|
+
candidates,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
return (0, nginx_route_context_1.sendBadRequest)(reply, error);
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
fastify.post('/servers/import/analyze', async (request, reply) => {
|
|
216
|
+
try {
|
|
217
|
+
const requests = Array.isArray(request.body?.requests) ? request.body.requests : [];
|
|
218
|
+
const candidates = await nginx.server.analyzeImportRequests(requests);
|
|
219
|
+
return reply.send({
|
|
220
|
+
success: true,
|
|
221
|
+
candidates,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
return (0, nginx_route_context_1.sendBadRequest)(reply, error);
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
fastify.post('/servers/validate-ssl-paths', async (request, reply) => {
|
|
229
|
+
try {
|
|
230
|
+
const certPath = String(request.body?.sslCert || '').trim();
|
|
231
|
+
const keyPath = String(request.body?.sslKey || '').trim();
|
|
232
|
+
const cert = certPath
|
|
233
|
+
? await nginx.service.validateFileReadable(certPath)
|
|
234
|
+
: { exists: false, readable: false, error: '证书路径为空' };
|
|
235
|
+
const key = keyPath
|
|
236
|
+
? await nginx.service.validateFileReadable(keyPath)
|
|
237
|
+
: { exists: false, readable: false, error: '私钥路径为空' };
|
|
238
|
+
return reply.send({
|
|
239
|
+
success: true,
|
|
240
|
+
valid: cert.exists && cert.readable && key.exists && key.readable,
|
|
241
|
+
cert,
|
|
242
|
+
key,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
catch (error) {
|
|
246
|
+
return (0, nginx_route_context_1.sendBadRequest)(reply, error);
|
|
247
|
+
}
|
|
248
|
+
});
|
|
97
249
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.default = nodeVersionRoutes;
|
|
4
|
-
const
|
|
4
|
+
const errors_1 = require("@yinuo-ngm/errors");
|
|
5
5
|
async function nodeVersionRoutes(fastify) {
|
|
6
6
|
fastify.get('/current', async () => {
|
|
7
7
|
const service = fastify.core.nodeVersion;
|
|
@@ -10,7 +10,7 @@ async function nodeVersionRoutes(fastify) {
|
|
|
10
10
|
fastify.post('/switch', async (req) => {
|
|
11
11
|
const { version } = req.body || {};
|
|
12
12
|
if (!version) {
|
|
13
|
-
throw new
|
|
13
|
+
throw new errors_1.CoreError(errors_1.CoreErrorCodes.VERSION_REQUIRED, '请指定要切换的 Node 版本', {});
|
|
14
14
|
}
|
|
15
15
|
const service = fastify.core.nodeVersion;
|
|
16
16
|
return await service.switchVersion(version);
|
|
@@ -18,7 +18,7 @@ async function nodeVersionRoutes(fastify) {
|
|
|
18
18
|
fastify.post('/project-requirement', async (req) => {
|
|
19
19
|
const { projectPath } = req.body || {};
|
|
20
20
|
if (!projectPath) {
|
|
21
|
-
throw new
|
|
21
|
+
throw new errors_1.CoreError(errors_1.CoreErrorCodes.PROJECT_PATH_REQUIRED, '请指定项目路径', {});
|
|
22
22
|
}
|
|
23
23
|
const service = fastify.core.nodeVersion;
|
|
24
24
|
return await service.detectProjectRequirement(projectPath);
|
|
@@ -26,7 +26,7 @@ async function nodeVersionRoutes(fastify) {
|
|
|
26
26
|
fastify.post('/install', async (req) => {
|
|
27
27
|
const { version } = req.body || {};
|
|
28
28
|
if (!version) {
|
|
29
|
-
throw new
|
|
29
|
+
throw new errors_1.CoreError(errors_1.CoreErrorCodes.VERSION_REQUIRED, '请指定要安装的 Node 版本', {});
|
|
30
30
|
}
|
|
31
31
|
const service = fastify.core.nodeVersion;
|
|
32
32
|
const result = await service.installNodeVersion(version);
|
|
@@ -35,7 +35,7 @@ async function nodeVersionRoutes(fastify) {
|
|
|
35
35
|
fastify.post('/uninstall', async (req) => {
|
|
36
36
|
const { version } = req.body || {};
|
|
37
37
|
if (!version) {
|
|
38
|
-
throw new
|
|
38
|
+
throw new errors_1.CoreError(errors_1.CoreErrorCodes.VERSION_REQUIRED, '请指定要卸载的 Node 版本', {});
|
|
39
39
|
}
|
|
40
40
|
const service = fastify.core.nodeVersion;
|
|
41
41
|
const success = await service.uninstallNodeVersion(version);
|
|
@@ -44,10 +44,10 @@ async function nodeVersionRoutes(fastify) {
|
|
|
44
44
|
fastify.post('/write-engine-config', async (req) => {
|
|
45
45
|
const { projectPath, version } = req.body || {};
|
|
46
46
|
if (!projectPath) {
|
|
47
|
-
throw new
|
|
47
|
+
throw new errors_1.CoreError(errors_1.CoreErrorCodes.PROJECT_PATH_REQUIRED, '请指定项目路径', {});
|
|
48
48
|
}
|
|
49
49
|
if (!version) {
|
|
50
|
-
throw new
|
|
50
|
+
throw new errors_1.CoreError(errors_1.CoreErrorCodes.VERSION_REQUIRED, '请指定版本要求', {});
|
|
51
51
|
}
|
|
52
52
|
const service = fastify.core.nodeVersion;
|
|
53
53
|
const success = await service.writeEngineConfig(projectPath, version);
|