@yinuo-ngm/server 1.0.0
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.d.ts +2 -0
- package/lib/app.js +65 -0
- package/lib/common/api.d.ts +2 -0
- package/lib/common/api.js +13 -0
- package/lib/common/editor.d.ts +5 -0
- package/lib/common/editor.js +69 -0
- package/lib/env.d.ts +7 -0
- package/lib/env.js +16 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +20 -0
- package/lib/plugins/core.plugin.d.ts +9 -0
- package/lib/plugins/core.plugin.js +16 -0
- package/lib/plugins/error-handler.plugin.d.ts +6 -0
- package/lib/plugins/error-handler.plugin.js +113 -0
- package/lib/plugins/request-id.plugin.d.ts +3 -0
- package/lib/plugins/request-id.plugin.js +14 -0
- package/lib/plugins/routes.plugin.d.ts +3 -0
- package/lib/plugins/routes.plugin.js +17 -0
- package/lib/plugins/static.plugin.d.ts +2 -0
- package/lib/plugins/static.plugin.js +24 -0
- package/lib/plugins/success-handle.plugin.d.ts +3 -0
- package/lib/plugins/success-handle.plugin.js +30 -0
- package/lib/plugins/ws/topics/index.d.ts +1 -0
- package/lib/plugins/ws/topics/index.js +17 -0
- package/lib/plugins/ws/topics/syslog.ws.d.ts +9 -0
- package/lib/plugins/ws/topics/syslog.ws.js +28 -0
- package/lib/plugins/ws/topics/task.ws.d.ts +12 -0
- package/lib/plugins/ws/topics/task.ws.js +75 -0
- package/lib/plugins/ws/ws.context.d.ts +12 -0
- package/lib/plugins/ws/ws.context.js +35 -0
- package/lib/plugins/ws/ws.plugin.d.ts +3 -0
- package/lib/plugins/ws/ws.plugin.js +76 -0
- package/lib/plugins/ws/ws.router.d.ts +20 -0
- package/lib/plugins/ws/ws.router.js +67 -0
- package/lib/routes/config.routes.d.ts +2 -0
- package/lib/routes/config.routes.js +65 -0
- package/lib/routes/dashboard.routes.d.ts +2 -0
- package/lib/routes/dashboard.routes.js +33 -0
- package/lib/routes/deps.route.d.ts +2 -0
- package/lib/routes/deps.route.js +25 -0
- package/lib/routes/fs.routes.d.ts +2 -0
- package/lib/routes/fs.routes.js +29 -0
- package/lib/routes/index.d.ts +9 -0
- package/lib/routes/index.js +22 -0
- package/lib/routes/project.routes.d.ts +2 -0
- package/lib/routes/project.routes.js +196 -0
- package/lib/routes/rss.routes.d.ts +2 -0
- package/lib/routes/rss.routes.js +53 -0
- package/lib/routes/system.routes.d.ts +2 -0
- package/lib/routes/system.routes.js +28 -0
- package/lib/routes/task.routes.d.ts +2 -0
- package/lib/routes/task.routes.js +48 -0
- package/package.json +29 -0
- package/src/app.ts +76 -0
- package/src/common/api.ts +12 -0
- package/src/common/editor.ts +49 -0
- package/src/env.ts +12 -0
- package/src/index.ts +21 -0
- package/src/plugins/core.plugin.ts +34 -0
- package/src/plugins/error-handler.plugin.ts +168 -0
- package/src/plugins/request-id.plugin.ts +14 -0
- package/src/plugins/routes.plugin.ts +24 -0
- package/src/plugins/static.plugin.ts +30 -0
- package/src/plugins/success-handle.plugin.ts +33 -0
- package/src/plugins/ws/topics/index.ts +1 -0
- package/src/plugins/ws/topics/syslog.ws.ts +36 -0
- package/src/plugins/ws/topics/task.ws.ts +96 -0
- package/src/plugins/ws/ws.context.ts +32 -0
- package/src/plugins/ws/ws.plugin.ts +103 -0
- package/src/plugins/ws/ws.router.ts +79 -0
- package/src/routes/config.routes.ts +86 -0
- package/src/routes/dashboard.routes.ts +43 -0
- package/src/routes/deps.route.ts +51 -0
- package/src/routes/fs.routes.ts +31 -0
- package/src/routes/index.ts +19 -0
- package/src/routes/project.routes.ts +265 -0
- package/src/routes/rss.routes.ts +58 -0
- package/src/routes/system.routes.ts +32 -0
- package/src/routes/task.routes.ts +85 -0
- package/tsconfig.json +13 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WsContext = void 0;
|
|
4
|
+
class WsContext {
|
|
5
|
+
constructor(connId, socket) {
|
|
6
|
+
this.socket = socket;
|
|
7
|
+
this.subs = new Map();
|
|
8
|
+
this.connId = connId;
|
|
9
|
+
}
|
|
10
|
+
send(msg) {
|
|
11
|
+
if (this.socket.readyState !== this.socket.OPEN)
|
|
12
|
+
return;
|
|
13
|
+
this.socket.send(JSON.stringify(msg));
|
|
14
|
+
}
|
|
15
|
+
addSub(topic, key) {
|
|
16
|
+
const set = this.subs.get(topic) ?? new Set();
|
|
17
|
+
set.add(key);
|
|
18
|
+
this.subs.set(topic, set);
|
|
19
|
+
}
|
|
20
|
+
delSub(topic, key) {
|
|
21
|
+
const set = this.subs.get(topic);
|
|
22
|
+
if (!set)
|
|
23
|
+
return;
|
|
24
|
+
set.delete(key);
|
|
25
|
+
if (set.size === 0)
|
|
26
|
+
this.subs.delete(topic);
|
|
27
|
+
}
|
|
28
|
+
hasSub(topic, key) {
|
|
29
|
+
return this.subs.get(topic)?.has(key) ?? false;
|
|
30
|
+
}
|
|
31
|
+
clearAll() {
|
|
32
|
+
this.subs.clear();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
exports.WsContext = WsContext;
|
|
@@ -0,0 +1,76 @@
|
|
|
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
|
+
const core_1 = require("@yinuo-ngm/core");
|
|
7
|
+
const websocket_1 = __importDefault(require("@fastify/websocket"));
|
|
8
|
+
const fastify_plugin_1 = __importDefault(require("fastify-plugin"));
|
|
9
|
+
const topics_1 = require("./topics");
|
|
10
|
+
const syslog_ws_1 = require("./topics/syslog.ws");
|
|
11
|
+
const ws_context_1 = require("./ws.context");
|
|
12
|
+
const ws_router_1 = require("./ws.router");
|
|
13
|
+
function uid() {
|
|
14
|
+
return Math.random().toString(16).slice(2) + Date.now().toString(16);
|
|
15
|
+
}
|
|
16
|
+
exports.default = (0, fastify_plugin_1.default)(async function wsPlugin(fastify) {
|
|
17
|
+
await fastify.register(websocket_1.default);
|
|
18
|
+
const clients = new Map();
|
|
19
|
+
const router = new ws_router_1.WsRouter();
|
|
20
|
+
const taskHandler = (0, topics_1.createTaskTopicHandler)({
|
|
21
|
+
getTaskSnapshotByTaskId: (taskId) => fastify.core.task.getSnapshotByTaskId(taskId),
|
|
22
|
+
getTaskTailLogsByRun: (runId, tail) => fastify.core.task.getTailLogsByRun(runId, tail),
|
|
23
|
+
resizeRun: (taskId, cols, rows) => fastify.core.task.resizeRun(taskId, cols, rows),
|
|
24
|
+
}, () => clients.values());
|
|
25
|
+
router.register(taskHandler);
|
|
26
|
+
const syslogHandler = (0, syslog_ws_1.createSyslogTopicHandler)({ getSyslogTail: (tail) => fastify.core.sysLog.tail(tail) }, () => clients.values());
|
|
27
|
+
router.register(syslogHandler);
|
|
28
|
+
const offs = [];
|
|
29
|
+
offs.push(fastify.core.events.on(core_1.Events.TASK_OUTPUT, (e) => {
|
|
30
|
+
taskHandler.pushOutput(e);
|
|
31
|
+
}));
|
|
32
|
+
offs.push(fastify.core.events.on(core_1.Events.TASK_STARTED, (e) => {
|
|
33
|
+
taskHandler.pushEvent("started", e);
|
|
34
|
+
}));
|
|
35
|
+
offs.push(fastify.core.events.on(core_1.Events.TASK_STOP_REQUESTED, (e) => {
|
|
36
|
+
taskHandler.pushEvent("stopRequested", e);
|
|
37
|
+
}));
|
|
38
|
+
offs.push(fastify.core.events.on(core_1.Events.TASK_EXITED, (e) => {
|
|
39
|
+
taskHandler.pushEvent("exited", e);
|
|
40
|
+
}));
|
|
41
|
+
offs.push(fastify.core.events.on(core_1.Events.TASK_FAILED, (e) => {
|
|
42
|
+
taskHandler.pushEvent("failed", e);
|
|
43
|
+
}));
|
|
44
|
+
offs.push(fastify.core.events.on(core_1.Events.SYSLOG_APPENDED, (e) => {
|
|
45
|
+
syslogHandler.push(e.entry);
|
|
46
|
+
}));
|
|
47
|
+
offs.push(fastify.core.events.on(core_1.Events.PROJECT_BOOTSTRAP_DONE, (e) => {
|
|
48
|
+
taskHandler.pushEvent("bootstrapDone", e);
|
|
49
|
+
}));
|
|
50
|
+
offs.push(fastify.core.events.on(core_1.Events.PROJECT_BOOTSTRAP_FAILED, (e) => {
|
|
51
|
+
taskHandler.pushEvent("bootstrapFailed", e);
|
|
52
|
+
}));
|
|
53
|
+
offs.push(fastify.core.events.on(core_1.Events.PROJECT_BOOTSTRAP_NEED_PICK_ROOT, (e) => {
|
|
54
|
+
taskHandler.pushEvent("bootstrapNeedPickRoot", e);
|
|
55
|
+
}));
|
|
56
|
+
fastify.addHook("onClose", async () => {
|
|
57
|
+
offs.forEach((off) => {
|
|
58
|
+
try {
|
|
59
|
+
off();
|
|
60
|
+
}
|
|
61
|
+
catch { }
|
|
62
|
+
});
|
|
63
|
+
clients.clear();
|
|
64
|
+
});
|
|
65
|
+
fastify.get("/ws", { websocket: true }, (ws) => {
|
|
66
|
+
const connId = uid();
|
|
67
|
+
const ctx = new ws_context_1.WsContext(connId, ws);
|
|
68
|
+
clients.set(connId, ctx);
|
|
69
|
+
ctx.send({ op: "hello", connId, ts: Date.now() });
|
|
70
|
+
ws.on("message", (data) => router.handleRaw(ctx, data));
|
|
71
|
+
ws.on("close", () => {
|
|
72
|
+
ctx.clearAll();
|
|
73
|
+
clients.delete(connId);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { WsClientMsg, WsTopic } from "@yinuo-ngm/core";
|
|
2
|
+
import { WsContext } from "./ws.context";
|
|
3
|
+
export type TopicHandler = {
|
|
4
|
+
topic: WsTopic;
|
|
5
|
+
sub(ctx: WsContext, msg: Extract<WsClientMsg, {
|
|
6
|
+
op: "sub";
|
|
7
|
+
}>): Promise<void> | void;
|
|
8
|
+
unsub(ctx: WsContext, msg: Extract<WsClientMsg, {
|
|
9
|
+
op: "unsub";
|
|
10
|
+
}>): Promise<void> | void;
|
|
11
|
+
resize?(ctx: WsContext, msg: Extract<WsClientMsg, {
|
|
12
|
+
op: "resize";
|
|
13
|
+
}>): Promise<void> | void;
|
|
14
|
+
};
|
|
15
|
+
export declare class WsRouter {
|
|
16
|
+
private handlers;
|
|
17
|
+
register(handler: TopicHandler): void;
|
|
18
|
+
handleRaw(ctx: WsContext, raw: any): undefined;
|
|
19
|
+
private err;
|
|
20
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WsRouter = void 0;
|
|
4
|
+
class WsRouter {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.handlers = new Map();
|
|
7
|
+
}
|
|
8
|
+
register(handler) {
|
|
9
|
+
this.handlers.set(handler.topic, handler);
|
|
10
|
+
}
|
|
11
|
+
handleRaw(ctx, raw) {
|
|
12
|
+
let msg = null;
|
|
13
|
+
try {
|
|
14
|
+
const text = typeof raw === "string" ? raw : raw?.toString?.() ?? "";
|
|
15
|
+
msg = JSON.parse(text);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
ctx.send(this.err("BAD_JSON", "Invalid JSON"));
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (!msg || typeof msg !== "object" || !("op" in msg)) {
|
|
22
|
+
ctx.send(this.err("BAD_MSG", "Invalid message"));
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (msg.op === "ping") {
|
|
26
|
+
ctx.send({ op: "pong", ts: Date.now() });
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (msg.op === "resize") {
|
|
30
|
+
const topic = msg.topic;
|
|
31
|
+
const h = this.handlers.get(topic);
|
|
32
|
+
if (!h || !h.resize) {
|
|
33
|
+
ctx.send(this.err("OP_NOT_SUPPORTED", `resize not supported on topic: ${String(topic)}`, { topic }));
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
return void h.resize(ctx, msg);
|
|
38
|
+
}
|
|
39
|
+
catch (e) {
|
|
40
|
+
ctx.send(this.err("HANDLER_FAILED", e?.message ?? "handler failed", { topic, op: msg.op }));
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (msg.op === "sub" || msg.op === "unsub") {
|
|
45
|
+
const topic = msg.topic;
|
|
46
|
+
const h = this.handlers.get(topic);
|
|
47
|
+
if (!h) {
|
|
48
|
+
ctx.send(this.err("TOPIC_NOT_FOUND", `Unknown topic: ${String(topic)}`, { topic }));
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
if (msg.op === "sub")
|
|
53
|
+
return void h.sub(ctx, msg);
|
|
54
|
+
return void h.unsub(ctx, msg);
|
|
55
|
+
}
|
|
56
|
+
catch (e) {
|
|
57
|
+
ctx.send(this.err("HANDLER_FAILED", e?.message ?? "handler failed", { topic, op: msg.op }));
|
|
58
|
+
}
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
ctx.send(this.err("OP_NOT_FOUND", `Unknown op: ${msg.op}`));
|
|
62
|
+
}
|
|
63
|
+
err(code, message, details) {
|
|
64
|
+
return { op: "error", code, message, details, ts: Date.now() };
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
exports.WsRouter = WsRouter;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = configRoutes;
|
|
4
|
+
const core_1 = require("@yinuo-ngm/core");
|
|
5
|
+
const editor_1 = require("../common/editor");
|
|
6
|
+
async function configRoutes(fastify) {
|
|
7
|
+
fastify.get("/catalog/:projectId", async (req) => {
|
|
8
|
+
const { projectId } = req.params;
|
|
9
|
+
return await fastify.core.config.getCatalog(projectId);
|
|
10
|
+
});
|
|
11
|
+
fastify.get("/readDoc/:projectId/:docId", async (req) => {
|
|
12
|
+
const { projectId, docId } = req.params;
|
|
13
|
+
return await fastify.core.config.readDoc(projectId, docId);
|
|
14
|
+
});
|
|
15
|
+
fastify.post("/writeDoc/:projectId/:docId", async (req) => {
|
|
16
|
+
const { projectId, docId } = req.params;
|
|
17
|
+
const body = req.body;
|
|
18
|
+
const next = body?.raw ?? body?.data;
|
|
19
|
+
if (next === undefined) {
|
|
20
|
+
throw new core_1.AppError("CONFIG_WRITE_FAILED", "missing body.raw or body.data", { projectId, docId });
|
|
21
|
+
}
|
|
22
|
+
return await fastify.core.config.writeDoc(projectId, docId, next);
|
|
23
|
+
});
|
|
24
|
+
fastify.post("/openInEditor/:projectId/:docId", async (req) => {
|
|
25
|
+
try {
|
|
26
|
+
const { projectId, docId } = req.params;
|
|
27
|
+
const { filePath } = await fastify.core.config.openDoc(projectId, docId);
|
|
28
|
+
await (0, editor_1.openFolder)(filePath, { editor: 'code' });
|
|
29
|
+
return { ok: true };
|
|
30
|
+
}
|
|
31
|
+
catch (e) {
|
|
32
|
+
throw new core_1.AppError("EDITOR_LAUNCH_FAILED", e?.message || "openInEditor failed");
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
fastify.get("/readSchema/:projectId/:domainId", async (req) => {
|
|
36
|
+
const { projectId, domainId } = req.params;
|
|
37
|
+
return await fastify.core.config.readDomainSchema(projectId, domainId);
|
|
38
|
+
});
|
|
39
|
+
fastify.post("/writeSchema/:projectId/:domainId", async (req) => {
|
|
40
|
+
const { projectId, domainId } = req.params;
|
|
41
|
+
const body = req.body;
|
|
42
|
+
const vm = body?.vm;
|
|
43
|
+
if (vm === undefined) {
|
|
44
|
+
throw new core_1.AppError("BAD_REQUEST", "missing body.vm");
|
|
45
|
+
}
|
|
46
|
+
await fastify.core.config.writeDomainSchema(projectId, domainId, vm);
|
|
47
|
+
return {
|
|
48
|
+
projectId,
|
|
49
|
+
domainId,
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
fastify.get("/getDomainSchema/:projectId/:domainId", async (req) => {
|
|
53
|
+
const { projectId, domainId } = req.params;
|
|
54
|
+
return await fastify.core.config.getDomainSchemaDoc(projectId, domainId);
|
|
55
|
+
});
|
|
56
|
+
fastify.post("/diffSchema/:projectId/:domainId", async (req) => {
|
|
57
|
+
const { projectId, domainId } = req.params;
|
|
58
|
+
const body = req.body;
|
|
59
|
+
const vm = body?.vm;
|
|
60
|
+
if (vm === undefined) {
|
|
61
|
+
throw new core_1.AppError("BAD_REQUEST", "missing body.vm");
|
|
62
|
+
}
|
|
63
|
+
return await fastify.core.config.diffDomainSchema(projectId, domainId, vm);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = dashboardRoutes;
|
|
4
|
+
async function dashboardRoutes(fastify) {
|
|
5
|
+
fastify.get("/getInfo/:projectId", async (req) => {
|
|
6
|
+
const { projectId } = req.params;
|
|
7
|
+
return await fastify.core.dashboard.getOrCreate(projectId);
|
|
8
|
+
});
|
|
9
|
+
fastify.post("/update/:projectId", async (req) => {
|
|
10
|
+
const { projectId } = req.params;
|
|
11
|
+
const body = req.body;
|
|
12
|
+
return await fastify.core.dashboard.saveWithConflictCheck(projectId, body);
|
|
13
|
+
});
|
|
14
|
+
fastify.get("/widgets/:projectId", async (req) => {
|
|
15
|
+
const { projectId } = req.params;
|
|
16
|
+
return await fastify.core.dashboard.getAvailableWidgets(projectId);
|
|
17
|
+
});
|
|
18
|
+
fastify.get("/addWidget/:projectId/:widgetKey/:x/:y", async (req) => {
|
|
19
|
+
let { projectId, widgetKey, x, y } = req.params;
|
|
20
|
+
x = Number(x) || 0;
|
|
21
|
+
y = Number(y) || 0;
|
|
22
|
+
return await fastify.core.dashboard.addWidget(projectId, widgetKey, x, y);
|
|
23
|
+
});
|
|
24
|
+
fastify.get("/removeWidget/:projectId/:widgetId", async (req) => {
|
|
25
|
+
const { projectId, widgetId } = req.params;
|
|
26
|
+
return await fastify.core.dashboard.removeWidget(projectId, widgetId);
|
|
27
|
+
});
|
|
28
|
+
fastify.post("/updateItemConfig/:projectId/:widgetId", async (req) => {
|
|
29
|
+
const { projectId, widgetId } = req.params;
|
|
30
|
+
const config = req.body;
|
|
31
|
+
return await fastify.core.dashboard.updateItemConfig(projectId, widgetId, config);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = depsRoutes;
|
|
4
|
+
async function depsRoutes(fastify) {
|
|
5
|
+
fastify.get("/list/:projectId", async (req) => {
|
|
6
|
+
const { projectId } = req.params;
|
|
7
|
+
const data = await fastify.core.deps.list(projectId);
|
|
8
|
+
return data;
|
|
9
|
+
});
|
|
10
|
+
fastify.post("/install/:projectId", async (req) => {
|
|
11
|
+
const { projectId } = req.params;
|
|
12
|
+
const body = req.body;
|
|
13
|
+
await fastify.core.deps.install(projectId, body);
|
|
14
|
+
return { ok: true };
|
|
15
|
+
});
|
|
16
|
+
fastify.post("/uninstall/:projectId", async (req) => {
|
|
17
|
+
const { projectId } = req.params;
|
|
18
|
+
const body = req.body;
|
|
19
|
+
await fastify.core.deps.uninstall(projectId, body);
|
|
20
|
+
return { ok: true };
|
|
21
|
+
});
|
|
22
|
+
fastify.post("/devtools/install/:projectId", async (req) => {
|
|
23
|
+
return { ok: true };
|
|
24
|
+
});
|
|
25
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
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 = fsRoutes;
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
+
async function fsRoutes(fastify) {
|
|
9
|
+
fastify.get("/ls", async (req) => {
|
|
10
|
+
const q = req.query;
|
|
11
|
+
const showSystem = q.showSystem === "1" || q.showSystem === true || q.showSystem === "true";
|
|
12
|
+
return fastify.core.fs.ls(q.path || "", { showSystem, detectProject: true, detectConcurrency: 8 });
|
|
13
|
+
});
|
|
14
|
+
fastify.post("/mkdir", async (req) => {
|
|
15
|
+
const body = req.body;
|
|
16
|
+
return fastify.core.fs.mkdir(body?.path || "", body?.name || "", {
|
|
17
|
+
recursive: true,
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
fastify.get("/path-exists", async (req) => {
|
|
21
|
+
const q = req.query;
|
|
22
|
+
const raw = String(q?.path ?? "").trim();
|
|
23
|
+
if (!raw)
|
|
24
|
+
return { exists: false };
|
|
25
|
+
const p = node_path_1.default.resolve(raw);
|
|
26
|
+
const exists = await fastify.core.fs.exists(p);
|
|
27
|
+
return { exists };
|
|
28
|
+
});
|
|
29
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import configRoutes from './config.routes';
|
|
2
|
+
import dashboardRoutes from './dashboard.routes';
|
|
3
|
+
import depsRoutes from './deps.route';
|
|
4
|
+
import fsRoutes from './fs.routes';
|
|
5
|
+
import projectRoutes from './project.routes';
|
|
6
|
+
import rssRoutes from './rss.routes';
|
|
7
|
+
import systemRoutes from './system.routes';
|
|
8
|
+
import taskRoutes from './task.routes';
|
|
9
|
+
export { configRoutes, depsRoutes, fsRoutes, projectRoutes, systemRoutes, taskRoutes, dashboardRoutes, rssRoutes, };
|
|
@@ -0,0 +1,22 @@
|
|
|
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.rssRoutes = exports.dashboardRoutes = exports.taskRoutes = exports.systemRoutes = exports.projectRoutes = exports.fsRoutes = exports.depsRoutes = exports.configRoutes = void 0;
|
|
7
|
+
const config_routes_1 = __importDefault(require("./config.routes"));
|
|
8
|
+
exports.configRoutes = config_routes_1.default;
|
|
9
|
+
const dashboard_routes_1 = __importDefault(require("./dashboard.routes"));
|
|
10
|
+
exports.dashboardRoutes = dashboard_routes_1.default;
|
|
11
|
+
const deps_route_1 = __importDefault(require("./deps.route"));
|
|
12
|
+
exports.depsRoutes = deps_route_1.default;
|
|
13
|
+
const fs_routes_1 = __importDefault(require("./fs.routes"));
|
|
14
|
+
exports.fsRoutes = fs_routes_1.default;
|
|
15
|
+
const project_routes_1 = __importDefault(require("./project.routes"));
|
|
16
|
+
exports.projectRoutes = project_routes_1.default;
|
|
17
|
+
const rss_routes_1 = __importDefault(require("./rss.routes"));
|
|
18
|
+
exports.rssRoutes = rss_routes_1.default;
|
|
19
|
+
const system_routes_1 = __importDefault(require("./system.routes"));
|
|
20
|
+
exports.systemRoutes = system_routes_1.default;
|
|
21
|
+
const task_routes_1 = __importDefault(require("./task.routes"));
|
|
22
|
+
exports.taskRoutes = task_routes_1.default;
|
|
@@ -0,0 +1,196 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.default = projectRoutes;
|
|
37
|
+
const core_1 = require("@yinuo-ngm/core");
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const editor_1 = require("../common/editor");
|
|
40
|
+
async function projectRoutes(fastify) {
|
|
41
|
+
fastify.get("/list", async () => {
|
|
42
|
+
const projects = await fastify.core.project.list();
|
|
43
|
+
return projects;
|
|
44
|
+
});
|
|
45
|
+
fastify.post("/update/:id", async (req, reply) => {
|
|
46
|
+
const { id } = req.params;
|
|
47
|
+
const body = req.body;
|
|
48
|
+
try {
|
|
49
|
+
if (body.root) {
|
|
50
|
+
reply.code(400);
|
|
51
|
+
return { message: "root is immutable" };
|
|
52
|
+
}
|
|
53
|
+
const updated = await fastify.core.project.update(id, {
|
|
54
|
+
...(body.name !== undefined ? { name: body.name } : {}),
|
|
55
|
+
...(body.env !== undefined ? { env: body.env } : {}),
|
|
56
|
+
...(body.scripts !== undefined ? { scripts: body.scripts } : {}),
|
|
57
|
+
});
|
|
58
|
+
return updated;
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
if (err instanceof Error) {
|
|
62
|
+
reply.code(400);
|
|
63
|
+
return { message: err.message };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
fastify.get("/getInfo/:id", async (req) => {
|
|
68
|
+
const { id } = req.params;
|
|
69
|
+
const project = await fastify.core.project.get(id);
|
|
70
|
+
return project;
|
|
71
|
+
});
|
|
72
|
+
fastify.get("/delete/:id", async (req) => {
|
|
73
|
+
const { id } = req.params;
|
|
74
|
+
await fastify.core.project.remove(id);
|
|
75
|
+
return { id };
|
|
76
|
+
});
|
|
77
|
+
fastify.post("/check", async (req) => {
|
|
78
|
+
const body = req.body;
|
|
79
|
+
return fastify.core.project.checkRoot(body.rootPath);
|
|
80
|
+
});
|
|
81
|
+
fastify.post("/detect", async (req) => {
|
|
82
|
+
const body = req.body;
|
|
83
|
+
const root = path.resolve(body.rootPath);
|
|
84
|
+
const meta = await fastify.core.project.scan(root);
|
|
85
|
+
return {
|
|
86
|
+
framework: meta.framework ?? "unknown",
|
|
87
|
+
hasPackageJson: !!meta.scripts && Object.keys(meta.scripts).length > 0,
|
|
88
|
+
scripts: meta.scripts ?? {},
|
|
89
|
+
recommendedScript: meta.scripts?.dev
|
|
90
|
+
? "dev"
|
|
91
|
+
: meta.scripts?.start
|
|
92
|
+
? "start"
|
|
93
|
+
: Object.keys(meta.scripts ?? {})[0],
|
|
94
|
+
hasGit: meta.hasGit ?? false,
|
|
95
|
+
};
|
|
96
|
+
});
|
|
97
|
+
fastify.post("/import", async (req) => {
|
|
98
|
+
const body = req.body;
|
|
99
|
+
const project = await fastify.core.project.importProject({
|
|
100
|
+
root: body.root,
|
|
101
|
+
name: body.name,
|
|
102
|
+
});
|
|
103
|
+
if (body.syncTasks === true) {
|
|
104
|
+
await fastify.core.task.refreshByProject(project.id);
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
id: project.id,
|
|
108
|
+
};
|
|
109
|
+
});
|
|
110
|
+
fastify.post("/checkImport", async (req) => {
|
|
111
|
+
const body = req.body;
|
|
112
|
+
return fastify.core.project.checkImport(body.root);
|
|
113
|
+
});
|
|
114
|
+
fastify.post("/create", async (req) => {
|
|
115
|
+
const body = req.body;
|
|
116
|
+
const project = await fastify.core.project.create({
|
|
117
|
+
name: body.name,
|
|
118
|
+
root: body.root,
|
|
119
|
+
});
|
|
120
|
+
return { id: project.id };
|
|
121
|
+
});
|
|
122
|
+
fastify.post("/favorite/:id", async (req, reply) => {
|
|
123
|
+
const { id } = req.params;
|
|
124
|
+
const body = req.body;
|
|
125
|
+
if (typeof body?.isFavorite !== "boolean") {
|
|
126
|
+
reply.code(400);
|
|
127
|
+
return { message: "isFavorite must be boolean" };
|
|
128
|
+
}
|
|
129
|
+
const updated = await fastify.core.project.setFavorite(id, body.isFavorite);
|
|
130
|
+
return updated;
|
|
131
|
+
});
|
|
132
|
+
fastify.post("/favorite/:id/toggle", async (req) => {
|
|
133
|
+
const { id } = req.params;
|
|
134
|
+
const updated = await fastify.core.project.toggleFavorite(id);
|
|
135
|
+
return updated;
|
|
136
|
+
});
|
|
137
|
+
fastify.post("/lastOpened/:id", async (req) => {
|
|
138
|
+
const { id } = req.params;
|
|
139
|
+
const body = req.body;
|
|
140
|
+
if (typeof body?.timestamp !== "number") {
|
|
141
|
+
throw new core_1.AppError("INVALID_TIMESTAMP", "timestamp must be a number");
|
|
142
|
+
}
|
|
143
|
+
const updated = await fastify.core.project.setLastOpened(id, body.timestamp);
|
|
144
|
+
return updated;
|
|
145
|
+
});
|
|
146
|
+
fastify.post("/rename/:id", async (req) => {
|
|
147
|
+
const { id } = req.params;
|
|
148
|
+
const body = req.body;
|
|
149
|
+
if (typeof body?.name !== "string" || body.name.trim() === "") {
|
|
150
|
+
throw new core_1.AppError("INVALID_NAME", "name must be a non-empty string");
|
|
151
|
+
}
|
|
152
|
+
const updated = await fastify.core.project.rename(id, body.name.trim());
|
|
153
|
+
return updated;
|
|
154
|
+
});
|
|
155
|
+
fastify.post("/edit/:id", async (req) => {
|
|
156
|
+
const { id } = req.params;
|
|
157
|
+
const body = req.body;
|
|
158
|
+
if (typeof body?.name !== "string" || body.name.trim() === "") {
|
|
159
|
+
throw new core_1.AppError("INVALID_NAME", "name must be a non-empty string");
|
|
160
|
+
}
|
|
161
|
+
const updated = await fastify.core.project.edit(id, { name: body.name.trim(), description: body.description?.trim(), repoPageUrl: body.repoPageUrl?.trim() });
|
|
162
|
+
return updated;
|
|
163
|
+
});
|
|
164
|
+
fastify.post("/openInEditor/:id", async (req) => {
|
|
165
|
+
try {
|
|
166
|
+
const { id } = req.params;
|
|
167
|
+
const body = req.body;
|
|
168
|
+
const p = await fastify.core.project.get(id);
|
|
169
|
+
const editor = body?.editor || "code";
|
|
170
|
+
await (0, editor_1.openFolder)(p.root, { editor });
|
|
171
|
+
return { ok: true };
|
|
172
|
+
}
|
|
173
|
+
catch (e) {
|
|
174
|
+
throw new core_1.AppError("EDITOR_LAUNCH_FAILED", e?.message || "openInEditor failed");
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
fastify.post("/bootstrap/cli", async (req) => {
|
|
178
|
+
const body = req.body;
|
|
179
|
+
return await fastify.core.bootstrap.bootstrapByCli(body);
|
|
180
|
+
});
|
|
181
|
+
fastify.post("/bootstrap/git", async (req) => {
|
|
182
|
+
const body = req.body;
|
|
183
|
+
return await fastify.core.bootstrap.bootstrapByGit(body);
|
|
184
|
+
});
|
|
185
|
+
fastify.post("/bootstrap/pickRoot", async (req) => {
|
|
186
|
+
const body = req.body;
|
|
187
|
+
const taskId = String(body?.taskId ?? "").trim();
|
|
188
|
+
const pickedRoot = String(body?.pickedRoot ?? "").trim();
|
|
189
|
+
if (!taskId)
|
|
190
|
+
throw new core_1.AppError("BAD_REQUEST", "taskId is required");
|
|
191
|
+
if (!pickedRoot)
|
|
192
|
+
throw new core_1.AppError("BAD_REQUEST", "pickedRoot is required");
|
|
193
|
+
const r = await fastify.core.bootstrap.pickWorkspaceRoot({ taskId, pickedRoot });
|
|
194
|
+
return r;
|
|
195
|
+
});
|
|
196
|
+
}
|
|
@@ -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 = rssRoutes;
|
|
7
|
+
const core_1 = require("@yinuo-ngm/core");
|
|
8
|
+
const rss_parser_1 = __importDefault(require("rss-parser"));
|
|
9
|
+
const cache = new Map();
|
|
10
|
+
const parser = new rss_parser_1.default({
|
|
11
|
+
timeout: 15000,
|
|
12
|
+
});
|
|
13
|
+
async function rssRoutes(app) {
|
|
14
|
+
app.get("/preview", async (req) => {
|
|
15
|
+
const q = req.query;
|
|
16
|
+
const url = (q.url ?? "").trim();
|
|
17
|
+
if (!url) {
|
|
18
|
+
throw new core_1.AppError("INVALID_RSS_URL", "url required", { query: q });
|
|
19
|
+
}
|
|
20
|
+
const limit = Math.min(Math.max(parseInt(q.limit ?? "20", 10) || 20, 1), 100);
|
|
21
|
+
const force = (q.force ?? "0") === "1";
|
|
22
|
+
const cacheSec = Math.min(Math.max(parseInt(q.cacheSec ?? "2400", 10) || 2400, 30), 3600);
|
|
23
|
+
const key = `${url}|${limit}`;
|
|
24
|
+
const now = Date.now();
|
|
25
|
+
if (!force) {
|
|
26
|
+
const hit = cache.get(key);
|
|
27
|
+
if (hit && hit.expireAt > now)
|
|
28
|
+
return hit.payload;
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
const feed = await parser.parseURL(url);
|
|
32
|
+
const items = (feed.items ?? []).slice(0, limit).map((it) => ({
|
|
33
|
+
title: it.title ?? "",
|
|
34
|
+
link: (it.link ?? it.guid ?? "").toString(),
|
|
35
|
+
pubDate: it.isoDate ?? (it.pubDate ? new Date(it.pubDate).toISOString() : undefined),
|
|
36
|
+
author: (it.creator ?? it.author ?? "")?.toString() || undefined,
|
|
37
|
+
summary: (it.contentSnippet ?? it.content ?? "")?.toString() || undefined,
|
|
38
|
+
}));
|
|
39
|
+
const payload = {
|
|
40
|
+
title: feed.title,
|
|
41
|
+
description: feed.description,
|
|
42
|
+
link: feed.link,
|
|
43
|
+
items,
|
|
44
|
+
fetchedAt: new Date().toISOString(),
|
|
45
|
+
};
|
|
46
|
+
cache.set(key, { expireAt: now + cacheSec * 1000, payload });
|
|
47
|
+
return payload;
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
throw new core_1.AppError(`RSS_FETCH_FAILED`, e?.message || String(e), { url });
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|