@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.
Files changed (112) hide show
  1. package/AGENTS.md +2 -2
  2. package/back/protos/api.proto +3 -0
  3. package/deploy/kubernetes/README.md +81 -0
  4. package/deploy/kubernetes/base/kustomization.yaml +5 -0
  5. package/deploy/kubernetes/base/qinglong.yaml +95 -0
  6. package/deploy/kubernetes/overlays/example/kustomization.yaml +10 -0
  7. package/package.json +2 -4
  8. package/sample/config.sample.sh +7 -2
  9. package/sample/notify.js +64 -0
  10. package/sample/notify.py +56 -0
  11. package/shell/api.sh +7 -1
  12. package/shell/share.sh +1 -1
  13. package/static/build/api/cron.js +3 -3
  14. package/static/build/api/dashboard.js +13 -6
  15. package/static/build/api/env.js +2 -2
  16. package/static/build/api/log.js +10 -1
  17. package/static/build/api/system.js +6 -7
  18. package/static/build/app.js +19 -19
  19. package/static/build/config/const.js +1 -0
  20. package/static/build/config/grpcCerts.js +4 -4
  21. package/static/build/config/index.js +0 -4
  22. package/static/build/config/util.js +3 -3
  23. package/static/build/data/notify.js +8 -1
  24. package/static/build/data/sock.js +1 -0
  25. package/static/build/loaders/app.js +6 -6
  26. package/static/build/loaders/db.js +2 -2
  27. package/static/build/loaders/express.js +17 -14
  28. package/static/build/loaders/initData.js +8 -7
  29. package/static/build/loaders/initFile.js +1 -1
  30. package/static/build/loaders/initTask.js +3 -2
  31. package/static/build/loaders/server.js +1 -1
  32. package/static/build/protos/api.js +23 -1
  33. package/static/build/schedule/addCron.js +67 -10
  34. package/static/build/schedule/delCron.js +11 -1
  35. package/static/build/services/cron.js +103 -53
  36. package/static/build/services/dependence.js +19 -18
  37. package/static/build/services/grpc.js +1 -1
  38. package/static/build/services/http.js +1 -1
  39. package/static/build/services/notify.js +47 -5
  40. package/static/build/services/subscription.js +6 -4
  41. package/static/build/services/system.js +12 -2
  42. package/static/build/services/user.js +31 -6
  43. package/static/build/shared/auth.js +7 -1
  44. package/static/build/shared/i18n.js +59 -4
  45. package/static/build/shared/pLimit.js +3 -2
  46. package/static/build/shared/store.js +7 -0
  47. package/static/build/validation/schedule.js +9 -0
  48. package/static/dist/1147.62793604.async.js +1 -0
  49. package/static/dist/2208.85285476.async.js +1 -0
  50. package/static/dist/2286.f371c743.async.js +1 -0
  51. package/static/dist/3191.545ab873.async.js +1 -0
  52. package/static/dist/8826.db34ad8d.async.js +1 -0
  53. package/static/dist/index.html +2 -2
  54. package/static/dist/{preload_helper.0431c0f3.js → preload_helper.3048fe5b.js} +1 -1
  55. package/static/dist/src__pages__crontab__detail.b98f0084.async.js +1 -0
  56. package/static/dist/src__pages__crontab__index.68e78a33.async.js +1 -0
  57. package/static/dist/{src__pages__crontab__logModal.0b8cce8c.async.js → src__pages__crontab__logModal.831d5e4b.async.js} +1 -1
  58. package/static/dist/src__pages__dashboard__index.58177357.async.js +1 -0
  59. package/static/dist/src__pages__dependence__index.b7bbc795.async.js +1 -0
  60. package/static/dist/src__pages__dependence__modal.f7bb47ba.async.js +1 -0
  61. package/static/dist/src__pages__env__editNameModal.48715576.async.js +1 -0
  62. package/static/dist/src__pages__env__index.452cdc47.async.js +1 -0
  63. package/static/dist/src__pages__env__modal.e1234834.async.js +1 -0
  64. package/static/dist/src__pages__error__index.dedb2b90.async.js +1 -0
  65. package/static/dist/src__pages__initialization__index.22620a77.async.js +1 -0
  66. package/static/dist/src__pages__log__index.9c66639f.async.js +1 -0
  67. package/static/dist/src__pages__script__editNameModal.532a3044.async.js +1 -0
  68. package/static/dist/src__pages__script__index.1fc20f22.async.js +1 -0
  69. package/static/dist/src__pages__script__renameModal.5cdde919.async.js +1 -0
  70. package/static/dist/src__pages__script__saveModal.5ffed35f.async.js +1 -0
  71. package/static/dist/src__pages__script__setting.1a82c6d7.async.js +1 -0
  72. package/static/dist/src__pages__setting__appModal.43b7758a.async.js +1 -0
  73. package/static/dist/src__pages__setting__checkUpdate.34d773f2.async.js +1 -0
  74. package/static/dist/src__pages__setting__dependence.9c04c79a.async.js +1 -0
  75. package/static/dist/src__pages__setting__index.7e070838.async.js +1 -0
  76. package/static/dist/src__pages__setting__notification.bac481c2.async.js +1 -0
  77. package/static/dist/src__pages__setting__other.707cfafe.async.js +1 -0
  78. package/static/dist/src__pages__setting__security.b7235753.async.js +1 -0
  79. package/static/dist/src__pages__setting__systemLog.b81a8c1d.async.js +1 -0
  80. package/static/dist/src__pages__subscription__modal.562448e2.async.js +1 -0
  81. package/static/dist/{umi.ca04a019.js → umi.a54a59f6.js} +1 -1
  82. package/CLAUDE.md +0 -43
  83. package/static/dist/1147.c420132e.async.js +0 -1
  84. package/static/dist/2208.f7ba3dfa.async.js +0 -1
  85. package/static/dist/2286.164bb089.async.js +0 -1
  86. package/static/dist/3191.63263871.async.js +0 -1
  87. package/static/dist/8826.2447a104.async.js +0 -1
  88. package/static/dist/src__pages__crontab__detail.b9c36808.async.js +0 -1
  89. package/static/dist/src__pages__crontab__index.fed99fd0.async.js +0 -1
  90. package/static/dist/src__pages__dashboard__index.b30f2f47.async.js +0 -1
  91. package/static/dist/src__pages__dependence__index.3d87ceac.async.js +0 -1
  92. package/static/dist/src__pages__dependence__modal.11124896.async.js +0 -1
  93. package/static/dist/src__pages__env__editNameModal.5d264b25.async.js +0 -1
  94. package/static/dist/src__pages__env__index.baa27d4e.async.js +0 -1
  95. package/static/dist/src__pages__env__modal.7f2ef1bc.async.js +0 -1
  96. package/static/dist/src__pages__error__index.f156b45e.async.js +0 -1
  97. package/static/dist/src__pages__initialization__index.e96d4ba8.async.js +0 -1
  98. package/static/dist/src__pages__log__index.e0978bae.async.js +0 -1
  99. package/static/dist/src__pages__script__editNameModal.3e7e100d.async.js +0 -1
  100. package/static/dist/src__pages__script__index.2765d1b8.async.js +0 -1
  101. package/static/dist/src__pages__script__renameModal.5e987ef5.async.js +0 -1
  102. package/static/dist/src__pages__script__saveModal.3f9d23d6.async.js +0 -1
  103. package/static/dist/src__pages__script__setting.a535793a.async.js +0 -1
  104. package/static/dist/src__pages__setting__appModal.251cd14f.async.js +0 -1
  105. package/static/dist/src__pages__setting__checkUpdate.46efe057.async.js +0 -1
  106. package/static/dist/src__pages__setting__dependence.8a7a9529.async.js +0 -1
  107. package/static/dist/src__pages__setting__index.cc85fdfb.async.js +0 -1
  108. package/static/dist/src__pages__setting__notification.390fc905.async.js +0 -1
  109. package/static/dist/src__pages__setting__other.b22d2165.async.js +0 -1
  110. package/static/dist/src__pages__setting__security.598720a8.async.js +0 -1
  111. package/static/dist/src__pages__setting__systemLog.67406721.async.js +0 -1
  112. package/static/dist/src__pages__subscription__modal.3562c670.async.js +0 -1
@@ -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('✌️ gRPC worker is ready, starting HTTP worker');
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('✌️ Failed to wait for gRPC worker:', 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(`✌️ ${metadata.serviceType} worker ${worker.process.pid} died (${signal || code}). Restarting...`);
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('✌️ gRPC worker restarted and ready');
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('✌️ Sent reregister-crons message to HTTP worker');
97
+ logger_1.default.info('Sent reregister-crons message to HTTP worker');
98
98
  }
99
99
  catch (error) {
100
- logger_1.default.error('✌️ Failed to send reregister-crons message:', 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('✌️ Failed to restart gRPC worker:', 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(`✌️ Restarted ${metadata.serviceType} worker (PID: ${newWorker.process.pid})`);
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(`✌️ Worker ${worker.process.pid} exited`);
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(`✌️ Failed to send shutdown to worker ${worker.process.pid}:`, error);
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('✌️ Worker shutdown timeout reached');
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('✌️ Error during worker shutdown:', 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('✌️ Invalid SERVICE_TYPE:', serviceType);
208
+ logger_1.default.error('[boot] Invalid SERVICE_TYPE:', serviceType);
209
209
  process.exit(1);
210
210
  }
211
- logger_1.default.info(`✌️ ${serviceType} worker started (PID: ${process.pid})`);
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(`✌️ ${serviceType} worker failed:`, 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('✌️ Received reregister-crons message, re-registering cron jobs...');
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('✌️ Cron jobs re-registered successfully');
258
+ logger_1.default.info('[boot] Cron jobs re-registered successfully');
259
259
  }
260
260
  catch (error) {
261
- logger_1.default.error('✌️ Failed to re-register cron jobs:', 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(`✌️ [${serviceType}] Error during shutdown:`, error);
284
+ logger_1.default.error(`[${serviceType}] Error during shutdown:`, error);
285
285
  process.exit(1);
286
286
  }
287
287
  }
@@ -47,6 +47,7 @@ exports.NotificationModeStringMap = {
47
47
  18: 'chronocat',
48
48
  19: 'ntfy',
49
49
  20: 'wxPusherBot',
50
+ 21: 'wxPusherSpt',
50
51
  };
51
52
  exports.LINUX_DEPENDENCE_COMMAND = {
52
53
  Debian: {
@@ -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失败]', error);
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失败]', error);
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失败]', error);
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);
@@ -6,6 +6,7 @@ class SockMessage {
6
6
  this.type = options.type;
7
7
  this.message = options.message;
8
8
  this.references = options.references;
9
+ this.status = options.status;
9
10
  }
10
11
  }
11
12
  exports.SockMessage = SockMessage;
@@ -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('✌️ Dependency loaded');
15
+ logger_1.default.info('[boot] Dependency loaded');
16
16
  await (0, deps_1.default)();
17
- logger_1.default.info('✌️ Link deps loaded');
17
+ logger_1.default.info('[boot] Link deps loaded');
18
18
  await (0, initFile_1.default)();
19
- logger_1.default.info('✌️ Init file loaded');
19
+ logger_1.default.info('[boot] Init file loaded');
20
20
  await (0, initData_1.default)();
21
- logger_1.default.info('✌️ Init data loaded');
21
+ logger_1.default.info('[boot] Init data loaded');
22
22
  (0, initTask_1.default)();
23
- logger_1.default.info('✌️ Init task loaded');
23
+ logger_1.default.info('[boot] Init task loaded');
24
24
  (0, express_1.default)({ app });
25
- logger_1.default.info('✌️ Express loaded');
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('✌️ DB loaded');
61
+ logger_1.default.info('[boot] DB loaded');
62
62
  }
63
63
  catch (error) {
64
- logger_1.default.error('✌️ DB load failed', 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
- currentToken &&
83
- currentToken.expiration >= Math.round(Date.now() / 1000)) {
84
- return next();
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}${req.path === '/' ? '' : req.path}`;
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
- ? 'jwt malformed'
101
- : 'No authorization token was found';
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(req.path)) {
120
+ ].includes(pathLower)) {
113
121
  return next();
114
122
  }
115
123
  const authInfo = (await store_1.shareStore.getAuthInfo()) || {};
116
- let isInitialized = true;
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, _j;
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
- // 初始化保存一次ck和定时任务数据
210
- await cronService.autosave_crontab();
211
- // 确保 lang_env.sh 存在,提供默认 QL_LANG
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 (_k) { }
220
- (0, i18n_1.setLang)(((_j = systemConfig.info) === null || _j === void 0 ? void 0 : _j.lang) || 'zh');
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();
@@ -117,6 +117,6 @@ exports.default = async () => {
117
117
  await (0, utils_1.writeFileWithLock)(item.target, content);
118
118
  }
119
119
  }
120
- logger_1.default.info('✌️ Init file down');
120
+ logger_1.default.info('[boot] Init file down');
121
121
  };
122
122
  //# sourceMappingURL=initFile.js.map
@@ -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('✌️ Sock loaded');
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
- data_1.scheduleStacks.set(id, [
25
- node_schedule_1.default.scheduleJob(id, schedule, async () => {
26
- logger_1.default.info(`[schedule][准备运行任务] 命令: ${command}`);
27
- (0, runCron_1.runCron)(command, item);
28
- }),
29
- ...((extra_schedules === null || extra_schedules === void 0 ? void 0 : extra_schedules.length)
30
- ? extra_schedules.map((x) => node_schedule_1.default.scheduleJob(id, x.schedule, async () => {
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
- (_a = data_1.scheduleStacks.get(id)) === null || _a === void 0 ? void 0 : _a.forEach(x => x.cancel());
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
  }