@yinuo-ngm/server 1.0.11 → 1.0.13
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 -1
- package/lib/plugins/error-handler.plugin.js +14 -0
- package/lib/plugins/static.plugin.js +4 -12
- package/lib/plugins/ws/topics/index.d.ts +2 -0
- package/lib/plugins/ws/topics/index.js +2 -0
- package/lib/plugins/ws/topics/svn.ws.d.ts +9 -0
- package/lib/plugins/ws/topics/svn.ws.js +46 -0
- package/lib/plugins/ws/ws.plugin.js +27 -11
- package/lib/routes/index.js +6 -0
- package/lib/routes/project.routes.js +16 -2
- package/lib/routes/sprite-browse.routes.d.ts +2 -0
- package/lib/routes/sprite-browse.routes.js +107 -0
- package/lib/routes/sprite.routes.js +40 -61
- package/lib/routes/static-files.routes.d.ts +2 -0
- package/lib/routes/static-files.routes.js +83 -0
- package/lib/routes/svn.routes.d.ts +2 -0
- package/lib/routes/svn.routes.js +53 -0
- package/package.json +6 -5
- package/www/3rdpartylicenses.txt +26 -0
- package/www/browser/{chunk-DJJC6FPO.js → chunk-25RT7L2O.js} +1 -1
- package/www/browser/{chunk-EP45FT55.js → chunk-2P5WKSWB.js} +2 -2
- package/www/browser/{chunk-FNLA2MNI.js → chunk-2XZHN52E.js} +1 -1
- package/www/browser/chunk-3MBM5AMR.js +1 -0
- package/www/browser/{chunk-AZV6AWGC.js → chunk-3V5PIKHE.js} +1 -1
- package/www/browser/{chunk-EC33AXSR.js → chunk-42JVTVYH.js} +1 -1
- package/www/browser/chunk-4IQIZX7Y.js +1 -0
- package/www/browser/{chunk-3MAJTMXX.js → chunk-4UAHOL42.js} +1 -1
- package/www/browser/{chunk-YVSVIACA.js → chunk-56SDBJDG.js} +1 -1
- package/www/browser/{chunk-IMQG3SAE.js → chunk-5DD65JMJ.js} +1 -1
- package/www/browser/{chunk-PTMJ422K.js → chunk-5EQOKH7G.js} +6 -6
- package/www/browser/chunk-6JH45SUC.js +1 -0
- package/www/browser/{chunk-G73D24UX.js → chunk-6X6PEDE5.js} +1 -1
- package/www/browser/{chunk-CO7S3LZV.js → chunk-AI6SNMEH.js} +1 -1
- package/www/browser/chunk-AMXCOT2F.js +1 -0
- package/www/browser/{chunk-J2JNFWJN.js → chunk-DP3VTELD.js} +1 -1
- package/www/browser/chunk-E6LF4XBJ.js +3 -0
- package/www/browser/chunk-EMK65RIU.js +1 -0
- package/www/browser/chunk-EYGWZTTT.js +1 -0
- package/www/browser/{chunk-CJISJEP3.js → chunk-GKY4LJF4.js} +1 -1
- package/www/browser/{chunk-V27A2EK4.js → chunk-HJC5MNP5.js} +1 -1
- package/www/browser/chunk-ITXWAN4J.js +1 -0
- package/www/browser/{chunk-GCKL62DR.js → chunk-IZNTCWTR.js} +1 -1
- package/www/browser/{chunk-CPA2B3DD.js → chunk-KN3WZJH7.js} +1 -1
- package/www/browser/chunk-KPO75P5E.js +1 -0
- package/www/browser/chunk-LWBZC6ZL.js +2 -0
- package/www/browser/{chunk-HCBKQAGF.js → chunk-NYXWJKSW.js} +1 -1
- package/www/browser/{chunk-ROB7EMN5.js → chunk-OC62FHHS.js} +1 -1
- package/www/browser/{chunk-POZS3PD3.js → chunk-QM5ROT66.js} +1 -1
- package/www/browser/{chunk-35COW2FA.js → chunk-W633D3VG.js} +1 -1
- package/www/browser/{chunk-53YRDYLS.js → chunk-X3HFJACM.js} +2 -2
- package/www/browser/chunk-YFAKNLWB.js +2 -0
- package/www/browser/{chunk-3PWCJ3D5.js → chunk-ZSPNEYSK.js} +1 -1
- package/www/browser/images/placeholder.png +0 -0
- package/www/browser/index.html +1 -1
- package/www/browser/main-O5GAJRDW.js +34 -0
- package/www/browser/chunk-37KQ3UTB.js +0 -1
- package/www/browser/chunk-3LDMO67C.js +0 -1
- package/www/browser/chunk-MD6RDGU4.js +0 -1
- package/www/browser/chunk-OUE23ZE4.js +0 -2
- package/www/browser/chunk-W6TCGG53.js +0 -1
- package/www/browser/chunk-XN5C33GH.js +0 -3
- package/www/browser/main-V3VSEZ7K.js +0 -29
package/lib/app.js
CHANGED
|
@@ -13,6 +13,7 @@ const static_plugin_1 = __importDefault(require("./plugins/static.plugin"));
|
|
|
13
13
|
const success_handle_plugin_1 = __importDefault(require("./plugins/success-handle.plugin"));
|
|
14
14
|
const ws_plugin_1 = __importDefault(require("./plugins/ws/ws.plugin"));
|
|
15
15
|
const api_client_plugin_1 = __importDefault(require("./plugins/api-client.plugin"));
|
|
16
|
+
const env_1 = require("./env");
|
|
16
17
|
function normalizeLogLevel(v) {
|
|
17
18
|
const lv = (v ?? "").toLowerCase().trim();
|
|
18
19
|
if (!lv)
|
|
@@ -22,7 +23,8 @@ function normalizeLogLevel(v) {
|
|
|
22
23
|
return "info";
|
|
23
24
|
}
|
|
24
25
|
function createFastifyLogger() {
|
|
25
|
-
const level = normalizeLogLevel(
|
|
26
|
+
const level = normalizeLogLevel(env_1.env.logLevel);
|
|
27
|
+
console.log(`Logger initialized with level: ${level || "silent (disabled)"}`);
|
|
26
28
|
if (!level)
|
|
27
29
|
return false;
|
|
28
30
|
const isDev = process.env.NODE_ENV !== "production";
|
|
@@ -11,11 +11,18 @@ exports.ERROR_STATUS = {
|
|
|
11
11
|
PROJECT_NOT_FOUND: 404,
|
|
12
12
|
PROJECT_ROOT_INVALID: 400,
|
|
13
13
|
PROJECT_ALREADY_EXISTS: 409,
|
|
14
|
+
PROJECT_ID_REQUIRED: 400,
|
|
14
15
|
PROJECT_IMPORT_NOT_EXISTS: 404,
|
|
15
16
|
PROJECT_IMPORT_NOT_DIR: 400,
|
|
16
17
|
PROJECT_IMPORT_ALREADY_REGISTERED: 409,
|
|
17
18
|
PROJECT_IMPORT_NOT_RECOGNIZED: 422,
|
|
18
19
|
PROJECT_IMPORT_SCAN_FAILED: 500,
|
|
20
|
+
ASSET_NOT_FOUND: 404,
|
|
21
|
+
ASSET_KIND_NOT_SUPPORTED: 400,
|
|
22
|
+
ASSET_URL_REQUIRED: 400,
|
|
23
|
+
ASSET_LABEL_REQUIRED: 400,
|
|
24
|
+
ASSET_URL_INVALID: 400,
|
|
25
|
+
ASSET_MODE_INVALID: 400,
|
|
19
26
|
INVALID_NAME: 400,
|
|
20
27
|
TARGET_EXISTS: 409,
|
|
21
28
|
INVALID_REPO_URL: 400,
|
|
@@ -75,6 +82,13 @@ exports.ERROR_STATUS = {
|
|
|
75
82
|
UNKNOWN_ERROR: 500,
|
|
76
83
|
BAD_REQUEST: 400,
|
|
77
84
|
NOT_IMPLEMENTED: 501,
|
|
85
|
+
NOT_FOUND: 404,
|
|
86
|
+
SVN_SYNC_ALREADY_RUNNING: 409,
|
|
87
|
+
SVN_SYNC_FAILED: 500,
|
|
88
|
+
SVN_SOURCE_ID_REQUIRED: 400,
|
|
89
|
+
SPRITE_CONFIG_NOT_FOUND: 404,
|
|
90
|
+
SPRITE_ICONS_ROOT_NOT_FOUND: 404,
|
|
91
|
+
SPRITE_GROUP_NOT_FOUND: 404,
|
|
78
92
|
};
|
|
79
93
|
function mapStatus(code) {
|
|
80
94
|
return exports.ERROR_STATUS[code] ?? 400;
|
|
@@ -11,16 +11,14 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
11
11
|
exports.default = (0, fastify_plugin_1.default)(async function staticPlugin(fastify) {
|
|
12
12
|
const webRoot = path_1.default.resolve(__dirname, "../../www/browser");
|
|
13
13
|
const dataDir = env_1.env.dataDir;
|
|
14
|
-
const spritesRoot = path_1.default.join(dataDir, "sprites");
|
|
15
|
-
const
|
|
16
|
-
for (const dir of [dataDir, spritesRoot, spriteCssRoot]) {
|
|
14
|
+
const spritesRoot = path_1.default.join(env_1.env.dataDir, "cache", "sprites");
|
|
15
|
+
for (const dir of [dataDir]) {
|
|
17
16
|
if (!fs_1.default.existsSync(dir))
|
|
18
17
|
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
19
18
|
}
|
|
20
19
|
fastify.log.info(`[static] serving webapp from ${webRoot}`);
|
|
21
20
|
fastify.log.info(`[static] dataDir=${dataDir}`);
|
|
22
|
-
fastify.log.info(`[static] serving sprites from ${spritesRoot}
|
|
23
|
-
fastify.log.info(`[static] serving sprite-css from ${spriteCssRoot} at /sprite-css/`);
|
|
21
|
+
fastify.log.info(`[static] serving sprites from ${spritesRoot} `);
|
|
24
22
|
await fastify.register(static_1.default, {
|
|
25
23
|
root: webRoot,
|
|
26
24
|
index: "index.html",
|
|
@@ -31,18 +29,12 @@ exports.default = (0, fastify_plugin_1.default)(async function staticPlugin(fast
|
|
|
31
29
|
prefix: "/sprites/",
|
|
32
30
|
decorateReply: false,
|
|
33
31
|
});
|
|
34
|
-
await fastify.register(static_1.default, {
|
|
35
|
-
root: spriteCssRoot,
|
|
36
|
-
prefix: "/sprite-css/",
|
|
37
|
-
decorateReply: false,
|
|
38
|
-
});
|
|
39
32
|
fastify.setNotFoundHandler((req, reply) => {
|
|
40
33
|
const url = req.url || "";
|
|
41
34
|
const isSpaFallbackCandidate = req.method === "GET" &&
|
|
42
35
|
!url.startsWith("/api") &&
|
|
43
36
|
!url.startsWith("/ws") &&
|
|
44
|
-
!url.startsWith("/sprites/")
|
|
45
|
-
!url.startsWith("/sprite-css/");
|
|
37
|
+
!url.startsWith("/sprites/");
|
|
46
38
|
if (isSpaFallbackCandidate) {
|
|
47
39
|
return reply.sendFile("index.html");
|
|
48
40
|
}
|
|
@@ -15,3 +15,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./task.ws"), exports);
|
|
18
|
+
__exportStar(require("./syslog.ws"), exports);
|
|
19
|
+
__exportStar(require("./svn.ws"), exports);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { SvnEventPayloadMap, SvnEventType, SvnRuntime } from "@yinuo-ngm/core";
|
|
2
|
+
import { WsContext } from "../ws.context";
|
|
3
|
+
import { TopicHandler } from "../ws.router";
|
|
4
|
+
export type SvnSyncWsDeps = {
|
|
5
|
+
getSvnRuntimeByProjectId?: (projectId: string, tail?: number) => Promise<SvnRuntime[]>;
|
|
6
|
+
};
|
|
7
|
+
export declare function createSvnSyncTopicHandler(deps: SvnSyncWsDeps, getAllClients: () => Iterable<WsContext>): TopicHandler & {
|
|
8
|
+
pushEvent<K extends SvnEventType>(type: K, payload: SvnEventPayloadMap[K]): void;
|
|
9
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createSvnSyncTopicHandler = createSvnSyncTopicHandler;
|
|
4
|
+
const keyOf = (projectId) => `svn:${projectId}`;
|
|
5
|
+
function createSvnSyncTopicHandler(deps, getAllClients) {
|
|
6
|
+
return {
|
|
7
|
+
topic: "svn",
|
|
8
|
+
async sub(ctx, msg) {
|
|
9
|
+
const projectId = String(msg?.projectId ?? "").trim();
|
|
10
|
+
if (!projectId) {
|
|
11
|
+
ctx.send({ op: "error", code: "PROJECT_ID_REQUIRED", message: "projectId is required", ts: Date.now() });
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const t = Number(msg?.tail ?? 0) || 0;
|
|
15
|
+
ctx.addSub("svn", keyOf(projectId));
|
|
16
|
+
if (deps.getSvnRuntimeByProjectId) {
|
|
17
|
+
const runtimes = await deps.getSvnRuntimeByProjectId(projectId, t);
|
|
18
|
+
if (runtimes?.length) {
|
|
19
|
+
runtimes.forEach(runtime => {
|
|
20
|
+
const m = {
|
|
21
|
+
op: "svn.event",
|
|
22
|
+
type: "runtime",
|
|
23
|
+
payload: runtime,
|
|
24
|
+
ts: Date.now(),
|
|
25
|
+
};
|
|
26
|
+
ctx.send(m);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
async unsub(ctx, msg) {
|
|
32
|
+
const projectId = String(msg?.projectId ?? "").trim();
|
|
33
|
+
if (!projectId) {
|
|
34
|
+
ctx.send({ op: "error", code: "PROJECT_ID_REQUIRED", message: "projectId is required", ts: Date.now() });
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
ctx.delSub("svn", keyOf(projectId));
|
|
38
|
+
},
|
|
39
|
+
pushEvent(type, payload) {
|
|
40
|
+
const m = { op: "svn.event", type, payload, ts: Date.now() };
|
|
41
|
+
for (const c of getAllClients())
|
|
42
|
+
if (c.hasSub("svn", keyOf(payload.projectId)))
|
|
43
|
+
c.send(m);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -7,7 +7,6 @@ const core_1 = require("@yinuo-ngm/core");
|
|
|
7
7
|
const websocket_1 = __importDefault(require("@fastify/websocket"));
|
|
8
8
|
const fastify_plugin_1 = __importDefault(require("fastify-plugin"));
|
|
9
9
|
const topics_1 = require("./topics");
|
|
10
|
-
const syslog_ws_1 = require("./topics/syslog.ws");
|
|
11
10
|
const ws_context_1 = require("./ws.context");
|
|
12
11
|
const ws_router_1 = require("./ws.router");
|
|
13
12
|
function uid() {
|
|
@@ -23,36 +22,53 @@ exports.default = (0, fastify_plugin_1.default)(async function wsPlugin(fastify)
|
|
|
23
22
|
resizeRun: (taskId, cols, rows) => fastify.core.task.resizeRun(taskId, cols, rows),
|
|
24
23
|
}, () => clients.values());
|
|
25
24
|
router.register(taskHandler);
|
|
26
|
-
const syslogHandler = (0,
|
|
25
|
+
const syslogHandler = (0, topics_1.createSyslogTopicHandler)({ getSyslogTail: (tail) => fastify.core.sysLog.tail(tail) }, () => clients.values());
|
|
27
26
|
router.register(syslogHandler);
|
|
27
|
+
const svnSyncHandler = (0, topics_1.createSvnSyncTopicHandler)({
|
|
28
|
+
getSvnRuntimeByProjectId: (projectId, tail) => fastify.core.svnSync.getRuntimeByProjectId(projectId, tail),
|
|
29
|
+
}, () => clients.values());
|
|
30
|
+
router.register(svnSyncHandler);
|
|
28
31
|
const offs = [];
|
|
29
|
-
|
|
32
|
+
const events = fastify.core.events;
|
|
33
|
+
offs.push(events.on(core_1.Events.TASK_OUTPUT, (e) => {
|
|
30
34
|
taskHandler.pushOutput(e);
|
|
31
35
|
}));
|
|
32
|
-
offs.push(
|
|
36
|
+
offs.push(events.on(core_1.Events.TASK_STARTED, (e) => {
|
|
33
37
|
taskHandler.pushEvent("started", e);
|
|
34
38
|
}));
|
|
35
|
-
offs.push(
|
|
39
|
+
offs.push(events.on(core_1.Events.TASK_STOP_REQUESTED, (e) => {
|
|
36
40
|
taskHandler.pushEvent("stopRequested", e);
|
|
37
41
|
}));
|
|
38
|
-
offs.push(
|
|
42
|
+
offs.push(events.on(core_1.Events.TASK_EXITED, (e) => {
|
|
39
43
|
taskHandler.pushEvent("exited", e);
|
|
40
44
|
}));
|
|
41
|
-
offs.push(
|
|
45
|
+
offs.push(events.on(core_1.Events.TASK_FAILED, (e) => {
|
|
42
46
|
taskHandler.pushEvent("failed", e);
|
|
43
47
|
}));
|
|
44
|
-
offs.push(
|
|
48
|
+
offs.push(events.on(core_1.Events.SYSLOG_APPENDED, (e) => {
|
|
45
49
|
syslogHandler.push(e.entry);
|
|
46
50
|
}));
|
|
47
|
-
offs.push(
|
|
51
|
+
offs.push(events.on(core_1.Events.PROJECT_BOOTSTRAP_DONE, (e) => {
|
|
48
52
|
taskHandler.pushEvent("bootstrapDone", e);
|
|
49
53
|
}));
|
|
50
|
-
offs.push(
|
|
54
|
+
offs.push(events.on(core_1.Events.PROJECT_BOOTSTRAP_FAILED, (e) => {
|
|
51
55
|
taskHandler.pushEvent("bootstrapFailed", e);
|
|
52
56
|
}));
|
|
53
|
-
offs.push(
|
|
57
|
+
offs.push(events.on(core_1.Events.PROJECT_BOOTSTRAP_NEED_PICK_ROOT, (e) => {
|
|
54
58
|
taskHandler.pushEvent("bootstrapNeedPickRoot", e);
|
|
55
59
|
}));
|
|
60
|
+
offs.push(events.on(core_1.Events.SVN_SYNC_STARTED, (e) => {
|
|
61
|
+
svnSyncHandler.pushEvent("started", e);
|
|
62
|
+
}));
|
|
63
|
+
offs.push(events.on(core_1.Events.SVN_SYNC_OUTPUT, (e) => {
|
|
64
|
+
svnSyncHandler.pushEvent("output", e);
|
|
65
|
+
}));
|
|
66
|
+
offs.push(events.on(core_1.Events.SVN_SYNC_PROGRESS, (e) => {
|
|
67
|
+
svnSyncHandler.pushEvent("progress", e);
|
|
68
|
+
}));
|
|
69
|
+
offs.push(events.on(core_1.Events.SVN_SYNC_DONE, (e) => {
|
|
70
|
+
svnSyncHandler.pushEvent("done", e);
|
|
71
|
+
}));
|
|
56
72
|
fastify.addHook("onClose", async () => {
|
|
57
73
|
offs.forEach((off) => {
|
|
58
74
|
try {
|
package/lib/routes/index.js
CHANGED
|
@@ -14,6 +14,9 @@ const sprite_routes_1 = require("./sprite.routes");
|
|
|
14
14
|
const system_routes_1 = __importDefault(require("./system.routes"));
|
|
15
15
|
const task_routes_1 = __importDefault(require("./task.routes"));
|
|
16
16
|
const api_client_1 = require("./api-client");
|
|
17
|
+
const svn_routes_1 = __importDefault(require("./svn.routes"));
|
|
18
|
+
const sprite_browse_routes_1 = __importDefault(require("./sprite-browse.routes"));
|
|
19
|
+
const static_files_routes_1 = __importDefault(require("./static-files.routes"));
|
|
17
20
|
async function routes(fastify) {
|
|
18
21
|
await fastify.register(system_routes_1.default);
|
|
19
22
|
await fastify.register(task_routes_1.default, { prefix: '/api/tasks' });
|
|
@@ -24,6 +27,9 @@ async function routes(fastify) {
|
|
|
24
27
|
await fastify.register(dashboard_routes_1.default, { prefix: '/api/dashboard' });
|
|
25
28
|
await fastify.register(rss_routes_1.default, { prefix: '/api/rss' });
|
|
26
29
|
await fastify.register(sprite_routes_1.spriteRoutes, { prefix: '/api/sprite' });
|
|
30
|
+
await fastify.register(svn_routes_1.default, { prefix: '/api/svn' });
|
|
31
|
+
await fastify.register(sprite_browse_routes_1.default, { prefix: '/api/sprite/browse' });
|
|
32
|
+
await fastify.register(static_files_routes_1.default, { prefix: '/api/static' });
|
|
27
33
|
await fastify.register(api_client_1.apiClientEnvsRoutes, { prefix: '/api/client/envs' });
|
|
28
34
|
await fastify.register(api_client_1.apiClientRequestsRoutes, { prefix: '/api/client/requests' });
|
|
29
35
|
await fastify.register(api_client_1.apiClientHistoryRoutes, { prefix: '/api/client/history' });
|
|
@@ -69,7 +69,7 @@ async function projectRoutes(fastify) {
|
|
|
69
69
|
const project = await fastify.core.project.get(id);
|
|
70
70
|
return project;
|
|
71
71
|
});
|
|
72
|
-
fastify.
|
|
72
|
+
fastify.delete("/delete/:id", async (req) => {
|
|
73
73
|
const { id } = req.params;
|
|
74
74
|
await fastify.core.project.remove(id);
|
|
75
75
|
return { id };
|
|
@@ -158,7 +158,21 @@ async function projectRoutes(fastify) {
|
|
|
158
158
|
if (typeof body?.name !== "string" || body.name.trim() === "") {
|
|
159
159
|
throw new core_1.AppError("INVALID_NAME", "name must be a non-empty string");
|
|
160
160
|
}
|
|
161
|
-
const updated = await fastify.core.project.edit(id, {
|
|
161
|
+
const updated = await fastify.core.project.edit(id, {
|
|
162
|
+
name: body.name.trim(),
|
|
163
|
+
description: body.description?.trim(),
|
|
164
|
+
repoPageUrl: body.repoPageUrl?.trim(),
|
|
165
|
+
});
|
|
166
|
+
return updated;
|
|
167
|
+
});
|
|
168
|
+
fastify.post("/updateAssets/:id", async (req) => {
|
|
169
|
+
const { id } = req.params;
|
|
170
|
+
const body = req.body;
|
|
171
|
+
const assets = body?.assets;
|
|
172
|
+
if (!assets || !assets.iconsSvn) {
|
|
173
|
+
throw new core_1.AppError("BAD_REQUEST", "参数错误,assets.iconsSvn 是必需的");
|
|
174
|
+
}
|
|
175
|
+
const updated = await fastify.core.project.updateAssets(id, assets);
|
|
162
176
|
return updated;
|
|
163
177
|
});
|
|
164
178
|
fastify.post("/openInEditor/:id", async (req) => {
|
|
@@ -0,0 +1,107 @@
|
|
|
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.default = spriteBrowseRoutes;
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const SKIP = new Set([
|
|
10
|
+
".svn",
|
|
11
|
+
".git",
|
|
12
|
+
".hg",
|
|
13
|
+
".DS_Store",
|
|
14
|
+
"__MACOSX",
|
|
15
|
+
"Thumbs.db",
|
|
16
|
+
]);
|
|
17
|
+
function safeJoin(root, sub) {
|
|
18
|
+
const resolved = node_path_1.default.resolve(root, sub);
|
|
19
|
+
const rootResolved = node_path_1.default.resolve(root);
|
|
20
|
+
if (!resolved.startsWith(rootResolved)) {
|
|
21
|
+
throw new Error("INVALID_PATH");
|
|
22
|
+
}
|
|
23
|
+
return resolved;
|
|
24
|
+
}
|
|
25
|
+
function listDir(root) {
|
|
26
|
+
if (!node_fs_1.default.existsSync(root))
|
|
27
|
+
return [];
|
|
28
|
+
return node_fs_1.default
|
|
29
|
+
.readdirSync(root, { withFileTypes: true })
|
|
30
|
+
.filter(d => !SKIP.has(d.name) && !d.name.startsWith("."))
|
|
31
|
+
.map(d => ({
|
|
32
|
+
name: d.name,
|
|
33
|
+
kind: d.isDirectory() ? "dir" : "file",
|
|
34
|
+
ext: d.isDirectory() ? undefined : node_path_1.default.extname(d.name).toLowerCase(),
|
|
35
|
+
})).sort((a, b) => {
|
|
36
|
+
if (a.kind === b.kind) {
|
|
37
|
+
return a.name.localeCompare(b.name, "zh-Hans-CN", { numeric: true });
|
|
38
|
+
}
|
|
39
|
+
return a.kind === "dir" ? -1 : 1;
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
function countFiles(dir) {
|
|
43
|
+
if (!node_fs_1.default.existsSync(dir) || !node_fs_1.default.statSync(dir).isDirectory())
|
|
44
|
+
return 0;
|
|
45
|
+
return node_fs_1.default.readdirSync(dir, { withFileTypes: true }).filter(d => d.isFile()).length;
|
|
46
|
+
}
|
|
47
|
+
async function spriteBrowseRoutes(fastify) {
|
|
48
|
+
fastify.get("/icons/groups/:projectId", async (req) => {
|
|
49
|
+
const { projectId } = req.params;
|
|
50
|
+
const project = await fastify.core.project.get(projectId);
|
|
51
|
+
const root = project?.assets?.iconsSvn?.localDir;
|
|
52
|
+
if (!root) {
|
|
53
|
+
return { root: "", entries: [] };
|
|
54
|
+
}
|
|
55
|
+
return { root, entries: listDir(root).filter(e => e.kind === "dir") };
|
|
56
|
+
});
|
|
57
|
+
fastify.get("/icons/files/:projectId", async (req) => {
|
|
58
|
+
const { projectId } = req.params;
|
|
59
|
+
const group = req.query?.group;
|
|
60
|
+
const project = await fastify.core.project.get(projectId);
|
|
61
|
+
const root = project?.assets?.iconsSvn?.localDir;
|
|
62
|
+
if (!root) {
|
|
63
|
+
return { root: "", entries: [] };
|
|
64
|
+
}
|
|
65
|
+
const groupDir = safeJoin(root, group || '');
|
|
66
|
+
const entries = listDir(groupDir)
|
|
67
|
+
.filter(e => e.kind === "file")
|
|
68
|
+
.map(e => ({
|
|
69
|
+
...e,
|
|
70
|
+
url: `/api/static/svn/${projectId}/icons/${group}/${e.name}`,
|
|
71
|
+
}));
|
|
72
|
+
return { root: groupDir, entries };
|
|
73
|
+
});
|
|
74
|
+
fastify.get("/images/list/:projectId", async (req) => {
|
|
75
|
+
const { projectId } = req.params;
|
|
76
|
+
const project = await fastify.core.project.get(projectId);
|
|
77
|
+
const root = project?.assets?.cutImageSvn?.localDir;
|
|
78
|
+
if (!root) {
|
|
79
|
+
return { root: "", entries: [] };
|
|
80
|
+
}
|
|
81
|
+
const dir = String(req.query?.dir ?? "").trim();
|
|
82
|
+
if (dir.split(/[\\/]/g).some(seg => seg === "..")) {
|
|
83
|
+
return { root, dir: "", entries: [] };
|
|
84
|
+
}
|
|
85
|
+
const absDir = dir ? safeJoin(root, dir) : root;
|
|
86
|
+
if (!node_fs_1.default.existsSync(absDir))
|
|
87
|
+
return { root, dir, entries: [] };
|
|
88
|
+
const entries = listDir(absDir).map(e => {
|
|
89
|
+
if (e.kind === "file") {
|
|
90
|
+
const rel = dir ? `${dir}/${e.name}` : e.name;
|
|
91
|
+
return {
|
|
92
|
+
...e,
|
|
93
|
+
url: `/api/static/svn/${projectId}/images/${rel}`,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
else if (e.kind === "dir") {
|
|
97
|
+
const rel = dir ? `${dir}/${e.name}` : e.name;
|
|
98
|
+
return {
|
|
99
|
+
...e,
|
|
100
|
+
fileCount: countFiles(safeJoin(root, rel))
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
return e;
|
|
104
|
+
});
|
|
105
|
+
return { root, dir, entries };
|
|
106
|
+
});
|
|
107
|
+
}
|
|
@@ -4,71 +4,50 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.spriteRoutes = spriteRoutes;
|
|
7
|
-
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
7
|
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
-
const
|
|
10
|
-
const sprite_1 = require("@yinuo-ngm/sprite");
|
|
11
|
-
function ensureDir(dir) {
|
|
12
|
-
if (!node_fs_1.default.existsSync(dir))
|
|
13
|
-
node_fs_1.default.mkdirSync(dir, { recursive: true });
|
|
14
|
-
}
|
|
8
|
+
const core_1 = require("@yinuo-ngm/core");
|
|
15
9
|
async function spriteRoutes(fastify) {
|
|
16
|
-
fastify.
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const spriteUrl = `/sprites/${encodeURIComponent(projectId)}/${encodeURIComponent(group)}.png`;
|
|
36
|
-
const type = (0, sprite_1.detectGroupType)(groupDir);
|
|
37
|
-
let data;
|
|
38
|
-
if (type === "mixed") {
|
|
39
|
-
throw new Error(`分组 ${group} 同时存在 png 与 svg,请统一格式(建议拆分目录)`);
|
|
40
|
-
}
|
|
41
|
-
if (type === "svg") {
|
|
42
|
-
data = await (0, sprite_1.generateSvgGroup)({
|
|
43
|
-
group,
|
|
44
|
-
groupDir,
|
|
45
|
-
prefix,
|
|
46
|
-
urlResolver: ({ group, file }) => `/icons/${encodeURIComponent(projectId)}/icons/${encodeURIComponent(group)}/${encodeURIComponent(file)}`,
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
else {
|
|
50
|
-
data = await (0, sprite_1.generatePngGroup)({
|
|
51
|
-
group,
|
|
52
|
-
groupDir,
|
|
53
|
-
outDir,
|
|
54
|
-
spriteUrl,
|
|
55
|
-
css: {
|
|
56
|
-
prefix,
|
|
57
|
-
spriteUrlResolver: ({ spriteUrl }) => spriteUrl,
|
|
58
|
-
},
|
|
59
|
-
cache: {
|
|
60
|
-
enabled: true,
|
|
61
|
-
forceRefresh,
|
|
62
|
-
persistLess,
|
|
63
|
-
},
|
|
64
|
-
spritesmith: { algorithm },
|
|
65
|
-
});
|
|
10
|
+
fastify.get("/config/:projectId", async (req) => {
|
|
11
|
+
const { projectId } = req.params;
|
|
12
|
+
return await fastify.core.sprite.getConfig(projectId);
|
|
13
|
+
});
|
|
14
|
+
fastify.post("/config/:projectId", async (req) => {
|
|
15
|
+
const { projectId } = req.params;
|
|
16
|
+
const body = req.body;
|
|
17
|
+
if (!body || !body.config || !body.assets) {
|
|
18
|
+
throw new core_1.AppError('BAD_REQUEST', 'Missing config or assets in request body');
|
|
19
|
+
}
|
|
20
|
+
const nextCfg = body.config;
|
|
21
|
+
const nextAssets = body.assets;
|
|
22
|
+
if (!nextAssets.iconsSvn) {
|
|
23
|
+
throw new core_1.AppError('BAD_REQUEST', 'iconsSvn asset is required');
|
|
24
|
+
}
|
|
25
|
+
if (nextCfg.localDir) {
|
|
26
|
+
nextAssets.iconsSvn.localDir = node_path_1.default.join(nextCfg.localDir, nextAssets.iconsSvn.label || 'icons');
|
|
27
|
+
if (nextAssets.cutImageSvn) {
|
|
28
|
+
nextAssets.cutImageSvn.localDir = node_path_1.default.join(nextCfg.localDir, nextAssets.cutImageSvn.label || 'images');
|
|
66
29
|
}
|
|
67
|
-
return data;
|
|
68
30
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
31
|
+
const p = await fastify.core.project.updateAssets(projectId, nextAssets);
|
|
32
|
+
if (p.assets?.iconsSvn) {
|
|
33
|
+
nextCfg.sourceId = p.assets.iconsSvn.id;
|
|
72
34
|
}
|
|
35
|
+
const cfg = await fastify.core.sprite.createConfig(projectId, nextCfg);
|
|
36
|
+
return { cfg, project: p };
|
|
37
|
+
});
|
|
38
|
+
fastify.post("/generate/:projectId", async (req) => {
|
|
39
|
+
const { projectId } = req.params;
|
|
40
|
+
const body = req.body || {};
|
|
41
|
+
const result = await fastify.core.sprite.generate(projectId, {
|
|
42
|
+
groups: body.groups,
|
|
43
|
+
forceRefresh: !!body.forceRefresh,
|
|
44
|
+
concurrency: body.concurrency ?? 1,
|
|
45
|
+
continueOnError: body.continueOnError ?? true,
|
|
46
|
+
});
|
|
47
|
+
return result;
|
|
48
|
+
});
|
|
49
|
+
fastify.get("/list/:projectId", async (req) => {
|
|
50
|
+
const { projectId } = req.params;
|
|
51
|
+
return await fastify.core.sprite.getSprites(projectId);
|
|
73
52
|
});
|
|
74
53
|
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.default = staticFileRoutes;
|
|
40
|
+
const core_1 = require("@yinuo-ngm/core");
|
|
41
|
+
const mime_types_1 = __importDefault(require("mime-types"));
|
|
42
|
+
const node_fs_1 = __importStar(require("node:fs"));
|
|
43
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
44
|
+
function safeJoin(root, sub) {
|
|
45
|
+
const resolved = node_path_1.default.resolve(root, sub);
|
|
46
|
+
const rootResolved = node_path_1.default.resolve(root);
|
|
47
|
+
if (!resolved.startsWith(rootResolved)) {
|
|
48
|
+
throw new Error("INVALID_PATH");
|
|
49
|
+
}
|
|
50
|
+
return resolved;
|
|
51
|
+
}
|
|
52
|
+
async function staticFileRoutes(fastify) {
|
|
53
|
+
fastify.get("/cache/sprites/:projectId/*", async (req, reply) => {
|
|
54
|
+
const { projectId } = req.params;
|
|
55
|
+
const file = req.params["*"];
|
|
56
|
+
const cacheDir = fastify.core.sprite.ensureCacheDir(projectId);
|
|
57
|
+
const filePath = safeJoin(cacheDir, file);
|
|
58
|
+
if (!node_fs_1.default.existsSync(filePath)) {
|
|
59
|
+
throw new core_1.AppError("NOT_FOUND", "Requested file not found in cache");
|
|
60
|
+
}
|
|
61
|
+
const ct = mime_types_1.default.lookup(filePath) || "application/octet-stream";
|
|
62
|
+
reply.header("Content-Type", ct);
|
|
63
|
+
return reply.send((0, node_fs_1.createReadStream)(filePath));
|
|
64
|
+
});
|
|
65
|
+
fastify.get("/svn/:projectId/:kind/*", async (req, reply) => {
|
|
66
|
+
const { projectId, kind } = req.params;
|
|
67
|
+
const file = req.params["*"];
|
|
68
|
+
const project = await fastify.core.project.get(projectId);
|
|
69
|
+
const root = kind === "icons"
|
|
70
|
+
? project?.assets?.iconsSvn?.localDir
|
|
71
|
+
: project?.assets?.cutImageSvn?.localDir;
|
|
72
|
+
if (!root) {
|
|
73
|
+
throw new core_1.AppError("ASSET_NOT_FOUND", "Project asset source not found");
|
|
74
|
+
}
|
|
75
|
+
const filePath = safeJoin(root, file);
|
|
76
|
+
if (!node_fs_1.default.existsSync(filePath)) {
|
|
77
|
+
throw new core_1.AppError("NOT_FOUND", "Requested file not found in SVN");
|
|
78
|
+
}
|
|
79
|
+
const ct = mime_types_1.default.lookup(filePath) || "application/octet-stream";
|
|
80
|
+
reply.header("Content-Type", ct);
|
|
81
|
+
return reply.send((0, node_fs_1.createReadStream)(filePath));
|
|
82
|
+
});
|
|
83
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
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.default = svnRoutes;
|
|
7
|
+
const core_1 = require("@yinuo-ngm/core");
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const env_1 = require("../env");
|
|
10
|
+
async function svnRoutes(fastify) {
|
|
11
|
+
fastify.post("/sync/:projectId", async (req) => {
|
|
12
|
+
const { projectId } = req.params;
|
|
13
|
+
const p = await fastify.core.project.get(projectId);
|
|
14
|
+
const assets = p.assets;
|
|
15
|
+
if (!assets) {
|
|
16
|
+
throw new core_1.AppError('ASSET_NOT_FOUND', 'Project assets are required for SVN sync');
|
|
17
|
+
}
|
|
18
|
+
const { iconsSvn, cutImageSvn } = assets;
|
|
19
|
+
const svnSources = [iconsSvn, cutImageSvn].filter(s => s && s.kind === "svn");
|
|
20
|
+
const results = [];
|
|
21
|
+
for (const s of svnSources) {
|
|
22
|
+
let localDir = s.localDir;
|
|
23
|
+
if (!localDir) {
|
|
24
|
+
localDir = node_path_1.default.join(env_1.env.dataDir, 'svn', projectId, s.label || s.id);
|
|
25
|
+
}
|
|
26
|
+
const r = await fastify.core.svnSync.sync(projectId, s.id, localDir, s.url);
|
|
27
|
+
results.push(r);
|
|
28
|
+
}
|
|
29
|
+
return results;
|
|
30
|
+
});
|
|
31
|
+
fastify.get("/runtime/:projectId", async (req) => {
|
|
32
|
+
const { projectId } = req.params;
|
|
33
|
+
const runtimes = await fastify.core.svnSync.getRuntimeByProjectId(projectId);
|
|
34
|
+
return runtimes;
|
|
35
|
+
});
|
|
36
|
+
fastify.post("/sync/stream/:projectId", async (req) => {
|
|
37
|
+
const { projectId } = req.params;
|
|
38
|
+
const assets = (await fastify.core.project.get(projectId)).assets;
|
|
39
|
+
if (!assets) {
|
|
40
|
+
throw new core_1.AppError('ASSET_NOT_FOUND', 'Project assets are required for SVN sync');
|
|
41
|
+
}
|
|
42
|
+
const { iconsSvn, cutImageSvn } = assets;
|
|
43
|
+
const svnSources = [iconsSvn, cutImageSvn].filter(s => s && s.kind === "svn");
|
|
44
|
+
for (const s of svnSources) {
|
|
45
|
+
let localDir = s.localDir;
|
|
46
|
+
if (!localDir) {
|
|
47
|
+
localDir = node_path_1.default.join(env_1.env.dataDir, 'svn', projectId, s.label || s.id);
|
|
48
|
+
}
|
|
49
|
+
await fastify.core.svnSync.syncWithStream(projectId, s.id, localDir, s.url);
|
|
50
|
+
}
|
|
51
|
+
return { message: "SVN sync started" };
|
|
52
|
+
});
|
|
53
|
+
}
|