@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.
Files changed (118) hide show
  1. package/lib/app.js +3 -15
  2. package/lib/common/editor.js +2 -2
  3. package/lib/env.d.ts +1 -0
  4. package/lib/env.js +1 -0
  5. package/lib/plugins/error-handler.plugin.d.ts +2 -2
  6. package/lib/plugins/error-handler.plugin.js +97 -93
  7. package/lib/plugins/ws/topics/nginx.ws.js +2 -1
  8. package/lib/plugins/ws/topics/svn.ws.d.ts +2 -1
  9. package/lib/plugins/ws/topics/svn.ws.js +3 -2
  10. package/lib/plugins/ws/topics/syslog.ws.d.ts +1 -1
  11. package/lib/plugins/ws/topics/task.ws.d.ts +2 -1
  12. package/lib/plugins/ws/topics/task.ws.js +2 -1
  13. package/lib/plugins/ws/ws.context.d.ts +1 -1
  14. package/lib/plugins/ws/ws.plugin.js +4 -3
  15. package/lib/plugins/ws/ws.router.d.ts +2 -2
  16. package/lib/plugins/ws/ws.router.js +8 -7
  17. package/lib/routes/api-client/collection.routes.js +8 -6
  18. package/lib/routes/api-client/env.routes.js +4 -3
  19. package/lib/routes/api-client/history.routes.js +3 -2
  20. package/lib/routes/api-client/hub-token.routes.js +11 -11
  21. package/lib/routes/api-client/request.routes.js +7 -6
  22. package/lib/routes/api-client/send.routes.js +3 -2
  23. package/lib/routes/config.routes.js +5 -5
  24. package/lib/routes/hub.routes.js +10 -10
  25. package/lib/routes/nginx/nginx-lifecycle.routes.js +4 -0
  26. package/lib/routes/nginx/nginx-route.context.js +23 -7
  27. package/lib/routes/nginx/nginx-server.routes.js +158 -6
  28. package/lib/routes/node-version.routes.js +7 -7
  29. package/lib/routes/project.routes.js +9 -9
  30. package/lib/routes/rss.routes.js +3 -3
  31. package/lib/routes/sprite-browse.routes.js +2 -1
  32. package/lib/routes/sprite.routes.js +3 -3
  33. package/lib/routes/static-files.routes.js +7 -7
  34. package/lib/routes/svn.routes.js +5 -3
  35. package/lib/routes/system.routes.js +20 -1
  36. package/lib/routes/task.routes.js +4 -4
  37. package/package.json +13 -6
  38. package/www/3rdpartylicenses.txt +52 -52
  39. package/www/browser/chunk-2L7NUOMX.js +2 -0
  40. package/www/browser/chunk-2NZJ7CN2.js +20 -0
  41. package/www/browser/{chunk-SPRWNZHF.js → chunk-3CM4SKDO.js} +2 -2
  42. package/www/browser/{chunk-EEVPZGEY.js → chunk-3I7BQCXS.js} +1 -1
  43. package/www/browser/{chunk-DXF7BVK5.js → chunk-3M56F2S2.js} +1 -1
  44. package/www/browser/chunk-3OHBKMAA.js +1 -0
  45. package/www/browser/{chunk-4KETC6EB.js → chunk-3XNNQFWR.js} +1 -1
  46. package/www/browser/chunk-4BRW6LCZ.js +4 -0
  47. package/www/browser/{chunk-HGONFYP6.js → chunk-4LBSLURA.js} +1 -1
  48. package/www/browser/{chunk-BYEU6KGP.js → chunk-4X42HB6N.js} +1 -1
  49. package/www/browser/chunk-5DYX4DUX.js +11 -0
  50. package/www/browser/chunk-6SYVDN5L.js +1 -0
  51. package/www/browser/{chunk-XJ5KZQNN.js → chunk-6YYNHZ2A.js} +1 -1
  52. package/www/browser/{chunk-G2W3B7TJ.js → chunk-75W3GVSO.js} +1 -1
  53. package/www/browser/{chunk-76TVIB33.js → chunk-7U44RF5F.js} +1 -1
  54. package/www/browser/{chunk-WD4IAQR3.js → chunk-AELTP6YN.js} +1 -1
  55. package/www/browser/chunk-AMXRL4GR.js +1 -0
  56. package/www/browser/chunk-AV2ZODEH.js +1 -0
  57. package/www/browser/chunk-AZ6SIMYH.js +1 -0
  58. package/www/browser/chunk-B3C35ET3.js +2 -0
  59. package/www/browser/chunk-BTQIUVTQ.js +1 -0
  60. package/www/browser/chunk-CN5J4WNO.js +1 -0
  61. package/www/browser/{chunk-N4LRZJBP.js → chunk-D2ODDESN.js} +1 -1
  62. package/www/browser/{chunk-O2EYEF7J.js → chunk-DE6E23ET.js} +1 -1
  63. package/www/browser/{chunk-SVQWPHF5.js → chunk-DIJPUYIA.js} +1 -1
  64. package/www/browser/{chunk-SYCNSLAW.js → chunk-DLGJD6YU.js} +4 -4
  65. package/www/browser/{chunk-JHMEKUZ5.js → chunk-EEDA5U4V.js} +1 -1
  66. package/www/browser/chunk-FK6Z4HLL.js +1 -0
  67. package/www/browser/chunk-FL6GDGHW.js +1 -0
  68. package/www/browser/{chunk-HRXCR3IN.js → chunk-FXCG34QS.js} +1 -1
  69. package/www/browser/{chunk-UJOHBN2Y.js → chunk-H2USFIYR.js} +1 -1
  70. package/www/browser/chunk-H5HGMOE6.js +1 -0
  71. package/www/browser/chunk-HB3HECPD.js +1 -0
  72. package/www/browser/{chunk-INL2PELS.js → chunk-HDNG236Q.js} +1 -1
  73. package/www/browser/{chunk-WF2QTF5L.js → chunk-HJTXXSMC.js} +1 -1
  74. package/www/browser/{chunk-LQ5OXSW7.js → chunk-HUMCWAKJ.js} +1 -1
  75. package/www/browser/chunk-IKB3EQCP.js +2 -0
  76. package/www/browser/{chunk-TMX5TTV3.js → chunk-K7PESFPY.js} +1 -1
  77. package/www/browser/{chunk-T3KK7ZXB.js → chunk-KRYMOHYF.js} +1 -1
  78. package/www/browser/{chunk-SQQNR223.js → chunk-M4QRBV3K.js} +1 -1
  79. package/www/browser/chunk-N2PELLMM.js +30 -0
  80. package/www/browser/{chunk-ACAZUX6C.js → chunk-ONXBYGIG.js} +1 -1
  81. package/www/browser/{chunk-JZULA5JV.js → chunk-OSBDR36P.js} +1 -1
  82. package/www/browser/chunk-OZCK4XVV.js +1 -0
  83. package/www/browser/{chunk-GDWS2L66.js → chunk-QJP5F735.js} +1 -1
  84. package/www/browser/{chunk-2X3MRS27.js → chunk-RGOYDY7H.js} +1 -1
  85. package/www/browser/{chunk-B6MBYCXI.js → chunk-UJKK4A7Y.js} +1 -1
  86. package/www/browser/{chunk-ZNTJRLVH.js → chunk-WD2EKZQC.js} +1 -1
  87. package/www/browser/{chunk-L5D75AMV.js → chunk-WUA5JFDD.js} +1 -1
  88. package/www/browser/chunk-XLFHB7RS.js +3 -0
  89. package/www/browser/{chunk-JU3TEDBV.js → chunk-YNW4HEJO.js} +1 -1
  90. package/www/browser/{chunk-RW2JPJV7.js → chunk-ZTDLWBW5.js} +1 -1
  91. package/www/browser/index.html +1 -1
  92. package/www/browser/main-N64WJCHX.js +34 -0
  93. package/www/browser/chunk-2BZRE4G7.js +0 -1
  94. package/www/browser/chunk-5T5KA5PG.js +0 -1
  95. package/www/browser/chunk-6CGHNKJI.js +0 -1
  96. package/www/browser/chunk-7J24TP36.js +0 -1
  97. package/www/browser/chunk-CZ5AZ3VW.js +0 -1
  98. package/www/browser/chunk-DW7F5PEA.js +0 -1
  99. package/www/browser/chunk-HHBPULJW.js +0 -2
  100. package/www/browser/chunk-KVFR7GFV.js +0 -20
  101. package/www/browser/chunk-L7TMCSHV.js +0 -1
  102. package/www/browser/chunk-MVQTKINJ.js +0 -1
  103. package/www/browser/chunk-OMDHJIUB.js +0 -1
  104. package/www/browser/chunk-OWUAAOHW.js +0 -2
  105. package/www/browser/chunk-PIK5YPIB.js +0 -1
  106. package/www/browser/chunk-RHLQRQDK.js +0 -9
  107. package/www/browser/chunk-S3SJ4VVM.js +0 -11
  108. package/www/browser/chunk-SIVPP74B.js +0 -0
  109. package/www/browser/chunk-UFY3FLDK.js +0 -1
  110. package/www/browser/chunk-UQGCUFNM.js +0 -1
  111. package/www/browser/chunk-UXXWRMM6.js +0 -1
  112. package/www/browser/chunk-UZRJGJTD.js +0 -2
  113. package/www/browser/chunk-WI67LAOV.js +0 -4
  114. package/www/browser/chunk-WNCM6QKB.js +0 -1
  115. package/www/browser/chunk-YETDFSQE.js +0 -1
  116. package/www/browser/chunk-YMTC5GZK.js +0 -1
  117. package/www/browser/chunk-YVZHJ76K.js +0 -1
  118. 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 core_1 = require("@yinuo-ngm/core");
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 core_1.AppError("BAD_REQUEST", `${field} is required`);
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 core_1.AppError("BAD_REQUEST", "projectId is required when baseUrl/token are not provided");
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 core_1.AppError("BAD_REQUEST", tokenType === "personal"
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 core_1.AppError("BAD_REQUEST", payload?.message || `hub-v2 request failed (${response.status})`, {
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 core_1.AppError("BAD_REQUEST", payload?.message || `hub-v2 request failed (${response.status})`, {
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 core_1.AppError("BAD_REQUEST", payload?.message || `hub-v2 request failed (${response.status})`, {
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 core_1.AppError("BAD_REQUEST", payload?.message || `hub-v2 request failed (${response.status})`, {
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 core_1.AppError("BAD_REQUEST", payload.message || "hub-v2 response error", {
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 core_1.AppError("BAD_REQUEST", "path must use projectKey (or business relative path), not local projectId");
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 core_1.AppError("BAD_REQUEST", "projectKey is required when path does not include /projects/:projectKey");
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 Error("projectId is required when scope=project");
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 Error("projectId is required when scope=project");
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 Error("request.id is required");
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 Error("projectId is required when scope=project");
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 Error("request.id is required");
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 Error("request not found: " + body.request.id);
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 Error("projectId is required when scope=project");
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 Error("request or requestId is required");
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 core_1 = require("@yinuo-ngm/core");
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 core_1.AppError("CONFIG_WRITE_FAILED", "missing body.raw or body.data", { projectId, docId });
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 core_1.AppError("EDITOR_LAUNCH_FAILED", e?.message || "openInEditor failed");
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 core_1.AppError("BAD_REQUEST", "missing body.vm");
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 core_1.AppError("BAD_REQUEST", "missing body.vm");
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
  });
@@ -1,23 +1,23 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.default = hubRoutes;
4
- const core_1 = require("@yinuo-ngm/core");
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 core_1.AppError("BAD_REQUEST", `${fieldName} must be string`);
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 core_1.AppError("BAD_REQUEST", `${fieldName} length must <= ${maxLen}`);
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 core_1.AppError("BAD_REQUEST", "invalid feedback payload");
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 core_1.AppError("BAD_REQUEST", "invalid feedback category");
32
+ throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.BAD_REQUEST, "invalid feedback category");
33
33
  }
34
34
  if (!title) {
35
- throw new core_1.AppError("BAD_REQUEST", "feedback title is required");
35
+ throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.BAD_REQUEST, "feedback title is required");
36
36
  }
37
37
  if (!content) {
38
- throw new core_1.AppError("BAD_REQUEST", "feedback content is required");
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 core_1.AppError("UNKNOWN_ERROR", "failed to connect hub service", { cause: error?.message || String(error), targetUrl });
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 core_1.AppError("UNKNOWN_ERROR", message, { status: response.status, payload });
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 core_1.AppError("UNKNOWN_ERROR", payload?.message || "hub feedback submit failed", { payload });
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 core_1 = require("@yinuo-ngm/core");
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 Error('配置文件路径不能为空');
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(included.map(item => normalizeFsPath(item)));
17
- const normalizedTarget = normalizeFsPath(filePath);
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 Error('配置文件不在当前可管理列表中');
35
+ throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.OP_NOT_FOUND, '配置文件不在当前可管理列表中');
20
36
  }
21
- return (0, path_1.resolve)(filePath);
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 core_1.AppError('BAD_REQUEST', message, {
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 core_1 = require("@yinuo-ngm/core");
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 core_1.AppError('NOT_FOUND', 'Server 不存在');
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 server = await nginx.server.createServer(request.body);
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 core_1 = require("@yinuo-ngm/core");
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 core_1.AppError('VERSION_REQUIRED', '请指定要切换的 Node 版本', {});
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 core_1.AppError('PROJECT_PATH_REQUIRED', '请指定项目路径', {});
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 core_1.AppError('VERSION_REQUIRED', '请指定要安装的 Node 版本', {});
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 core_1.AppError('VERSION_REQUIRED', '请指定要卸载的 Node 版本', {});
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 core_1.AppError('PROJECT_PATH_REQUIRED', '请指定项目路径', {});
47
+ throw new errors_1.CoreError(errors_1.CoreErrorCodes.PROJECT_PATH_REQUIRED, '请指定项目路径', {});
48
48
  }
49
49
  if (!version) {
50
- throw new core_1.AppError('VERSION_REQUIRED', '请指定版本要求', {});
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);