@whyour/qinglong 2.21.0-1 → 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/AGENTS.md +2 -2
- 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/package.json +2 -4
- package/sample/config.sample.sh +7 -2
- package/sample/notify.js +64 -0
- package/sample/notify.py +56 -0
- package/shell/api.sh +7 -1
- package/shell/share.sh +1 -1
- package/static/build/api/cron.js +3 -3
- package/static/build/api/dashboard.js +13 -6
- package/static/build/api/env.js +2 -2
- package/static/build/api/log.js +10 -1
- package/static/build/api/system.js +6 -7
- package/static/build/app.js +19 -19
- package/static/build/config/const.js +1 -0
- package/static/build/config/grpcCerts.js +4 -4
- package/static/build/config/index.js +0 -4
- package/static/build/config/util.js +3 -3
- package/static/build/data/notify.js +8 -1
- package/static/build/data/sock.js +1 -0
- package/static/build/loaders/app.js +6 -6
- package/static/build/loaders/db.js +2 -2
- package/static/build/loaders/express.js +17 -14
- package/static/build/loaders/initData.js +8 -7
- package/static/build/loaders/initFile.js +1 -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/delCron.js +11 -1
- package/static/build/services/cron.js +103 -53
- package/static/build/services/dependence.js +19 -18
- package/static/build/services/grpc.js +1 -1
- package/static/build/services/http.js +1 -1
- package/static/build/services/notify.js +47 -5
- package/static/build/services/subscription.js +6 -4
- package/static/build/services/system.js +12 -2
- package/static/build/services/user.js +31 -6
- package/static/build/shared/auth.js +7 -1
- package/static/build/shared/i18n.js +59 -4
- package/static/build/shared/pLimit.js +3 -2
- package/static/build/shared/store.js +7 -0
- package/static/build/validation/schedule.js +9 -0
- package/static/dist/1147.62793604.async.js +1 -0
- package/static/dist/2208.85285476.async.js +1 -0
- package/static/dist/2286.f371c743.async.js +1 -0
- package/static/dist/3191.545ab873.async.js +1 -0
- package/static/dist/8826.db34ad8d.async.js +1 -0
- package/static/dist/index.html +2 -2
- package/static/dist/{preload_helper.0431c0f3.js → preload_helper.3048fe5b.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.0b8cce8c.async.js → src__pages__crontab__logModal.831d5e4b.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__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__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__modal.562448e2.async.js +1 -0
- package/static/dist/{umi.ca04a019.js → umi.a54a59f6.js} +1 -1
- package/CLAUDE.md +0 -43
- package/static/dist/1147.c420132e.async.js +0 -1
- package/static/dist/2208.f7ba3dfa.async.js +0 -1
- package/static/dist/2286.164bb089.async.js +0 -1
- package/static/dist/3191.63263871.async.js +0 -1
- package/static/dist/8826.2447a104.async.js +0 -1
- package/static/dist/src__pages__crontab__detail.b9c36808.async.js +0 -1
- package/static/dist/src__pages__crontab__index.fed99fd0.async.js +0 -1
- package/static/dist/src__pages__dashboard__index.b30f2f47.async.js +0 -1
- package/static/dist/src__pages__dependence__index.3d87ceac.async.js +0 -1
- package/static/dist/src__pages__dependence__modal.11124896.async.js +0 -1
- package/static/dist/src__pages__env__editNameModal.5d264b25.async.js +0 -1
- package/static/dist/src__pages__env__index.baa27d4e.async.js +0 -1
- package/static/dist/src__pages__env__modal.7f2ef1bc.async.js +0 -1
- package/static/dist/src__pages__error__index.f156b45e.async.js +0 -1
- package/static/dist/src__pages__initialization__index.e96d4ba8.async.js +0 -1
- package/static/dist/src__pages__log__index.e0978bae.async.js +0 -1
- package/static/dist/src__pages__script__editNameModal.3e7e100d.async.js +0 -1
- package/static/dist/src__pages__script__index.2765d1b8.async.js +0 -1
- package/static/dist/src__pages__script__renameModal.5e987ef5.async.js +0 -1
- package/static/dist/src__pages__script__saveModal.3f9d23d6.async.js +0 -1
- package/static/dist/src__pages__script__setting.a535793a.async.js +0 -1
- package/static/dist/src__pages__setting__appModal.251cd14f.async.js +0 -1
- package/static/dist/src__pages__setting__checkUpdate.46efe057.async.js +0 -1
- package/static/dist/src__pages__setting__dependence.8a7a9529.async.js +0 -1
- package/static/dist/src__pages__setting__index.cc85fdfb.async.js +0 -1
- package/static/dist/src__pages__setting__notification.390fc905.async.js +0 -1
- package/static/dist/src__pages__setting__other.b22d2165.async.js +0 -1
- package/static/dist/src__pages__setting__security.598720a8.async.js +0 -1
- package/static/dist/src__pages__setting__systemLog.67406721.async.js +0 -1
- package/static/dist/src__pages__subscription__modal.3562c670.async.js +0 -1
package/static/build/app.js
CHANGED
|
@@ -72,37 +72,37 @@ class Application {
|
|
|
72
72
|
// Wait for gRPC worker to signal it's ready before starting HTTP worker
|
|
73
73
|
this.waitForWorkerReady(grpcWorker, 30000)
|
|
74
74
|
.then(() => {
|
|
75
|
-
logger_1.default.info('
|
|
75
|
+
logger_1.default.info('[boot] gRPC worker is ready, starting HTTP worker');
|
|
76
76
|
this.httpWorker = this.forkWorker('http');
|
|
77
77
|
})
|
|
78
78
|
.catch((error) => {
|
|
79
|
-
logger_1.default.error('
|
|
79
|
+
logger_1.default.error('[boot] Failed to wait for gRPC worker:', error);
|
|
80
80
|
process.exit(1);
|
|
81
81
|
});
|
|
82
82
|
cluster_1.default.on('exit', (worker, code, signal) => {
|
|
83
83
|
const metadata = this.workerMetadataMap.get(worker.id);
|
|
84
84
|
if (metadata) {
|
|
85
85
|
if (!this.isShuttingDown) {
|
|
86
|
-
logger_1.default.error(
|
|
86
|
+
logger_1.default.error(`${metadata.serviceType} worker ${worker.process.pid} died (${signal || code}). Restarting...`);
|
|
87
87
|
// If gRPC worker died, restart it and wait for it to be ready
|
|
88
88
|
if (metadata.serviceType === 'grpc') {
|
|
89
89
|
const newGrpcWorker = this.forkWorker('grpc');
|
|
90
90
|
this.waitForWorkerReady(newGrpcWorker, 30000)
|
|
91
91
|
.then(() => {
|
|
92
|
-
logger_1.default.info('
|
|
92
|
+
logger_1.default.info('gRPC worker restarted and ready');
|
|
93
93
|
// Re-register cron jobs by notifying the HTTP worker
|
|
94
94
|
if (this.httpWorker) {
|
|
95
95
|
try {
|
|
96
96
|
this.httpWorker.send('reregister-crons');
|
|
97
|
-
logger_1.default.info('
|
|
97
|
+
logger_1.default.info('Sent reregister-crons message to HTTP worker');
|
|
98
98
|
}
|
|
99
99
|
catch (error) {
|
|
100
|
-
logger_1.default.error('
|
|
100
|
+
logger_1.default.error('Failed to send reregister-crons message:', error);
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
103
|
})
|
|
104
104
|
.catch((error) => {
|
|
105
|
-
logger_1.default.error('
|
|
105
|
+
logger_1.default.error('Failed to restart gRPC worker:', error);
|
|
106
106
|
process.exit(1);
|
|
107
107
|
});
|
|
108
108
|
}
|
|
@@ -110,7 +110,7 @@ class Application {
|
|
|
110
110
|
// For HTTP worker, just restart it
|
|
111
111
|
const newWorker = this.forkWorker(metadata.serviceType);
|
|
112
112
|
this.httpWorker = newWorker;
|
|
113
|
-
logger_1.default.info(
|
|
113
|
+
logger_1.default.info(`Restarted ${metadata.serviceType} worker (PID: ${newWorker.process.pid})`);
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
116
|
this.workerMetadataMap.delete(worker.id);
|
|
@@ -168,14 +168,14 @@ class Application {
|
|
|
168
168
|
if (worker) {
|
|
169
169
|
const exitPromise = new Promise((resolve) => {
|
|
170
170
|
worker.once('exit', () => {
|
|
171
|
-
logger_1.default.info(
|
|
171
|
+
logger_1.default.info(`Worker ${worker.process.pid} exited`);
|
|
172
172
|
resolve();
|
|
173
173
|
});
|
|
174
174
|
try {
|
|
175
175
|
worker.send('shutdown');
|
|
176
176
|
}
|
|
177
177
|
catch (error) {
|
|
178
|
-
logger_1.default.warn(
|
|
178
|
+
logger_1.default.warn(`Failed to send shutdown to worker ${worker.process.pid}:`, error);
|
|
179
179
|
}
|
|
180
180
|
});
|
|
181
181
|
workerPromises.push(exitPromise);
|
|
@@ -186,7 +186,7 @@ class Application {
|
|
|
186
186
|
Promise.all(workerPromises),
|
|
187
187
|
new Promise((resolve) => {
|
|
188
188
|
setTimeout(() => {
|
|
189
|
-
logger_1.default.warn('
|
|
189
|
+
logger_1.default.warn('Worker shutdown timeout reached');
|
|
190
190
|
resolve();
|
|
191
191
|
}, 10000);
|
|
192
192
|
}),
|
|
@@ -194,7 +194,7 @@ class Application {
|
|
|
194
194
|
process.exit(0);
|
|
195
195
|
}
|
|
196
196
|
catch (error) {
|
|
197
|
-
logger_1.default.error('
|
|
197
|
+
logger_1.default.error('Error during worker shutdown:', error);
|
|
198
198
|
process.exit(1);
|
|
199
199
|
}
|
|
200
200
|
};
|
|
@@ -205,10 +205,10 @@ class Application {
|
|
|
205
205
|
var _a;
|
|
206
206
|
const serviceType = process.env.SERVICE_TYPE;
|
|
207
207
|
if (!serviceType || !['http', 'grpc'].includes(serviceType)) {
|
|
208
|
-
logger_1.default.error('
|
|
208
|
+
logger_1.default.error('[boot] Invalid SERVICE_TYPE:', serviceType);
|
|
209
209
|
process.exit(1);
|
|
210
210
|
}
|
|
211
|
-
logger_1.default.info(
|
|
211
|
+
logger_1.default.info(`[boot] ${serviceType} worker started (PID: ${process.pid})`);
|
|
212
212
|
try {
|
|
213
213
|
if (serviceType === 'http') {
|
|
214
214
|
await this.startHttpService();
|
|
@@ -219,7 +219,7 @@ class Application {
|
|
|
219
219
|
(_a = process.send) === null || _a === void 0 ? void 0 : _a.call(process, 'ready');
|
|
220
220
|
}
|
|
221
221
|
catch (error) {
|
|
222
|
-
logger_1.default.error(
|
|
222
|
+
logger_1.default.error(`[boot] ${serviceType} worker failed:`, error);
|
|
223
223
|
process.exit(1);
|
|
224
224
|
}
|
|
225
225
|
}
|
|
@@ -251,14 +251,14 @@ class Application {
|
|
|
251
251
|
else if (msg === 'reregister-crons' && serviceType === 'http') {
|
|
252
252
|
// Re-register cron jobs when gRPC worker restarts
|
|
253
253
|
try {
|
|
254
|
-
logger_1.default.info('
|
|
254
|
+
logger_1.default.info('[boot] Received reregister-crons message, re-registering cron jobs...');
|
|
255
255
|
const CronService = (await Promise.resolve().then(() => __importStar(require('./services/cron')))).default;
|
|
256
256
|
const cronService = typedi_1.Container.get(CronService);
|
|
257
257
|
await cronService.autosave_crontab();
|
|
258
|
-
logger_1.default.info('
|
|
258
|
+
logger_1.default.info('[boot] Cron jobs re-registered successfully');
|
|
259
259
|
}
|
|
260
260
|
catch (error) {
|
|
261
|
-
logger_1.default.error('
|
|
261
|
+
logger_1.default.error('[boot] Failed to re-register cron jobs:', error);
|
|
262
262
|
}
|
|
263
263
|
}
|
|
264
264
|
});
|
|
@@ -281,7 +281,7 @@ class Application {
|
|
|
281
281
|
process.exit(0);
|
|
282
282
|
}
|
|
283
283
|
catch (error) {
|
|
284
|
-
logger_1.default.error(
|
|
284
|
+
logger_1.default.error(`[${serviceType}] Error during shutdown:`, error);
|
|
285
285
|
process.exit(1);
|
|
286
286
|
}
|
|
287
287
|
}
|
|
@@ -52,7 +52,7 @@ async function tmpFile(prefix) {
|
|
|
52
52
|
return path_1.default.join(dir, `.${prefix}_${Date.now()}_${Math.random().toString(36).slice(2)}.pem`);
|
|
53
53
|
}
|
|
54
54
|
async function generateAllCerts() {
|
|
55
|
-
logger_1.default.info('Generating gRPC mTLS certificates...');
|
|
55
|
+
logger_1.default.info('[boot] Generating gRPC mTLS certificates...');
|
|
56
56
|
const caKeyTmp = await tmpFile('ca_key');
|
|
57
57
|
const caCertTmp = await tmpFile('ca_cert');
|
|
58
58
|
const serverKeyTmp = await tmpFile('server_key');
|
|
@@ -92,7 +92,7 @@ async function generateAllCerts() {
|
|
|
92
92
|
const clientCert = run(`openssl x509 -req -days 3650 -in '${clientCsrTmp}' -CA '${caCertTmp}' -CAkey '${caKeyTmp}' -CAcreateserial -extfile '${clientExtTmp}' 2>/dev/null`);
|
|
93
93
|
const clientKey = await fs.readFile(clientKeyTmp, 'utf-8');
|
|
94
94
|
await cleanup();
|
|
95
|
-
logger_1.default.info('gRPC mTLS certificates generated successfully');
|
|
95
|
+
logger_1.default.info('[boot] gRPC mTLS certificates generated successfully');
|
|
96
96
|
return { caCert, serverCert, serverKey, clientCert, clientKey };
|
|
97
97
|
}
|
|
98
98
|
catch (e) {
|
|
@@ -107,7 +107,7 @@ async function saveCerts(tlsConfig) {
|
|
|
107
107
|
await fs.writeFile(serverKeyPath, tlsConfig.serverKey, { mode: 0o600 });
|
|
108
108
|
await fs.writeFile(clientCertPath, tlsConfig.clientCert, { mode: 0o644 });
|
|
109
109
|
await fs.writeFile(clientKeyPath, tlsConfig.clientKey, { mode: 0o600 });
|
|
110
|
-
logger_1.default.info(`gRPC mTLS certificates saved to ${certDir}`);
|
|
110
|
+
logger_1.default.info(`[boot] gRPC mTLS certificates saved to ${certDir}`);
|
|
111
111
|
}
|
|
112
112
|
async function loadExistingCerts() {
|
|
113
113
|
const exists = await Promise.all([
|
|
@@ -127,7 +127,7 @@ async function loadExistingCerts() {
|
|
|
127
127
|
fs.readFile(clientCertPath, 'utf-8'),
|
|
128
128
|
fs.readFile(clientKeyPath, 'utf-8'),
|
|
129
129
|
]);
|
|
130
|
-
logger_1.default.info('Loaded existing gRPC mTLS certificates from disk');
|
|
130
|
+
logger_1.default.info('[boot] Loaded existing gRPC mTLS certificates from disk');
|
|
131
131
|
return { caCert, serverCert, serverKey, clientCert, clientKey };
|
|
132
132
|
}
|
|
133
133
|
async function initGrpcCerts() {
|
|
@@ -87,8 +87,6 @@ const extraFile = path_1.default.join(configPath, 'extra.sh');
|
|
|
87
87
|
const confBakDir = path_1.default.join(dataPath, 'config/bak/');
|
|
88
88
|
const sampleFile = path_1.default.join(samplePath, 'config.sample.sh');
|
|
89
89
|
const sqliteFile = path_1.default.join(samplePath, 'database.sqlite');
|
|
90
|
-
const authError = '错误的用户名密码,请重试';
|
|
91
|
-
const loginFaild = '请先登录!';
|
|
92
90
|
const configString = 'config sample crontab shareCode diy';
|
|
93
91
|
const versionFile = path_1.default.join(rootPath, 'version.yaml');
|
|
94
92
|
const dataTgzFile = path_1.default.join(tmpPath, 'data.tgz');
|
|
@@ -105,8 +103,6 @@ exports.default = Object.assign(Object.assign({}, config), { jwt: config.jwt, ba
|
|
|
105
103
|
shareShellFile,
|
|
106
104
|
dependenceProxyFile,
|
|
107
105
|
configString,
|
|
108
|
-
loginFaild,
|
|
109
|
-
authError,
|
|
110
106
|
logPath,
|
|
111
107
|
extraFile,
|
|
112
108
|
authConfigFile,
|
|
@@ -498,7 +498,7 @@ function safeJSONParse(value) {
|
|
|
498
498
|
return JSON.parse(value);
|
|
499
499
|
}
|
|
500
500
|
catch (error) {
|
|
501
|
-
logger_1.default.error('[safeJSONParse
|
|
501
|
+
logger_1.default.error('[safeJSONParse error]', error);
|
|
502
502
|
return {};
|
|
503
503
|
}
|
|
504
504
|
}
|
|
@@ -511,7 +511,7 @@ async function rmPath(path) {
|
|
|
511
511
|
}
|
|
512
512
|
}
|
|
513
513
|
catch (error) {
|
|
514
|
-
logger_1.default.error('[rmPath
|
|
514
|
+
logger_1.default.error('[rmPath error]', error);
|
|
515
515
|
}
|
|
516
516
|
}
|
|
517
517
|
exports.rmPath = rmPath;
|
|
@@ -525,7 +525,7 @@ async function setSystemTimezone(timezone) {
|
|
|
525
525
|
return true;
|
|
526
526
|
}
|
|
527
527
|
catch (error) {
|
|
528
|
-
logger_1.default.error('[setSystemTimezone
|
|
528
|
+
logger_1.default.error('[setSystemTimezone error]', error);
|
|
529
529
|
return false;
|
|
530
530
|
}
|
|
531
531
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.OpeniLinkNotification = exports.WxPusherBotNotification = exports.NtfyNotification = exports.LarkNotification = exports.WebhookNotification = exports.ChronocatNotification = exports.PushMeNotification = exports.EmailNotification = exports.WePlusBotNotification = exports.PushPlusNotification = exports.IGotNotification = exports.AibotkNotification = exports.WeWorkAppNotification = exports.WeWorkBotNotification = exports.DingtalkBotNotification = exports.TelegramBotNotification = exports.BarkNotification = exports.synologyChatNotification = exports.PushDeerNotification = exports.ServerChanNotification = exports.GoCqHttpBotNotification = exports.GotifyNotification = exports.NotificationMode = void 0;
|
|
3
|
+
exports.OpeniLinkNotification = exports.WxPusherSptNotification = exports.WxPusherBotNotification = exports.NtfyNotification = exports.LarkNotification = exports.WebhookNotification = exports.ChronocatNotification = exports.PushMeNotification = exports.EmailNotification = exports.WePlusBotNotification = exports.PushPlusNotification = exports.IGotNotification = exports.AibotkNotification = exports.WeWorkAppNotification = exports.WeWorkBotNotification = exports.DingtalkBotNotification = exports.TelegramBotNotification = exports.BarkNotification = exports.synologyChatNotification = exports.PushDeerNotification = exports.ServerChanNotification = exports.GoCqHttpBotNotification = exports.GotifyNotification = exports.NotificationMode = void 0;
|
|
4
4
|
var NotificationMode;
|
|
5
5
|
(function (NotificationMode) {
|
|
6
6
|
NotificationMode["gotify"] = "gotify";
|
|
@@ -223,6 +223,13 @@ class WxPusherBotNotification extends NotificationBaseInfo {
|
|
|
223
223
|
}
|
|
224
224
|
}
|
|
225
225
|
exports.WxPusherBotNotification = WxPusherBotNotification;
|
|
226
|
+
class WxPusherSptNotification extends NotificationBaseInfo {
|
|
227
|
+
constructor() {
|
|
228
|
+
super(...arguments);
|
|
229
|
+
this.wxPusherSptList = '';
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
exports.WxPusherSptNotification = WxPusherSptNotification;
|
|
226
233
|
class OpeniLinkNotification extends NotificationBaseInfo {
|
|
227
234
|
constructor() {
|
|
228
235
|
super(...arguments);
|
|
@@ -12,16 +12,16 @@ const initTask_1 = __importDefault(require("./initTask"));
|
|
|
12
12
|
const initFile_1 = __importDefault(require("./initFile"));
|
|
13
13
|
exports.default = async ({ app }) => {
|
|
14
14
|
(0, depInjector_1.default)();
|
|
15
|
-
logger_1.default.info('
|
|
15
|
+
logger_1.default.info('[boot] Dependency loaded');
|
|
16
16
|
await (0, deps_1.default)();
|
|
17
|
-
logger_1.default.info('
|
|
17
|
+
logger_1.default.info('[boot] Link deps loaded');
|
|
18
18
|
await (0, initFile_1.default)();
|
|
19
|
-
logger_1.default.info('
|
|
19
|
+
logger_1.default.info('[boot] Init file loaded');
|
|
20
20
|
await (0, initData_1.default)();
|
|
21
|
-
logger_1.default.info('
|
|
21
|
+
logger_1.default.info('[boot] Init data loaded');
|
|
22
22
|
(0, initTask_1.default)();
|
|
23
|
-
logger_1.default.info('
|
|
23
|
+
logger_1.default.info('[boot] Init task loaded');
|
|
24
24
|
(0, express_1.default)({ app });
|
|
25
|
-
logger_1.default.info('
|
|
25
|
+
logger_1.default.info('[boot] Express loaded');
|
|
26
26
|
};
|
|
27
27
|
//# sourceMappingURL=app.js.map
|
|
@@ -58,10 +58,10 @@ exports.default = async () => {
|
|
|
58
58
|
// Column already exists or other error, continue
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
|
-
logger_1.default.info('
|
|
61
|
+
logger_1.default.info('[boot] DB loaded');
|
|
62
62
|
}
|
|
63
63
|
catch (error) {
|
|
64
|
-
logger_1.default.error('
|
|
64
|
+
logger_1.default.error('[boot] DB load failed', error);
|
|
65
65
|
}
|
|
66
66
|
};
|
|
67
67
|
//# sourceMappingURL=db.js.map
|
|
@@ -78,14 +78,22 @@ exports.default = ({ app }) => {
|
|
|
78
78
|
const currentToken = doc.tokens.find((x) => x.value === headerToken);
|
|
79
79
|
const keyMatch = pathLower.match(/\/open\/([a-z]+)\/*/);
|
|
80
80
|
const key = keyMatch && keyMatch[1];
|
|
81
|
-
if (doc.scopes.includes(key)
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
81
|
+
if (!doc.scopes.includes(key)) {
|
|
82
|
+
const err = new express_jwt_1.UnauthorizedError('credentials_bad_scheme', {
|
|
83
|
+
message: (0, i18n_1.t)('暂无权限'),
|
|
84
|
+
});
|
|
85
|
+
return next(err);
|
|
85
86
|
}
|
|
87
|
+
if (!currentToken || currentToken.expiration < Math.round(Date.now() / 1000)) {
|
|
88
|
+
const err = new express_jwt_1.UnauthorizedError('invalid_token', {
|
|
89
|
+
message: (0, i18n_1.t)('Token 已失效'),
|
|
90
|
+
});
|
|
91
|
+
return next(err);
|
|
92
|
+
}
|
|
93
|
+
return next();
|
|
86
94
|
}
|
|
87
95
|
}
|
|
88
|
-
const originPath = `${req.baseUrl}${
|
|
96
|
+
const originPath = `${req.baseUrl}${pathLower === '/' ? '' : pathLower}`;
|
|
89
97
|
if (!headerToken &&
|
|
90
98
|
originPath &&
|
|
91
99
|
config_1.default.apiWhiteList.includes(originPath)) {
|
|
@@ -97,8 +105,8 @@ exports.default = ({ app }) => {
|
|
|
97
105
|
}
|
|
98
106
|
const errorCode = headerToken ? 'invalid_token' : 'credentials_required';
|
|
99
107
|
const errorMessage = headerToken
|
|
100
|
-
? '
|
|
101
|
-
:
|
|
108
|
+
? (0, i18n_1.t)('Token 已失效')
|
|
109
|
+
: (0, i18n_1.t)('请先登录');
|
|
102
110
|
const err = new express_jwt_1.UnauthorizedError(errorCode, { message: errorMessage });
|
|
103
111
|
next(err);
|
|
104
112
|
});
|
|
@@ -109,16 +117,11 @@ exports.default = ({ app }) => {
|
|
|
109
117
|
'/api/user/notification/init',
|
|
110
118
|
'/open/user/init',
|
|
111
119
|
'/open/user/notification/init',
|
|
112
|
-
].includes(
|
|
120
|
+
].includes(pathLower)) {
|
|
113
121
|
return next();
|
|
114
122
|
}
|
|
115
123
|
const authInfo = (await store_1.shareStore.getAuthInfo()) || {};
|
|
116
|
-
let isInitialized =
|
|
117
|
-
if (Object.keys(authInfo).length === 2 &&
|
|
118
|
-
authInfo.username === 'admin' &&
|
|
119
|
-
authInfo.password === 'admin') {
|
|
120
|
-
isInitialized = false;
|
|
121
|
-
}
|
|
124
|
+
let isInitialized = !(0, auth_1.isDefaultAuthInfo)(authInfo);
|
|
122
125
|
if (isInitialized) {
|
|
123
126
|
return res.send({ code: 450, message: (0, i18n_1.t)('未知错误') });
|
|
124
127
|
}
|
|
@@ -49,7 +49,7 @@ const open_2 = require("../data/open");
|
|
|
49
49
|
const runningInstance_1 = require("../data/runningInstance");
|
|
50
50
|
const i18n_1 = require("../shared/i18n");
|
|
51
51
|
exports.default = async () => {
|
|
52
|
-
var _a, _b, _c, _d, _e, _f, _g, _h
|
|
52
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
53
53
|
const cronService = typedi_1.Container.get(cron_2.default);
|
|
54
54
|
const envService = typedi_1.Container.get(env_1.default);
|
|
55
55
|
const dependenceService = typedi_1.Container.get(dependence_1.default);
|
|
@@ -206,18 +206,19 @@ exports.default = async () => {
|
|
|
206
206
|
}
|
|
207
207
|
}
|
|
208
208
|
});
|
|
209
|
-
//
|
|
210
|
-
|
|
211
|
-
|
|
209
|
+
// 初始化语言(必须在 autosave_crontab 之前)
|
|
210
|
+
const lang = ((_h = systemConfig.info) === null || _h === void 0 ? void 0 : _h.lang) || (0, i18n_1.systemLang)();
|
|
211
|
+
(0, i18n_1.setLang)(lang);
|
|
212
|
+
// 确保 lang_env.sh 存在
|
|
212
213
|
try {
|
|
213
214
|
const langEnvExist = await (0, util_1.fileExist)(config_1.default.langEnvFile);
|
|
214
215
|
if (!langEnvExist) {
|
|
215
|
-
const lang = ((_h = systemConfig.info) === null || _h === void 0 ? void 0 : _h.lang) || 'zh';
|
|
216
216
|
await (0, promises_1.writeFile)(config_1.default.langEnvFile, `export QL_LANG='${lang}'\n`);
|
|
217
217
|
}
|
|
218
218
|
}
|
|
219
|
-
catch (
|
|
220
|
-
|
|
219
|
+
catch (_j) { }
|
|
220
|
+
// 初始化保存一次ck和定时任务数据
|
|
221
|
+
await cronService.autosave_crontab();
|
|
221
222
|
await envService.set_envs();
|
|
222
223
|
const authInfo = await userService.getAuthInfo();
|
|
223
224
|
const apps = await openService.findApps();
|
|
@@ -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
|
};
|
|
@@ -7,7 +7,7 @@ const logger_1 = __importDefault(require("./logger"));
|
|
|
7
7
|
const sock_1 = __importDefault(require("./sock"));
|
|
8
8
|
exports.default = async ({ server }) => {
|
|
9
9
|
await (0, sock_1.default)({ server });
|
|
10
|
-
logger_1.default.info('
|
|
10
|
+
logger_1.default.info('[boot] Sock loaded');
|
|
11
11
|
process.on('uncaughtException', (error) => {
|
|
12
12
|
logger_1.default.error('Uncaught exception:', error);
|
|
13
13
|
});
|
|
@@ -33,6 +33,7 @@ var NotificationMode;
|
|
|
33
33
|
NotificationMode[NotificationMode["chronocat"] = 18] = "chronocat";
|
|
34
34
|
NotificationMode[NotificationMode["ntfy"] = 19] = "ntfy";
|
|
35
35
|
NotificationMode[NotificationMode["wxPusherBot"] = 20] = "wxPusherBot";
|
|
36
|
+
NotificationMode[NotificationMode["wxPusherSpt"] = 21] = "wxPusherSpt";
|
|
36
37
|
NotificationMode[NotificationMode["UNRECOGNIZED"] = -1] = "UNRECOGNIZED";
|
|
37
38
|
})(NotificationMode || (exports.NotificationMode = NotificationMode = {}));
|
|
38
39
|
function notificationModeFromJSON(object) {
|
|
@@ -100,6 +101,9 @@ function notificationModeFromJSON(object) {
|
|
|
100
101
|
case 20:
|
|
101
102
|
case "wxPusherBot":
|
|
102
103
|
return NotificationMode.wxPusherBot;
|
|
104
|
+
case 21:
|
|
105
|
+
case "wxPusherSpt":
|
|
106
|
+
return NotificationMode.wxPusherSpt;
|
|
103
107
|
case -1:
|
|
104
108
|
case "UNRECOGNIZED":
|
|
105
109
|
default:
|
|
@@ -151,6 +155,8 @@ function notificationModeToJSON(object) {
|
|
|
151
155
|
return "ntfy";
|
|
152
156
|
case NotificationMode.wxPusherBot:
|
|
153
157
|
return "wxPusherBot";
|
|
158
|
+
case NotificationMode.wxPusherSpt:
|
|
159
|
+
return "wxPusherSpt";
|
|
154
160
|
case NotificationMode.UNRECOGNIZED:
|
|
155
161
|
default:
|
|
156
162
|
return "UNRECOGNIZED";
|
|
@@ -2495,6 +2501,7 @@ function createBaseNotificationInfo() {
|
|
|
2495
2501
|
wxPusherBotAppToken: undefined,
|
|
2496
2502
|
wxPusherBotTopicIds: undefined,
|
|
2497
2503
|
wxPusherBotUids: undefined,
|
|
2504
|
+
wxPusherSptList: undefined,
|
|
2498
2505
|
};
|
|
2499
2506
|
}
|
|
2500
2507
|
exports.NotificationInfo = {
|
|
@@ -2706,6 +2713,9 @@ exports.NotificationInfo = {
|
|
|
2706
2713
|
if (message.wxPusherBotUids !== undefined) {
|
|
2707
2714
|
writer.uint32(546).string(message.wxPusherBotUids);
|
|
2708
2715
|
}
|
|
2716
|
+
if (message.wxPusherSptList !== undefined) {
|
|
2717
|
+
writer.uint32(562).string(message.wxPusherSptList);
|
|
2718
|
+
}
|
|
2709
2719
|
return writer;
|
|
2710
2720
|
},
|
|
2711
2721
|
decode(input, length) {
|
|
@@ -3198,6 +3208,13 @@ exports.NotificationInfo = {
|
|
|
3198
3208
|
message.wxPusherBotUids = reader.string();
|
|
3199
3209
|
continue;
|
|
3200
3210
|
}
|
|
3211
|
+
case 70: {
|
|
3212
|
+
if (tag !== 562) {
|
|
3213
|
+
break;
|
|
3214
|
+
}
|
|
3215
|
+
message.wxPusherSptList = reader.string();
|
|
3216
|
+
continue;
|
|
3217
|
+
}
|
|
3201
3218
|
}
|
|
3202
3219
|
if ((tag & 7) === 4 || tag === 0) {
|
|
3203
3220
|
break;
|
|
@@ -3289,6 +3306,7 @@ exports.NotificationInfo = {
|
|
|
3289
3306
|
? globalThis.String(object.wxPusherBotTopicIds)
|
|
3290
3307
|
: undefined,
|
|
3291
3308
|
wxPusherBotUids: isSet(object.wxPusherBotUids) ? globalThis.String(object.wxPusherBotUids) : undefined,
|
|
3309
|
+
wxPusherSptList: isSet(object.wxPusherSptList) ? globalThis.String(object.wxPusherSptList) : undefined,
|
|
3292
3310
|
};
|
|
3293
3311
|
},
|
|
3294
3312
|
toJSON(message) {
|
|
@@ -3500,13 +3518,16 @@ exports.NotificationInfo = {
|
|
|
3500
3518
|
if (message.wxPusherBotUids !== undefined) {
|
|
3501
3519
|
obj.wxPusherBotUids = message.wxPusherBotUids;
|
|
3502
3520
|
}
|
|
3521
|
+
if (message.wxPusherSptList !== undefined) {
|
|
3522
|
+
obj.wxPusherSptList = message.wxPusherSptList;
|
|
3523
|
+
}
|
|
3503
3524
|
return obj;
|
|
3504
3525
|
},
|
|
3505
3526
|
create(base) {
|
|
3506
3527
|
return exports.NotificationInfo.fromPartial(base !== null && base !== void 0 ? base : {});
|
|
3507
3528
|
},
|
|
3508
3529
|
fromPartial(object) {
|
|
3509
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44;
|
|
3530
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45;
|
|
3510
3531
|
const message = createBaseNotificationInfo();
|
|
3511
3532
|
message.type = (_a = object.type) !== null && _a !== void 0 ? _a : 0;
|
|
3512
3533
|
message.gotifyUrl = (_b = object.gotifyUrl) !== null && _b !== void 0 ? _b : undefined;
|
|
@@ -3577,6 +3598,7 @@ exports.NotificationInfo = {
|
|
|
3577
3598
|
message.wxPusherBotAppToken = (_42 = object.wxPusherBotAppToken) !== null && _42 !== void 0 ? _42 : undefined;
|
|
3578
3599
|
message.wxPusherBotTopicIds = (_43 = object.wxPusherBotTopicIds) !== null && _43 !== void 0 ? _43 : undefined;
|
|
3579
3600
|
message.wxPusherBotUids = (_44 = object.wxPusherBotUids) !== null && _44 !== void 0 ? _44 : undefined;
|
|
3601
|
+
message.wxPusherSptList = (_45 = object.wxPusherSptList) !== null && _45 !== void 0 ? _45 : undefined;
|
|
3580
3602
|
return message;
|
|
3581
3603
|
},
|
|
3582
3604
|
};
|
|
@@ -4,14 +4,59 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.addCron = void 0;
|
|
7
|
+
const grpc_js_1 = require("@grpc/grpc-js");
|
|
7
8
|
const node_schedule_1 = __importDefault(require("node-schedule"));
|
|
8
9
|
const data_1 = require("./data");
|
|
9
10
|
const runCron_1 = require("../shared/runCron");
|
|
10
11
|
const logger_1 = __importDefault(require("../loaders/logger"));
|
|
12
|
+
const i18n_1 = require("../shared/i18n");
|
|
13
|
+
/**
|
|
14
|
+
* 预校验 cron 表达式,检测 node-schedule 会拒绝但 cron-parser 会接受的 pattern。
|
|
15
|
+
* node-schedule 对 bare /N(字段以 / 开头,如前无星号/数字前缀的 /6)返回 null,
|
|
16
|
+
* 提前拦截避免走 scheduleJob 后才发现无效。
|
|
17
|
+
*/
|
|
18
|
+
const isValidCronField = (cron) => {
|
|
19
|
+
// 检测 bare /N 模式:字段以 / 开头如 "/6",或空格后紧跟 "/6"
|
|
20
|
+
// node-schedule 会对这种字段返回 null
|
|
21
|
+
if (/\s\/\d/.test(cron) || /^\/\d/.test(cron)) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
// 检测 ? 字符:Quartz cron 语法,node-schedule 在大多数位置返回 null
|
|
25
|
+
// cron-parser 接受但 node-schedule 拒绝,提前拦截
|
|
26
|
+
if (/\?/.test(cron)) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
return true;
|
|
30
|
+
};
|
|
11
31
|
const addCron = (call, callback) => {
|
|
12
32
|
var _a;
|
|
33
|
+
// ===== 第一遍:预校验所有 cron 表达式 =====
|
|
34
|
+
const validationErrors = [];
|
|
35
|
+
for (const item of call.request.crons) {
|
|
36
|
+
const { id, schedule, extra_schedules } = item;
|
|
37
|
+
if (!isValidCronField(schedule)) {
|
|
38
|
+
validationErrors.push((0, i18n_1.tf)('任务ID %s: 无效的 cron 表达式 "%s"(不支持裸 /N 步长和 ? 字符)', String(id), schedule));
|
|
39
|
+
}
|
|
40
|
+
if (extra_schedules === null || extra_schedules === void 0 ? void 0 : extra_schedules.length) {
|
|
41
|
+
extra_schedules.forEach((x) => {
|
|
42
|
+
if (!isValidCronField(x.schedule)) {
|
|
43
|
+
validationErrors.push((0, i18n_1.tf)('任务ID %s (extra_schedule): 无效的 cron 表达式 "%s"(不支持裸 /N 步长和 ? 字符)', String(id), x.schedule));
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (validationErrors.length > 0) {
|
|
49
|
+
const details = validationErrors.join('\n');
|
|
50
|
+
const err = new Error(details);
|
|
51
|
+
err.code = grpc_js_1.status.INVALID_ARGUMENT;
|
|
52
|
+
err.details = details;
|
|
53
|
+
callback(err, null);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
// ===== 第二遍:注册所有任务 =====
|
|
13
57
|
for (const item of call.request.crons) {
|
|
14
58
|
const { id, schedule, command, extra_schedules, name } = item;
|
|
59
|
+
// 取消该 id 已有的旧任务
|
|
15
60
|
if (data_1.scheduleStacks.has(id)) {
|
|
16
61
|
(_a = data_1.scheduleStacks.get(id)) === null || _a === void 0 ? void 0 : _a.forEach((x) => x.cancel());
|
|
17
62
|
}
|
|
@@ -21,18 +66,30 @@ const addCron = (call, callback) => {
|
|
|
21
66
|
logger_1.default.info('[schedule][创建定时任务] 任务ID: %s, 名称: %s, cron: %s, 执行命令: %s', id, name, x.schedule, command);
|
|
22
67
|
});
|
|
23
68
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
69
|
+
const mainJob = node_schedule_1.default.scheduleJob(id, schedule, async () => {
|
|
70
|
+
logger_1.default.info(`[schedule][准备运行任务] 命令: ${command}`);
|
|
71
|
+
(0, runCron_1.runCron)(command, item);
|
|
72
|
+
});
|
|
73
|
+
if (!mainJob) {
|
|
74
|
+
logger_1.default.warn('[schedule][创建定时任务] scheduleJob 返回 null(不符合预期,已通过预校验): 任务ID: %s, cron: %s', id, schedule);
|
|
75
|
+
}
|
|
76
|
+
const extraJobs = (extra_schedules === null || extra_schedules === void 0 ? void 0 : extra_schedules.length)
|
|
77
|
+
? extra_schedules.map((x) => {
|
|
78
|
+
const job = node_schedule_1.default.scheduleJob(id, x.schedule, async () => {
|
|
31
79
|
logger_1.default.info(`[schedule][准备运行任务] 命令: ${command}`);
|
|
32
80
|
(0, runCron_1.runCron)(command, item);
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
|
|
81
|
+
});
|
|
82
|
+
if (!job) {
|
|
83
|
+
logger_1.default.warn('[schedule][创建定时任务] scheduleJob 返回 null(不符合预期,已通过预校验): 任务ID: %s, cron: %s', id, x.schedule);
|
|
84
|
+
}
|
|
85
|
+
return job;
|
|
86
|
+
})
|
|
87
|
+
: [];
|
|
88
|
+
// 过滤 null(兜底保护,正常情况下预校验已拦截)
|
|
89
|
+
const jobs = [mainJob, ...extraJobs].filter((x) => x != null);
|
|
90
|
+
if (jobs.length > 0) {
|
|
91
|
+
data_1.scheduleStacks.set(id, jobs);
|
|
92
|
+
}
|
|
36
93
|
}
|
|
37
94
|
callback(null, null);
|
|
38
95
|
};
|
|
@@ -11,7 +11,17 @@ const delCron = (call, callback) => {
|
|
|
11
11
|
for (const id of call.request.ids) {
|
|
12
12
|
if (data_1.scheduleStacks.has(id)) {
|
|
13
13
|
logger_1.default.info('[schedule][取消定时任务] 任务ID: %s', id);
|
|
14
|
-
|
|
14
|
+
// 过滤掉 nodeSchedule.scheduleJob() 对无效表达式返回的 null,
|
|
15
|
+
// 否则对 null 调 cancel() 会让整个取消流程抛出 UNKNOWN 错误,
|
|
16
|
+
// 进而导致 HTTP 端的 remove() 跳过 setCrontab(),造成 crontab.list 残留。
|
|
17
|
+
(_a = data_1.scheduleStacks.get(id)) === null || _a === void 0 ? void 0 : _a.filter((x) => x != null).forEach((x) => {
|
|
18
|
+
try {
|
|
19
|
+
x.cancel();
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
logger_1.default.warn('[schedule][取消任务失败] 任务ID: %s, 错误: %s', id, (error === null || error === void 0 ? void 0 : error.message) || error);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
15
25
|
data_1.scheduleStacks.delete(id);
|
|
16
26
|
}
|
|
17
27
|
}
|