@whyour/qinglong 2.20.2-3 → 2.21.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/.env.example +5 -0
- package/AGENTS.md +43 -0
- package/CLAUDE.md +43 -0
- package/README-en.md +11 -0
- package/README.md +11 -0
- package/docker/Dockerfile +51 -69
- package/docker/Dockerfile.310 +94 -0
- package/docker/Dockerfile.debian +120 -0
- package/docker/{310.Dockerfile → Dockerfile.debian310} +13 -5
- package/docker/docker-entrypoint.sh +95 -3
- package/docs/PROJECT_ARCHITECTURE.md +550 -0
- package/package.json +28 -25
- package/sample/config.sample.sh +12 -1
- package/sample/notify.js +81 -9
- package/sample/notify.py +54 -11
- package/shell/api.sh +44 -20
- package/shell/bot.sh +30 -16
- package/shell/check.sh +21 -19
- package/shell/lang/en.sh +105 -0
- package/shell/lang/zh.sh +105 -0
- package/shell/otask.sh +114 -28
- package/shell/preload/client.js +20 -2
- package/shell/preload/esm-loader.mjs +41 -0
- package/shell/preload/sitecustomize.js +36 -0
- package/shell/pub.sh +8 -8
- package/shell/rmlog.sh +5 -5
- package/shell/share.sh +49 -30
- package/shell/start.sh +40 -27
- package/shell/task.sh +10 -2
- package/shell/update.sh +22 -22
- package/static/build/api/config.js +17 -7
- package/static/build/api/cron.js +38 -1
- package/static/build/api/dashboard.js +337 -0
- package/static/build/api/env.js +40 -2
- package/static/build/api/index.js +2 -0
- package/static/build/api/log.js +5 -4
- package/static/build/api/script.js +34 -9
- package/static/build/api/system.js +16 -1
- package/static/build/api/user.js +6 -5
- package/static/build/app.js +3 -0
- package/static/build/config/const.js +7 -6
- package/static/build/config/container.js +12 -0
- package/static/build/config/grpcCerts.js +150 -0
- package/static/build/config/index.js +6 -0
- package/static/build/config/util.js +66 -15
- package/static/build/data/cron.js +3 -1
- package/static/build/data/cronStats.js +59 -0
- package/static/build/data/env.js +2 -0
- package/static/build/data/notify.js +11 -1
- package/static/build/data/runningInstance.js +57 -0
- package/static/build/loaders/app.js +1 -1
- package/static/build/loaders/db.js +6 -0
- package/static/build/loaders/express.js +2 -1
- package/static/build/loaders/initData.js +19 -2
- package/static/build/loaders/initFile.js +2 -0
- package/static/build/schedule/api.js +1 -1
- package/static/build/schedule/client.js +9 -1
- package/static/build/schedule/health.js +1 -1
- package/static/build/services/config.js +19 -5
- package/static/build/services/cron.js +118 -8
- package/static/build/services/env.js +31 -3
- package/static/build/services/grpc.js +32 -6
- package/static/build/services/http.js +33 -18
- package/static/build/services/notify.js +40 -3
- package/static/build/services/open.js +2 -1
- package/static/build/services/sshKey.js +6 -3
- package/static/build/services/subscription.js +2 -1
- package/static/build/services/system.js +22 -7
- package/static/build/services/user.js +8 -7
- package/static/build/shared/i18n.js +105 -0
- package/static/build/shared/pLimit.js +12 -1
- package/static/build/shared/runCron.js +5 -0
- package/static/build/validation/schedule.js +1 -0
- package/static/dist/1089.0ecc5383.async.js +1 -0
- package/static/dist/1147.c420132e.async.js +1 -0
- package/static/dist/1192.4e5740f2.async.js +1 -0
- package/static/dist/1214.c6469b53.async.js +1 -0
- package/static/dist/134.54df9e38.async.js +1 -0
- package/static/dist/1352.96a77e1d.async.js +1 -0
- package/static/dist/{1765.d8e002d7.async.js → 1765.5ac01a93.async.js} +1 -1
- package/static/dist/1836.f0b74cf1.async.js +1 -0
- package/static/dist/{1885.e0d00d2d.async.js → 1885.e1ea09f6.async.js} +1 -1
- package/static/dist/1967.3f3945d0.async.js +1 -0
- package/static/dist/{2096.383c1047.async.js → 2096.6d98fd12.async.js} +1 -1
- package/static/dist/2208.f7ba3dfa.async.js +1 -0
- package/static/dist/2286.164bb089.async.js +1 -0
- package/static/dist/2476.4e9b0992.async.js +1 -0
- package/static/dist/2537.2b262ee0.async.js +1 -0
- package/static/dist/{255.12f03ab2.async.js → 255.8a80b983.async.js} +1 -1
- package/static/dist/2635.d4c59d23.async.js +1 -0
- package/static/dist/2808.cdc0995c.async.js +1 -0
- package/static/dist/{2821.be3dc88e.async.js → 2821.651f31e5.async.js} +1 -1
- package/static/dist/2986.4c49eef7.async.js +1 -0
- package/static/dist/300.08ac9875.async.js +1 -0
- package/static/dist/{3191.70bc19db.async.js → 3191.63263871.async.js} +1 -1
- package/static/dist/3714.8d6dba9e.async.js +1 -0
- package/static/dist/3906.a5eee612.async.js +1 -0
- package/static/dist/{6541.a6d499de.async.js → 3948.4fe809fd.async.js} +1 -1
- package/static/dist/{5171.7fc6d0a2.async.js → 4573.16f19278.async.js} +1 -1
- package/static/dist/{4859.93e63ea6.async.js → 4859.6592cebb.async.js} +1 -1
- package/static/dist/4887.8e3ae573.async.js +1 -0
- package/static/dist/{4902.54ecbdb5.async.js → 4902.555f92ab.async.js} +1 -1
- package/static/dist/{4934.1ca6b6b0.async.js → 4934.f3539d08.async.js} +1 -1
- package/static/dist/5077.cbd111cd.async.js +1 -0
- package/static/dist/{6247.b550d996.async.js → 5157.7c5af144.async.js} +1 -1
- package/static/dist/{540.481d4708.async.js → 540.19ddf020.async.js} +1 -1
- package/static/dist/{5970.10ac4f16.async.js → 5970.d3a6e7bd.async.js} +1 -1
- package/static/dist/{6013.2d7bb12a.async.js → 6013.4e38de36.async.js} +1 -1
- package/static/dist/{6016.9c379049.async.js → 6016.1e6574b6.async.js} +1 -1
- package/static/dist/{6035.5889ddc7.async.js → 6035.32ebfad9.async.js} +1 -1
- package/static/dist/6339.ab305e96.async.js +1 -0
- package/static/dist/6569.55177f6a.async.js +1 -0
- package/static/dist/6610.c111af7e.async.js +1 -0
- package/static/dist/{3034.6413c38e.async.js → 6638.87a163d2.async.js} +1 -1
- package/static/dist/{6646.5fc37228.async.js → 6646.95e92533.async.js} +1 -1
- package/static/dist/6665.1d099ad1.async.js +1 -0
- package/static/dist/{7355.b7c0562f.async.js → 7355.f9f263d8.async.js} +1 -1
- package/static/dist/{7384.065ccae2.async.js → 7384.1bb449d5.async.js} +1 -1
- package/static/dist/7441.ee1bf2bb.async.js +1 -0
- package/static/dist/{7508.a31662a3.async.js → 7508.7dcee91c.async.js} +1 -1
- package/static/dist/{7802.6b73f16a.async.js → 7802.34255805.async.js} +1 -1
- package/static/dist/{7984.e6bb9378.async.js → 7984.594296a5.async.js} +1 -1
- package/static/dist/{5653.4fce7ce8.async.js → 8033.5cb31493.async.js} +1 -1
- package/static/dist/8147.2fb55202.async.js +1 -0
- package/static/dist/8187.48c130d6.async.js +1 -0
- package/static/dist/8495.d53e15ca.async.js +1 -0
- package/static/dist/8587.315b06c0.async.js +1 -0
- package/static/dist/8826.2447a104.async.js +1 -0
- package/static/dist/{2742.4852aac8.async.js → 8865.ed665d31.async.js} +1 -1
- package/static/dist/901.7ee5c6d3.async.js +1 -0
- package/static/dist/9271.231db2ce.async.js +1 -0
- package/static/dist/9323.a33f47da.async.js +1 -0
- package/static/dist/{9730.30083c91.async.js → 9730.801665a3.async.js} +1 -1
- package/static/dist/{9761.627ca3b5.async.js → 9761.360a19d8.async.js} +1 -1
- package/static/dist/index.html +2 -2
- package/static/dist/layouts__index.a7a2bfe0.async.js +1 -0
- package/static/dist/layouts__index.adf0692f.chunk.css +1 -0
- package/static/dist/preload_helper.0431c0f3.js +1 -0
- package/static/dist/{src__pages__config__index.622b6ee8.async.js → src__pages__config__index.a22ff7dc.async.js} +1 -1
- package/static/dist/{src__pages__crontab__const.323d5124.async.js → src__pages__crontab__const.aba07deb.async.js} +1 -1
- package/static/dist/src__pages__crontab__detail.b9c36808.async.js +1 -0
- package/static/dist/src__pages__crontab__index.fed99fd0.async.js +1 -0
- package/static/dist/{src__pages__crontab__logModal.5e6a4bf2.async.js → src__pages__crontab__logModal.0b8cce8c.async.js} +1 -1
- package/static/dist/src__pages__crontab__modal.f7041c7c.async.js +1 -0
- package/static/dist/src__pages__crontab__type.d7af36e5.async.js +1 -0
- package/static/dist/{src__pages__crontab__viewCreateModal.ffcf7a24.async.js → src__pages__crontab__viewCreateModal.4d589f66.async.js} +1 -1
- package/static/dist/{src__pages__crontab__viewManageModal.c2724575.async.js → src__pages__crontab__viewManageModal.0e317746.async.js} +1 -1
- package/static/dist/src__pages__dashboard__index.b30f2f47.async.js +1 -0
- package/static/dist/{src__pages__dependence__logModal.f123e2ac.async.js → src__pages__dependence__logModal.0681830b.async.js} +1 -1
- package/static/dist/src__pages__dependence__modal.11124896.async.js +1 -0
- package/static/dist/src__pages__env__editNameModal.5d264b25.async.js +1 -0
- package/static/dist/src__pages__env__index.baa27d4e.async.js +1 -0
- package/static/dist/src__pages__env__modal.7f2ef1bc.async.js +1 -0
- package/static/dist/src__pages__error__index.f156b45e.async.js +1 -0
- package/static/dist/src__pages__initialization__index.e96d4ba8.async.js +1 -0
- package/static/dist/{src__pages__log__index.cf00c9af.async.js → src__pages__log__index.e0978bae.async.js} +1 -1
- package/static/dist/{src__pages__login__index.cd6e3152.async.js → src__pages__login__index.8c813eb1.async.js} +1 -1
- package/static/dist/{src__pages__script__components__UnsupportedFilePreview__index.39074c68.async.js → src__pages__script__components__UnsupportedFilePreview__index.c347a13a.async.js} +1 -1
- package/static/dist/src__pages__script__editNameModal.3e7e100d.async.js +1 -0
- package/static/dist/src__pages__script__index.2765d1b8.async.js +1 -0
- package/static/dist/src__pages__script__renameModal.5e987ef5.async.js +1 -0
- package/static/dist/src__pages__script__saveModal.3f9d23d6.async.js +1 -0
- package/static/dist/src__pages__script__setting.a535793a.async.js +1 -0
- package/static/dist/src__pages__setting__appModal.251cd14f.async.js +1 -0
- package/static/dist/src__pages__setting__dependence.8a7a9529.async.js +1 -0
- package/static/dist/src__pages__setting__index.cc85fdfb.async.js +1 -0
- package/static/dist/src__pages__setting__notification.390fc905.async.js +1 -0
- package/static/dist/src__pages__setting__other.b22d2165.async.js +1 -0
- package/static/dist/src__pages__setting__security.598720a8.async.js +1 -0
- package/static/dist/src__pages__setting__systemLog.67406721.async.js +1 -0
- package/static/dist/{src__pages__subscription__logModal.0caa7283.async.js → src__pages__subscription__logModal.3864b37f.async.js} +1 -1
- package/static/dist/src__pages__subscription__modal.3562c670.async.js +1 -0
- package/static/dist/{umi.ef8199a4.js → umi.ca04a019.js} +1 -1
- package/version.yaml +33 -4
- package/sample/notify.py.save +0 -1010
- package/static/dist/105.85a5c47a.async.js +0 -1
- package/static/dist/1083.f86ce804.async.js +0 -1
- package/static/dist/1147.32f41a88.async.js +0 -1
- package/static/dist/1352.ab6da08e.async.js +0 -1
- package/static/dist/1690.f0290540.async.js +0 -1
- package/static/dist/1742.6cbe5aca.async.js +0 -1
- package/static/dist/2208.8e3a7325.async.js +0 -1
- package/static/dist/5312.74b95311.async.js +0 -1
- package/static/dist/5691.931f59c5.async.js +0 -1
- package/static/dist/6159.55cb068a.async.js +0 -1
- package/static/dist/7025.f4080d63.async.js +0 -1
- package/static/dist/739.6be5552a.async.js +0 -1
- package/static/dist/7571.4f6240b1.async.js +0 -1
- package/static/dist/786.59fc381c.async.js +0 -1
- package/static/dist/8317.c44c1ebd.async.js +0 -1
- package/static/dist/8826.0291edfd.async.js +0 -1
- package/static/dist/955.3c9481f7.async.js +0 -1
- package/static/dist/layouts__index.1fce90e0.chunk.css +0 -1
- package/static/dist/layouts__index.8dcf1576.async.js +0 -1
- package/static/dist/preload_helper.116a62f6.js +0 -1
- package/static/dist/src__pages__crontab__detail.b07f0c0a.async.js +0 -1
- package/static/dist/src__pages__crontab__index.2e2e1096.async.js +0 -1
- package/static/dist/src__pages__crontab__modal.4d8c2a22.async.js +0 -1
- package/static/dist/src__pages__crontab__type.db7c1858.async.js +0 -1
- package/static/dist/src__pages__dependence__modal.631ffb5b.async.js +0 -1
- package/static/dist/src__pages__env__editNameModal.ff85ef8c.async.js +0 -1
- package/static/dist/src__pages__env__index.a0a2fece.async.js +0 -1
- package/static/dist/src__pages__env__modal.d1004662.async.js +0 -1
- package/static/dist/src__pages__error__index.1bc3c90b.async.js +0 -1
- package/static/dist/src__pages__initialization__index.8b1cbaf9.async.js +0 -1
- package/static/dist/src__pages__script__editNameModal.53424d49.async.js +0 -1
- package/static/dist/src__pages__script__index.e65df827.async.js +0 -1
- package/static/dist/src__pages__script__renameModal.4bbe7fb1.async.js +0 -1
- package/static/dist/src__pages__script__saveModal.cf449f3c.async.js +0 -1
- package/static/dist/src__pages__script__setting.b345d59a.async.js +0 -1
- package/static/dist/src__pages__setting__appModal.03faec89.async.js +0 -1
- package/static/dist/src__pages__setting__dependence.4495c7b6.async.js +0 -1
- package/static/dist/src__pages__setting__index.6919c399.async.js +0 -1
- package/static/dist/src__pages__setting__notification.d6a3884f.async.js +0 -1
- package/static/dist/src__pages__setting__other.0d931d6f.async.js +0 -1
- package/static/dist/src__pages__setting__security.91cb545f.async.js +0 -1
- package/static/dist/src__pages__setting__systemLog.1d433a48.async.js +0 -1
- package/static/dist/src__pages__subscription__modal.0b84f6cf.async.js +0 -1
package/static/build/api/cron.js
CHANGED
|
@@ -9,6 +9,8 @@ const cron_1 = __importDefault(require("../services/cron"));
|
|
|
9
9
|
const cronView_1 = __importDefault(require("../services/cronView"));
|
|
10
10
|
const celebrate_1 = require("celebrate");
|
|
11
11
|
const schedule_1 = require("../validation/schedule");
|
|
12
|
+
const runningInstance_1 = require("../data/runningInstance");
|
|
13
|
+
const i18n_1 = require("../shared/i18n");
|
|
12
14
|
const route = (0, express_1.Router)();
|
|
13
15
|
exports.default = (app) => {
|
|
14
16
|
app.use('/crons', route);
|
|
@@ -51,7 +53,7 @@ exports.default = (app) => {
|
|
|
51
53
|
try {
|
|
52
54
|
const cronViewService = typedi_1.Container.get(cronView_1.default);
|
|
53
55
|
if (req.body.type === 1) {
|
|
54
|
-
return res.send({ code: 400, message: '参数错误' });
|
|
56
|
+
return res.send({ code: 400, message: (0, i18n_1.t)('参数错误') });
|
|
55
57
|
}
|
|
56
58
|
else {
|
|
57
59
|
const data = await cronViewService.update(req.body);
|
|
@@ -349,6 +351,41 @@ exports.default = (app) => {
|
|
|
349
351
|
return next(e);
|
|
350
352
|
}
|
|
351
353
|
});
|
|
354
|
+
route.get('/:id/instances', (0, celebrate_1.celebrate)({
|
|
355
|
+
params: celebrate_1.Joi.object({
|
|
356
|
+
id: celebrate_1.Joi.number().required(),
|
|
357
|
+
}),
|
|
358
|
+
}), async (req, res, next) => {
|
|
359
|
+
try {
|
|
360
|
+
const instances = await runningInstance_1.RunningInstanceModel.findAll({
|
|
361
|
+
where: {
|
|
362
|
+
cron_id: req.params.id,
|
|
363
|
+
status: runningInstance_1.InstanceStatus.running,
|
|
364
|
+
},
|
|
365
|
+
order: [['started_at', 'DESC']],
|
|
366
|
+
raw: true,
|
|
367
|
+
});
|
|
368
|
+
return res.send({ code: 200, data: instances });
|
|
369
|
+
}
|
|
370
|
+
catch (e) {
|
|
371
|
+
return next(e);
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
route.post('/:id/instances/:instanceId/stop', (0, celebrate_1.celebrate)({
|
|
375
|
+
params: celebrate_1.Joi.object({
|
|
376
|
+
id: celebrate_1.Joi.number().required(),
|
|
377
|
+
instanceId: celebrate_1.Joi.number().required(),
|
|
378
|
+
}),
|
|
379
|
+
}), async (req, res, next) => {
|
|
380
|
+
try {
|
|
381
|
+
const cronService = typedi_1.Container.get(cron_1.default);
|
|
382
|
+
const data = await cronService.stopInstance(req.params.instanceId);
|
|
383
|
+
return res.send(data);
|
|
384
|
+
}
|
|
385
|
+
catch (e) {
|
|
386
|
+
return next(e);
|
|
387
|
+
}
|
|
388
|
+
});
|
|
352
389
|
route.get('/:id/logs', (0, celebrate_1.celebrate)({
|
|
353
390
|
params: celebrate_1.Joi.object({
|
|
354
391
|
id: celebrate_1.Joi.number().required(),
|
|
@@ -0,0 +1,337 @@
|
|
|
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 express_1 = require("express");
|
|
7
|
+
const sequelize_1 = require("sequelize");
|
|
8
|
+
const cron_1 = require("../data/cron");
|
|
9
|
+
const cronStats_1 = require("../data/cronStats");
|
|
10
|
+
const runningInstance_1 = require("../data/runningInstance");
|
|
11
|
+
const dayjs_1 = __importDefault(require("dayjs"));
|
|
12
|
+
const os_1 = __importDefault(require("os"));
|
|
13
|
+
const route = (0, express_1.Router)();
|
|
14
|
+
exports.default = (app) => {
|
|
15
|
+
app.use('/dashboard', route);
|
|
16
|
+
route.post('/record', async (req, res) => {
|
|
17
|
+
try {
|
|
18
|
+
const { ref_id, code, elapsed } = req.body;
|
|
19
|
+
if (!ref_id)
|
|
20
|
+
return res.send({ code: 400, message: 'ref_id required' });
|
|
21
|
+
const today = (0, dayjs_1.default)().format('YYYY-MM-DD');
|
|
22
|
+
const isSuccess = code === 0 ? 1 : 0;
|
|
23
|
+
const isFail = code !== 0 ? 1 : 0;
|
|
24
|
+
const elapsedMs = (Number(elapsed) || 0) * 1000;
|
|
25
|
+
const existing = await cronStats_1.CrontabStatModel.findOne({
|
|
26
|
+
where: { ref_id: Number(ref_id), date: today },
|
|
27
|
+
});
|
|
28
|
+
if (existing) {
|
|
29
|
+
await cronStats_1.CrontabStatModel.update({
|
|
30
|
+
run_count: (existing.run_count || 0) + 1,
|
|
31
|
+
success_count: (existing.success_count || 0) + isSuccess,
|
|
32
|
+
fail_count: (existing.fail_count || 0) + isFail,
|
|
33
|
+
total_time: (existing.total_time || 0) + elapsedMs,
|
|
34
|
+
max_time: Math.max(existing.max_time || 0, elapsedMs),
|
|
35
|
+
}, { where: { id: existing.id } });
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
await cronStats_1.CrontabStatModel.create({
|
|
39
|
+
ref_id: Number(ref_id),
|
|
40
|
+
date: today,
|
|
41
|
+
run_count: 1,
|
|
42
|
+
success_count: isSuccess,
|
|
43
|
+
fail_count: isFail,
|
|
44
|
+
total_time: elapsedMs,
|
|
45
|
+
max_time: elapsedMs,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
res.send({ code: 200 });
|
|
49
|
+
}
|
|
50
|
+
catch (e) {
|
|
51
|
+
res.send({ code: 500 });
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
route.get('/overview', async (req, res, next) => {
|
|
55
|
+
try {
|
|
56
|
+
const today = (0, dayjs_1.default)().format('YYYY-MM-DD');
|
|
57
|
+
const [total, enabled, disabled, stats] = await Promise.all([
|
|
58
|
+
cron_1.CrontabModel.count(),
|
|
59
|
+
cron_1.CrontabModel.count({ where: { isDisabled: 0 } }),
|
|
60
|
+
cron_1.CrontabModel.count({ where: { isDisabled: 1 } }),
|
|
61
|
+
cronStats_1.CrontabStatModel.findOne({
|
|
62
|
+
attributes: [
|
|
63
|
+
[(0, sequelize_1.fn)('SUM', (0, sequelize_1.col)('run_count')), 'total_runs'],
|
|
64
|
+
[(0, sequelize_1.fn)('SUM', (0, sequelize_1.col)('success_count')), 'total_success'],
|
|
65
|
+
[(0, sequelize_1.fn)('SUM', (0, sequelize_1.col)('fail_count')), 'total_fail'],
|
|
66
|
+
[(0, sequelize_1.fn)('SUM', (0, sequelize_1.col)('total_time')), 'total_time'],
|
|
67
|
+
],
|
|
68
|
+
where: { date: today },
|
|
69
|
+
raw: true,
|
|
70
|
+
}),
|
|
71
|
+
]);
|
|
72
|
+
const row = stats;
|
|
73
|
+
const totalRuns = Number(row === null || row === void 0 ? void 0 : row.total_runs) || 0;
|
|
74
|
+
const totalSuccess = Number(row === null || row === void 0 ? void 0 : row.total_success) || 0;
|
|
75
|
+
const totalFail = Number(row === null || row === void 0 ? void 0 : row.total_fail) || 0;
|
|
76
|
+
const totalTime = Number(row === null || row === void 0 ? void 0 : row.total_time) || 0;
|
|
77
|
+
res.send({
|
|
78
|
+
code: 200,
|
|
79
|
+
data: {
|
|
80
|
+
total,
|
|
81
|
+
enabled,
|
|
82
|
+
disabled,
|
|
83
|
+
todayRuns: totalRuns,
|
|
84
|
+
todaySuccess: totalSuccess,
|
|
85
|
+
todayFail: totalFail,
|
|
86
|
+
successRate: totalRuns > 0 ? ((totalSuccess / totalRuns) * 100).toFixed(1) : '0',
|
|
87
|
+
avgTime: totalRuns > 0 ? Math.round(totalTime / totalRuns) : 0,
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
catch (e) {
|
|
92
|
+
next(e);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
route.get('/trend', async (req, res, next) => {
|
|
96
|
+
try {
|
|
97
|
+
const days = parseInt(req.query.days) || 7;
|
|
98
|
+
const dates = [];
|
|
99
|
+
for (let i = days - 1; i >= 0; i--) {
|
|
100
|
+
dates.push((0, dayjs_1.default)().subtract(i, 'day').format('YYYY-MM-DD'));
|
|
101
|
+
}
|
|
102
|
+
const rows = (await cronStats_1.CrontabStatModel.findAll({
|
|
103
|
+
attributes: [
|
|
104
|
+
'date',
|
|
105
|
+
[(0, sequelize_1.fn)('SUM', (0, sequelize_1.col)('run_count')), 'total_runs'],
|
|
106
|
+
[(0, sequelize_1.fn)('SUM', (0, sequelize_1.col)('success_count')), 'total_success'],
|
|
107
|
+
[(0, sequelize_1.fn)('SUM', (0, sequelize_1.col)('fail_count')), 'total_fail'],
|
|
108
|
+
],
|
|
109
|
+
where: {
|
|
110
|
+
date: { [sequelize_1.Op.in]: dates },
|
|
111
|
+
},
|
|
112
|
+
group: ['date'],
|
|
113
|
+
order: [['date', 'ASC']],
|
|
114
|
+
raw: true,
|
|
115
|
+
}));
|
|
116
|
+
const dataMap = {};
|
|
117
|
+
rows.forEach((r) => {
|
|
118
|
+
dataMap[r.date] = {
|
|
119
|
+
total: Number(r.total_runs) || 0,
|
|
120
|
+
success: Number(r.total_success) || 0,
|
|
121
|
+
fail: Number(r.total_fail) || 0,
|
|
122
|
+
};
|
|
123
|
+
});
|
|
124
|
+
const data = dates.map((d) => (Object.assign({ date: (0, dayjs_1.default)(d).format('MM-DD') }, (dataMap[d] || { total: 0, success: 0, fail: 0 }))));
|
|
125
|
+
res.send({ code: 200, data });
|
|
126
|
+
}
|
|
127
|
+
catch (e) {
|
|
128
|
+
next(e);
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
route.get('/top-time', async (req, res, next) => {
|
|
132
|
+
try {
|
|
133
|
+
const today = (0, dayjs_1.default)().format('YYYY-MM-DD');
|
|
134
|
+
const rows = (await cronStats_1.CrontabStatModel.findAll({
|
|
135
|
+
attributes: [
|
|
136
|
+
'ref_id',
|
|
137
|
+
[(0, sequelize_1.fn)('SUM', (0, sequelize_1.col)('total_time')), 'total_time'],
|
|
138
|
+
[(0, sequelize_1.fn)('SUM', (0, sequelize_1.col)('run_count')), 'run_count'],
|
|
139
|
+
[(0, sequelize_1.fn)('MAX', (0, sequelize_1.col)('max_time')), 'max_time'],
|
|
140
|
+
],
|
|
141
|
+
where: { date: today, run_count: { [sequelize_1.Op.gt]: 0 } },
|
|
142
|
+
group: ['ref_id'],
|
|
143
|
+
order: [[(0, sequelize_1.fn)('SUM', (0, sequelize_1.col)('total_time')), 'DESC']],
|
|
144
|
+
limit: 5,
|
|
145
|
+
raw: true,
|
|
146
|
+
}));
|
|
147
|
+
const ids = rows.map((r) => Number(r.ref_id));
|
|
148
|
+
const crons = await cron_1.CrontabModel.findAll({
|
|
149
|
+
where: { id: { [sequelize_1.Op.in]: ids } },
|
|
150
|
+
raw: true,
|
|
151
|
+
});
|
|
152
|
+
const nameMap = {};
|
|
153
|
+
crons.forEach((c) => { nameMap[c.id] = c.name || c.command; });
|
|
154
|
+
const data = rows.map((r, i) => ({
|
|
155
|
+
rank: i + 1,
|
|
156
|
+
name: nameMap[Number(r.ref_id)] || `任务#${r.ref_id}`,
|
|
157
|
+
avgTime: Math.round(Number(r.total_time) / Number(r.run_count)),
|
|
158
|
+
maxTime: Number(r.max_time),
|
|
159
|
+
}));
|
|
160
|
+
res.send({ code: 200, data });
|
|
161
|
+
}
|
|
162
|
+
catch (e) {
|
|
163
|
+
next(e);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
route.get('/top-count', async (req, res, next) => {
|
|
167
|
+
try {
|
|
168
|
+
const today = (0, dayjs_1.default)().format('YYYY-MM-DD');
|
|
169
|
+
const rows = (await cronStats_1.CrontabStatModel.findAll({
|
|
170
|
+
attributes: [
|
|
171
|
+
'ref_id',
|
|
172
|
+
[(0, sequelize_1.fn)('SUM', (0, sequelize_1.col)('run_count')), 'run_count'],
|
|
173
|
+
[(0, sequelize_1.fn)('SUM', (0, sequelize_1.col)('total_time')), 'total_time'],
|
|
174
|
+
[(0, sequelize_1.fn)('SUM', (0, sequelize_1.col)('success_count')), 'success_count'],
|
|
175
|
+
],
|
|
176
|
+
where: { date: today, run_count: { [sequelize_1.Op.gt]: 0 } },
|
|
177
|
+
group: ['ref_id'],
|
|
178
|
+
order: [[(0, sequelize_1.fn)('SUM', (0, sequelize_1.col)('run_count')), 'DESC']],
|
|
179
|
+
limit: 5,
|
|
180
|
+
raw: true,
|
|
181
|
+
}));
|
|
182
|
+
const ids = rows.map((r) => Number(r.ref_id));
|
|
183
|
+
const crons = await cron_1.CrontabModel.findAll({
|
|
184
|
+
where: { id: { [sequelize_1.Op.in]: ids } },
|
|
185
|
+
raw: true,
|
|
186
|
+
});
|
|
187
|
+
const nameMap = {};
|
|
188
|
+
crons.forEach((c) => { nameMap[c.id] = c.name || c.command; });
|
|
189
|
+
const data = rows.map((r, i) => ({
|
|
190
|
+
rank: i + 1,
|
|
191
|
+
name: nameMap[Number(r.ref_id)] || `任务#${r.ref_id}`,
|
|
192
|
+
runCount: Number(r.run_count),
|
|
193
|
+
avgTime: Math.round(Number(r.total_time) / Number(r.run_count)),
|
|
194
|
+
successRate: Number(r.run_count) > 0
|
|
195
|
+
? ((Number(r.success_count) / Number(r.run_count)) * 100).toFixed(1)
|
|
196
|
+
: '0',
|
|
197
|
+
}));
|
|
198
|
+
res.send({ code: 200, data });
|
|
199
|
+
}
|
|
200
|
+
catch (e) {
|
|
201
|
+
next(e);
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
route.get('/runtime', async (req, res, next) => {
|
|
205
|
+
try {
|
|
206
|
+
const runningInstances = await runningInstance_1.RunningInstanceModel.findAll({
|
|
207
|
+
where: {
|
|
208
|
+
status: runningInstance_1.InstanceStatus.running,
|
|
209
|
+
},
|
|
210
|
+
raw: true,
|
|
211
|
+
});
|
|
212
|
+
const queuedCrons = await cron_1.CrontabModel.findAll({
|
|
213
|
+
where: {
|
|
214
|
+
status: 3, // queued
|
|
215
|
+
},
|
|
216
|
+
raw: true,
|
|
217
|
+
});
|
|
218
|
+
// Fetch cron names for running instances
|
|
219
|
+
const cronIds = [
|
|
220
|
+
...new Set(runningInstances.map((i) => i.cron_id)),
|
|
221
|
+
];
|
|
222
|
+
const crons = cronIds.length > 0
|
|
223
|
+
? await cron_1.CrontabModel.findAll({
|
|
224
|
+
where: { id: cronIds },
|
|
225
|
+
raw: true,
|
|
226
|
+
})
|
|
227
|
+
: [];
|
|
228
|
+
const cronMap = new Map(crons.map((c) => [c.id, c]));
|
|
229
|
+
const now = (0, dayjs_1.default)().unix();
|
|
230
|
+
const running = runningInstances.map((inst) => {
|
|
231
|
+
const cron = cronMap.get(inst.cron_id);
|
|
232
|
+
return {
|
|
233
|
+
instanceId: inst.id,
|
|
234
|
+
id: inst.cron_id,
|
|
235
|
+
name: (cron === null || cron === void 0 ? void 0 : cron.name) || (cron === null || cron === void 0 ? void 0 : cron.command) || `任务#${inst.cron_id}`,
|
|
236
|
+
pid: inst.pid,
|
|
237
|
+
elapsed: inst.started_at ? now - inst.started_at : 0,
|
|
238
|
+
logPath: inst.log_path,
|
|
239
|
+
};
|
|
240
|
+
});
|
|
241
|
+
const dayAgo = (0, dayjs_1.default)().subtract(24, 'hour').unix();
|
|
242
|
+
const idleTasks = await cron_1.CrontabModel.findAll({
|
|
243
|
+
where: {
|
|
244
|
+
isDisabled: 0,
|
|
245
|
+
status: 1,
|
|
246
|
+
last_execution_time: { [sequelize_1.Op.lt]: dayAgo },
|
|
247
|
+
},
|
|
248
|
+
order: [['last_execution_time', 'ASC']],
|
|
249
|
+
limit: 5,
|
|
250
|
+
raw: true,
|
|
251
|
+
});
|
|
252
|
+
res.send({
|
|
253
|
+
code: 200,
|
|
254
|
+
data: {
|
|
255
|
+
runningCount: running.length,
|
|
256
|
+
queuedCount: queuedCrons.length,
|
|
257
|
+
running,
|
|
258
|
+
idleTasks: idleTasks.map((c) => ({
|
|
259
|
+
id: c.id,
|
|
260
|
+
name: c.name || c.command || `任务#${c.id}`,
|
|
261
|
+
lastRun: c.last_execution_time
|
|
262
|
+
? dayjs_1.default.unix(c.last_execution_time).format('MM-DD HH:mm')
|
|
263
|
+
: '-',
|
|
264
|
+
})),
|
|
265
|
+
},
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
catch (e) {
|
|
269
|
+
next(e);
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
route.get('/labels', async (req, res, next) => {
|
|
273
|
+
try {
|
|
274
|
+
const today = (0, dayjs_1.default)().format('YYYY-MM-DD');
|
|
275
|
+
const [crons, stats] = (await Promise.all([
|
|
276
|
+
cron_1.CrontabModel.findAll({ where: { isDisabled: 0 }, raw: true }),
|
|
277
|
+
cronStats_1.CrontabStatModel.findAll({ where: { date: today }, raw: true }),
|
|
278
|
+
]));
|
|
279
|
+
const statMap = {};
|
|
280
|
+
stats.forEach((s) => { statMap[s.ref_id] = s; });
|
|
281
|
+
const labelMap = {};
|
|
282
|
+
crons.forEach((c) => {
|
|
283
|
+
let rawLabels = c.labels;
|
|
284
|
+
if (typeof rawLabels === 'string')
|
|
285
|
+
rawLabels = JSON.parse(rawLabels);
|
|
286
|
+
const labels = Array.isArray(rawLabels) && rawLabels.length > 0 ? rawLabels : ['未分类'];
|
|
287
|
+
const st = statMap[c.id];
|
|
288
|
+
labels.forEach((label) => {
|
|
289
|
+
if (!labelMap[label])
|
|
290
|
+
labelMap[label] = { count: 0, runs: 0, success: 0, totalTime: 0 };
|
|
291
|
+
labelMap[label].count += 1;
|
|
292
|
+
if (st) {
|
|
293
|
+
labelMap[label].runs += Number(st.run_count) || 0;
|
|
294
|
+
labelMap[label].success += Number(st.success_count) || 0;
|
|
295
|
+
labelMap[label].totalTime += Number(st.total_time) || 0;
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
const data = Object.entries(labelMap)
|
|
300
|
+
.map(([label, v]) => ({
|
|
301
|
+
label,
|
|
302
|
+
count: v.count,
|
|
303
|
+
todayRuns: v.runs,
|
|
304
|
+
successRate: v.runs > 0 ? ((v.success / v.runs) * 100).toFixed(1) : '0',
|
|
305
|
+
avgTime: v.runs > 0 ? Math.round(v.totalTime / v.runs) : 0,
|
|
306
|
+
}))
|
|
307
|
+
.sort((a, b) => b.todayRuns - a.todayRuns);
|
|
308
|
+
res.send({ code: 200, data });
|
|
309
|
+
}
|
|
310
|
+
catch (e) {
|
|
311
|
+
next(e);
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
route.get('/system', async (req, res, next) => {
|
|
315
|
+
try {
|
|
316
|
+
const memUsage = process.memoryUsage();
|
|
317
|
+
res.send({
|
|
318
|
+
code: 200,
|
|
319
|
+
data: {
|
|
320
|
+
platform: os_1.default.platform(),
|
|
321
|
+
uptime: Math.floor(os_1.default.uptime()),
|
|
322
|
+
memTotal: os_1.default.totalmem(),
|
|
323
|
+
memFree: os_1.default.freemem(),
|
|
324
|
+
memUsagePercent: ((1 - os_1.default.freemem() / os_1.default.totalmem()) * 100).toFixed(1),
|
|
325
|
+
heapUsed: Math.round(memUsage.heapUsed / 1024 / 1024),
|
|
326
|
+
heapTotal: Math.round(memUsage.heapTotal / 1024 / 1024),
|
|
327
|
+
loadAvg: os_1.default.loadavg().map((v) => Number(v.toFixed(2))),
|
|
328
|
+
cpus: os_1.default.cpus().length,
|
|
329
|
+
},
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
catch (e) {
|
|
333
|
+
next(e);
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
};
|
|
337
|
+
//# sourceMappingURL=dashboard.js.map
|
package/static/build/api/env.js
CHANGED
|
@@ -10,6 +10,7 @@ const multer_1 = __importDefault(require("multer"));
|
|
|
10
10
|
const typedi_1 = require("typedi");
|
|
11
11
|
const config_1 = __importDefault(require("../config"));
|
|
12
12
|
const util_1 = require("../config/util");
|
|
13
|
+
const i18n_1 = require("../shared/i18n");
|
|
13
14
|
const env_1 = __importDefault(require("../services/env"));
|
|
14
15
|
const route = (0, express_1.Router)();
|
|
15
16
|
const storage = multer_1.default.diskStorage({
|
|
@@ -21,6 +22,10 @@ const storage = multer_1.default.diskStorage({
|
|
|
21
22
|
},
|
|
22
23
|
});
|
|
23
24
|
const upload = (0, multer_1.default)({ storage: storage });
|
|
25
|
+
const labelSchema = celebrate_1.Joi.array()
|
|
26
|
+
.items(celebrate_1.Joi.string().trim().required())
|
|
27
|
+
.min(1)
|
|
28
|
+
.required();
|
|
24
29
|
exports.default = (app) => {
|
|
25
30
|
app.use('/envs', route);
|
|
26
31
|
route.get('/', async (req, res, next) => {
|
|
@@ -42,6 +47,7 @@ exports.default = (app) => {
|
|
|
42
47
|
.required()
|
|
43
48
|
.pattern(/^[a-zA-Z_][0-9a-zA-Z_]*$/),
|
|
44
49
|
remarks: celebrate_1.Joi.string().optional().allow(''),
|
|
50
|
+
labels: celebrate_1.Joi.array().items(celebrate_1.Joi.string().trim()).optional(),
|
|
45
51
|
})),
|
|
46
52
|
}), async (req, res, next) => {
|
|
47
53
|
var _a;
|
|
@@ -49,7 +55,7 @@ exports.default = (app) => {
|
|
|
49
55
|
try {
|
|
50
56
|
const envService = typedi_1.Container.get(env_1.default);
|
|
51
57
|
if (!((_a = req.body) === null || _a === void 0 ? void 0 : _a.length)) {
|
|
52
|
-
return res.send({ code: 400, message: '参数不正确' });
|
|
58
|
+
return res.send({ code: 400, message: (0, i18n_1.t)('参数不正确') });
|
|
53
59
|
}
|
|
54
60
|
const data = await envService.create(req.body);
|
|
55
61
|
return res.send({ code: 200, data });
|
|
@@ -64,6 +70,7 @@ exports.default = (app) => {
|
|
|
64
70
|
name: celebrate_1.Joi.string().required(),
|
|
65
71
|
remarks: celebrate_1.Joi.string().optional().allow('').allow(null),
|
|
66
72
|
id: celebrate_1.Joi.number().required(),
|
|
73
|
+
labels: celebrate_1.Joi.array().items(celebrate_1.Joi.string().trim()).optional(),
|
|
67
74
|
}),
|
|
68
75
|
}), async (req, res, next) => {
|
|
69
76
|
const logger = typedi_1.Container.get('logger');
|
|
@@ -190,6 +197,36 @@ exports.default = (app) => {
|
|
|
190
197
|
return next(e);
|
|
191
198
|
}
|
|
192
199
|
});
|
|
200
|
+
route.post('/labels', (0, celebrate_1.celebrate)({
|
|
201
|
+
body: celebrate_1.Joi.object({
|
|
202
|
+
ids: celebrate_1.Joi.array().items(celebrate_1.Joi.number().required()).min(1).required(),
|
|
203
|
+
labels: labelSchema,
|
|
204
|
+
}),
|
|
205
|
+
}), async (req, res, next) => {
|
|
206
|
+
try {
|
|
207
|
+
const envService = typedi_1.Container.get(env_1.default);
|
|
208
|
+
const data = await envService.addLabels(req.body.ids, req.body.labels);
|
|
209
|
+
return res.send({ code: 200, data });
|
|
210
|
+
}
|
|
211
|
+
catch (e) {
|
|
212
|
+
return next(e);
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
route.delete('/labels', (0, celebrate_1.celebrate)({
|
|
216
|
+
body: celebrate_1.Joi.object({
|
|
217
|
+
ids: celebrate_1.Joi.array().items(celebrate_1.Joi.number().required()).min(1).required(),
|
|
218
|
+
labels: labelSchema,
|
|
219
|
+
}),
|
|
220
|
+
}), async (req, res, next) => {
|
|
221
|
+
try {
|
|
222
|
+
const envService = typedi_1.Container.get(env_1.default);
|
|
223
|
+
const data = await envService.removeLabels(req.body.ids, req.body.labels);
|
|
224
|
+
return res.send({ code: 200, data });
|
|
225
|
+
}
|
|
226
|
+
catch (e) {
|
|
227
|
+
return next(e);
|
|
228
|
+
}
|
|
229
|
+
});
|
|
193
230
|
route.post('/upload', upload.single('env'), async (req, res, next) => {
|
|
194
231
|
const logger = typedi_1.Container.get('logger');
|
|
195
232
|
try {
|
|
@@ -204,13 +241,14 @@ exports.default = (app) => {
|
|
|
204
241
|
name: x.name,
|
|
205
242
|
value: x.value,
|
|
206
243
|
remarks: x.remarks,
|
|
244
|
+
labels: x.labels,
|
|
207
245
|
})));
|
|
208
246
|
return res.send({ code: 200, data: result });
|
|
209
247
|
}
|
|
210
248
|
else {
|
|
211
249
|
return res.send({
|
|
212
250
|
code: 400,
|
|
213
|
-
message: '每条数据 name 或者 value 字段不能为空,参考导出文件格式',
|
|
251
|
+
message: (0, i18n_1.t)('每条数据 name 或者 value 字段不能为空,参考导出文件格式'),
|
|
214
252
|
});
|
|
215
253
|
}
|
|
216
254
|
}
|
|
@@ -15,6 +15,7 @@ const dependence_1 = __importDefault(require("./dependence"));
|
|
|
15
15
|
const system_1 = __importDefault(require("./system"));
|
|
16
16
|
const subscription_1 = __importDefault(require("./subscription"));
|
|
17
17
|
const update_1 = __importDefault(require("./update"));
|
|
18
|
+
const dashboard_1 = __importDefault(require("./dashboard"));
|
|
18
19
|
const health_1 = __importDefault(require("./health"));
|
|
19
20
|
exports.default = () => {
|
|
20
21
|
const app = (0, express_1.Router)();
|
|
@@ -29,6 +30,7 @@ exports.default = () => {
|
|
|
29
30
|
(0, system_1.default)(app);
|
|
30
31
|
(0, subscription_1.default)(app);
|
|
31
32
|
(0, update_1.default)(app);
|
|
33
|
+
(0, dashboard_1.default)(app);
|
|
32
34
|
(0, health_1.default)(app);
|
|
33
35
|
return app;
|
|
34
36
|
};
|
package/static/build/api/log.js
CHANGED
|
@@ -7,6 +7,7 @@ const celebrate_1 = require("celebrate");
|
|
|
7
7
|
const express_1 = require("express");
|
|
8
8
|
const typedi_1 = require("typedi");
|
|
9
9
|
const config_1 = __importDefault(require("../config"));
|
|
10
|
+
const i18n_1 = require("../shared/i18n");
|
|
10
11
|
const util_1 = require("../config/util");
|
|
11
12
|
const log_1 = __importDefault(require("../services/log"));
|
|
12
13
|
const route = (0, express_1.Router)();
|
|
@@ -34,7 +35,7 @@ exports.default = (app) => {
|
|
|
34
35
|
if (!finalPath || blacklist.includes(req.query.path)) {
|
|
35
36
|
return res.send({
|
|
36
37
|
code: 403,
|
|
37
|
-
message: '暂无权限',
|
|
38
|
+
message: (0, i18n_1.t)('暂无权限'),
|
|
38
39
|
});
|
|
39
40
|
}
|
|
40
41
|
const content = await (0, util_1.getFileContentByName)(finalPath);
|
|
@@ -51,7 +52,7 @@ exports.default = (app) => {
|
|
|
51
52
|
if (!finalPath || blacklist.includes(req.query.path)) {
|
|
52
53
|
return res.send({
|
|
53
54
|
code: 403,
|
|
54
|
-
message: '暂无权限',
|
|
55
|
+
message: (0, i18n_1.t)('暂无权限'),
|
|
55
56
|
});
|
|
56
57
|
}
|
|
57
58
|
const content = await (0, util_1.getFileContentByName)(finalPath);
|
|
@@ -75,7 +76,7 @@ exports.default = (app) => {
|
|
|
75
76
|
if (!finalPath || blacklist.includes(path)) {
|
|
76
77
|
return res.send({
|
|
77
78
|
code: 403,
|
|
78
|
-
message: '暂无权限',
|
|
79
|
+
message: (0, i18n_1.t)('暂无权限'),
|
|
79
80
|
});
|
|
80
81
|
}
|
|
81
82
|
await (0, util_1.rmPath)(finalPath);
|
|
@@ -98,7 +99,7 @@ exports.default = (app) => {
|
|
|
98
99
|
if (!filePath) {
|
|
99
100
|
return res.send({
|
|
100
101
|
code: 403,
|
|
101
|
-
message: '暂无权限',
|
|
102
|
+
message: (0, i18n_1.t)('暂无权限'),
|
|
102
103
|
});
|
|
103
104
|
}
|
|
104
105
|
return res.download(filePath, filename, (err) => {
|
|
@@ -32,11 +32,16 @@ const typedi_1 = require("typedi");
|
|
|
32
32
|
const config_1 = __importDefault(require("../config"));
|
|
33
33
|
const fs = __importStar(require("fs/promises"));
|
|
34
34
|
const celebrate_1 = require("celebrate");
|
|
35
|
-
const path_1 = require("path");
|
|
35
|
+
const path_1 = __importStar(require("path"));
|
|
36
36
|
const script_1 = __importDefault(require("../services/script"));
|
|
37
|
+
const i18n_1 = require("../shared/i18n");
|
|
37
38
|
const multer_1 = __importDefault(require("multer"));
|
|
38
39
|
const utils_1 = require("../shared/utils");
|
|
39
40
|
const route = (0, express_1.Router)();
|
|
41
|
+
function isPathAllowed(targetPath) {
|
|
42
|
+
const resolved = path_1.default.resolve(targetPath);
|
|
43
|
+
return config_1.default.writePathList.some((x) => resolved.startsWith(x));
|
|
44
|
+
}
|
|
40
45
|
const storage = multer_1.default.diskStorage({
|
|
41
46
|
destination: function (req, file, cb) {
|
|
42
47
|
cb(null, config_1.default.scriptPath);
|
|
@@ -145,23 +150,34 @@ exports.default = (app) => {
|
|
|
145
150
|
if (config_1.default.writePathList.every((x) => !path.startsWith(x))) {
|
|
146
151
|
return res.send({
|
|
147
152
|
code: 403,
|
|
148
|
-
message: '暂无权限',
|
|
153
|
+
message: (0, i18n_1.t)('暂无权限'),
|
|
149
154
|
});
|
|
150
155
|
}
|
|
151
156
|
if (req.file) {
|
|
152
|
-
|
|
157
|
+
const uploadPath = (0, path_1.join)(path, filename);
|
|
158
|
+
if (!isPathAllowed(uploadPath)) {
|
|
159
|
+
return res.send({ code: 403, message: (0, i18n_1.t)('暂无权限') });
|
|
160
|
+
}
|
|
161
|
+
await fs.rename(req.file.path, uploadPath);
|
|
153
162
|
return res.send({ code: 200 });
|
|
154
163
|
}
|
|
155
164
|
if (directory) {
|
|
156
|
-
|
|
165
|
+
const dirPath = (0, path_1.join)(path, directory);
|
|
166
|
+
if (!isPathAllowed(dirPath)) {
|
|
167
|
+
return res.send({ code: 403, message: (0, i18n_1.t)('暂无权限') });
|
|
168
|
+
}
|
|
169
|
+
await fs.mkdir(dirPath, { recursive: true });
|
|
157
170
|
return res.send({ code: 200 });
|
|
158
171
|
}
|
|
159
172
|
if (!originFilename) {
|
|
160
173
|
originFilename = filename;
|
|
161
174
|
}
|
|
162
|
-
const originFilePath = (0, path_1.join)(path,
|
|
175
|
+
const originFilePath = (0, path_1.join)(path, originFilename);
|
|
176
|
+
const filePath = (0, path_1.join)(path, filename);
|
|
177
|
+
if (!isPathAllowed(filePath) || !isPathAllowed(originFilePath)) {
|
|
178
|
+
return res.send({ code: 403, message: (0, i18n_1.t)('暂无权限') });
|
|
179
|
+
}
|
|
163
180
|
await fs.mkdir(path, { recursive: true });
|
|
164
|
-
const filePath = (0, path_1.join)(path, `${filename.replace(/\//g, '')}`);
|
|
165
181
|
const fileExists = await (0, util_1.fileExist)(filePath);
|
|
166
182
|
if (fileExists) {
|
|
167
183
|
await fs.copyFile(originFilePath, (0, path_1.join)(config_1.default.bakPath, originFilename.replace(/\//g, '')));
|
|
@@ -190,7 +206,7 @@ exports.default = (app) => {
|
|
|
190
206
|
if (!filePath) {
|
|
191
207
|
return res.send({
|
|
192
208
|
code: 403,
|
|
193
|
-
message: '暂无权限',
|
|
209
|
+
message: (0, i18n_1.t)('暂无权限'),
|
|
194
210
|
});
|
|
195
211
|
}
|
|
196
212
|
await (0, utils_1.writeFileWithLock)(filePath, content);
|
|
@@ -217,7 +233,7 @@ exports.default = (app) => {
|
|
|
217
233
|
if (!filePath) {
|
|
218
234
|
return res.send({
|
|
219
235
|
code: 403,
|
|
220
|
-
message: '暂无权限',
|
|
236
|
+
message: (0, i18n_1.t)('暂无权限'),
|
|
221
237
|
});
|
|
222
238
|
}
|
|
223
239
|
await (0, util_1.rmPath)(filePath);
|
|
@@ -243,7 +259,7 @@ exports.default = (app) => {
|
|
|
243
259
|
if (!filePath) {
|
|
244
260
|
return res.send({
|
|
245
261
|
code: 403,
|
|
246
|
-
message: '暂无权限',
|
|
262
|
+
message: (0, i18n_1.t)('暂无权限'),
|
|
247
263
|
});
|
|
248
264
|
}
|
|
249
265
|
return res.download(filePath, filename, (err) => {
|
|
@@ -271,6 +287,9 @@ exports.default = (app) => {
|
|
|
271
287
|
}
|
|
272
288
|
const { name, ext } = (0, path_1.parse)(filename);
|
|
273
289
|
const filePath = (0, path_1.join)(config_1.default.scriptPath, path, `${name}.swap${ext}`);
|
|
290
|
+
if (!isPathAllowed(filePath)) {
|
|
291
|
+
return res.send({ code: 403, message: (0, i18n_1.t)('暂无权限') });
|
|
292
|
+
}
|
|
274
293
|
await (0, utils_1.writeFileWithLock)(filePath, content || '');
|
|
275
294
|
const scriptService = typedi_1.Container.get(script_1.default);
|
|
276
295
|
const result = await scriptService.runScript(filePath);
|
|
@@ -294,6 +313,9 @@ exports.default = (app) => {
|
|
|
294
313
|
}
|
|
295
314
|
const { name, ext } = (0, path_1.parse)(filename);
|
|
296
315
|
const filePath = (0, path_1.join)(config_1.default.scriptPath, path, `${name}.swap${ext}`);
|
|
316
|
+
if (!isPathAllowed(filePath)) {
|
|
317
|
+
return res.send({ code: 403, message: (0, i18n_1.t)('暂无权限') });
|
|
318
|
+
}
|
|
297
319
|
const logPath = (0, path_1.join)(config_1.default.logPath, path, `${name}.swap`);
|
|
298
320
|
const scriptService = typedi_1.Container.get(script_1.default);
|
|
299
321
|
const result = await scriptService.stopScript(filePath, pid);
|
|
@@ -320,6 +342,9 @@ exports.default = (app) => {
|
|
|
320
342
|
}
|
|
321
343
|
const filePath = (0, path_1.join)(config_1.default.scriptPath, path, filename);
|
|
322
344
|
const newPath = (0, path_1.join)(config_1.default.scriptPath, path, newFilename);
|
|
345
|
+
if (!isPathAllowed(filePath) || !isPathAllowed(newPath)) {
|
|
346
|
+
return res.send({ code: 403, message: (0, i18n_1.t)('暂无权限') });
|
|
347
|
+
}
|
|
323
348
|
await fs.rename(filePath, newPath);
|
|
324
349
|
res.send({ code: 200 });
|
|
325
350
|
}
|