@whyour/qinglong 2.20.2-3 → 2.21.0-2
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 +117 -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 +40 -3
- package/static/build/api/dashboard.js +344 -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 -4
- package/static/build/config/util.js +69 -18
- 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/data/sock.js +1 -0
- package/static/build/loaders/app.js +1 -1
- package/static/build/loaders/db.js +6 -0
- package/static/build/loaders/deps.js +1 -22
- 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/loaders/initTask.js +3 -2
- 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 +126 -13
- package/static/build/services/dependence.js +19 -18
- 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 +45 -8
- 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 +34 -9
- package/static/build/services/user.js +36 -13
- package/static/build/shared/i18n.js +145 -0
- package/static/build/shared/pLimit.js +15 -3
- 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.6879b271.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.c1c91aaa.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.cf4a6aaf.async.js +1 -0
- package/static/dist/src__pages__crontab__index.5b67b1a2.async.js +1 -0
- package/static/dist/{src__pages__crontab__logModal.5e6a4bf2.async.js → src__pages__crontab__logModal.831d5e4b.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.58177357.async.js +1 -0
- package/static/dist/src__pages__dependence__index.b7bbc795.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.9a565d2b.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.9c66639f.async.js +1 -0
- 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.e869812f.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__checkUpdate.34d773f2.async.js +1 -0
- package/static/dist/src__pages__setting__dependence.29f13e8c.async.js +1 -0
- package/static/dist/src__pages__setting__index.7a39d415.async.js +1 -0
- package/static/dist/src__pages__setting__notification.390fc905.async.js +1 -0
- package/static/dist/src__pages__setting__other.707cfafe.async.js +1 -0
- package/static/dist/src__pages__setting__security.598720a8.async.js +1 -0
- package/static/dist/src__pages__setting__systemLog.1a5c855f.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.97fb5a40.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__index.3d87ceac.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__log__index.cf00c9af.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__checkUpdate.46efe057.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
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RunningInstanceModel = exports.RunningInstance = exports.InstanceStatus = void 0;
|
|
4
|
+
const _1 = require(".");
|
|
5
|
+
const sequelize_1 = require("sequelize");
|
|
6
|
+
var InstanceStatus;
|
|
7
|
+
(function (InstanceStatus) {
|
|
8
|
+
InstanceStatus[InstanceStatus["running"] = 0] = "running";
|
|
9
|
+
InstanceStatus[InstanceStatus["finished"] = 1] = "finished";
|
|
10
|
+
InstanceStatus[InstanceStatus["stopped"] = 2] = "stopped";
|
|
11
|
+
InstanceStatus[InstanceStatus["error"] = 3] = "error";
|
|
12
|
+
})(InstanceStatus || (exports.InstanceStatus = InstanceStatus = {}));
|
|
13
|
+
class RunningInstance {
|
|
14
|
+
constructor(options) {
|
|
15
|
+
this.id = options.id;
|
|
16
|
+
this.cron_id = options.cron_id;
|
|
17
|
+
this.pid = options.pid;
|
|
18
|
+
this.log_path = options.log_path;
|
|
19
|
+
this.started_at = options.started_at;
|
|
20
|
+
this.finished_at = options.finished_at;
|
|
21
|
+
this.status = options.status;
|
|
22
|
+
this.exit_code = options.exit_code;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.RunningInstance = RunningInstance;
|
|
26
|
+
exports.RunningInstanceModel = _1.sequelize.define('RunningInstance', {
|
|
27
|
+
cron_id: {
|
|
28
|
+
type: sequelize_1.DataTypes.NUMBER,
|
|
29
|
+
allowNull: false,
|
|
30
|
+
},
|
|
31
|
+
pid: {
|
|
32
|
+
type: sequelize_1.DataTypes.NUMBER,
|
|
33
|
+
allowNull: true,
|
|
34
|
+
},
|
|
35
|
+
log_path: {
|
|
36
|
+
type: sequelize_1.DataTypes.STRING,
|
|
37
|
+
allowNull: true,
|
|
38
|
+
},
|
|
39
|
+
started_at: {
|
|
40
|
+
type: sequelize_1.DataTypes.NUMBER,
|
|
41
|
+
allowNull: false,
|
|
42
|
+
},
|
|
43
|
+
finished_at: {
|
|
44
|
+
type: sequelize_1.DataTypes.NUMBER,
|
|
45
|
+
allowNull: true,
|
|
46
|
+
},
|
|
47
|
+
status: {
|
|
48
|
+
type: sequelize_1.DataTypes.NUMBER,
|
|
49
|
+
allowNull: false,
|
|
50
|
+
defaultValue: InstanceStatus.running,
|
|
51
|
+
},
|
|
52
|
+
exit_code: {
|
|
53
|
+
type: sequelize_1.DataTypes.NUMBER,
|
|
54
|
+
allowNull: true,
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
//# sourceMappingURL=runningInstance.js.map
|
|
@@ -15,7 +15,7 @@ exports.default = async ({ app }) => {
|
|
|
15
15
|
logger_1.default.info('✌️ Dependency loaded');
|
|
16
16
|
await (0, deps_1.default)();
|
|
17
17
|
logger_1.default.info('✌️ Link deps loaded');
|
|
18
|
-
(0, initFile_1.default)();
|
|
18
|
+
await (0, initFile_1.default)();
|
|
19
19
|
logger_1.default.info('✌️ Init file loaded');
|
|
20
20
|
await (0, initData_1.default)();
|
|
21
21
|
logger_1.default.info('✌️ Init data loaded');
|
|
@@ -11,6 +11,8 @@ const open_1 = require("../data/open");
|
|
|
11
11
|
const system_1 = require("../data/system");
|
|
12
12
|
const subscription_1 = require("../data/subscription");
|
|
13
13
|
const cronView_1 = require("../data/cronView");
|
|
14
|
+
const cronStats_1 = require("../data/cronStats");
|
|
15
|
+
const runningInstance_1 = require("../data/runningInstance");
|
|
14
16
|
const data_1 = require("../data");
|
|
15
17
|
exports.default = async () => {
|
|
16
18
|
try {
|
|
@@ -21,6 +23,8 @@ exports.default = async () => {
|
|
|
21
23
|
await env_1.EnvModel.sync();
|
|
22
24
|
await subscription_1.SubscriptionModel.sync();
|
|
23
25
|
await cronView_1.CrontabViewModel.sync();
|
|
26
|
+
await cronStats_1.CrontabStatModel.sync();
|
|
27
|
+
await runningInstance_1.RunningInstanceModel.sync();
|
|
24
28
|
// 初始化新增字段
|
|
25
29
|
const migrations = [
|
|
26
30
|
{
|
|
@@ -42,7 +46,9 @@ exports.default = async () => {
|
|
|
42
46
|
column: 'allow_multiple_instances',
|
|
43
47
|
type: 'NUMBER',
|
|
44
48
|
},
|
|
49
|
+
{ table: 'Crontabs', column: 'work_dir', type: 'VARCHAR(255)' },
|
|
45
50
|
{ table: 'Envs', column: 'isPinned', type: 'NUMBER' },
|
|
51
|
+
{ table: 'Envs', column: 'labels', type: 'JSON' },
|
|
46
52
|
];
|
|
47
53
|
for (const migration of migrations) {
|
|
48
54
|
try {
|
|
@@ -6,20 +6,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
const path_1 = __importDefault(require("path"));
|
|
7
7
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
8
8
|
const os_1 = __importDefault(require("os"));
|
|
9
|
-
const chokidar_1 = __importDefault(require("chokidar"));
|
|
10
9
|
const index_1 = __importDefault(require("../config/index"));
|
|
11
10
|
const logger_1 = __importDefault(require("./logger"));
|
|
12
|
-
async function linkToNodeModule(src, dst) {
|
|
13
|
-
const target = path_1.default.join(index_1.default.rootPath, 'node_modules', dst || src);
|
|
14
|
-
const source = path_1.default.join(index_1.default.rootPath, src);
|
|
15
|
-
try {
|
|
16
|
-
const stats = await promises_1.default.lstat(target);
|
|
17
|
-
if (!stats) {
|
|
18
|
-
await promises_1.default.symlink(source, target, 'dir');
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
catch (error) { }
|
|
22
|
-
}
|
|
23
11
|
async function linkCommand() {
|
|
24
12
|
const homeDir = os_1.default.homedir();
|
|
25
13
|
let userBinDir = path_1.default.join(homeDir, 'bin');
|
|
@@ -59,16 +47,7 @@ async function linkCommandToDir(commandDir) {
|
|
|
59
47
|
await promises_1.default.rename(tmpTarget, target);
|
|
60
48
|
}
|
|
61
49
|
}
|
|
62
|
-
exports.default = async (
|
|
50
|
+
exports.default = async () => {
|
|
63
51
|
await linkCommand();
|
|
64
|
-
await linkToNodeModule(src);
|
|
65
|
-
const source = path_1.default.join(index_1.default.rootPath, src);
|
|
66
|
-
const watcher = chokidar_1.default.watch(source, {
|
|
67
|
-
ignored: /(^|[\/\\])\../,
|
|
68
|
-
persistent: true,
|
|
69
|
-
});
|
|
70
|
-
watcher
|
|
71
|
-
.on('add', () => linkToNodeModule(src))
|
|
72
|
-
.on('change', () => linkToNodeModule(src));
|
|
73
52
|
};
|
|
74
53
|
//# sourceMappingURL=deps.js.map
|
|
@@ -16,6 +16,7 @@ const serverEnv_1 = require("../config/serverEnv");
|
|
|
16
16
|
const store_1 = require("../shared/store");
|
|
17
17
|
const auth_1 = require("../shared/auth");
|
|
18
18
|
const path_1 = __importDefault(require("path"));
|
|
19
|
+
const i18n_1 = require("../shared/i18n");
|
|
19
20
|
exports.default = ({ app }) => {
|
|
20
21
|
// Security: Enable strict routing to prevent case-insensitive path bypass
|
|
21
22
|
app.set('case sensitive routing', true);
|
|
@@ -119,7 +120,7 @@ exports.default = ({ app }) => {
|
|
|
119
120
|
isInitialized = false;
|
|
120
121
|
}
|
|
121
122
|
if (isInitialized) {
|
|
122
|
-
return res.send({ code: 450, message: '未知错误' });
|
|
123
|
+
return res.send({ code: 450, message: (0, i18n_1.t)('未知错误') });
|
|
123
124
|
}
|
|
124
125
|
else {
|
|
125
126
|
return next();
|
|
@@ -46,8 +46,10 @@ const open_1 = __importDefault(require("../services/open"));
|
|
|
46
46
|
const store_1 = require("../shared/store");
|
|
47
47
|
const logger_1 = __importDefault(require("./logger"));
|
|
48
48
|
const open_2 = require("../data/open");
|
|
49
|
+
const runningInstance_1 = require("../data/runningInstance");
|
|
50
|
+
const i18n_1 = require("../shared/i18n");
|
|
49
51
|
exports.default = async () => {
|
|
50
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
52
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
51
53
|
const cronService = typedi_1.Container.get(cron_2.default);
|
|
52
54
|
const envService = typedi_1.Container.get(env_1.default);
|
|
53
55
|
const dependenceService = typedi_1.Container.get(dependence_1.default);
|
|
@@ -61,11 +63,14 @@ exports.default = async () => {
|
|
|
61
63
|
if (!systemApp) {
|
|
62
64
|
systemApp = await open_2.AppModel.create({
|
|
63
65
|
name: 'system',
|
|
64
|
-
scopes: ['crons', 'system'],
|
|
66
|
+
scopes: ['crons', 'system', 'dashboard'],
|
|
65
67
|
client_id: (0, util_1.createRandomString)(12, 12),
|
|
66
68
|
client_secret: (0, util_1.createRandomString)(24, 24),
|
|
67
69
|
});
|
|
68
70
|
}
|
|
71
|
+
else if (!systemApp.scopes.includes('dashboard')) {
|
|
72
|
+
await open_2.AppModel.update({ scopes: [...systemApp.scopes, 'dashboard'] }, { where: { name: 'system' } });
|
|
73
|
+
}
|
|
69
74
|
const [systemConfig] = await system_1.SystemModel.findOrCreate({
|
|
70
75
|
where: { type: system_1.AuthDataType.systemConfig },
|
|
71
76
|
});
|
|
@@ -146,6 +151,8 @@ exports.default = async () => {
|
|
|
146
151
|
});
|
|
147
152
|
// 初始化更新所有任务状态为空闲
|
|
148
153
|
await cron_1.CrontabModel.update({ status: cron_1.CrontabStatus.idle }, { where: {} });
|
|
154
|
+
// 清空所有运行中的实例记录(服务重启后进程已不存在)
|
|
155
|
+
await runningInstance_1.RunningInstanceModel.update({ status: runningInstance_1.InstanceStatus.stopped }, { where: { status: runningInstance_1.InstanceStatus.running } });
|
|
149
156
|
// 初始化时执行一次所有的 ql repo 任务
|
|
150
157
|
cron_1.CrontabModel.findAll({
|
|
151
158
|
where: {
|
|
@@ -201,6 +208,16 @@ exports.default = async () => {
|
|
|
201
208
|
});
|
|
202
209
|
// 初始化保存一次ck和定时任务数据
|
|
203
210
|
await cronService.autosave_crontab();
|
|
211
|
+
// 确保 lang_env.sh 存在,提供默认 QL_LANG
|
|
212
|
+
try {
|
|
213
|
+
const langEnvExist = await (0, util_1.fileExist)(config_1.default.langEnvFile);
|
|
214
|
+
if (!langEnvExist) {
|
|
215
|
+
const lang = ((_h = systemConfig.info) === null || _h === void 0 ? void 0 : _h.lang) || 'zh';
|
|
216
|
+
await (0, promises_1.writeFile)(config_1.default.langEnvFile, `export QL_LANG='${lang}'\n`);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
catch (_k) { }
|
|
220
|
+
(0, i18n_1.setLang)(((_j = systemConfig.info) === null || _j === void 0 ? void 0 : _j.lang) || 'zh');
|
|
204
221
|
await envService.set_envs();
|
|
205
222
|
const authInfo = await userService.getAuthInfo();
|
|
206
223
|
const apps = await openService.findApps();
|
|
@@ -22,6 +22,7 @@ const uploadPath = path_1.default.join(dataPath, 'upload/');
|
|
|
22
22
|
const bakPath = path_1.default.join(dataPath, 'bak/');
|
|
23
23
|
const samplePath = path_1.default.join(rootPath, 'sample/');
|
|
24
24
|
const tmpPath = path_1.default.join(logPath, '.tmp/');
|
|
25
|
+
const rootTmpPath = path_1.default.join(rootPath, '.tmp/');
|
|
25
26
|
const confFile = path_1.default.join(configPath, 'config.sh');
|
|
26
27
|
const sampleConfigFile = path_1.default.join(samplePath, 'config.sample.sh');
|
|
27
28
|
const sampleTaskShellFile = path_1.default.join(samplePath, 'task.sample.sh');
|
|
@@ -45,6 +46,7 @@ const directories = [
|
|
|
45
46
|
preloadPath,
|
|
46
47
|
logPath,
|
|
47
48
|
tmpPath,
|
|
49
|
+
rootTmpPath,
|
|
48
50
|
uploadPath,
|
|
49
51
|
sshPath,
|
|
50
52
|
bakPath,
|
|
@@ -11,6 +11,7 @@ const sshKey_1 = __importDefault(require("../services/sshKey"));
|
|
|
11
11
|
const config_1 = __importDefault(require("../config"));
|
|
12
12
|
const util_1 = require("../config/util");
|
|
13
13
|
const path_1 = require("path");
|
|
14
|
+
const i18n_1 = require("../shared/i18n");
|
|
14
15
|
exports.default = async () => {
|
|
15
16
|
const systemService = typedi_1.Container.get(system_1.default);
|
|
16
17
|
const scheduleService = typedi_1.Container.get(schedule_1.default);
|
|
@@ -24,7 +25,7 @@ exports.default = async () => {
|
|
|
24
25
|
}
|
|
25
26
|
const cron = {
|
|
26
27
|
id: NaN,
|
|
27
|
-
name: '生成token',
|
|
28
|
+
name: (0, i18n_1.t)('生成token'),
|
|
28
29
|
command: tokenCommand,
|
|
29
30
|
runOrigin: 'system',
|
|
30
31
|
};
|
|
@@ -38,7 +39,7 @@ exports.default = async () => {
|
|
|
38
39
|
if (data.info.logRemoveFrequency) {
|
|
39
40
|
const rmlogCron = {
|
|
40
41
|
id: data.id,
|
|
41
|
-
name: '删除日志',
|
|
42
|
+
name: (0, i18n_1.t)('删除日志'),
|
|
42
43
|
command: `ql rmlog ${data.info.logRemoveFrequency}`,
|
|
43
44
|
runOrigin: 'system',
|
|
44
45
|
};
|
|
@@ -203,7 +203,7 @@ const normalizeCronData = (data) => {
|
|
|
203
203
|
var _a, _b, _c, _d, _e;
|
|
204
204
|
if (!data)
|
|
205
205
|
return undefined;
|
|
206
|
-
return Object.assign(Object.assign({}, data), { sub_id: (_a = data.sub_id) !== null && _a !== void 0 ? _a : undefined, extra_schedules: (_b = data.extra_schedules) !== null && _b !== void 0 ? _b :
|
|
206
|
+
return Object.assign(Object.assign({}, data), { sub_id: (_a = data.sub_id) !== null && _a !== void 0 ? _a : undefined, extra_schedules: (_b = data.extra_schedules) !== null && _b !== void 0 ? _b : [], pid: (_c = data.pid) !== null && _c !== void 0 ? _c : undefined, task_before: (_d = data.task_before) !== null && _d !== void 0 ? _d : undefined, task_after: (_e = data.task_after) !== null && _e !== void 0 ? _e : undefined });
|
|
207
207
|
};
|
|
208
208
|
const getCronDetail = async (call, callback) => {
|
|
209
209
|
try {
|
|
@@ -6,9 +6,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
const grpc_js_1 = require("@grpc/grpc-js");
|
|
7
7
|
const cron_1 = require("../protos/cron");
|
|
8
8
|
const config_1 = __importDefault(require("../config"));
|
|
9
|
+
const grpcCerts_1 = require("../config/grpcCerts");
|
|
9
10
|
class Client {
|
|
10
11
|
constructor() {
|
|
11
|
-
this.
|
|
12
|
+
this._client = null;
|
|
13
|
+
}
|
|
14
|
+
get client() {
|
|
15
|
+
if (!this._client) {
|
|
16
|
+
const tlsConfig = (0, grpcCerts_1.getGrpcCerts)();
|
|
17
|
+
this._client = new cron_1.CronClient(`localhost:${config_1.default.grpcPort}`, grpc_js_1.credentials.createSsl(Buffer.from(tlsConfig.caCert), Buffer.from(tlsConfig.clientKey), Buffer.from(tlsConfig.clientCert)), { 'grpc.enable_http_proxy': 0 });
|
|
18
|
+
}
|
|
19
|
+
return this._client;
|
|
12
20
|
}
|
|
13
21
|
addCron(request) {
|
|
14
22
|
return new Promise((resolve, reject) => {
|
|
@@ -9,7 +9,7 @@ const util_1 = require("../config/util");
|
|
|
9
9
|
const check = async (call, callback) => {
|
|
10
10
|
switch (call.request.service) {
|
|
11
11
|
case 'cron':
|
|
12
|
-
const res = await (0, util_1.promiseExec)(`curl -s --noproxy '*' http://
|
|
12
|
+
const res = await (0, util_1.promiseExec)(`curl -s --noproxy '*' http://localhost:${config_1.default.port}/api/system`);
|
|
13
13
|
if (res.includes('200')) {
|
|
14
14
|
return callback(null, { status: 1 });
|
|
15
15
|
}
|
|
@@ -39,16 +39,30 @@ const typedi_1 = require("typedi");
|
|
|
39
39
|
const path_1 = __importStar(require("path"));
|
|
40
40
|
const config_1 = __importDefault(require("../config"));
|
|
41
41
|
const util_1 = require("../config/util");
|
|
42
|
+
const i18n_1 = require("../shared/i18n");
|
|
42
43
|
const undici_1 = require("undici");
|
|
43
44
|
let ConfigService = class ConfigService {
|
|
44
45
|
constructor() { }
|
|
45
46
|
async getFile(filePath, res) {
|
|
46
47
|
let content = '';
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
if (!filePath) {
|
|
49
|
+
return res.send({ code: 403, message: (0, i18n_1.t)('文件无法访问') });
|
|
50
|
+
}
|
|
51
|
+
const normalized = path_1.default.normalize(filePath);
|
|
52
|
+
if (normalized.startsWith('..') || path_1.default.isAbsolute(normalized)) {
|
|
53
|
+
return res.send({ code: 403, message: (0, i18n_1.t)('文件无法访问') });
|
|
54
|
+
}
|
|
55
|
+
const resolvedRoot = path_1.default.resolve(config_1.default.rootPath, normalized);
|
|
56
|
+
const resolvedConfig = path_1.default.resolve(config_1.default.configPath, normalized);
|
|
57
|
+
const isValidPath = resolvedRoot.startsWith(config_1.default.scriptPath) ||
|
|
58
|
+
resolvedRoot.startsWith(config_1.default.configPath) ||
|
|
59
|
+
resolvedConfig.startsWith(config_1.default.scriptPath) ||
|
|
60
|
+
resolvedConfig.startsWith(config_1.default.configPath);
|
|
61
|
+
if (!isValidPath) {
|
|
62
|
+
return res.send({ code: 403, message: (0, i18n_1.t)('文件无法访问') });
|
|
63
|
+
}
|
|
64
|
+
if (config_1.default.blackFileList.includes(path_1.default.basename(normalized))) {
|
|
65
|
+
return res.send({ code: 403, message: (0, i18n_1.t)('文件无法访问') });
|
|
52
66
|
}
|
|
53
67
|
if (filePath.startsWith('sample/')) {
|
|
54
68
|
const res = await (0, undici_1.request)(`https://gitlab.com/whyour/qinglong/-/raw/master/${filePath}`);
|
|
@@ -19,6 +19,7 @@ const typedi_1 = require("typedi");
|
|
|
19
19
|
const winston_1 = __importDefault(require("winston"));
|
|
20
20
|
const config_1 = __importDefault(require("../config"));
|
|
21
21
|
const cron_1 = require("../data/cron");
|
|
22
|
+
const runningInstance_1 = require("../data/runningInstance");
|
|
22
23
|
const child_process_1 = require("child_process");
|
|
23
24
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
24
25
|
const cron_parser_1 = __importDefault(require("cron-parser"));
|
|
@@ -33,8 +34,10 @@ const dayjs_1 = __importDefault(require("dayjs"));
|
|
|
33
34
|
const pickBy_1 = __importDefault(require("lodash/pickBy"));
|
|
34
35
|
const omit_1 = __importDefault(require("lodash/omit"));
|
|
35
36
|
const utils_1 = require("../shared/utils");
|
|
37
|
+
const i18n_1 = require("../shared/i18n");
|
|
36
38
|
const schedule_1 = require("../interface/schedule");
|
|
37
39
|
const logStreamManager_1 = require("../shared/logStreamManager");
|
|
40
|
+
const lodash_1 = require("lodash");
|
|
38
41
|
let CronService = class CronService {
|
|
39
42
|
constructor(logger) {
|
|
40
43
|
this.logger = logger;
|
|
@@ -46,6 +49,26 @@ let CronService = class CronService {
|
|
|
46
49
|
}
|
|
47
50
|
return false;
|
|
48
51
|
}
|
|
52
|
+
get schedulerMode() {
|
|
53
|
+
const env = process.env.QL_SCHEDULER;
|
|
54
|
+
if (env === 'system')
|
|
55
|
+
return 'system';
|
|
56
|
+
if (env === 'node')
|
|
57
|
+
return 'node';
|
|
58
|
+
try {
|
|
59
|
+
(0, child_process_1.execSync)('which crond', { stdio: 'ignore' });
|
|
60
|
+
return 'system';
|
|
61
|
+
}
|
|
62
|
+
catch (_a) {
|
|
63
|
+
return 'node';
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
shouldUseCronClient(cron) {
|
|
67
|
+
if (this.schedulerMode === 'node') {
|
|
68
|
+
return !this.isSpecialSchedule(cron.schedule);
|
|
69
|
+
}
|
|
70
|
+
return this.isNodeCron(cron) && !this.isSpecialSchedule(cron.schedule);
|
|
71
|
+
}
|
|
49
72
|
isOnceSchedule(schedule) {
|
|
50
73
|
return schedule === null || schedule === void 0 ? void 0 : schedule.startsWith(schedule_1.ScheduleType.ONCE);
|
|
51
74
|
}
|
|
@@ -81,7 +104,7 @@ let CronService = class CronService {
|
|
|
81
104
|
if ((0, util_1.isDemoEnv)()) {
|
|
82
105
|
return doc;
|
|
83
106
|
}
|
|
84
|
-
if (
|
|
107
|
+
if (this.shouldUseCronClient(doc)) {
|
|
85
108
|
await client_1.default.addCron([
|
|
86
109
|
{
|
|
87
110
|
name: doc.name || '',
|
|
@@ -108,7 +131,7 @@ let CronService = class CronService {
|
|
|
108
131
|
return newDoc;
|
|
109
132
|
}
|
|
110
133
|
await client_1.default.delCron([String(newDoc.id)]);
|
|
111
|
-
if (
|
|
134
|
+
if (this.shouldUseCronClient(newDoc)) {
|
|
112
135
|
await client_1.default.addCron([
|
|
113
136
|
{
|
|
114
137
|
name: doc.name || '',
|
|
@@ -148,6 +171,31 @@ let CronService = class CronService {
|
|
|
148
171
|
if (status === cron_1.CrontabStatus.idle && log_path !== cron.log_path) {
|
|
149
172
|
options = (0, omit_1.default)(options, ['status', 'log_path', 'pid']);
|
|
150
173
|
}
|
|
174
|
+
// Manage RunningInstance records for status transitions from shell scripts
|
|
175
|
+
if (status === cron_1.CrontabStatus.running) {
|
|
176
|
+
// Create a new running instance record
|
|
177
|
+
await runningInstance_1.RunningInstanceModel.create({
|
|
178
|
+
cron_id: id,
|
|
179
|
+
pid: pid || undefined,
|
|
180
|
+
log_path: log_path || undefined,
|
|
181
|
+
started_at: last_execution_time || (0, dayjs_1.default)().unix(),
|
|
182
|
+
status: runningInstance_1.InstanceStatus.running,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
else if (status === cron_1.CrontabStatus.idle) {
|
|
186
|
+
// Mark the matching running instance as finished
|
|
187
|
+
const finishedAt = (0, dayjs_1.default)().unix();
|
|
188
|
+
await runningInstance_1.RunningInstanceModel.update({
|
|
189
|
+
finished_at: finishedAt,
|
|
190
|
+
status: runningInstance_1.InstanceStatus.finished,
|
|
191
|
+
}, {
|
|
192
|
+
where: {
|
|
193
|
+
cron_id: id,
|
|
194
|
+
pid: pid || undefined,
|
|
195
|
+
status: runningInstance_1.InstanceStatus.running,
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
}
|
|
151
199
|
await cron_1.CrontabModel.update(Object.assign({}, (0, pickBy_1.default)(options, (v) => v === 0 || !!v)), { where: { id } });
|
|
152
200
|
}
|
|
153
201
|
}
|
|
@@ -308,7 +356,7 @@ let CronService = class CronService {
|
|
|
308
356
|
}
|
|
309
357
|
}
|
|
310
358
|
formatFilterQuery(query, filterQuery) {
|
|
311
|
-
if (filterQuery) {
|
|
359
|
+
if (!(0, lodash_1.isEmpty)(filterQuery)) {
|
|
312
360
|
if (!query[sequelize_1.Op.and]) {
|
|
313
361
|
query[sequelize_1.Op.and] = [];
|
|
314
362
|
}
|
|
@@ -418,8 +466,36 @@ let CronService = class CronService {
|
|
|
418
466
|
this.logger.error(`[panel][停止任务失败] 任务ID: ${doc.id}, 错误: ${error}`);
|
|
419
467
|
}
|
|
420
468
|
}
|
|
469
|
+
// Mark all running instances as stopped
|
|
470
|
+
const finishedAt = (0, dayjs_1.default)().unix();
|
|
471
|
+
await runningInstance_1.RunningInstanceModel.update({ status: runningInstance_1.InstanceStatus.stopped, finished_at: finishedAt }, { where: { cron_id: ids, status: runningInstance_1.InstanceStatus.running } });
|
|
421
472
|
await cron_1.CrontabModel.update({ status: cron_1.CrontabStatus.idle, pid: undefined }, { where: { id: ids } });
|
|
422
473
|
}
|
|
474
|
+
async stopInstance(instanceId) {
|
|
475
|
+
const instance = await runningInstance_1.RunningInstanceModel.findOne({
|
|
476
|
+
where: { id: instanceId, status: runningInstance_1.InstanceStatus.running },
|
|
477
|
+
});
|
|
478
|
+
if (!instance) {
|
|
479
|
+
return { code: 400, message: (0, i18n_1.t)('实例不存在或已停止') };
|
|
480
|
+
}
|
|
481
|
+
if (instance.pid) {
|
|
482
|
+
try {
|
|
483
|
+
await (0, util_1.killTask)(instance.pid);
|
|
484
|
+
}
|
|
485
|
+
catch (error) {
|
|
486
|
+
this.logger.error(`[panel][停止实例失败] 实例ID: ${instanceId}, PID: ${instance.pid}, 错误: ${error}`);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
await runningInstance_1.RunningInstanceModel.update({ status: runningInstance_1.InstanceStatus.stopped, finished_at: (0, dayjs_1.default)().unix() }, { where: { id: instanceId } });
|
|
490
|
+
// Check if there are still other running instances for this cron
|
|
491
|
+
const otherRunning = await runningInstance_1.RunningInstanceModel.count({
|
|
492
|
+
where: { cron_id: instance.cron_id, status: runningInstance_1.InstanceStatus.running },
|
|
493
|
+
});
|
|
494
|
+
if (otherRunning === 0) {
|
|
495
|
+
await cron_1.CrontabModel.update({ status: cron_1.CrontabStatus.idle, pid: undefined }, { where: { id: instance.cron_id } });
|
|
496
|
+
}
|
|
497
|
+
return { code: 200, message: (0, i18n_1.t)('实例已停止') };
|
|
498
|
+
}
|
|
423
499
|
async runSingle(cronId) {
|
|
424
500
|
return pLimit_1.default.manualRunWithCronLimit(() => {
|
|
425
501
|
return new Promise(async (resolve) => {
|
|
@@ -445,6 +521,14 @@ let CronService = class CronService {
|
|
|
445
521
|
const logPath = `${uniqPath}/${logTime}.log`;
|
|
446
522
|
const absolutePath = path_1.default.resolve(config_1.default.logPath, `${logPath}`);
|
|
447
523
|
const cp = (0, cross_spawn_1.spawn)(`real_log_path=${logPath} no_delay=true ${this.makeCommand(cron, true)}`, { shell: '/bin/bash' });
|
|
524
|
+
const startedAt = (0, dayjs_1.default)().unix();
|
|
525
|
+
const instance = await runningInstance_1.RunningInstanceModel.create({
|
|
526
|
+
cron_id: id,
|
|
527
|
+
pid: cp.pid,
|
|
528
|
+
log_path: logPath,
|
|
529
|
+
started_at: startedAt,
|
|
530
|
+
status: runningInstance_1.InstanceStatus.running,
|
|
531
|
+
});
|
|
448
532
|
await cron_1.CrontabModel.update({ status: cron_1.CrontabStatus.running, pid: cp.pid, log_path: logPath }, { where: { id } });
|
|
449
533
|
cp.stdout.on('data', async (data) => {
|
|
450
534
|
await logStreamManager_1.logStreamManager.write(absolutePath, data.toString());
|
|
@@ -459,9 +543,20 @@ let CronService = class CronService {
|
|
|
459
543
|
});
|
|
460
544
|
cp.on('exit', async (code) => {
|
|
461
545
|
this.logger.info('[panel][执行任务结束] 参数: %s, 退出码: %j', JSON.stringify(params), code);
|
|
462
|
-
// Close the stream after task completion
|
|
463
546
|
await logStreamManager_1.logStreamManager.closeStream(absolutePath);
|
|
464
|
-
|
|
547
|
+
const finishedAt = (0, dayjs_1.default)().unix();
|
|
548
|
+
await runningInstance_1.RunningInstanceModel.update({
|
|
549
|
+
finished_at: finishedAt,
|
|
550
|
+
status: code === 0 ? runningInstance_1.InstanceStatus.finished : runningInstance_1.InstanceStatus.error,
|
|
551
|
+
exit_code: code !== null && code !== void 0 ? code : undefined,
|
|
552
|
+
}, { where: { id: instance.id } });
|
|
553
|
+
// Only set cron to idle if no other running instances exist
|
|
554
|
+
const otherRunning = await runningInstance_1.RunningInstanceModel.count({
|
|
555
|
+
where: { cron_id: id, status: runningInstance_1.InstanceStatus.running },
|
|
556
|
+
});
|
|
557
|
+
if (otherRunning === 0) {
|
|
558
|
+
await cron_1.CrontabModel.update({ status: cron_1.CrontabStatus.idle, pid: undefined }, { where: { id } });
|
|
559
|
+
}
|
|
465
560
|
resolve(Object.assign(Object.assign({}, params), { pid: cp.pid, code }));
|
|
466
561
|
});
|
|
467
562
|
});
|
|
@@ -475,7 +570,9 @@ let CronService = class CronService {
|
|
|
475
570
|
async enabled(ids) {
|
|
476
571
|
await cron_1.CrontabModel.update({ isDisabled: 0 }, { where: { id: ids } });
|
|
477
572
|
const docs = await cron_1.CrontabModel.findAll({ where: { id: ids } });
|
|
478
|
-
const crons = docs
|
|
573
|
+
const crons = docs
|
|
574
|
+
.filter((x) => this.shouldUseCronClient(x))
|
|
575
|
+
.map((doc) => ({
|
|
479
576
|
name: doc.name || '',
|
|
480
577
|
id: String(doc.id),
|
|
481
578
|
schedule: doc.schedule,
|
|
@@ -491,21 +588,24 @@ let CronService = class CronService {
|
|
|
491
588
|
async log(id) {
|
|
492
589
|
const doc = await this.getDb({ id });
|
|
493
590
|
if (!doc) {
|
|
494
|
-
return '';
|
|
591
|
+
return { content: '', status: 'empty' };
|
|
495
592
|
}
|
|
496
593
|
if (doc.log_name === '/dev/null') {
|
|
497
|
-
return '日志设置为忽略';
|
|
594
|
+
return { content: (0, i18n_1.t)('日志设置为忽略'), status: 'ignored' };
|
|
498
595
|
}
|
|
499
596
|
const absolutePath = path_1.default.resolve(config_1.default.logPath, `${doc.log_path}`);
|
|
500
597
|
const logFileExist = doc.log_path && (await (0, util_1.fileExist)(absolutePath));
|
|
501
598
|
if (logFileExist) {
|
|
502
|
-
|
|
599
|
+
const content = await (0, util_1.getFileContentByName)(`${absolutePath}`);
|
|
600
|
+
const isRunning = typeof doc.status === 'number' &&
|
|
601
|
+
[cron_1.CrontabStatus.running, cron_1.CrontabStatus.queued].includes(doc.status);
|
|
602
|
+
return { content, status: isRunning ? 'running' : 'completed' };
|
|
503
603
|
}
|
|
504
604
|
else {
|
|
505
605
|
return typeof doc.status === 'number' &&
|
|
506
606
|
[cron_1.CrontabStatus.queued, cron_1.CrontabStatus.running].includes(doc.status)
|
|
507
|
-
? '运行中...'
|
|
508
|
-
: '日志不存在...';
|
|
607
|
+
? { content: (0, i18n_1.t)('运行中...'), status: 'running' }
|
|
608
|
+
: { content: (0, i18n_1.t)('日志不存在...'), status: 'notFound' };
|
|
509
609
|
}
|
|
510
610
|
}
|
|
511
611
|
async logs(id) {
|
|
@@ -550,6 +650,9 @@ let CronService = class CronService {
|
|
|
550
650
|
.replace(/;? *\n/g, ';')
|
|
551
651
|
.trim()}' `;
|
|
552
652
|
}
|
|
653
|
+
if (tab.work_dir) {
|
|
654
|
+
commandVariable += `work_dir='${tab.work_dir.replace(/'/g, "'\\''")}' `;
|
|
655
|
+
}
|
|
553
656
|
const crontab_job_string = `${commandVariable}${command}`;
|
|
554
657
|
return crontab_job_string;
|
|
555
658
|
}
|
|
@@ -574,6 +677,15 @@ let CronService = class CronService {
|
|
|
574
677
|
}
|
|
575
678
|
});
|
|
576
679
|
await (0, utils_1.writeFileWithLock)(config_1.default.crontabFile, crontab_string);
|
|
680
|
+
if (this.schedulerMode === 'system') {
|
|
681
|
+
try {
|
|
682
|
+
(0, child_process_1.execSync)(`crontab ${config_1.default.crontabFile}`);
|
|
683
|
+
}
|
|
684
|
+
catch (error) {
|
|
685
|
+
const errorMsg = error.message || String(error);
|
|
686
|
+
this.logger.error('[crontab] Failed to update system crontab:', errorMsg);
|
|
687
|
+
}
|
|
688
|
+
}
|
|
577
689
|
await cron_1.CrontabModel.update({ saved: true }, { where: {} });
|
|
578
690
|
}
|
|
579
691
|
importCrontab() {
|
|
@@ -611,7 +723,8 @@ let CronService = class CronService {
|
|
|
611
723
|
async autosave_crontab() {
|
|
612
724
|
const tabs = await this.crontabs();
|
|
613
725
|
const regularCrons = tabs.data
|
|
614
|
-
.filter((x) => x.isDisabled !== 1 &&
|
|
726
|
+
.filter((x) => x.isDisabled !== 1 &&
|
|
727
|
+
this.shouldUseCronClient(x))
|
|
615
728
|
.map((doc) => ({
|
|
616
729
|
name: doc.name || '',
|
|
617
730
|
id: String(doc.id),
|
|
@@ -632,7 +745,7 @@ let CronService = class CronService {
|
|
|
632
745
|
if (bootTasks.length > 0) {
|
|
633
746
|
await cron_1.CrontabModel.update({ status: cron_1.CrontabStatus.queued }, { where: { id: bootTasks.map((t) => t.id) } });
|
|
634
747
|
for (const task of bootTasks) {
|
|
635
|
-
|
|
748
|
+
this.runSingle(task.id);
|
|
636
749
|
}
|
|
637
750
|
}
|
|
638
751
|
}
|