@yinuo-ngm/server 1.0.20 → 1.0.22

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 (113) hide show
  1. package/lib/app.js +0 -4
  2. package/lib/env.js +5 -1
  3. package/lib/plugins/success-handle.plugin.js +1 -1
  4. package/lib/plugins/ws/topics/nginx.ws.js +1 -1
  5. package/lib/plugins/ws/ws.context.js +3 -1
  6. package/lib/plugins/ws/ws.plugin.js +14 -2
  7. package/lib/routes/api-client/collection.routes.js +35 -35
  8. package/lib/routes/api-client/env.routes.js +12 -8
  9. package/lib/routes/api-client/history.routes.js +10 -5
  10. package/lib/routes/api-client/request.routes.js +14 -8
  11. package/lib/routes/api-client/route-mappers.d.ts +7 -0
  12. package/lib/routes/api-client/route-mappers.js +78 -0
  13. package/lib/routes/api-client/send.routes.js +12 -8
  14. package/lib/routes/config.routes.js +73 -49
  15. package/lib/routes/deps.route.js +47 -5
  16. package/lib/routes/index.js +5 -3
  17. package/lib/routes/nginx/nginx-config.routes.js +3 -3
  18. package/lib/routes/nginx/nginx-lifecycle.routes.js +14 -13
  19. package/lib/routes/nginx/nginx-module.routes.js +6 -6
  20. package/lib/routes/nginx/nginx-route.context.d.ts +16 -1
  21. package/lib/routes/nginx/nginx-route.context.js +136 -1
  22. package/lib/routes/nginx/nginx-server.routes.js +5 -5
  23. package/lib/routes/node-runtime.routes.d.ts +2 -0
  24. package/lib/routes/node-runtime.routes.js +69 -0
  25. package/lib/routes/project.routes.js +117 -13
  26. package/lib/routes/sprite/sprite-browse.routes.d.ts +2 -0
  27. package/lib/routes/sprite/sprite-browse.routes.js +137 -0
  28. package/lib/routes/sprite/sprite-quick.utils.d.ts +19 -0
  29. package/lib/routes/sprite/sprite-quick.utils.js +247 -0
  30. package/lib/routes/sprite/sprite.routes.d.ts +2 -0
  31. package/lib/routes/sprite/sprite.routes.js +212 -0
  32. package/lib/routes/sprite-browse.routes.js +17 -11
  33. package/lib/routes/sprite.routes.d.ts +1 -1
  34. package/lib/routes/sprite.routes.js +68 -11
  35. package/lib/routes/svn.routes.js +48 -14
  36. package/lib/routes/task.routes.js +99 -7
  37. package/package.json +7 -7
  38. package/www/3rdpartylicenses.txt +5 -5
  39. package/www/browser/{chunk-OZCK4XVV.js → chunk-2VJVGBXG.js} +1 -1
  40. package/www/browser/{chunk-FL6GDGHW.js → chunk-3ODVU46S.js} +1 -1
  41. package/www/browser/chunk-442QFABJ.js +1 -0
  42. package/www/browser/{chunk-WD2EKZQC.js → chunk-6J6G7JEP.js} +1 -1
  43. package/www/browser/{chunk-7U44RF5F.js → chunk-6UFDNETG.js} +1 -1
  44. package/www/browser/{chunk-75W3GVSO.js → chunk-7AKVG375.js} +1 -1
  45. package/www/browser/chunk-7GLWEFTM.js +1 -0
  46. package/www/browser/{chunk-DIJPUYIA.js → chunk-7QR6RHLA.js} +1 -1
  47. package/www/browser/{chunk-HJTXXSMC.js → chunk-CO3CQHKV.js} +1 -1
  48. package/www/browser/{chunk-3XNNQFWR.js → chunk-CPJTSA6E.js} +1 -1
  49. package/www/browser/chunk-DENFXXOY.js +1 -0
  50. package/www/browser/{chunk-HDNG236Q.js → chunk-HC5KLPHD.js} +1 -1
  51. package/www/browser/chunk-HJAP6WBI.js +1 -0
  52. package/www/browser/{chunk-N2PELLMM.js → chunk-JM6DCZP6.js} +7 -7
  53. package/www/browser/chunk-K6C5ZIV2.js +20 -0
  54. package/www/browser/chunk-M6NZ6C2R.js +2 -0
  55. package/www/browser/{chunk-RGOYDY7H.js → chunk-MBPB43C6.js} +1 -1
  56. package/www/browser/chunk-MPLGMTLT.js +3 -0
  57. package/www/browser/chunk-NNOWADVM.js +1 -0
  58. package/www/browser/{chunk-M4QRBV3K.js → chunk-NREWZQVN.js} +1 -1
  59. package/www/browser/{chunk-6YYNHZ2A.js → chunk-OUGN4APK.js} +1 -1
  60. package/www/browser/chunk-PB2LGZNW.js +1 -0
  61. package/www/browser/chunk-PDI3HSSH.js +4 -0
  62. package/www/browser/chunk-PUG3VNTI.js +3 -0
  63. package/www/browser/{chunk-QJP5F735.js → chunk-PY3AUTHC.js} +1 -1
  64. package/www/browser/{chunk-3M56F2S2.js → chunk-R4HSZU2J.js} +1 -1
  65. package/www/browser/{chunk-DE6E23ET.js → chunk-RKK4I2RT.js} +1 -1
  66. package/www/browser/chunk-RMIYLAEM.js +1 -0
  67. package/www/browser/chunk-RZA3IFQV.js +2 -0
  68. package/www/browser/{chunk-K7PESFPY.js → chunk-SD2HFD54.js} +1 -1
  69. package/www/browser/{chunk-BTQIUVTQ.js → chunk-SEJGGBE2.js} +1 -1
  70. package/www/browser/chunk-SSCUT2GF.js +1 -0
  71. package/www/browser/chunk-TCBOC5FF.js +1 -0
  72. package/www/browser/{chunk-AZ6SIMYH.js → chunk-TS5ZQYYY.js} +1 -1
  73. package/www/browser/chunk-TVJHI463.js +1 -0
  74. package/www/browser/{chunk-YNW4HEJO.js → chunk-TXAXE73U.js} +17 -17
  75. package/www/browser/{chunk-4X42HB6N.js → chunk-U2YWR3HF.js} +1 -1
  76. package/www/browser/{chunk-D2ODDESN.js → chunk-U3EUR236.js} +1 -1
  77. package/www/browser/chunk-UKCMUTYL.js +1 -0
  78. package/www/browser/{chunk-DLGJD6YU.js → chunk-UPOORT3S.js} +1 -1
  79. package/www/browser/chunk-VW3S7C2Z.js +1 -0
  80. package/www/browser/chunk-VZYJ3MSW.js +15 -0
  81. package/www/browser/{chunk-AELTP6YN.js → chunk-WCHN62X6.js} +1 -1
  82. package/www/browser/chunk-WWIPELAV.js +1 -0
  83. package/www/browser/{chunk-B3C35ET3.js → chunk-WYNERG74.js} +1 -1
  84. package/www/browser/{chunk-ZTDLWBW5.js → chunk-XJDJL3TQ.js} +1 -1
  85. package/www/browser/{chunk-EEDA5U4V.js → chunk-YV6QPLF5.js} +1 -1
  86. package/www/browser/{chunk-ONXBYGIG.js → chunk-YVRGRFK2.js} +1 -1
  87. package/www/browser/index.html +1 -1
  88. package/www/browser/main-7GYUOEJY.js +38 -0
  89. package/lib/plugins/api-client.plugin.d.ts +0 -3
  90. package/lib/plugins/api-client.plugin.js +0 -55
  91. package/lib/plugins/nginx.binding.store.d.ts +0 -4
  92. package/lib/plugins/nginx.binding.store.js +0 -39
  93. package/lib/plugins/nginx.plugin.d.ts +0 -3
  94. package/lib/plugins/nginx.plugin.js +0 -23
  95. package/www/browser/chunk-2L7NUOMX.js +0 -2
  96. package/www/browser/chunk-2NZJ7CN2.js +0 -20
  97. package/www/browser/chunk-3CM4SKDO.js +0 -15
  98. package/www/browser/chunk-3OHBKMAA.js +0 -1
  99. package/www/browser/chunk-4LBSLURA.js +0 -1
  100. package/www/browser/chunk-5DYX4DUX.js +0 -11
  101. package/www/browser/chunk-6SYVDN5L.js +0 -1
  102. package/www/browser/chunk-AMXRL4GR.js +0 -1
  103. package/www/browser/chunk-AV2ZODEH.js +0 -1
  104. package/www/browser/chunk-CN5J4WNO.js +0 -1
  105. package/www/browser/chunk-FK6Z4HLL.js +0 -1
  106. package/www/browser/chunk-FXCG34QS.js +0 -1
  107. package/www/browser/chunk-H5HGMOE6.js +0 -1
  108. package/www/browser/chunk-HB3HECPD.js +0 -1
  109. package/www/browser/chunk-HUMCWAKJ.js +0 -3
  110. package/www/browser/chunk-IKB3EQCP.js +0 -2
  111. package/www/browser/chunk-OSBDR36P.js +0 -1
  112. package/www/browser/chunk-XLFHB7RS.js +0 -3
  113. package/www/browser/main-N64WJCHX.js +0 -34
@@ -0,0 +1,247 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MISC_PROXY_PREFIX = exports.QUICK_SPRITE_PROXY_PREFIX = void 0;
4
+ exports.getBaseUrl = getBaseUrl;
5
+ exports.buildQuickPreviewUrl = buildQuickPreviewUrl;
6
+ exports.quickFetch = quickFetch;
7
+ exports.resolveEnabledRemoteProjectId = resolveEnabledRemoteProjectId;
8
+ exports.fetchRemoteProject = fetchRemoteProject;
9
+ exports.mapToGroup = mapToGroup;
10
+ exports.mapQuickGroupsToSnapshot = mapQuickGroupsToSnapshot;
11
+ exports.copyRawResponseHeaders = copyRawResponseHeaders;
12
+ exports.hasQuickSprite = hasQuickSprite;
13
+ exports.fetchAndCacheRemoteMiscImages = fetchAndCacheRemoteMiscImages;
14
+ exports.buildBrowseFromCache = buildBrowseFromCache;
15
+ exports.buildRemoteMiscUrl = buildRemoteMiscUrl;
16
+ const errors_1 = require("@yinuo-ngm/errors");
17
+ const sprite_1 = require("@yinuo-ngm/sprite");
18
+ function getBaseUrl(cfg) {
19
+ return (cfg?.quickSpriteBaseUrl?.trim() ||
20
+ "http://192.168.1.31:7010").replace(/\/+$/, "");
21
+ }
22
+ exports.QUICK_SPRITE_PROXY_PREFIX = "/api/sprite/proxy";
23
+ function buildQuickPreviewUrl(localProjectId, group) {
24
+ return `${exports.QUICK_SPRITE_PROXY_PREFIX}/${encodeURIComponent(localProjectId)}/${encodeURIComponent(group)}.png`;
25
+ }
26
+ async function quickFetch(fastify, baseUrl, path, init) {
27
+ const url = `${baseUrl}${path}`;
28
+ fastify.log.info(`[sprite-quick] → ${init?.method || "GET"} ${url}`);
29
+ let res;
30
+ try {
31
+ res = await fetch(url, {
32
+ ...init,
33
+ headers: {
34
+ "Content-Type": "application/json",
35
+ ...init?.headers,
36
+ },
37
+ });
38
+ }
39
+ catch (err) {
40
+ fastify.log.error(`[sprite-quick] ✗ 无法连接远端 ${url}: ${err?.message || err}`);
41
+ throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.INTERNAL_ERROR, `无法连接远端雪碧图服务 (${baseUrl}):${err?.message || err}`);
42
+ }
43
+ if (!res.ok) {
44
+ const text = await res.text().catch(() => "");
45
+ fastify.log.error(`[sprite-quick] ← ${res.status} ${url}: ${text}`);
46
+ throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.INTERNAL_ERROR, `远端服务返回 ${res.status}: ${text || res.statusText}`);
47
+ }
48
+ const data = (await res.json());
49
+ fastify.log.info(`[sprite-quick] ← ${res.status} ${url}`);
50
+ return data;
51
+ }
52
+ async function resolveEnabledRemoteProjectId(fastify, projectId) {
53
+ const cfg = await fastify.core.sprite.getConfig(projectId);
54
+ if (!cfg?.quickSpriteEnabled || !cfg?.quickSpriteProjectId)
55
+ return null;
56
+ return cfg.quickSpriteProjectId;
57
+ }
58
+ async function fetchRemoteProject(fastify, baseUrl, quickProjectId) {
59
+ try {
60
+ return await quickFetch(fastify, baseUrl, `/api/projects/${encodeURIComponent(quickProjectId)}`);
61
+ }
62
+ catch {
63
+ return null;
64
+ }
65
+ }
66
+ function mapToGroup(result, localProjectId, prefix, spriteUrlTemplate) {
67
+ const classes = (result.classes || []).map((c) => ({
68
+ name: c.name,
69
+ className: `${prefix}-${result.group.split("-")[0]}-${c.name}`,
70
+ x: c.x,
71
+ y: c.y,
72
+ width: c.width,
73
+ height: c.height,
74
+ }));
75
+ const derived = deriveSpriteSize(classes);
76
+ const spriteWidth = derived.width > 0 ? derived.width : result.width || 0;
77
+ const spriteHeight = derived.height > 0 ? derived.height : result.height || 0;
78
+ const [tileW, tileH] = parseTileSize(result.group);
79
+ const resolvedSpriteUrl = spriteUrlTemplate
80
+ ? spriteUrlTemplate.replace(/{group}/g, result.group).replace(/{size}/g, String(tileW))
81
+ : result.spriteUrl;
82
+ const cssOpts = {
83
+ prefix,
84
+ spriteUrlResolver: ({ spriteUrl }) => spriteUrl,
85
+ };
86
+ const lessText = (0, sprite_1.buildLessForSprite)({ group: result.group, spriteUrl: resolvedSpriteUrl, classes }, cssOpts);
87
+ return {
88
+ group: result.group,
89
+ kind: "png",
90
+ spriteUrl: resolvedSpriteUrl,
91
+ previewSpriteUrl: buildQuickPreviewUrl(localProjectId, result.group),
92
+ lessText,
93
+ status: "ok",
94
+ meta: {
95
+ mode: result.mode,
96
+ group: result.group,
97
+ tileWidth: tileW,
98
+ tileHeight: tileH,
99
+ spriteWidth,
100
+ spriteHeight,
101
+ classes,
102
+ },
103
+ };
104
+ }
105
+ function deriveSpriteSize(classes) {
106
+ if (!classes.length)
107
+ return { width: 0, height: 0 };
108
+ let maxW = 0;
109
+ let maxH = 0;
110
+ for (const c of classes) {
111
+ const r = c.x + c.width;
112
+ const b = c.y + c.height;
113
+ if (r > maxW)
114
+ maxW = r;
115
+ if (b > maxH)
116
+ maxH = b;
117
+ }
118
+ return { width: maxW, height: maxH };
119
+ }
120
+ function parseTileSize(group) {
121
+ if (!group)
122
+ return [0, 0];
123
+ const parts = group.split("-");
124
+ if (parts.length === 0)
125
+ return [0, 0];
126
+ if (parts.length === 1) {
127
+ const n = parseInt(parts[0], 10);
128
+ return Number.isFinite(n) ? [n, n] : [0, 0];
129
+ }
130
+ const w = parseInt(parts[0], 10);
131
+ const h = parseInt(parts[1], 10);
132
+ return [Number.isFinite(w) ? w : 0, Number.isFinite(h) ? h : 0];
133
+ }
134
+ function mapQuickGroupsToSnapshot(projectId, results, remoteProj, localCfg) {
135
+ const cssPrefix = localCfg?.prefix ?? "sl";
136
+ const spriteUrlTpl = localCfg?.spriteUrl ?? "";
137
+ const groups = (results || []).map((r) => mapToGroup(r, projectId, cssPrefix, spriteUrlTpl));
138
+ return {
139
+ projectId,
140
+ sourceId: "",
141
+ iconsRoot: remoteProj?.iconsPath ?? "",
142
+ cacheOutDir: remoteProj?.exportSpritesDir ?? "",
143
+ config: {
144
+ projectId,
145
+ enabled: true,
146
+ sourceId: "",
147
+ localDir: "",
148
+ prefix: cssPrefix,
149
+ algorithm: "binary-tree",
150
+ persistLess: false,
151
+ updatedAt: remoteProj?.updatedAt
152
+ ? new Date(remoteProj.updatedAt).getTime()
153
+ : Date.now(),
154
+ template: localCfg?.template ?? '<i class="{base} {class}"></i>',
155
+ spriteUrl: localCfg?.spriteUrl ?? "",
156
+ spriteExportDir: localCfg?.spriteExportDir ?? "",
157
+ lessExportDir: localCfg?.lessExportDir ?? "",
158
+ localImageRoot: localCfg?.localImageRoot ?? "",
159
+ localCacheDir: localCfg?.localCacheDir ?? "",
160
+ },
161
+ total: groups.length,
162
+ success: groups.length,
163
+ failed: 0,
164
+ groups,
165
+ };
166
+ }
167
+ function copyRawResponseHeaders(response, reply) {
168
+ const contentType = response.headers.get("content-type");
169
+ if (contentType)
170
+ reply.header("content-type", contentType);
171
+ const contentLength = response.headers.get("content-length");
172
+ if (contentLength)
173
+ reply.header("content-length", contentLength);
174
+ const cacheControl = response.headers.get("cache-control");
175
+ if (cacheControl)
176
+ reply.header("cache-control", cacheControl);
177
+ const etag = response.headers.get("etag");
178
+ if (etag)
179
+ reply.header("etag", etag);
180
+ const lastModified = response.headers.get("last-modified");
181
+ if (lastModified)
182
+ reply.header("last-modified", lastModified);
183
+ }
184
+ async function hasQuickSprite(fastify, projectId) {
185
+ const cfg = await fastify.core.sprite.getConfig(projectId);
186
+ return !!cfg?.quickSpriteProjectId;
187
+ }
188
+ const miscCache = new Map();
189
+ const MISC_CACHE_TTL = 5 * 60 * 1000;
190
+ async function fetchAndCacheRemoteMiscImages(fastify, baseUrl, quickProjectId, forceRefresh = false) {
191
+ const cached = miscCache.get(quickProjectId);
192
+ if (!forceRefresh && cached && Date.now() - cached.time < MISC_CACHE_TTL) {
193
+ return cached.data;
194
+ }
195
+ const data = await quickFetch(fastify, baseUrl, `/api/misc/list?projectId=${encodeURIComponent(quickProjectId)}`);
196
+ miscCache.set(quickProjectId, { time: Date.now(), data });
197
+ return data;
198
+ }
199
+ function buildBrowseFromCache(quickProjectId, cache, dir) {
200
+ const list = cache.list || [];
201
+ const quickDir = dir ? `${dir}` : ".";
202
+ const filtered = list.filter((item) => item.dir === quickDir);
203
+ const sorted = [...filtered].sort((a, b) => a.name.localeCompare(b.name, "zh-Hans-CN", { numeric: true }));
204
+ const fileEntries = sorted.map((item) => {
205
+ const encodedName = encodeURIComponent(item.name);
206
+ return {
207
+ name: item.name,
208
+ kind: "file",
209
+ ext: item.name.split(".").pop()?.toLowerCase(),
210
+ url: `/api/sprite/misc-proxy/${encodeURIComponent(quickProjectId)}/${encodeURIComponent(item.dir)}/${encodedName}`,
211
+ };
212
+ });
213
+ const dirSet = new Set();
214
+ for (const item of list) {
215
+ if (item.dir)
216
+ dirSet.add(item.dir);
217
+ }
218
+ const subDirSet = new Set();
219
+ for (const d of dirSet) {
220
+ if (d === quickDir)
221
+ continue;
222
+ if (d.startsWith(quickDir) || quickDir === ".") {
223
+ const dirName = quickDir === "."
224
+ ? d.split("/")[0]
225
+ : d.slice(quickDir.length).split("/")[1];
226
+ subDirSet.add(dirName);
227
+ }
228
+ }
229
+ const dirEntries = Array.from(subDirSet)
230
+ .sort((a, b) => a.localeCompare(b, "zh-Hans-CN", { numeric: true }))
231
+ .map((d) => ({
232
+ name: d,
233
+ kind: "dir",
234
+ ext: undefined,
235
+ fileCount: list.filter((item) => item.dir === `${quickDir === "." ? "" : quickDir + "/"}${d}`).length,
236
+ }));
237
+ const entries = [...dirEntries, ...fileEntries];
238
+ return {
239
+ root: `remote:${quickProjectId}`,
240
+ dir,
241
+ entries,
242
+ };
243
+ }
244
+ exports.MISC_PROXY_PREFIX = "/api/sprite/misc-proxy";
245
+ function buildRemoteMiscUrl(baseUrl, quickProjectId, filename) {
246
+ return `${baseUrl}/misc/${encodeURIComponent(quickProjectId)}/misc/${encodeURIComponent(filename)}`;
247
+ }
@@ -0,0 +1,2 @@
1
+ import type { FastifyInstance } from "fastify";
2
+ export declare function spriteRoutes(fastify: FastifyInstance): Promise<void>;
@@ -0,0 +1,212 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.spriteRoutes = spriteRoutes;
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ const node_stream_1 = require("node:stream");
9
+ const errors_1 = require("@yinuo-ngm/errors");
10
+ const sprite_quick_utils_1 = require("./sprite-quick.utils");
11
+ function toSpriteConfigDto(cfg) {
12
+ return {
13
+ projectId: cfg.projectId,
14
+ enabled: cfg.enabled,
15
+ sourceId: cfg.sourceId,
16
+ localDir: cfg.localDir,
17
+ prefix: cfg.prefix,
18
+ template: cfg.template,
19
+ spriteUrl: cfg.spriteUrl,
20
+ spriteExportDir: cfg.spriteExportDir,
21
+ lessExportDir: cfg.lessExportDir,
22
+ algorithm: cfg.algorithm,
23
+ persistLess: cfg.persistLess,
24
+ updatedAt: cfg.updatedAt,
25
+ localImageRoot: cfg.localImageRoot,
26
+ localCacheDir: cfg.localCacheDir,
27
+ quickSpriteProjectId: cfg.quickSpriteProjectId,
28
+ quickSpriteEnabled: cfg.quickSpriteEnabled,
29
+ quickSpriteBaseUrl: cfg.quickSpriteBaseUrl,
30
+ };
31
+ }
32
+ function toSpriteGroupItemDto(item) {
33
+ return {
34
+ group: item.group,
35
+ kind: item.kind,
36
+ previewSpriteUrl: item.previewSpriteUrl,
37
+ spriteUrl: item.spriteUrl,
38
+ meta: item.meta,
39
+ lessText: item.lessText,
40
+ exported: item.exported,
41
+ status: item.status,
42
+ error: item.error,
43
+ };
44
+ }
45
+ function toSpriteSnapshotDto(snapshot) {
46
+ return {
47
+ projectId: snapshot.projectId,
48
+ sourceId: snapshot.sourceId,
49
+ iconsRoot: snapshot.iconsRoot,
50
+ cacheOutDir: snapshot.cacheOutDir,
51
+ config: toSpriteConfigDto(snapshot.config),
52
+ total: snapshot.total,
53
+ success: snapshot.success,
54
+ failed: snapshot.failed,
55
+ groups: (snapshot.groups ?? []).map(toSpriteGroupItemDto),
56
+ };
57
+ }
58
+ function toGenerateSpriteResultDto(snapshot) {
59
+ return toSpriteSnapshotDto(snapshot);
60
+ }
61
+ async function spriteRoutes(fastify) {
62
+ fastify.get("/config/:projectId", async (req) => {
63
+ const { projectId } = req.params;
64
+ const cfg = await fastify.core.sprite.getConfig(projectId);
65
+ if (cfg && !cfg?.quickSpriteBaseUrl) {
66
+ const base = (0, sprite_quick_utils_1.getBaseUrl)(cfg);
67
+ cfg.quickSpriteBaseUrl = base;
68
+ }
69
+ return cfg ? toSpriteConfigDto(cfg) : null;
70
+ });
71
+ fastify.post("/config/:projectId", async (req) => {
72
+ const { projectId } = req.params;
73
+ const body = req.body;
74
+ if (!body || !body.config) {
75
+ throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.BAD_REQUEST, "Missing config in request body");
76
+ }
77
+ const nextCfg = body.config;
78
+ const nextAssets = (body.assets || {});
79
+ const hasLocalImageRoot = !!String(nextCfg.localImageRoot ?? "").trim();
80
+ const enableQuickSprite = nextCfg.quickSpriteProjectId && nextCfg.quickSpriteEnabled;
81
+ if (!enableQuickSprite && !nextAssets.iconsSvn && !hasLocalImageRoot) {
82
+ throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.BAD_REQUEST, "iconsSvn asset or localImageRoot is required");
83
+ }
84
+ if (nextCfg.localDir && nextAssets.iconsSvn) {
85
+ nextAssets.iconsSvn.localDir = node_path_1.default.join(nextCfg.localDir, nextAssets.iconsSvn.label || "icons");
86
+ if (nextAssets.cutImageSvn) {
87
+ nextAssets.cutImageSvn.localDir = node_path_1.default.join(nextCfg.localDir, nextAssets.cutImageSvn.label || "images");
88
+ }
89
+ }
90
+ const shouldUpdateAssets = !!(nextAssets.iconsSvn || nextAssets.cutImageSvn);
91
+ const project = shouldUpdateAssets
92
+ ? await fastify.core.project.updateAssets(projectId, nextAssets)
93
+ : await fastify.core.project.get(projectId);
94
+ if (project.assets?.iconsSvn) {
95
+ nextCfg.sourceId = project.assets.iconsSvn.id;
96
+ }
97
+ const cfg = await fastify.core.sprite.createConfig(projectId, nextCfg);
98
+ const assets = project.assets
99
+ ? {
100
+ iconsSvn: project.assets.iconsSvn,
101
+ cutImageSvn: project.assets.cutImageSvn,
102
+ }
103
+ : undefined;
104
+ return {
105
+ cfg: toSpriteConfigDto(cfg),
106
+ project: { id: project.id, name: project.name, root: project.root, assets },
107
+ };
108
+ });
109
+ fastify.post("/generate/:projectId", async (req) => {
110
+ const { projectId } = req.params;
111
+ const body = req.body || {};
112
+ const quickProjectId = await (0, sprite_quick_utils_1.resolveEnabledRemoteProjectId)(fastify, projectId);
113
+ if (quickProjectId) {
114
+ const localCfg = await fastify.core.sprite.getConfig(projectId);
115
+ const baseUrl = (0, sprite_quick_utils_1.getBaseUrl)(localCfg);
116
+ const [results, remoteProj] = await Promise.all([
117
+ (0, sprite_quick_utils_1.quickFetch)(fastify, baseUrl, `/api/project-sprites?projectId=${encodeURIComponent(quickProjectId)}`),
118
+ (0, sprite_quick_utils_1.fetchRemoteProject)(fastify, baseUrl, quickProjectId),
119
+ ]);
120
+ if (!results?.length) {
121
+ throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.NOT_FOUND, `远端项目「${quickProjectId}」尚未生成任何雪碧图,请先在远端服务中为该项目生成雪碧图后再试`);
122
+ }
123
+ return (0, sprite_quick_utils_1.mapQuickGroupsToSnapshot)(projectId, results, remoteProj, localCfg);
124
+ }
125
+ const result = await fastify.core.sprite.generate(projectId, {
126
+ groups: body.groups,
127
+ forceRefresh: !!body.forceRefresh,
128
+ concurrency: body.concurrency ?? 1,
129
+ continueOnError: body.continueOnError ?? true,
130
+ });
131
+ return toGenerateSpriteResultDto(result);
132
+ });
133
+ fastify.get("/list/:projectId", async (req) => {
134
+ const { projectId } = req.params;
135
+ const local = String(req.query?.local ?? "").toLowerCase() === "true";
136
+ const quickProjectId = await (0, sprite_quick_utils_1.resolveEnabledRemoteProjectId)(fastify, projectId);
137
+ if (quickProjectId) {
138
+ const localCfg = await fastify.core.sprite.getConfig(projectId);
139
+ const baseUrl = (0, sprite_quick_utils_1.getBaseUrl)(localCfg);
140
+ const [results, remoteProj] = await Promise.all([
141
+ (0, sprite_quick_utils_1.quickFetch)(fastify, baseUrl, `/api/project-sprites?projectId=${encodeURIComponent(quickProjectId)}`),
142
+ (0, sprite_quick_utils_1.fetchRemoteProject)(fastify, baseUrl, quickProjectId),
143
+ ]);
144
+ return (0, sprite_quick_utils_1.mapQuickGroupsToSnapshot)(projectId, results, remoteProj, localCfg);
145
+ }
146
+ const snapshot = await fastify.core.sprite.getSprites(projectId, local);
147
+ return toSpriteSnapshotDto(snapshot);
148
+ });
149
+ fastify.get("/quick/projects", async (_req) => {
150
+ const baseUrl = (0, sprite_quick_utils_1.getBaseUrl)();
151
+ const projects = await (0, sprite_quick_utils_1.quickFetch)(fastify, baseUrl, "/api/projects");
152
+ return projects;
153
+ });
154
+ fastify.get("/quick/groups/:projectId", async (req) => {
155
+ const { projectId } = req.params;
156
+ const baseUrl = (0, sprite_quick_utils_1.getBaseUrl)();
157
+ const groups = await (0, sprite_quick_utils_1.quickFetch)(fastify, baseUrl, `/api/groups?projectId=${encodeURIComponent(projectId)}`);
158
+ return groups;
159
+ });
160
+ fastify.get("/proxy/:projectId/:group.png", async (req, reply) => {
161
+ const { projectId, group } = req.params;
162
+ const cfg = await fastify.core.sprite.getConfig(projectId);
163
+ const quickProjectId = cfg?.quickSpriteProjectId;
164
+ if (!quickProjectId) {
165
+ throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.NOT_FOUND, `项目「${projectId}」未配置快捷雪碧图远端映射`);
166
+ }
167
+ const baseUrl = (0, sprite_quick_utils_1.getBaseUrl)(cfg);
168
+ const remoteUrl = `${baseUrl}/sprites/${encodeURIComponent(quickProjectId)}/${encodeURIComponent(group)}.png`;
169
+ fastify.log.info(`[sprite-proxy] → GET ${remoteUrl}`);
170
+ let response;
171
+ try {
172
+ response = await fetch(remoteUrl);
173
+ }
174
+ catch (err) {
175
+ fastify.log.error(`[sprite-proxy] ✗ ${remoteUrl}: ${err?.message || err}`);
176
+ throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.INTERNAL_ERROR, `无法连接远端雪碧图服务: ${err?.message || err}`);
177
+ }
178
+ if (!response.ok) {
179
+ throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.NOT_FOUND, `远端雪碧图不存在: ${response.status}`);
180
+ }
181
+ (0, sprite_quick_utils_1.copyRawResponseHeaders)(response, reply);
182
+ const body = response.body;
183
+ if (!body) {
184
+ return reply.status(response.status).send();
185
+ }
186
+ return reply.status(response.status).send(node_stream_1.Readable.fromWeb(body));
187
+ });
188
+ fastify.get("/misc-proxy/:quickProjectId/*", async (req, reply) => {
189
+ const { quickProjectId } = req.params;
190
+ const filename = req.params["*"];
191
+ const baseUrl = (0, sprite_quick_utils_1.getBaseUrl)();
192
+ const remoteUrl = (0, sprite_quick_utils_1.buildRemoteMiscUrl)(baseUrl, quickProjectId, filename);
193
+ fastify.log.info(`[sprite-misc-proxy] → GET ${remoteUrl}`);
194
+ let response;
195
+ try {
196
+ response = await fetch(remoteUrl);
197
+ }
198
+ catch (err) {
199
+ fastify.log.error(`[sprite-misc-proxy] ✗ ${remoteUrl}: ${err?.message || err}`);
200
+ throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.INTERNAL_ERROR, `无法连接远端切图服务: ${err?.message || err}`);
201
+ }
202
+ if (!response.ok) {
203
+ throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.NOT_FOUND, `远端切图不存在: ${response.status}`);
204
+ }
205
+ (0, sprite_quick_utils_1.copyRawResponseHeaders)(response, reply);
206
+ const body = response.body;
207
+ if (!body) {
208
+ return reply.status(response.status).send();
209
+ }
210
+ return reply.status(response.status).send(node_stream_1.Readable.fromWeb(body));
211
+ });
212
+ }
@@ -31,7 +31,7 @@ function listDir(root) {
31
31
  .filter(d => !SKIP.has(d.name) && !d.name.startsWith("."))
32
32
  .map(d => ({
33
33
  name: d.name,
34
- kind: d.isDirectory() ? "dir" : "file",
34
+ kind: (d.isDirectory() ? "dir" : "file"),
35
35
  ext: d.isDirectory() ? undefined : node_path_1.default.extname(d.name).toLowerCase(),
36
36
  })).sort((a, b) => {
37
37
  if (a.kind === b.kind) {
@@ -45,6 +45,12 @@ function countFiles(dir) {
45
45
  return 0;
46
46
  return node_fs_1.default.readdirSync(dir, { withFileTypes: true }).filter(d => d.isFile()).length;
47
47
  }
48
+ function toBrowseEntriesDto(root, entries) {
49
+ return { root, entries };
50
+ }
51
+ function toBrowseFilesDto(root, dir, entries) {
52
+ return { root, dir, entries };
53
+ }
48
54
  async function spriteBrowseRoutes(fastify) {
49
55
  fastify.get("/icons/groups/:projectId", async (req) => {
50
56
  const { projectId } = req.params;
@@ -53,7 +59,7 @@ async function spriteBrowseRoutes(fastify) {
53
59
  const localRoot = String(cfg?.localImageRoot ?? "").trim();
54
60
  const root = localRoot || project?.assets?.iconsSvn?.localDir;
55
61
  if (!root) {
56
- return { root: "", entries: [] };
62
+ return toBrowseEntriesDto("", []);
57
63
  }
58
64
  const entries = listDir(root).filter(e => e.kind === "dir");
59
65
  if (localRoot) {
@@ -62,7 +68,7 @@ async function spriteBrowseRoutes(fastify) {
62
68
  entries.unshift({ name: "root", kind: "dir", ext: undefined });
63
69
  }
64
70
  }
65
- return { root, entries };
71
+ return toBrowseEntriesDto(root, entries);
66
72
  });
67
73
  fastify.get("/icons/files/:projectId", async (req) => {
68
74
  const { projectId } = req.params;
@@ -72,10 +78,10 @@ async function spriteBrowseRoutes(fastify) {
72
78
  const localRoot = String(cfg?.localImageRoot ?? "").trim();
73
79
  const root = localRoot || project?.assets?.iconsSvn?.localDir;
74
80
  if (!root) {
75
- return { root: "", entries: [] };
81
+ return toBrowseEntriesDto("", []);
76
82
  }
77
83
  const isRootGroup = !!localRoot && (group === "root");
78
- const groupDir = isRootGroup ? root : safeJoin(root, group || '');
84
+ const groupDir = isRootGroup ? root : safeJoin(root, group || "");
79
85
  const entries = listDir(groupDir)
80
86
  .filter(e => e.kind === "file")
81
87
  .map(e => ({
@@ -84,22 +90,22 @@ async function spriteBrowseRoutes(fastify) {
84
90
  ? `/api/static/local/${projectId}/${isRootGroup ? e.name : `${group}/${e.name}`}`
85
91
  : `/api/static/svn/${projectId}/icons/${group}/${e.name}`,
86
92
  }));
87
- return { root: groupDir, entries };
93
+ return toBrowseEntriesDto(groupDir, entries);
88
94
  });
89
95
  fastify.get("/images/list/:projectId", async (req) => {
90
96
  const { projectId } = req.params;
91
97
  const project = await fastify.core.project.get(projectId);
92
98
  const root = project?.assets?.cutImageSvn?.localDir;
93
99
  if (!root) {
94
- return { root: "", entries: [] };
100
+ return toBrowseEntriesDto("", []);
95
101
  }
96
102
  const dir = String(req.query?.dir ?? "").trim();
97
103
  if (dir.split(/[\\/]/g).some(seg => seg === "..")) {
98
- return { root, dir: "", entries: [] };
104
+ return toBrowseFilesDto(root, "", []);
99
105
  }
100
106
  const absDir = dir ? safeJoin(root, dir) : root;
101
107
  if (!node_fs_1.default.existsSync(absDir))
102
- return { root, dir, entries: [] };
108
+ return toBrowseFilesDto(root, dir, []);
103
109
  const entries = listDir(absDir).map(e => {
104
110
  if (e.kind === "file") {
105
111
  const rel = dir ? `${dir}/${e.name}` : e.name;
@@ -112,11 +118,11 @@ async function spriteBrowseRoutes(fastify) {
112
118
  const rel = dir ? `${dir}/${e.name}` : e.name;
113
119
  return {
114
120
  ...e,
115
- fileCount: countFiles(safeJoin(root, rel))
121
+ fileCount: countFiles(safeJoin(root, rel)),
116
122
  };
117
123
  }
118
124
  return e;
119
125
  });
120
- return { root, dir, entries };
126
+ return toBrowseFilesDto(root, dir, entries);
121
127
  });
122
128
  }
@@ -1,2 +1,2 @@
1
- import { FastifyInstance } from "fastify";
1
+ import type { FastifyInstance } from "fastify";
2
2
  export declare function spriteRoutes(fastify: FastifyInstance): Promise<void>;
@@ -6,27 +6,75 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.spriteRoutes = spriteRoutes;
7
7
  const node_path_1 = __importDefault(require("node:path"));
8
8
  const errors_1 = require("@yinuo-ngm/errors");
9
+ function toSpriteConfigDto(cfg) {
10
+ return {
11
+ projectId: cfg.projectId,
12
+ enabled: cfg.enabled,
13
+ sourceId: cfg.sourceId,
14
+ localDir: cfg.localDir,
15
+ prefix: cfg.prefix,
16
+ template: cfg.template,
17
+ spriteUrl: cfg.spriteUrl,
18
+ spriteExportDir: cfg.spriteExportDir,
19
+ lessExportDir: cfg.lessExportDir,
20
+ algorithm: cfg.algorithm,
21
+ persistLess: cfg.persistLess,
22
+ updatedAt: cfg.updatedAt,
23
+ localImageRoot: cfg.localImageRoot,
24
+ localCacheDir: cfg.localCacheDir,
25
+ };
26
+ }
27
+ function toSpriteGroupItemDto(item) {
28
+ return {
29
+ group: item.group,
30
+ kind: item.kind,
31
+ previewSpriteUrl: item.previewSpriteUrl,
32
+ spriteUrl: item.spriteUrl,
33
+ meta: item.meta,
34
+ lessText: item.lessText,
35
+ exported: item.exported,
36
+ status: item.status,
37
+ error: item.error,
38
+ };
39
+ }
40
+ function toSpriteSnapshotDto(snapshot) {
41
+ return {
42
+ projectId: snapshot.projectId,
43
+ sourceId: snapshot.sourceId,
44
+ iconsRoot: snapshot.iconsRoot,
45
+ cacheOutDir: snapshot.cacheOutDir,
46
+ config: toSpriteConfigDto(snapshot.config),
47
+ total: snapshot.total,
48
+ success: snapshot.success,
49
+ failed: snapshot.failed,
50
+ groups: (snapshot.groups ?? []).map(toSpriteGroupItemDto),
51
+ };
52
+ }
53
+ function toGenerateSpriteResultDto(snapshot) {
54
+ return toSpriteSnapshotDto(snapshot);
55
+ }
9
56
  async function spriteRoutes(fastify) {
10
57
  fastify.get("/config/:projectId", async (req) => {
11
58
  const { projectId } = req.params;
12
- return await fastify.core.sprite.getConfig(projectId);
59
+ const cfg = await fastify.core.sprite.getConfig(projectId);
60
+ return cfg ? toSpriteConfigDto(cfg) : null;
13
61
  });
14
62
  fastify.post("/config/:projectId", async (req) => {
15
63
  const { projectId } = req.params;
16
64
  const body = req.body;
17
65
  if (!body || !body.config) {
18
- throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.BAD_REQUEST, 'Missing config in request body');
66
+ throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.BAD_REQUEST, "Missing config in request body");
19
67
  }
20
68
  const nextCfg = body.config;
21
- const nextAssets = body.assets || {};
69
+ const nextAssets = (body.assets || {});
22
70
  const hasLocalImageRoot = !!String(nextCfg.localImageRoot ?? "").trim();
23
71
  if (!nextAssets.iconsSvn && !hasLocalImageRoot) {
24
- throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.BAD_REQUEST, 'iconsSvn asset or localImageRoot is required');
72
+ throw new errors_1.GlobalError(errors_1.GlobalErrorCodes.BAD_REQUEST, "iconsSvn asset or localImageRoot is required");
25
73
  }
26
74
  if (nextCfg.localDir && nextAssets.iconsSvn) {
27
- nextAssets.iconsSvn.localDir = node_path_1.default.join(nextCfg.localDir, nextAssets.iconsSvn.label || 'icons');
75
+ nextAssets.iconsSvn.localDir = node_path_1.default.join(nextCfg.localDir, nextAssets.iconsSvn.label || "icons");
28
76
  if (nextAssets.cutImageSvn) {
29
- nextAssets.cutImageSvn.localDir = node_path_1.default.join(nextCfg.localDir, nextAssets.cutImageSvn.label || 'images');
77
+ nextAssets.cutImageSvn.localDir = node_path_1.default.join(nextCfg.localDir, nextAssets.cutImageSvn.label || "images");
30
78
  }
31
79
  }
32
80
  const shouldUpdateAssets = !!(nextAssets.iconsSvn || nextAssets.cutImageSvn);
@@ -37,7 +85,16 @@ async function spriteRoutes(fastify) {
37
85
  nextCfg.sourceId = project.assets.iconsSvn.id;
38
86
  }
39
87
  const cfg = await fastify.core.sprite.createConfig(projectId, nextCfg);
40
- return { cfg, project };
88
+ const assets = project.assets
89
+ ? {
90
+ iconsSvn: project.assets.iconsSvn,
91
+ cutImageSvn: project.assets.cutImageSvn,
92
+ }
93
+ : undefined;
94
+ return {
95
+ cfg: toSpriteConfigDto(cfg),
96
+ project: { id: project.id, name: project.name, root: project.root, assets },
97
+ };
41
98
  });
42
99
  fastify.post("/generate/:projectId", async (req) => {
43
100
  const { projectId } = req.params;
@@ -48,12 +105,12 @@ async function spriteRoutes(fastify) {
48
105
  concurrency: body.concurrency ?? 1,
49
106
  continueOnError: body.continueOnError ?? true,
50
107
  });
51
- return result;
108
+ return toGenerateSpriteResultDto(result);
52
109
  });
53
110
  fastify.get("/list/:projectId", async (req) => {
54
111
  const { projectId } = req.params;
55
- const query = req.query;
56
- const local = String(query['local'] ?? '').toLowerCase() === 'true';
57
- return await fastify.core.sprite.getSprites(projectId, local);
112
+ const local = String(req.query?.local ?? "").toLowerCase() === "true";
113
+ const snapshot = await fastify.core.sprite.getSprites(projectId, local);
114
+ return toSpriteSnapshotDto(snapshot);
58
115
  });
59
116
  }