@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
package/lib/app.d.ts
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { FastifyBaseLogger } from "fastify";
|
|
2
|
+
export declare function createServer(): Promise<import("fastify").FastifyInstance<import("node:http").Server<typeof import("node:http").IncomingMessage, typeof import("node:http").ServerResponse>, import("node:http").IncomingMessage, import("node:http").ServerResponse<import("node:http").IncomingMessage>, FastifyBaseLogger, import("fastify").FastifyTypeProviderDefault>>;
|
package/lib/app.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
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.createServer = createServer;
|
|
7
|
+
const fastify_1 = __importDefault(require("fastify"));
|
|
8
|
+
const core_plugin_1 = __importDefault(require("./plugins/core.plugin"));
|
|
9
|
+
const error_handler_plugin_1 = require("./plugins/error-handler.plugin");
|
|
10
|
+
const request_id_plugin_1 = __importDefault(require("./plugins/request-id.plugin"));
|
|
11
|
+
const routes_plugin_1 = __importDefault(require("./plugins/routes.plugin"));
|
|
12
|
+
const static_plugin_1 = __importDefault(require("./plugins/static.plugin"));
|
|
13
|
+
const success_handle_plugin_1 = __importDefault(require("./plugins/success-handle.plugin"));
|
|
14
|
+
const ws_plugin_1 = __importDefault(require("./plugins/ws/ws.plugin"));
|
|
15
|
+
function normalizeLogLevel(v) {
|
|
16
|
+
const lv = (v ?? "").toLowerCase().trim();
|
|
17
|
+
if (!lv)
|
|
18
|
+
return undefined;
|
|
19
|
+
if (["fatal", "error", "warn", "info", "debug", "trace", "silent"].includes(lv))
|
|
20
|
+
return lv;
|
|
21
|
+
return "info";
|
|
22
|
+
}
|
|
23
|
+
function createFastifyLogger() {
|
|
24
|
+
const level = normalizeLogLevel(process.env.NGM_LOG_LEVEL);
|
|
25
|
+
if (!level)
|
|
26
|
+
return false;
|
|
27
|
+
const isDev = process.env.NODE_ENV !== "production";
|
|
28
|
+
if (isDev) {
|
|
29
|
+
return {
|
|
30
|
+
level,
|
|
31
|
+
transport: {
|
|
32
|
+
target: "pino-pretty",
|
|
33
|
+
options: {
|
|
34
|
+
colorize: true,
|
|
35
|
+
translateTime: "HH:MM:ss.l",
|
|
36
|
+
ignore: "pid,hostname",
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
return { level };
|
|
42
|
+
}
|
|
43
|
+
async function createServer() {
|
|
44
|
+
const fastify = (0, fastify_1.default)({
|
|
45
|
+
logger: createFastifyLogger(),
|
|
46
|
+
genReqId: (req) => {
|
|
47
|
+
const hdr = req.headers["x-request-id"];
|
|
48
|
+
if (typeof hdr === "string" && hdr.trim())
|
|
49
|
+
return hdr.trim();
|
|
50
|
+
return crypto.randomUUID();
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
await fastify.register(request_id_plugin_1.default);
|
|
54
|
+
await fastify.register(error_handler_plugin_1.errorHandlerPlugin);
|
|
55
|
+
await fastify.register(success_handle_plugin_1.default);
|
|
56
|
+
await fastify.register(core_plugin_1.default);
|
|
57
|
+
await fastify.register(ws_plugin_1.default);
|
|
58
|
+
await fastify.register(routes_plugin_1.default);
|
|
59
|
+
await fastify.register(static_plugin_1.default);
|
|
60
|
+
fastify.addHook('onClose', async () => {
|
|
61
|
+
await fastify.core.dispose?.();
|
|
62
|
+
fastify.log.info('Server is closing, performing cleanup...');
|
|
63
|
+
});
|
|
64
|
+
return fastify;
|
|
65
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import type { FastifyReply, FastifyRequest } from "fastify";
|
|
2
|
+
export declare function ok<T>(req: FastifyRequest, reply: FastifyReply, data: T, status?: number): FastifyReply<import("fastify").RouteGenericInterface, import("fastify").RawServerDefault, import("node:http").IncomingMessage, import("node:http").ServerResponse<import("node:http").IncomingMessage>, unknown, import("fastify").FastifySchema, import("fastify").FastifyTypeProviderDefault, unknown>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ok = ok;
|
|
4
|
+
function ok(req, reply, data, status = 200) {
|
|
5
|
+
return reply.status(status).send({
|
|
6
|
+
ok: true,
|
|
7
|
+
data,
|
|
8
|
+
meta: {
|
|
9
|
+
requestId: req.id,
|
|
10
|
+
ts: Date.now(),
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
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.openFolder = openFolder;
|
|
40
|
+
const core_1 = require("@yinuo-ngm/core");
|
|
41
|
+
const launch_editor_1 = __importDefault(require("launch-editor"));
|
|
42
|
+
const path = __importStar(require("path"));
|
|
43
|
+
async function openFolder(folder, opts = {}) {
|
|
44
|
+
const editor = opts.editor ?? "code";
|
|
45
|
+
const file = opts.file;
|
|
46
|
+
const target = file
|
|
47
|
+
? path.resolve(folder, file)
|
|
48
|
+
: path.resolve(folder);
|
|
49
|
+
return new Promise((resolve, reject) => {
|
|
50
|
+
let settled = false;
|
|
51
|
+
const timer = setTimeout(() => {
|
|
52
|
+
if (settled)
|
|
53
|
+
return;
|
|
54
|
+
settled = true;
|
|
55
|
+
resolve();
|
|
56
|
+
}, 200);
|
|
57
|
+
(0, launch_editor_1.default)(target, editor === "system" ? undefined : editor, (fileName, errorMsg) => {
|
|
58
|
+
if (settled)
|
|
59
|
+
return;
|
|
60
|
+
settled = true;
|
|
61
|
+
clearTimeout(timer);
|
|
62
|
+
if (errorMsg) {
|
|
63
|
+
reject(new core_1.AppError("EDITOR_NOT_FOUND", errorMsg, { fileName, editor, folder, file, target }));
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
resolve();
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
}
|
package/lib/env.d.ts
ADDED
package/lib/env.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
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.env = void 0;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const os_1 = __importDefault(require("os"));
|
|
9
|
+
exports.env = {
|
|
10
|
+
port: Number(process.env.NGM_SERVER_PORT || 3210),
|
|
11
|
+
host: process.env.NGM_SERVER_HOST || "127.0.0.1",
|
|
12
|
+
dataDir: process.env.NGM_DATA_DIR ||
|
|
13
|
+
path_1.default.join(os_1.default.homedir(), ".ng-manager"),
|
|
14
|
+
logLevel: process.env.NGM_LOG_LEVEL || "info",
|
|
15
|
+
sysLogCapacity: 3000,
|
|
16
|
+
};
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const app_1 = require("./app");
|
|
4
|
+
const env_1 = require("./env");
|
|
5
|
+
async function start() {
|
|
6
|
+
const { port, host } = env_1.env;
|
|
7
|
+
const app = await (0, app_1.createServer)();
|
|
8
|
+
await app.listen({ port, host });
|
|
9
|
+
app.log.info(`local server listening on http://${host}:${port}`);
|
|
10
|
+
app.core.sysLog.append({
|
|
11
|
+
level: "info",
|
|
12
|
+
text: `Server started at http://${host}:${port}`,
|
|
13
|
+
source: "server",
|
|
14
|
+
scope: "system",
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
start().catch((err) => {
|
|
18
|
+
console.error("Failed to start server:", err);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { FastifyInstance } from "fastify";
|
|
2
|
+
import { type CoreApp } from "@yinuo-ngm/core";
|
|
3
|
+
declare module "fastify" {
|
|
4
|
+
interface FastifyInstance {
|
|
5
|
+
core: CoreApp;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
declare const _default: (fastify: FastifyInstance) => Promise<void>;
|
|
9
|
+
export default _default;
|
|
@@ -0,0 +1,16 @@
|
|
|
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 fastify_plugin_1 = __importDefault(require("fastify-plugin"));
|
|
7
|
+
const core_1 = require("@yinuo-ngm/core");
|
|
8
|
+
const env_1 = require("../env");
|
|
9
|
+
exports.default = (0, fastify_plugin_1.default)(async function corePlugin(fastify) {
|
|
10
|
+
const coreApp = await (0, core_1.createCoreApp)({
|
|
11
|
+
sysLogCapacity: env_1.env.sysLogCapacity,
|
|
12
|
+
dataDir: env_1.env.dataDir,
|
|
13
|
+
});
|
|
14
|
+
fastify.decorate("core", coreApp);
|
|
15
|
+
fastify.log.info("[core] core app initialized");
|
|
16
|
+
});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { FastifyPluginAsync } from "fastify";
|
|
2
|
+
import { type ErrorCode } from "@yinuo-ngm/core";
|
|
3
|
+
export declare const ERROR_STATUS: Record<ErrorCode, number>;
|
|
4
|
+
export declare function mapStatus(code: ErrorCode): number;
|
|
5
|
+
export declare const errorHandlerPlugin: FastifyPluginAsync;
|
|
6
|
+
export default errorHandlerPlugin;
|
|
@@ -0,0 +1,113 @@
|
|
|
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.errorHandlerPlugin = exports.ERROR_STATUS = void 0;
|
|
7
|
+
exports.mapStatus = mapStatus;
|
|
8
|
+
const fastify_plugin_1 = __importDefault(require("fastify-plugin"));
|
|
9
|
+
const core_1 = require("@yinuo-ngm/core");
|
|
10
|
+
exports.ERROR_STATUS = {
|
|
11
|
+
PROJECT_NOT_FOUND: 404,
|
|
12
|
+
PROJECT_ROOT_INVALID: 400,
|
|
13
|
+
PROJECT_ALREADY_EXISTS: 409,
|
|
14
|
+
PROJECT_IMPORT_NOT_EXISTS: 404,
|
|
15
|
+
PROJECT_IMPORT_NOT_DIR: 400,
|
|
16
|
+
PROJECT_IMPORT_ALREADY_REGISTERED: 409,
|
|
17
|
+
PROJECT_IMPORT_NOT_RECOGNIZED: 422,
|
|
18
|
+
PROJECT_IMPORT_SCAN_FAILED: 500,
|
|
19
|
+
INVALID_NAME: 400,
|
|
20
|
+
TARGET_EXISTS: 409,
|
|
21
|
+
INVALID_REPO_URL: 400,
|
|
22
|
+
INVALID_PARENT_DIR: 400,
|
|
23
|
+
GIT_CHECKOUT_FAILED: 500,
|
|
24
|
+
BOOTSTRAP_NOT_IN_PICK_STATE: 400,
|
|
25
|
+
BOOTSTRAP_CTX_NOT_FOUND: 404,
|
|
26
|
+
BOOTSTRAP_INVALID_PICKED_ROOT: 400,
|
|
27
|
+
BOOTSTRAP_NOT_WAITING_PICK: 400,
|
|
28
|
+
PROJECT_ANGULAR_JSON_INVALID: 400,
|
|
29
|
+
PROJECT_ANGULAR_JSON_NOT_FOUND: 404,
|
|
30
|
+
PROJECT_VITE_CONFIG_INVALID: 400,
|
|
31
|
+
PROJECT_VUE_CONFIG_NOT_FOUND: 404,
|
|
32
|
+
CONFIG_BACKUP_NOT_FOUND: 404,
|
|
33
|
+
CONFIG_READ_FAILED: 500,
|
|
34
|
+
CONFIG_WRITE_FAILED: 500,
|
|
35
|
+
CONFIG_CONFLICT: 409,
|
|
36
|
+
CONFIG_OPEN_FAILED: 500,
|
|
37
|
+
CONFIG_SCHEMA_NOT_FOUND: 404,
|
|
38
|
+
CONFIG_DOMAIN_NOT_FOUND: 404,
|
|
39
|
+
CONFIG_DOC_NOT_FOUND: 404,
|
|
40
|
+
TASK_NOT_FOUND: 404,
|
|
41
|
+
TASK_ALREADY_RUNNING: 409,
|
|
42
|
+
PROCESS_SPAWN_FAILED: 500,
|
|
43
|
+
TASK_SPEC_NOT_FOUND: 404,
|
|
44
|
+
TASK_NOT_RUNNABLE: 400,
|
|
45
|
+
RUN_NOT_FOUND: 404,
|
|
46
|
+
TASK_ID_REQUIRED: 400,
|
|
47
|
+
COMMAND_NOT_FOUND: 404,
|
|
48
|
+
BAD_JSON: 400,
|
|
49
|
+
BAD_MSG: 400,
|
|
50
|
+
OP_NOT_SUPPORTED: 400,
|
|
51
|
+
HANDLER_FAILED: 500,
|
|
52
|
+
TOPIC_NOT_FOUND: 404,
|
|
53
|
+
OP_NOT_FOUND: 400,
|
|
54
|
+
FS_PATH_NOT_FOUND: 404,
|
|
55
|
+
FS_ALREADY_EXISTS: 409,
|
|
56
|
+
FS_PERMISSION_DENIED: 403,
|
|
57
|
+
FS_EXISTS_FAILED: 500,
|
|
58
|
+
FS_INVALID_NAME: 400,
|
|
59
|
+
FS_MKDIR_FAILED: 500,
|
|
60
|
+
EDITOR_NOT_FOUND: 404,
|
|
61
|
+
EDITOR_LAUNCH_FAILED: 500,
|
|
62
|
+
STORAGE_IO_ERROR: 500,
|
|
63
|
+
UNAUTHORIZED: 401,
|
|
64
|
+
INVALID_TIMESTAMP: 400,
|
|
65
|
+
DEP_INSTALL_FAILED: 500,
|
|
66
|
+
DEP_UNINSTALL_FAILED: 500,
|
|
67
|
+
DEP_NOT_FOUND: 404,
|
|
68
|
+
DASHBOARD_CONFLICT: 409,
|
|
69
|
+
WIDGET_NOT_FOUND: 404,
|
|
70
|
+
WIDGET_LOCKED: 423,
|
|
71
|
+
RSS_FETCH_FAILED: 500,
|
|
72
|
+
INVALID_RSS_URL: 400,
|
|
73
|
+
UNKNOWN_ERROR: 500,
|
|
74
|
+
BAD_REQUEST: 400,
|
|
75
|
+
NOT_IMPLEMENTED: 501,
|
|
76
|
+
};
|
|
77
|
+
function mapStatus(code) {
|
|
78
|
+
return exports.ERROR_STATUS[code] ?? 400;
|
|
79
|
+
}
|
|
80
|
+
function buildErrorBody(reqId, code, message, details) {
|
|
81
|
+
return {
|
|
82
|
+
ok: false,
|
|
83
|
+
error: {
|
|
84
|
+
code,
|
|
85
|
+
message,
|
|
86
|
+
...(details !== undefined ? { details } : {}),
|
|
87
|
+
},
|
|
88
|
+
meta: {
|
|
89
|
+
requestId: reqId,
|
|
90
|
+
ts: Date.now(),
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
exports.errorHandlerPlugin = (0, fastify_plugin_1.default)(async (app) => {
|
|
95
|
+
app.setErrorHandler((err, req, reply) => {
|
|
96
|
+
const requestId = req.id;
|
|
97
|
+
if (err instanceof core_1.AppError || isCoreAppError(err)) {
|
|
98
|
+
const status = mapStatus(err.code);
|
|
99
|
+
return reply.status(status).send(buildErrorBody(requestId, err.code, err.message, err.meta));
|
|
100
|
+
}
|
|
101
|
+
if (err?.validation) {
|
|
102
|
+
return reply
|
|
103
|
+
.status(400)
|
|
104
|
+
.send(buildErrorBody(requestId, "VALIDATION_ERROR", "参数不合法", err.validation));
|
|
105
|
+
}
|
|
106
|
+
req.log.error({ err, requestId }, "Unhandled error");
|
|
107
|
+
return reply.status(500).send(buildErrorBody(requestId, "INTERNAL_ERROR", "服务异常,请稍后重试"));
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
function isCoreAppError(err) {
|
|
111
|
+
return err && typeof err === "object" && typeof err.code === "string" && typeof err.message === "string";
|
|
112
|
+
}
|
|
113
|
+
exports.default = exports.errorHandlerPlugin;
|
|
@@ -0,0 +1,14 @@
|
|
|
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.requestIdPlugin = void 0;
|
|
7
|
+
const fastify_plugin_1 = __importDefault(require("fastify-plugin"));
|
|
8
|
+
exports.requestIdPlugin = (0, fastify_plugin_1.default)(async (app) => {
|
|
9
|
+
app.addHook("onSend", async (req, reply, payload) => {
|
|
10
|
+
reply.header("X-Request-Id", req.id);
|
|
11
|
+
return payload;
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
exports.default = exports.requestIdPlugin;
|
|
@@ -0,0 +1,17 @@
|
|
|
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 fastify_plugin_1 = __importDefault(require("fastify-plugin"));
|
|
7
|
+
const routes_1 = require("../routes");
|
|
8
|
+
exports.default = (0, fastify_plugin_1.default)(async function routesPlugin(fastify) {
|
|
9
|
+
await fastify.register(routes_1.systemRoutes);
|
|
10
|
+
await fastify.register(routes_1.taskRoutes, { prefix: '/api/tasks' });
|
|
11
|
+
await fastify.register(routes_1.projectRoutes, { prefix: '/api/projects' });
|
|
12
|
+
await fastify.register(routes_1.depsRoutes, { prefix: '/api/deps' });
|
|
13
|
+
await fastify.register(routes_1.fsRoutes, { prefix: '/api/fs' });
|
|
14
|
+
await fastify.register(routes_1.configRoutes, { prefix: '/api/config' });
|
|
15
|
+
await fastify.register(routes_1.dashboardRoutes, { prefix: '/api/dashboard' });
|
|
16
|
+
await fastify.register(routes_1.rssRoutes, { prefix: '/api/rss' });
|
|
17
|
+
});
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
declare const _default: (fastify: import("fastify").FastifyInstance<import("fastify").RawServerDefault, import("node:http").IncomingMessage, import("node:http").ServerResponse<import("node:http").IncomingMessage>, import("fastify").FastifyBaseLogger, import("fastify").FastifyTypeProviderDefault>) => Promise<void>;
|
|
2
|
+
export default _default;
|
|
@@ -0,0 +1,24 @@
|
|
|
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 fastify_plugin_1 = __importDefault(require("fastify-plugin"));
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const static_1 = __importDefault(require("@fastify/static"));
|
|
9
|
+
exports.default = (0, fastify_plugin_1.default)(async function staticPlugin(fastify) {
|
|
10
|
+
const webRoot = path_1.default.resolve(__dirname, "../../www/browser");
|
|
11
|
+
fastify.log.info(`[static] serving webapp from ${webRoot}`);
|
|
12
|
+
await fastify.register(static_1.default, {
|
|
13
|
+
root: webRoot,
|
|
14
|
+
index: "index.html",
|
|
15
|
+
});
|
|
16
|
+
fastify.setNotFoundHandler((req, reply) => {
|
|
17
|
+
if (req.method === "GET" &&
|
|
18
|
+
!req.url.startsWith("/api") &&
|
|
19
|
+
!req.url.startsWith("/ws")) {
|
|
20
|
+
return reply.sendFile("index.html");
|
|
21
|
+
}
|
|
22
|
+
reply.code(404).send({ ok: false, message: "Not Found" });
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
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.successHandlerPlugin = void 0;
|
|
7
|
+
const fastify_plugin_1 = __importDefault(require("fastify-plugin"));
|
|
8
|
+
exports.successHandlerPlugin = (0, fastify_plugin_1.default)(async (app) => {
|
|
9
|
+
app.addHook("preSerialization", async (req, reply, payload) => {
|
|
10
|
+
if (reply.statusCode < 200 || reply.statusCode >= 300) {
|
|
11
|
+
return payload;
|
|
12
|
+
}
|
|
13
|
+
if (payload === undefined || payload === null) {
|
|
14
|
+
return payload;
|
|
15
|
+
}
|
|
16
|
+
const type = reply.getHeader("content-type");
|
|
17
|
+
if (typeof type === "string" && !type.includes("application/json")) {
|
|
18
|
+
return payload;
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
ok: true,
|
|
22
|
+
data: payload,
|
|
23
|
+
meta: {
|
|
24
|
+
requestId: req.id,
|
|
25
|
+
ts: Date.now(),
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
exports.default = exports.successHandlerPlugin;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./task.ws";
|
|
@@ -0,0 +1,17 @@
|
|
|
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./task.ws"), exports);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { LogLine } from "@yinuo-ngm/core";
|
|
2
|
+
import { WsContext } from "../ws.context";
|
|
3
|
+
import type { TopicHandler } from "../ws.router";
|
|
4
|
+
export type SyslogWsDeps = {
|
|
5
|
+
getSyslogTail: (tail: number) => LogLine[];
|
|
6
|
+
};
|
|
7
|
+
export declare function createSyslogTopicHandler(deps: SyslogWsDeps, getAllClients: () => Iterable<WsContext>): TopicHandler & {
|
|
8
|
+
push(entry: LogLine): void;
|
|
9
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createSyslogTopicHandler = createSyslogTopicHandler;
|
|
4
|
+
const KEY_ALL = "syslog:all";
|
|
5
|
+
function createSyslogTopicHandler(deps, getAllClients) {
|
|
6
|
+
return {
|
|
7
|
+
topic: "syslog",
|
|
8
|
+
async sub(ctx, msg) {
|
|
9
|
+
const tail = Math.max(0, Number(msg?.tail ?? 0));
|
|
10
|
+
ctx.addSub("syslog", KEY_ALL);
|
|
11
|
+
if (tail > 0) {
|
|
12
|
+
const entries = await deps.getSyslogTail(tail);
|
|
13
|
+
const m = { op: "syslog.tail", entries: entries ?? [] };
|
|
14
|
+
ctx.send(m);
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
unsub(ctx) {
|
|
18
|
+
ctx.delSub("syslog", KEY_ALL);
|
|
19
|
+
},
|
|
20
|
+
push(entry) {
|
|
21
|
+
const m = { op: "syslog.append", entry };
|
|
22
|
+
for (const c of getAllClients()) {
|
|
23
|
+
if (c.hasSub("syslog", KEY_ALL))
|
|
24
|
+
c.send(m);
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { TaskEventPayloadMap, TaskEventType, TaskOutputPayload, LogLine, TaskRuntime } from "@yinuo-ngm/core";
|
|
2
|
+
import { WsContext } from "../ws.context";
|
|
3
|
+
import type { TopicHandler } from "../ws.router";
|
|
4
|
+
export type TaskWsDeps = {
|
|
5
|
+
getTaskSnapshotByTaskId?: (taskId: string) => Promise<TaskRuntime | null>;
|
|
6
|
+
getTaskTailLogsByRun?: (runId: string, tail: number) => Promise<LogLine[]>;
|
|
7
|
+
resizeRun?: (taskId: string, cols: number, rows: number) => void;
|
|
8
|
+
};
|
|
9
|
+
export declare function createTaskTopicHandler(deps: TaskWsDeps, getAllClients: () => Iterable<WsContext>): TopicHandler & {
|
|
10
|
+
pushOutput(payload: TaskOutputPayload): void;
|
|
11
|
+
pushEvent<K extends TaskEventType>(type: K, payload: TaskEventPayloadMap[K]): void;
|
|
12
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createTaskTopicHandler = createTaskTopicHandler;
|
|
4
|
+
const keyOfTask = (taskId) => `task:${taskId}`;
|
|
5
|
+
function createTaskTopicHandler(deps, getAllClients) {
|
|
6
|
+
return {
|
|
7
|
+
topic: "task",
|
|
8
|
+
async sub(ctx, msg) {
|
|
9
|
+
const taskId = String(msg?.taskId ?? "").trim();
|
|
10
|
+
const tail = Number(msg?.tail ?? 0);
|
|
11
|
+
if (!taskId) {
|
|
12
|
+
ctx.send({ op: "error", code: "TASK_ID_REQUIRED", message: "taskId is required", ts: Date.now() });
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
ctx.addSub("task", keyOfTask(taskId));
|
|
16
|
+
let snap;
|
|
17
|
+
if (deps.getTaskSnapshotByTaskId) {
|
|
18
|
+
snap = await deps.getTaskSnapshotByTaskId(taskId) ?? undefined;
|
|
19
|
+
if (snap) {
|
|
20
|
+
const m = {
|
|
21
|
+
op: "task.event",
|
|
22
|
+
type: "snapshot",
|
|
23
|
+
payload: snap,
|
|
24
|
+
ts: Date.now(),
|
|
25
|
+
};
|
|
26
|
+
ctx.send(m);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (snap?.runId && deps.getTaskTailLogsByRun && tail > 0) {
|
|
30
|
+
const entries = await deps.getTaskTailLogsByRun(snap.runId, tail);
|
|
31
|
+
for (const e of entries ?? []) {
|
|
32
|
+
const m = {
|
|
33
|
+
op: "task.output",
|
|
34
|
+
payload: {
|
|
35
|
+
taskId: taskId,
|
|
36
|
+
runId: snap.runId,
|
|
37
|
+
stream: (e?.level === "warn" || e?.level === "error") ? "stderr" : "stdout",
|
|
38
|
+
text: String(e?.text ?? ""),
|
|
39
|
+
},
|
|
40
|
+
ts: e?.ts ?? Date.now(),
|
|
41
|
+
};
|
|
42
|
+
ctx.send(m);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
resize(ctx, msg) {
|
|
47
|
+
const taskId = msg.taskId;
|
|
48
|
+
const cols = Number(msg.cols);
|
|
49
|
+
const rows = Number(msg.rows);
|
|
50
|
+
if (!taskId || !Number.isFinite(cols) || !Number.isFinite(rows))
|
|
51
|
+
return;
|
|
52
|
+
const c = Math.max(2, Math.floor(cols));
|
|
53
|
+
const r = Math.max(1, Math.floor(rows));
|
|
54
|
+
deps.resizeRun?.(taskId, c, r);
|
|
55
|
+
},
|
|
56
|
+
unsub(ctx, msg) {
|
|
57
|
+
const taskId = String(msg?.taskId ?? "").trim();
|
|
58
|
+
if (!taskId)
|
|
59
|
+
return;
|
|
60
|
+
ctx.delSub("task", keyOfTask(taskId));
|
|
61
|
+
},
|
|
62
|
+
pushOutput(payload) {
|
|
63
|
+
const m = { op: "task.output", payload, ts: Date.now() };
|
|
64
|
+
for (const c of getAllClients())
|
|
65
|
+
if (c.hasSub("task", keyOfTask(payload.taskId)))
|
|
66
|
+
c.send(m);
|
|
67
|
+
},
|
|
68
|
+
pushEvent(type, payload) {
|
|
69
|
+
const m = { op: "task.event", type, payload, ts: Date.now() };
|
|
70
|
+
for (const c of getAllClients())
|
|
71
|
+
if (c.hasSub("task", keyOfTask(payload.taskId)))
|
|
72
|
+
c.send(m);
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { WsServerMsg, WsTopic } from "@yinuo-ngm/core";
|
|
2
|
+
export declare class WsContext {
|
|
3
|
+
private socket;
|
|
4
|
+
readonly connId: string;
|
|
5
|
+
private subs;
|
|
6
|
+
constructor(connId: string, socket: WebSocket);
|
|
7
|
+
send(msg: WsServerMsg): void;
|
|
8
|
+
addSub(topic: WsTopic, key: string): void;
|
|
9
|
+
delSub(topic: WsTopic, key: string): void;
|
|
10
|
+
hasSub(topic: WsTopic, key: string): boolean;
|
|
11
|
+
clearAll(): void;
|
|
12
|
+
}
|