@whyour/qinglong 2.20.2-3 → 2.21.0-10
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/README-en.md +11 -0
- package/README.md +11 -0
- package/back/protos/api.proto +3 -0
- package/deploy/kubernetes/README.md +81 -0
- package/deploy/kubernetes/base/kustomization.yaml +5 -0
- package/deploy/kubernetes/base/qinglong.yaml +95 -0
- package/deploy/kubernetes/overlays/example/kustomization.yaml +10 -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 +29 -28
- package/sample/config.sample.sh +19 -3
- package/sample/notify.js +145 -9
- package/sample/notify.py +110 -11
- package/shell/api.sh +51 -21
- 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 +50 -31
- 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 +15 -5
- package/static/build/api/script.js +34 -9
- package/static/build/api/system.js +22 -8
- package/static/build/api/user.js +6 -5
- package/static/build/app.js +22 -19
- package/static/build/config/const.js +8 -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 +18 -1
- package/static/build/data/runningInstance.js +57 -0
- package/static/build/data/sock.js +1 -0
- package/static/build/loaders/app.js +7 -7
- package/static/build/loaders/db.js +8 -2
- package/static/build/loaders/deps.js +1 -22
- package/static/build/loaders/express.js +19 -15
- package/static/build/loaders/initData.js +20 -2
- package/static/build/loaders/initFile.js +3 -1
- package/static/build/loaders/initTask.js +3 -2
- package/static/build/loaders/server.js +1 -1
- package/static/build/protos/api.js +23 -1
- package/static/build/schedule/addCron.js +67 -10
- package/static/build/schedule/api.js +1 -1
- package/static/build/schedule/client.js +9 -1
- package/static/build/schedule/delCron.js +11 -1
- package/static/build/schedule/health.js +1 -1
- package/static/build/services/config.js +19 -5
- package/static/build/services/cron.js +198 -38
- 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 +87 -8
- package/static/build/services/open.js +2 -1
- package/static/build/services/sshKey.js +6 -3
- package/static/build/services/subscription.js +8 -5
- package/static/build/services/system.js +34 -9
- package/static/build/services/user.js +39 -13
- package/static/build/shared/auth.js +7 -1
- package/static/build/shared/i18n.js +160 -0
- package/static/build/shared/pLimit.js +15 -3
- package/static/build/shared/runCron.js +5 -0
- package/static/build/shared/store.js +7 -0
- package/static/build/validation/schedule.js +10 -0
- package/static/dist/1089.0ecc5383.async.js +1 -0
- package/static/dist/1147.62793604.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.85285476.async.js +1 -0
- package/static/dist/2286.f371c743.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.545ab873.async.js +1 -0
- 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.db34ad8d.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.3048fe5b.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.b98f0084.async.js +1 -0
- package/static/dist/src__pages__crontab__index.68e78a33.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.f7bb47ba.async.js +1 -0
- package/static/dist/src__pages__env__editNameModal.48715576.async.js +1 -0
- package/static/dist/src__pages__env__index.452cdc47.async.js +1 -0
- package/static/dist/src__pages__env__modal.e1234834.async.js +1 -0
- package/static/dist/src__pages__error__index.dedb2b90.async.js +1 -0
- package/static/dist/src__pages__initialization__index.22620a77.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.532a3044.async.js +1 -0
- package/static/dist/src__pages__script__index.1fc20f22.async.js +1 -0
- package/static/dist/src__pages__script__renameModal.5cdde919.async.js +1 -0
- package/static/dist/src__pages__script__saveModal.5ffed35f.async.js +1 -0
- package/static/dist/src__pages__script__setting.1a82c6d7.async.js +1 -0
- package/static/dist/src__pages__setting__appModal.43b7758a.async.js +1 -0
- package/static/dist/src__pages__setting__checkUpdate.34d773f2.async.js +1 -0
- package/static/dist/src__pages__setting__dependence.9c04c79a.async.js +1 -0
- package/static/dist/src__pages__setting__index.7e070838.async.js +1 -0
- package/static/dist/src__pages__setting__notification.bac481c2.async.js +1 -0
- package/static/dist/src__pages__setting__other.707cfafe.async.js +1 -0
- package/static/dist/src__pages__setting__security.b7235753.async.js +1 -0
- package/static/dist/src__pages__setting__systemLog.b81a8c1d.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.562448e2.async.js +1 -0
- package/static/dist/{umi.ef8199a4.js → umi.a54a59f6.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/3191.70bc19db.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
|
@@ -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,16 +104,25 @@ let CronService = class CronService {
|
|
|
81
104
|
if ((0, util_1.isDemoEnv)()) {
|
|
82
105
|
return doc;
|
|
83
106
|
}
|
|
84
|
-
if (
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
107
|
+
if (this.shouldUseCronClient(doc)) {
|
|
108
|
+
try {
|
|
109
|
+
await client_1.default.addCron([
|
|
110
|
+
{
|
|
111
|
+
name: doc.name || '',
|
|
112
|
+
id: String(doc.id),
|
|
113
|
+
schedule: doc.schedule,
|
|
114
|
+
command: this.makeCommand(doc),
|
|
115
|
+
extra_schedules: doc.extra_schedules || [],
|
|
116
|
+
},
|
|
117
|
+
]);
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
// gRPC 注册失败时回滚 DB 记录,避免产生"僵尸任务"
|
|
121
|
+
// (DB 和 crontab.list 有记录但调度器永远不会执行)
|
|
122
|
+
await cron_1.CrontabModel.destroy({ where: { id: doc.id } });
|
|
123
|
+
this.logger.error('[crontab] Failed to register cron job in scheduler, task creation rolled back:', (error === null || error === void 0 ? void 0 : error.message) || error);
|
|
124
|
+
throw new Error(`${(0, i18n_1.t)('调度器注册失败,任务创建已回滚')}: ${(error === null || error === void 0 ? void 0 : error.details) || (error === null || error === void 0 ? void 0 : error.message)}`);
|
|
125
|
+
}
|
|
94
126
|
}
|
|
95
127
|
await this.setCrontab();
|
|
96
128
|
return doc;
|
|
@@ -107,17 +139,46 @@ let CronService = class CronService {
|
|
|
107
139
|
if (doc.isDisabled === 1 || (0, util_1.isDemoEnv)()) {
|
|
108
140
|
return newDoc;
|
|
109
141
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
142
|
+
try {
|
|
143
|
+
await client_1.default.delCron([String(newDoc.id)]);
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
this.logger.warn('[crontab] Failed to unregister cron job in scheduler:', (error === null || error === void 0 ? void 0 : error.message) || error);
|
|
147
|
+
}
|
|
148
|
+
if (this.shouldUseCronClient(newDoc)) {
|
|
149
|
+
try {
|
|
150
|
+
await client_1.default.addCron([
|
|
151
|
+
{
|
|
152
|
+
name: doc.name || '',
|
|
153
|
+
id: String(newDoc.id),
|
|
154
|
+
schedule: newDoc.schedule,
|
|
155
|
+
command: this.makeCommand(newDoc),
|
|
156
|
+
extra_schedules: newDoc.extra_schedules || [],
|
|
157
|
+
},
|
|
158
|
+
]);
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
// gRPC 注册新任务失败 → 回滚 DB 到旧数据,并尝试恢复旧调度注册
|
|
162
|
+
await cron_1.CrontabModel.update(doc, { where: { id: doc.id } });
|
|
163
|
+
if (this.shouldUseCronClient(doc)) {
|
|
164
|
+
try {
|
|
165
|
+
await client_1.default.addCron([
|
|
166
|
+
{
|
|
167
|
+
name: doc.name || '',
|
|
168
|
+
id: String(doc.id),
|
|
169
|
+
schedule: doc.schedule,
|
|
170
|
+
command: this.makeCommand(doc),
|
|
171
|
+
extra_schedules: doc.extra_schedules || [],
|
|
172
|
+
},
|
|
173
|
+
]);
|
|
174
|
+
}
|
|
175
|
+
catch (_recoveryError) {
|
|
176
|
+
this.logger.warn('[crontab] Failed to restore old cron job in scheduler after rollback:', (_recoveryError === null || _recoveryError === void 0 ? void 0 : _recoveryError.message) || _recoveryError);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
this.logger.error('[crontab] Failed to register updated cron job in scheduler, update rolled back:', (error === null || error === void 0 ? void 0 : error.message) || error);
|
|
180
|
+
throw new Error(`${(0, i18n_1.t)('调度器注册失败,任务更新已回滚')}: ${(error === null || error === void 0 ? void 0 : error.details) || (error === null || error === void 0 ? void 0 : error.message)}`);
|
|
181
|
+
}
|
|
121
182
|
}
|
|
122
183
|
await this.setCrontab();
|
|
123
184
|
return newDoc;
|
|
@@ -126,7 +187,7 @@ let CronService = class CronService {
|
|
|
126
187
|
await cron_1.CrontabModel.update(payload, { where: { id: payload.id } });
|
|
127
188
|
return await this.getDb({ id: payload.id });
|
|
128
189
|
}
|
|
129
|
-
async status({ ids, status, pid, log_path, last_running_time = 0, last_execution_time = 0, }) {
|
|
190
|
+
async status({ ids, status, pid, log_path, last_running_time = 0, last_execution_time = 0, exit_code, }) {
|
|
130
191
|
let options = {
|
|
131
192
|
status,
|
|
132
193
|
pid,
|
|
@@ -148,12 +209,46 @@ let CronService = class CronService {
|
|
|
148
209
|
if (status === cron_1.CrontabStatus.idle && log_path !== cron.log_path) {
|
|
149
210
|
options = (0, omit_1.default)(options, ['status', 'log_path', 'pid']);
|
|
150
211
|
}
|
|
212
|
+
// Manage RunningInstance records for status transitions from shell scripts
|
|
213
|
+
if (status === cron_1.CrontabStatus.running) {
|
|
214
|
+
// Create a new running instance record
|
|
215
|
+
await runningInstance_1.RunningInstanceModel.create({
|
|
216
|
+
cron_id: id,
|
|
217
|
+
pid: pid || undefined,
|
|
218
|
+
log_path: log_path || undefined,
|
|
219
|
+
started_at: last_execution_time || (0, dayjs_1.default)().unix(),
|
|
220
|
+
status: runningInstance_1.InstanceStatus.running,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
else if (status === cron_1.CrontabStatus.idle) {
|
|
224
|
+
// Mark the matching running instance as finished
|
|
225
|
+
const finishedAt = (0, dayjs_1.default)().unix();
|
|
226
|
+
const instanceStatus = exit_code !== undefined && exit_code !== null && exit_code !== 0
|
|
227
|
+
? runningInstance_1.InstanceStatus.error
|
|
228
|
+
: runningInstance_1.InstanceStatus.finished;
|
|
229
|
+
await runningInstance_1.RunningInstanceModel.update({
|
|
230
|
+
finished_at: finishedAt,
|
|
231
|
+
status: instanceStatus,
|
|
232
|
+
exit_code: exit_code !== null && exit_code !== void 0 ? exit_code : undefined,
|
|
233
|
+
}, {
|
|
234
|
+
where: {
|
|
235
|
+
cron_id: id,
|
|
236
|
+
pid: pid || undefined,
|
|
237
|
+
status: runningInstance_1.InstanceStatus.running,
|
|
238
|
+
},
|
|
239
|
+
});
|
|
240
|
+
}
|
|
151
241
|
await cron_1.CrontabModel.update(Object.assign({}, (0, pickBy_1.default)(options, (v) => v === 0 || !!v)), { where: { id } });
|
|
152
242
|
}
|
|
153
243
|
}
|
|
154
244
|
async remove(ids) {
|
|
155
245
|
await cron_1.CrontabModel.destroy({ where: { id: ids } });
|
|
156
|
-
|
|
246
|
+
try {
|
|
247
|
+
await client_1.default.delCron(ids.map(String));
|
|
248
|
+
}
|
|
249
|
+
catch (error) {
|
|
250
|
+
this.logger.warn('[crontab] Failed to unregister cron job in scheduler:', (error === null || error === void 0 ? void 0 : error.message) || error);
|
|
251
|
+
}
|
|
157
252
|
await this.setCrontab();
|
|
158
253
|
}
|
|
159
254
|
async pin(ids) {
|
|
@@ -308,7 +403,7 @@ let CronService = class CronService {
|
|
|
308
403
|
}
|
|
309
404
|
}
|
|
310
405
|
formatFilterQuery(query, filterQuery) {
|
|
311
|
-
if (filterQuery) {
|
|
406
|
+
if (!(0, lodash_1.isEmpty)(filterQuery)) {
|
|
312
407
|
if (!query[sequelize_1.Op.and]) {
|
|
313
408
|
query[sequelize_1.Op.and] = [];
|
|
314
409
|
}
|
|
@@ -418,8 +513,36 @@ let CronService = class CronService {
|
|
|
418
513
|
this.logger.error(`[panel][停止任务失败] 任务ID: ${doc.id}, 错误: ${error}`);
|
|
419
514
|
}
|
|
420
515
|
}
|
|
516
|
+
// Mark all running instances as stopped
|
|
517
|
+
const finishedAt = (0, dayjs_1.default)().unix();
|
|
518
|
+
await runningInstance_1.RunningInstanceModel.update({ status: runningInstance_1.InstanceStatus.stopped, finished_at: finishedAt }, { where: { cron_id: ids, status: runningInstance_1.InstanceStatus.running } });
|
|
421
519
|
await cron_1.CrontabModel.update({ status: cron_1.CrontabStatus.idle, pid: undefined }, { where: { id: ids } });
|
|
422
520
|
}
|
|
521
|
+
async stopInstance(instanceId) {
|
|
522
|
+
const instance = await runningInstance_1.RunningInstanceModel.findOne({
|
|
523
|
+
where: { id: instanceId, status: runningInstance_1.InstanceStatus.running },
|
|
524
|
+
});
|
|
525
|
+
if (!instance) {
|
|
526
|
+
return { code: 400, message: (0, i18n_1.t)('实例不存在或已停止') };
|
|
527
|
+
}
|
|
528
|
+
if (instance.pid) {
|
|
529
|
+
try {
|
|
530
|
+
await (0, util_1.killTask)(instance.pid);
|
|
531
|
+
}
|
|
532
|
+
catch (error) {
|
|
533
|
+
this.logger.error(`[panel][停止实例失败] 实例ID: ${instanceId}, PID: ${instance.pid}, 错误: ${error}`);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
await runningInstance_1.RunningInstanceModel.update({ status: runningInstance_1.InstanceStatus.stopped, finished_at: (0, dayjs_1.default)().unix(), exit_code: 143 }, { where: { id: instanceId } });
|
|
537
|
+
// Check if there are still other running instances for this cron
|
|
538
|
+
const otherRunning = await runningInstance_1.RunningInstanceModel.count({
|
|
539
|
+
where: { cron_id: instance.cron_id, status: runningInstance_1.InstanceStatus.running },
|
|
540
|
+
});
|
|
541
|
+
if (otherRunning === 0) {
|
|
542
|
+
await cron_1.CrontabModel.update({ status: cron_1.CrontabStatus.idle, pid: undefined }, { where: { id: instance.cron_id } });
|
|
543
|
+
}
|
|
544
|
+
return { code: 200, message: (0, i18n_1.t)('实例已停止') };
|
|
545
|
+
}
|
|
423
546
|
async runSingle(cronId) {
|
|
424
547
|
return pLimit_1.default.manualRunWithCronLimit(() => {
|
|
425
548
|
return new Promise(async (resolve) => {
|
|
@@ -459,9 +582,7 @@ let CronService = class CronService {
|
|
|
459
582
|
});
|
|
460
583
|
cp.on('exit', async (code) => {
|
|
461
584
|
this.logger.info('[panel][执行任务结束] 参数: %s, 退出码: %j', JSON.stringify(params), code);
|
|
462
|
-
// Close the stream after task completion
|
|
463
585
|
await logStreamManager_1.logStreamManager.closeStream(absolutePath);
|
|
464
|
-
await cron_1.CrontabModel.update({ status: cron_1.CrontabStatus.idle, pid: undefined }, { where: { id } });
|
|
465
586
|
resolve(Object.assign(Object.assign({}, params), { pid: cp.pid, code }));
|
|
466
587
|
});
|
|
467
588
|
});
|
|
@@ -469,13 +590,20 @@ let CronService = class CronService {
|
|
|
469
590
|
}
|
|
470
591
|
async disabled(ids) {
|
|
471
592
|
await cron_1.CrontabModel.update({ isDisabled: 1 }, { where: { id: ids } });
|
|
472
|
-
|
|
593
|
+
try {
|
|
594
|
+
await client_1.default.delCron(ids.map(String));
|
|
595
|
+
}
|
|
596
|
+
catch (error) {
|
|
597
|
+
this.logger.warn('[crontab] Failed to unregister cron job in scheduler:', (error === null || error === void 0 ? void 0 : error.message) || error);
|
|
598
|
+
}
|
|
473
599
|
await this.setCrontab();
|
|
474
600
|
}
|
|
475
601
|
async enabled(ids) {
|
|
476
602
|
await cron_1.CrontabModel.update({ isDisabled: 0 }, { where: { id: ids } });
|
|
477
603
|
const docs = await cron_1.CrontabModel.findAll({ where: { id: ids } });
|
|
478
|
-
const crons = docs
|
|
604
|
+
const crons = docs
|
|
605
|
+
.filter((x) => this.shouldUseCronClient(x))
|
|
606
|
+
.map((doc) => ({
|
|
479
607
|
name: doc.name || '',
|
|
480
608
|
id: String(doc.id),
|
|
481
609
|
schedule: doc.schedule,
|
|
@@ -485,27 +613,38 @@ let CronService = class CronService {
|
|
|
485
613
|
if ((0, util_1.isDemoEnv)()) {
|
|
486
614
|
return;
|
|
487
615
|
}
|
|
488
|
-
|
|
616
|
+
try {
|
|
617
|
+
await client_1.default.addCron(crons);
|
|
618
|
+
}
|
|
619
|
+
catch (error) {
|
|
620
|
+
// gRPC 注册失败 → 回滚启用状态,避免 DB 显示已启用但调度器未注册
|
|
621
|
+
await cron_1.CrontabModel.update({ isDisabled: 1 }, { where: { id: ids } });
|
|
622
|
+
this.logger.error('[crontab] Failed to register cron job in scheduler, enable rolled back:', (error === null || error === void 0 ? void 0 : error.message) || error);
|
|
623
|
+
throw new Error(`${(0, i18n_1.t)('调度器注册失败,任务启用已回滚')}: ${(error === null || error === void 0 ? void 0 : error.details) || (error === null || error === void 0 ? void 0 : error.message)}`);
|
|
624
|
+
}
|
|
489
625
|
await this.setCrontab();
|
|
490
626
|
}
|
|
491
627
|
async log(id) {
|
|
492
628
|
const doc = await this.getDb({ id });
|
|
493
629
|
if (!doc) {
|
|
494
|
-
return '';
|
|
630
|
+
return { content: '', status: 'empty' };
|
|
495
631
|
}
|
|
496
632
|
if (doc.log_name === '/dev/null') {
|
|
497
|
-
return '日志设置为忽略';
|
|
633
|
+
return { content: (0, i18n_1.t)('日志设置为忽略'), status: 'ignored' };
|
|
498
634
|
}
|
|
499
635
|
const absolutePath = path_1.default.resolve(config_1.default.logPath, `${doc.log_path}`);
|
|
500
636
|
const logFileExist = doc.log_path && (await (0, util_1.fileExist)(absolutePath));
|
|
501
637
|
if (logFileExist) {
|
|
502
|
-
|
|
638
|
+
const content = await (0, util_1.getFileContentByName)(`${absolutePath}`);
|
|
639
|
+
const isRunning = typeof doc.status === 'number' &&
|
|
640
|
+
[cron_1.CrontabStatus.running, cron_1.CrontabStatus.queued].includes(doc.status);
|
|
641
|
+
return { content, status: isRunning ? 'running' : 'completed' };
|
|
503
642
|
}
|
|
504
643
|
else {
|
|
505
644
|
return typeof doc.status === 'number' &&
|
|
506
645
|
[cron_1.CrontabStatus.queued, cron_1.CrontabStatus.running].includes(doc.status)
|
|
507
|
-
? '运行中...'
|
|
508
|
-
: '日志不存在...';
|
|
646
|
+
? { content: (0, i18n_1.t)('运行中...'), status: 'running' }
|
|
647
|
+
: { content: (0, i18n_1.t)('日志不存在...'), status: 'notFound' };
|
|
509
648
|
}
|
|
510
649
|
}
|
|
511
650
|
async logs(id) {
|
|
@@ -550,6 +689,9 @@ let CronService = class CronService {
|
|
|
550
689
|
.replace(/;? *\n/g, ';')
|
|
551
690
|
.trim()}' `;
|
|
552
691
|
}
|
|
692
|
+
if (tab.work_dir) {
|
|
693
|
+
commandVariable += `work_dir='${tab.work_dir.replace(/'/g, "'\\''")}' `;
|
|
694
|
+
}
|
|
553
695
|
const crontab_job_string = `${commandVariable}${command}`;
|
|
554
696
|
return crontab_job_string;
|
|
555
697
|
}
|
|
@@ -574,6 +716,15 @@ let CronService = class CronService {
|
|
|
574
716
|
}
|
|
575
717
|
});
|
|
576
718
|
await (0, utils_1.writeFileWithLock)(config_1.default.crontabFile, crontab_string);
|
|
719
|
+
if (this.schedulerMode === 'system') {
|
|
720
|
+
try {
|
|
721
|
+
(0, child_process_1.execSync)(`crontab ${config_1.default.crontabFile}`);
|
|
722
|
+
}
|
|
723
|
+
catch (error) {
|
|
724
|
+
const errorMsg = error.message || String(error);
|
|
725
|
+
this.logger.error('[crontab] Failed to update system crontab:', errorMsg);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
577
728
|
await cron_1.CrontabModel.update({ saved: true }, { where: {} });
|
|
578
729
|
}
|
|
579
730
|
importCrontab() {
|
|
@@ -611,7 +762,8 @@ let CronService = class CronService {
|
|
|
611
762
|
async autosave_crontab() {
|
|
612
763
|
const tabs = await this.crontabs();
|
|
613
764
|
const regularCrons = tabs.data
|
|
614
|
-
.filter((x) => x.isDisabled !== 1 &&
|
|
765
|
+
.filter((x) => x.isDisabled !== 1 &&
|
|
766
|
+
this.shouldUseCronClient(x))
|
|
615
767
|
.map((doc) => ({
|
|
616
768
|
name: doc.name || '',
|
|
617
769
|
id: String(doc.id),
|
|
@@ -623,8 +775,16 @@ let CronService = class CronService {
|
|
|
623
775
|
await (0, utils_1.writeFileWithLock)(config_1.default.crontabFile, '');
|
|
624
776
|
return;
|
|
625
777
|
}
|
|
626
|
-
|
|
627
|
-
|
|
778
|
+
// 先同步 crontab.list 与系统 crontab,确保其始终反映数据库真实状态。
|
|
779
|
+
// gRPC 调度注册为尽力而为:失败时不阻断文件同步,调度器重启后会重新注册。
|
|
780
|
+
// 这避免了因调度器短暂不可用导致 crontab.list 与数据库脱节(订阅更新误判任务已存在)。
|
|
781
|
+
await this.setCrontab(tabs);
|
|
782
|
+
try {
|
|
783
|
+
await client_1.default.addCron(regularCrons);
|
|
784
|
+
}
|
|
785
|
+
catch (error) {
|
|
786
|
+
this.logger.warn('[crontab] Failed to register cron job in scheduler:', (error === null || error === void 0 ? void 0 : error.message) || error);
|
|
787
|
+
}
|
|
628
788
|
}
|
|
629
789
|
async bootTask() {
|
|
630
790
|
const tabs = await this.crontabs();
|
|
@@ -632,7 +792,7 @@ let CronService = class CronService {
|
|
|
632
792
|
if (bootTasks.length > 0) {
|
|
633
793
|
await cron_1.CrontabModel.update({ status: cron_1.CrontabStatus.queued }, { where: { id: bootTasks.map((t) => t.id) } });
|
|
634
794
|
for (const task of bootTasks) {
|
|
635
|
-
|
|
795
|
+
this.runSingle(task.id);
|
|
636
796
|
}
|
|
637
797
|
}
|
|
638
798
|
}
|
|
@@ -38,6 +38,7 @@ const dayjs_1 = __importDefault(require("dayjs"));
|
|
|
38
38
|
const pLimit_1 = __importDefault(require("../shared/pLimit"));
|
|
39
39
|
const util_2 = require("../config/util");
|
|
40
40
|
const const_1 = require("../config/const");
|
|
41
|
+
const i18n_1 = require("../shared/i18n");
|
|
41
42
|
let DependenceService = class DependenceService {
|
|
42
43
|
constructor(logger, sockService) {
|
|
43
44
|
this.logger = logger;
|
|
@@ -183,7 +184,7 @@ let DependenceService = class DependenceService {
|
|
|
183
184
|
}
|
|
184
185
|
const depIds = [dependency.id];
|
|
185
186
|
let depName = dependency.name.trim();
|
|
186
|
-
const actionText = isInstall ? '安装' : '删除';
|
|
187
|
+
const actionText = isInstall ? (0, i18n_1.t)('安装') : (0, i18n_1.t)('删除');
|
|
187
188
|
const socketMessageType = isInstall
|
|
188
189
|
? 'installDependence'
|
|
189
190
|
: 'uninstallDependence';
|
|
@@ -197,11 +198,12 @@ let DependenceService = class DependenceService {
|
|
|
197
198
|
if (!osType) {
|
|
198
199
|
await dependence_1.DependenceModel.update({ status: dependence_1.DependenceStatus.installFailed }, { where: { id: depIds } });
|
|
199
200
|
const startTime = (0, dayjs_1.default)();
|
|
200
|
-
const message =
|
|
201
|
+
const message = (0, i18n_1.tf)('开始%s依赖 %s,开始时间 %s\n\n当前系统不支持\n\n依赖%s失败,结束时间 %s,耗时 %s 秒', actionText, depName, startTime.format('YYYY-MM-DD HH:mm:ss'), actionText, startTime.format('YYYY-MM-DD HH:mm:ss'), String(startTime.diff(startTime, 'second')));
|
|
201
202
|
this.sockService.sendMessage({
|
|
202
203
|
type: socketMessageType,
|
|
203
204
|
message,
|
|
204
205
|
references: depIds,
|
|
206
|
+
status: dependence_1.DependenceStatus.installFailed,
|
|
205
207
|
});
|
|
206
208
|
this.updateLog(depIds, message);
|
|
207
209
|
return resolve(null);
|
|
@@ -221,11 +223,12 @@ let DependenceService = class DependenceService {
|
|
|
221
223
|
: `${linuxCommand.uninstall} ${depName.trim()}`;
|
|
222
224
|
}
|
|
223
225
|
const startTime = (0, dayjs_1.default)();
|
|
224
|
-
const message =
|
|
226
|
+
const message = (0, i18n_1.tf)('开始%s依赖 %s,开始时间 %s\n\n', actionText, depName, startTime.format('YYYY-MM-DD HH:mm:ss'));
|
|
225
227
|
this.sockService.sendMessage({
|
|
226
228
|
type: socketMessageType,
|
|
227
229
|
message,
|
|
228
230
|
references: depIds,
|
|
231
|
+
status,
|
|
229
232
|
});
|
|
230
233
|
this.updateLog(depIds, message);
|
|
231
234
|
// 判断是否已经安装过依赖
|
|
@@ -254,11 +257,12 @@ let DependenceService = class DependenceService {
|
|
|
254
257
|
isPythonDependence) &&
|
|
255
258
|
(!depVersion || depInfo.includes(depVersion))) {
|
|
256
259
|
const endTime = (0, dayjs_1.default)();
|
|
257
|
-
const _message =
|
|
260
|
+
const _message = (0, i18n_1.tf)('检测到已经安装 %s\n\n%s\n\n跳过安装\n\n依赖%s成功,结束时间 %s,耗时 %s 秒', depName, depInfo, actionText, endTime.format('YYYY-MM-DD HH:mm:ss'), String(endTime.diff(startTime, 'second')));
|
|
258
261
|
this.sockService.sendMessage({
|
|
259
262
|
type: socketMessageType,
|
|
260
263
|
message: _message,
|
|
261
264
|
references: depIds,
|
|
265
|
+
status: dependence_1.DependenceStatus.installed,
|
|
262
266
|
});
|
|
263
267
|
this.updateLog(depIds, _message);
|
|
264
268
|
await dependence_1.DependenceModel.update({ status: dependence_1.DependenceStatus.installed }, { where: { id: depIds } });
|
|
@@ -277,6 +281,7 @@ let DependenceService = class DependenceService {
|
|
|
277
281
|
type: socketMessageType,
|
|
278
282
|
message: data.toString(),
|
|
279
283
|
references: depIds,
|
|
284
|
+
status,
|
|
280
285
|
});
|
|
281
286
|
this.updateLog(depIds, data.toString());
|
|
282
287
|
});
|
|
@@ -285,6 +290,7 @@ let DependenceService = class DependenceService {
|
|
|
285
290
|
type: socketMessageType,
|
|
286
291
|
message: data.toString(),
|
|
287
292
|
references: depIds,
|
|
293
|
+
status,
|
|
288
294
|
});
|
|
289
295
|
this.updateLog(depIds, data.toString());
|
|
290
296
|
});
|
|
@@ -293,37 +299,32 @@ let DependenceService = class DependenceService {
|
|
|
293
299
|
type: socketMessageType,
|
|
294
300
|
message: JSON.stringify(err),
|
|
295
301
|
references: depIds,
|
|
302
|
+
status,
|
|
296
303
|
});
|
|
297
304
|
this.updateLog(depIds, JSON.stringify(err));
|
|
298
305
|
});
|
|
299
306
|
cp.on('exit', async (code) => {
|
|
300
307
|
const endTime = (0, dayjs_1.default)();
|
|
301
308
|
const isSucceed = code === 0;
|
|
302
|
-
const resultText = isSucceed ? '成功' : '失败';
|
|
303
|
-
const message =
|
|
309
|
+
const resultText = isSucceed ? (0, i18n_1.t)('成功') : (0, i18n_1.t)('失败');
|
|
310
|
+
const message = '\n' +
|
|
311
|
+
(0, i18n_1.tf)('依赖%s%s,结束时间 %s,耗时 %s 秒', actionText, resultText, endTime.format('YYYY-MM-DD HH:mm:ss'), String(endTime.diff(startTime, 'second')));
|
|
312
|
+
const exitStatus = isSucceed
|
|
313
|
+
? (isInstall ? dependence_1.DependenceStatus.installed : dependence_1.DependenceStatus.removed)
|
|
314
|
+
: (isInstall ? dependence_1.DependenceStatus.installFailed : dependence_1.DependenceStatus.removeFailed);
|
|
304
315
|
this.sockService.sendMessage({
|
|
305
316
|
type: socketMessageType,
|
|
306
317
|
message,
|
|
307
318
|
references: depIds,
|
|
319
|
+
status: exitStatus,
|
|
308
320
|
});
|
|
309
321
|
this.updateLog(depIds, message);
|
|
310
|
-
let status;
|
|
311
|
-
if (isSucceed) {
|
|
312
|
-
status = isInstall
|
|
313
|
-
? dependence_1.DependenceStatus.installed
|
|
314
|
-
: dependence_1.DependenceStatus.removed;
|
|
315
|
-
}
|
|
316
|
-
else {
|
|
317
|
-
status = isInstall
|
|
318
|
-
? dependence_1.DependenceStatus.installFailed
|
|
319
|
-
: dependence_1.DependenceStatus.removeFailed;
|
|
320
|
-
}
|
|
321
322
|
const docs = await dependence_1.DependenceModel.findAll({ where: { id: depIds } });
|
|
322
323
|
const _docIds = docs
|
|
323
324
|
.filter((x) => x.status !== dependence_1.DependenceStatus.cancelled)
|
|
324
325
|
.map((x) => x.id);
|
|
325
326
|
if (_docIds.length > 0) {
|
|
326
|
-
await dependence_1.DependenceModel.update({ status }, { where: { id: _docIds } });
|
|
327
|
+
await dependence_1.DependenceModel.update({ status: exitStatus }, { where: { id: _docIds } });
|
|
327
328
|
}
|
|
328
329
|
// 如果删除依赖成功或者强制删除
|
|
329
330
|
if ((isSucceed || force) && !isInstall) {
|
|
@@ -33,7 +33,7 @@ let EnvService = class EnvService {
|
|
|
33
33
|
if (envs &&
|
|
34
34
|
envs.length > 0 &&
|
|
35
35
|
typeof envs[envs.length - 1].position === 'number') {
|
|
36
|
-
position = envs[envs.length - 1].position;
|
|
36
|
+
position = this.getPrecisionPosition(envs[envs.length - 1].position);
|
|
37
37
|
}
|
|
38
38
|
const tabs = payloads.map((x) => {
|
|
39
39
|
position = position - env_1.stepPosition;
|
|
@@ -90,7 +90,7 @@ let EnvService = class EnvService {
|
|
|
90
90
|
return newDoc;
|
|
91
91
|
}
|
|
92
92
|
async checkPosition(position, edge = 0) {
|
|
93
|
-
const precisionPosition =
|
|
93
|
+
const precisionPosition = this.getPrecisionPosition(position);
|
|
94
94
|
if (precisionPosition < env_1.minPosition ||
|
|
95
95
|
precisionPosition > env_1.maxPosition ||
|
|
96
96
|
Math.abs(precisionPosition - edge) < env_1.minPosition) {
|
|
@@ -103,7 +103,7 @@ let EnvService = class EnvService {
|
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
105
|
getPrecisionPosition(position) {
|
|
106
|
-
return parseFloat(position.toPrecision(16));
|
|
106
|
+
return Math.trunc(parseFloat(position.toPrecision(16)));
|
|
107
107
|
}
|
|
108
108
|
async envs(searchText = '', query = {}) {
|
|
109
109
|
let condition = Object.assign({}, query);
|
|
@@ -171,6 +171,34 @@ let EnvService = class EnvService {
|
|
|
171
171
|
async unPin(ids) {
|
|
172
172
|
await env_1.EnvModel.update({ isPinned: 0 }, { where: { id: ids } });
|
|
173
173
|
}
|
|
174
|
+
async addLabels(ids, labels) {
|
|
175
|
+
await data_1.sequelize.transaction(async (transaction) => {
|
|
176
|
+
const docs = await env_1.EnvModel.findAll({
|
|
177
|
+
where: { id: ids },
|
|
178
|
+
transaction,
|
|
179
|
+
});
|
|
180
|
+
for (const doc of docs) {
|
|
181
|
+
const env = doc.get({ plain: true });
|
|
182
|
+
await env_1.EnvModel.update({ labels: Array.from(new Set([...(env.labels || []), ...labels])) }, { where: { id: env.id }, transaction });
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
return await this.find({ id: ids });
|
|
186
|
+
}
|
|
187
|
+
async removeLabels(ids, labels) {
|
|
188
|
+
await data_1.sequelize.transaction(async (transaction) => {
|
|
189
|
+
const docs = await env_1.EnvModel.findAll({
|
|
190
|
+
where: { id: ids },
|
|
191
|
+
transaction,
|
|
192
|
+
});
|
|
193
|
+
for (const doc of docs) {
|
|
194
|
+
const env = doc.get({ plain: true });
|
|
195
|
+
await env_1.EnvModel.update({
|
|
196
|
+
labels: (env.labels || []).filter((label) => !labels.includes(label)),
|
|
197
|
+
}, { where: { id: env.id }, transaction });
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
return await this.find({ id: ids });
|
|
201
|
+
}
|
|
174
202
|
async set_envs() {
|
|
175
203
|
const envs = await this.envs('', {
|
|
176
204
|
name: { [sequelize_1.Op.not]: null },
|
|
@@ -46,23 +46,49 @@ const util_1 = require("util");
|
|
|
46
46
|
const config_1 = __importDefault(require("../config"));
|
|
47
47
|
const metrics_1 = require("./metrics");
|
|
48
48
|
const typedi_1 = require("typedi");
|
|
49
|
+
const grpcCerts_1 = require("../config/grpcCerts");
|
|
49
50
|
let GrpcServerService = class GrpcServerService {
|
|
50
51
|
constructor() {
|
|
51
52
|
this.server = new grpc_js_1.Server({ 'grpc.enable_http_proxy': 0 });
|
|
52
53
|
}
|
|
54
|
+
formatGrpcAddress(host, port) {
|
|
55
|
+
if (host === '::') {
|
|
56
|
+
return `[::]:${port}`;
|
|
57
|
+
}
|
|
58
|
+
return `${host}:${port}`;
|
|
59
|
+
}
|
|
53
60
|
async initialize() {
|
|
54
61
|
try {
|
|
55
62
|
this.server.addService(health_1.HealthService, { check: health_2.check });
|
|
56
63
|
this.server.addService(cron_1.CronService, { addCron: addCron_1.addCron, delCron: delCron_1.delCron });
|
|
57
64
|
this.server.addService(api_1.ApiService, Api);
|
|
65
|
+
const tlsConfig = await (0, grpcCerts_1.initGrpcCerts)();
|
|
66
|
+
const credentials = grpc_js_1.ServerCredentials.createSsl(Buffer.from(tlsConfig.caCert), [{ cert_chain: Buffer.from(tlsConfig.serverCert), private_key: Buffer.from(tlsConfig.serverKey) }], true);
|
|
58
67
|
const grpcPort = config_1.default.grpcPort;
|
|
68
|
+
const hostsToTry = [
|
|
69
|
+
config_1.default.bindHostGrpc,
|
|
70
|
+
...(config_1.default.bindHostGrpc !== '0.0.0.0' ? ['0.0.0.0'] : [])
|
|
71
|
+
];
|
|
59
72
|
const bindAsync = (0, util_1.promisify)(this.server.bindAsync).bind(this.server);
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
73
|
+
let lastError = null;
|
|
74
|
+
for (const host of hostsToTry) {
|
|
75
|
+
try {
|
|
76
|
+
const address = this.formatGrpcAddress(host, grpcPort);
|
|
77
|
+
await bindAsync(address, credentials);
|
|
78
|
+
logger_1.default.debug(`[boot] gRPC service started successfully on ${address}`);
|
|
79
|
+
metrics_1.metricsService.record('grpc_service_start', 1, {
|
|
80
|
+
port: grpcPort.toString(),
|
|
81
|
+
host
|
|
82
|
+
});
|
|
83
|
+
return grpcPort;
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
lastError = err;
|
|
87
|
+
logger_1.default.warn(`Failed to bind gRPC on ${host}:${grpcPort}, trying next...`, err);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
logger_1.default.error('Failed to start gRPC service on all hosts');
|
|
91
|
+
throw lastError || new Error('Failed to start gRPC service');
|
|
66
92
|
}
|
|
67
93
|
catch (err) {
|
|
68
94
|
logger_1.default.error('Failed to start gRPC service:', err);
|