@whyour/qinglong 2.20.2-2 → 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
|
@@ -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 {
|
|
@@ -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,
|
|
@@ -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,
|
|
@@ -550,6 +647,9 @@ let CronService = class CronService {
|
|
|
550
647
|
.replace(/;? *\n/g, ';')
|
|
551
648
|
.trim()}' `;
|
|
552
649
|
}
|
|
650
|
+
if (tab.work_dir) {
|
|
651
|
+
commandVariable += `work_dir='${tab.work_dir.replace(/'/g, "'\\''")}' `;
|
|
652
|
+
}
|
|
553
653
|
const crontab_job_string = `${commandVariable}${command}`;
|
|
554
654
|
return crontab_job_string;
|
|
555
655
|
}
|
|
@@ -574,6 +674,15 @@ let CronService = class CronService {
|
|
|
574
674
|
}
|
|
575
675
|
});
|
|
576
676
|
await (0, utils_1.writeFileWithLock)(config_1.default.crontabFile, crontab_string);
|
|
677
|
+
if (this.schedulerMode === 'system') {
|
|
678
|
+
try {
|
|
679
|
+
(0, child_process_1.execSync)(`crontab ${config_1.default.crontabFile}`);
|
|
680
|
+
}
|
|
681
|
+
catch (error) {
|
|
682
|
+
const errorMsg = error.message || String(error);
|
|
683
|
+
this.logger.error('[crontab] Failed to update system crontab:', errorMsg);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
577
686
|
await cron_1.CrontabModel.update({ saved: true }, { where: {} });
|
|
578
687
|
}
|
|
579
688
|
importCrontab() {
|
|
@@ -611,7 +720,8 @@ let CronService = class CronService {
|
|
|
611
720
|
async autosave_crontab() {
|
|
612
721
|
const tabs = await this.crontabs();
|
|
613
722
|
const regularCrons = tabs.data
|
|
614
|
-
.filter((x) => x.isDisabled !== 1 &&
|
|
723
|
+
.filter((x) => x.isDisabled !== 1 &&
|
|
724
|
+
this.shouldUseCronClient(x))
|
|
615
725
|
.map((doc) => ({
|
|
616
726
|
name: doc.name || '',
|
|
617
727
|
id: String(doc.id),
|
|
@@ -632,7 +742,7 @@ let CronService = class CronService {
|
|
|
632
742
|
if (bootTasks.length > 0) {
|
|
633
743
|
await cron_1.CrontabModel.update({ status: cron_1.CrontabStatus.queued }, { where: { id: bootTasks.map((t) => t.id) } });
|
|
634
744
|
for (const task of bootTasks) {
|
|
635
|
-
|
|
745
|
+
this.runSingle(task.id);
|
|
636
746
|
}
|
|
637
747
|
}
|
|
638
748
|
}
|
|
@@ -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(`✌️ 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);
|