@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
@@ -105,15 +105,24 @@ let CronService = class CronService {
105
105
  return doc;
106
106
  }
107
107
  if (this.shouldUseCronClient(doc)) {
108
- await client_1.default.addCron([
109
- {
110
- name: doc.name || '',
111
- id: String(doc.id),
112
- schedule: doc.schedule,
113
- command: this.makeCommand(doc),
114
- extra_schedules: doc.extra_schedules || [],
115
- },
116
- ]);
108
+ try {
109
+ await client_1.default.addCron([
110
+ {
111
+ name: doc.name || '',
112
+ id: String(doc.id),
113
+ schedule: doc.schedule,
114
+ command: this.makeCommand(doc),
115
+ extra_schedules: doc.extra_schedules || [],
116
+ },
117
+ ]);
118
+ }
119
+ catch (error) {
120
+ // gRPC 注册失败时回滚 DB 记录,避免产生"僵尸任务"
121
+ // (DB 和 crontab.list 有记录但调度器永远不会执行)
122
+ await cron_1.CrontabModel.destroy({ where: { id: doc.id } });
123
+ this.logger.error('[crontab] Failed to register cron job in scheduler, task creation rolled back:', (error === null || error === void 0 ? void 0 : error.message) || error);
124
+ throw new Error(`${(0, i18n_1.t)('调度器注册失败,任务创建已回滚')}: ${(error === null || error === void 0 ? void 0 : error.details) || (error === null || error === void 0 ? void 0 : error.message)}`);
125
+ }
117
126
  }
118
127
  await this.setCrontab();
119
128
  return doc;
@@ -130,17 +139,46 @@ let CronService = class CronService {
130
139
  if (doc.isDisabled === 1 || (0, util_1.isDemoEnv)()) {
131
140
  return newDoc;
132
141
  }
133
- await client_1.default.delCron([String(newDoc.id)]);
142
+ try {
143
+ await client_1.default.delCron([String(newDoc.id)]);
144
+ }
145
+ catch (error) {
146
+ this.logger.warn('[crontab] Failed to unregister cron job in scheduler:', (error === null || error === void 0 ? void 0 : error.message) || error);
147
+ }
134
148
  if (this.shouldUseCronClient(newDoc)) {
135
- await client_1.default.addCron([
136
- {
137
- name: doc.name || '',
138
- id: String(newDoc.id),
139
- schedule: newDoc.schedule,
140
- command: this.makeCommand(newDoc),
141
- extra_schedules: newDoc.extra_schedules || [],
142
- },
143
- ]);
149
+ try {
150
+ await client_1.default.addCron([
151
+ {
152
+ name: doc.name || '',
153
+ id: String(newDoc.id),
154
+ schedule: newDoc.schedule,
155
+ command: this.makeCommand(newDoc),
156
+ extra_schedules: newDoc.extra_schedules || [],
157
+ },
158
+ ]);
159
+ }
160
+ catch (error) {
161
+ // gRPC 注册新任务失败 → 回滚 DB 到旧数据,并尝试恢复旧调度注册
162
+ await cron_1.CrontabModel.update(doc, { where: { id: doc.id } });
163
+ if (this.shouldUseCronClient(doc)) {
164
+ try {
165
+ await client_1.default.addCron([
166
+ {
167
+ name: doc.name || '',
168
+ id: String(doc.id),
169
+ schedule: doc.schedule,
170
+ command: this.makeCommand(doc),
171
+ extra_schedules: doc.extra_schedules || [],
172
+ },
173
+ ]);
174
+ }
175
+ catch (_recoveryError) {
176
+ this.logger.warn('[crontab] Failed to restore old cron job in scheduler after rollback:', (_recoveryError === null || _recoveryError === void 0 ? void 0 : _recoveryError.message) || _recoveryError);
177
+ }
178
+ }
179
+ this.logger.error('[crontab] Failed to register updated cron job in scheduler, update rolled back:', (error === null || error === void 0 ? void 0 : error.message) || error);
180
+ throw new Error(`${(0, i18n_1.t)('调度器注册失败,任务更新已回滚')}: ${(error === null || error === void 0 ? void 0 : error.details) || (error === null || error === void 0 ? void 0 : error.message)}`);
181
+ }
144
182
  }
145
183
  await this.setCrontab();
146
184
  return newDoc;
@@ -149,7 +187,7 @@ let CronService = class CronService {
149
187
  await cron_1.CrontabModel.update(payload, { where: { id: payload.id } });
150
188
  return await this.getDb({ id: payload.id });
151
189
  }
152
- async status({ ids, status, pid, log_path, last_running_time = 0, last_execution_time = 0, }) {
190
+ async status({ ids, status, pid, log_path, last_running_time = 0, last_execution_time = 0, exit_code, }) {
153
191
  let options = {
154
192
  status,
155
193
  pid,
@@ -185,9 +223,13 @@ let CronService = class CronService {
185
223
  else if (status === cron_1.CrontabStatus.idle) {
186
224
  // Mark the matching running instance as finished
187
225
  const finishedAt = (0, dayjs_1.default)().unix();
226
+ const instanceStatus = exit_code !== undefined && exit_code !== null && exit_code !== 0
227
+ ? runningInstance_1.InstanceStatus.error
228
+ : runningInstance_1.InstanceStatus.finished;
188
229
  await runningInstance_1.RunningInstanceModel.update({
189
230
  finished_at: finishedAt,
190
- status: runningInstance_1.InstanceStatus.finished,
231
+ status: instanceStatus,
232
+ exit_code: exit_code !== null && exit_code !== void 0 ? exit_code : undefined,
191
233
  }, {
192
234
  where: {
193
235
  cron_id: id,
@@ -201,7 +243,12 @@ let CronService = class CronService {
201
243
  }
202
244
  async remove(ids) {
203
245
  await cron_1.CrontabModel.destroy({ where: { id: ids } });
204
- await client_1.default.delCron(ids.map(String));
246
+ try {
247
+ await client_1.default.delCron(ids.map(String));
248
+ }
249
+ catch (error) {
250
+ this.logger.warn('[crontab] Failed to unregister cron job in scheduler:', (error === null || error === void 0 ? void 0 : error.message) || error);
251
+ }
205
252
  await this.setCrontab();
206
253
  }
207
254
  async pin(ids) {
@@ -486,7 +533,7 @@ let CronService = class CronService {
486
533
  this.logger.error(`[panel][停止实例失败] 实例ID: ${instanceId}, PID: ${instance.pid}, 错误: ${error}`);
487
534
  }
488
535
  }
489
- await runningInstance_1.RunningInstanceModel.update({ status: runningInstance_1.InstanceStatus.stopped, finished_at: (0, dayjs_1.default)().unix() }, { where: { id: instanceId } });
536
+ await runningInstance_1.RunningInstanceModel.update({ status: runningInstance_1.InstanceStatus.stopped, finished_at: (0, dayjs_1.default)().unix(), exit_code: 143 }, { where: { id: instanceId } });
490
537
  // Check if there are still other running instances for this cron
491
538
  const otherRunning = await runningInstance_1.RunningInstanceModel.count({
492
539
  where: { cron_id: instance.cron_id, status: runningInstance_1.InstanceStatus.running },
@@ -521,14 +568,6 @@ let CronService = class CronService {
521
568
  const logPath = `${uniqPath}/${logTime}.log`;
522
569
  const absolutePath = path_1.default.resolve(config_1.default.logPath, `${logPath}`);
523
570
  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
- });
532
571
  await cron_1.CrontabModel.update({ status: cron_1.CrontabStatus.running, pid: cp.pid, log_path: logPath }, { where: { id } });
533
572
  cp.stdout.on('data', async (data) => {
534
573
  await logStreamManager_1.logStreamManager.write(absolutePath, data.toString());
@@ -544,19 +583,6 @@ let CronService = class CronService {
544
583
  cp.on('exit', async (code) => {
545
584
  this.logger.info('[panel][执行任务结束] 参数: %s, 退出码: %j', JSON.stringify(params), code);
546
585
  await logStreamManager_1.logStreamManager.closeStream(absolutePath);
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
- }
560
586
  resolve(Object.assign(Object.assign({}, params), { pid: cp.pid, code }));
561
587
  });
562
588
  });
@@ -564,7 +590,12 @@ let CronService = class CronService {
564
590
  }
565
591
  async disabled(ids) {
566
592
  await cron_1.CrontabModel.update({ isDisabled: 1 }, { where: { id: ids } });
567
- await client_1.default.delCron(ids.map(String));
593
+ try {
594
+ await client_1.default.delCron(ids.map(String));
595
+ }
596
+ catch (error) {
597
+ this.logger.warn('[crontab] Failed to unregister cron job in scheduler:', (error === null || error === void 0 ? void 0 : error.message) || error);
598
+ }
568
599
  await this.setCrontab();
569
600
  }
570
601
  async enabled(ids) {
@@ -582,27 +613,38 @@ let CronService = class CronService {
582
613
  if ((0, util_1.isDemoEnv)()) {
583
614
  return;
584
615
  }
585
- await client_1.default.addCron(crons);
616
+ try {
617
+ await client_1.default.addCron(crons);
618
+ }
619
+ catch (error) {
620
+ // gRPC 注册失败 → 回滚启用状态,避免 DB 显示已启用但调度器未注册
621
+ await cron_1.CrontabModel.update({ isDisabled: 1 }, { where: { id: ids } });
622
+ this.logger.error('[crontab] Failed to register cron job in scheduler, enable rolled back:', (error === null || error === void 0 ? void 0 : error.message) || error);
623
+ throw new Error(`${(0, i18n_1.t)('调度器注册失败,任务启用已回滚')}: ${(error === null || error === void 0 ? void 0 : error.details) || (error === null || error === void 0 ? void 0 : error.message)}`);
624
+ }
586
625
  await this.setCrontab();
587
626
  }
588
627
  async log(id) {
589
628
  const doc = await this.getDb({ id });
590
629
  if (!doc) {
591
- return '';
630
+ return { content: '', status: 'empty' };
592
631
  }
593
632
  if (doc.log_name === '/dev/null') {
594
- return '日志设置为忽略';
633
+ return { content: (0, i18n_1.t)('日志设置为忽略'), status: 'ignored' };
595
634
  }
596
635
  const absolutePath = path_1.default.resolve(config_1.default.logPath, `${doc.log_path}`);
597
636
  const logFileExist = doc.log_path && (await (0, util_1.fileExist)(absolutePath));
598
637
  if (logFileExist) {
599
- return await (0, util_1.getFileContentByName)(`${absolutePath}`);
638
+ const content = await (0, util_1.getFileContentByName)(`${absolutePath}`);
639
+ const isRunning = typeof doc.status === 'number' &&
640
+ [cron_1.CrontabStatus.running, cron_1.CrontabStatus.queued].includes(doc.status);
641
+ return { content, status: isRunning ? 'running' : 'completed' };
600
642
  }
601
643
  else {
602
644
  return typeof doc.status === 'number' &&
603
645
  [cron_1.CrontabStatus.queued, cron_1.CrontabStatus.running].includes(doc.status)
604
- ? '运行中...'
605
- : '日志不存在...';
646
+ ? { content: (0, i18n_1.t)('运行中...'), status: 'running' }
647
+ : { content: (0, i18n_1.t)('日志不存在...'), status: 'notFound' };
606
648
  }
607
649
  }
608
650
  async logs(id) {
@@ -733,8 +775,16 @@ let CronService = class CronService {
733
775
  await (0, utils_1.writeFileWithLock)(config_1.default.crontabFile, '');
734
776
  return;
735
777
  }
736
- await client_1.default.addCron(regularCrons);
737
- this.setCrontab(tabs);
778
+ // 先同步 crontab.list 与系统 crontab,确保其始终反映数据库真实状态。
779
+ // gRPC 调度注册为尽力而为:失败时不阻断文件同步,调度器重启后会重新注册。
780
+ // 这避免了因调度器短暂不可用导致 crontab.list 与数据库脱节(订阅更新误判任务已存在)。
781
+ await this.setCrontab(tabs);
782
+ try {
783
+ await client_1.default.addCron(regularCrons);
784
+ }
785
+ catch (error) {
786
+ this.logger.warn('[crontab] Failed to register cron job in scheduler:', (error === null || error === void 0 ? void 0 : error.message) || error);
787
+ }
738
788
  }
739
789
  async bootTask() {
740
790
  const tabs = await this.crontabs();
@@ -38,6 +38,7 @@ const dayjs_1 = __importDefault(require("dayjs"));
38
38
  const pLimit_1 = __importDefault(require("../shared/pLimit"));
39
39
  const util_2 = require("../config/util");
40
40
  const const_1 = require("../config/const");
41
+ const i18n_1 = require("../shared/i18n");
41
42
  let DependenceService = class DependenceService {
42
43
  constructor(logger, sockService) {
43
44
  this.logger = logger;
@@ -183,7 +184,7 @@ let DependenceService = class DependenceService {
183
184
  }
184
185
  const depIds = [dependency.id];
185
186
  let depName = dependency.name.trim();
186
- const actionText = isInstall ? '安装' : '删除';
187
+ const actionText = isInstall ? (0, i18n_1.t)('安装') : (0, i18n_1.t)('删除');
187
188
  const socketMessageType = isInstall
188
189
  ? 'installDependence'
189
190
  : 'uninstallDependence';
@@ -197,11 +198,12 @@ let DependenceService = class DependenceService {
197
198
  if (!osType) {
198
199
  await dependence_1.DependenceModel.update({ status: dependence_1.DependenceStatus.installFailed }, { where: { id: depIds } });
199
200
  const startTime = (0, dayjs_1.default)();
200
- const message = `开始${actionText}依赖 ${depName},开始时间 ${startTime.format('YYYY-MM-DD HH:mm:ss')}\n\n当前系统不支持\n\n依赖${actionText}失败,结束时间 ${startTime.format('YYYY-MM-DD HH:mm:ss')},耗时 ${startTime.diff(startTime, 'second')} 秒`;
201
+ const message = (0, i18n_1.tf)('开始%s依赖 %s,开始时间 %s\n\n当前系统不支持\n\n依赖%s失败,结束时间 %s,耗时 %s 秒', actionText, depName, startTime.format('YYYY-MM-DD HH:mm:ss'), actionText, startTime.format('YYYY-MM-DD HH:mm:ss'), String(startTime.diff(startTime, 'second')));
201
202
  this.sockService.sendMessage({
202
203
  type: socketMessageType,
203
204
  message,
204
205
  references: depIds,
206
+ status: dependence_1.DependenceStatus.installFailed,
205
207
  });
206
208
  this.updateLog(depIds, message);
207
209
  return resolve(null);
@@ -221,11 +223,12 @@ let DependenceService = class DependenceService {
221
223
  : `${linuxCommand.uninstall} ${depName.trim()}`;
222
224
  }
223
225
  const startTime = (0, dayjs_1.default)();
224
- const message = `开始${actionText}依赖 ${depName},开始时间 ${startTime.format('YYYY-MM-DD HH:mm:ss')}\n\n`;
226
+ const message = (0, i18n_1.tf)('开始%s依赖 %s,开始时间 %s\n\n', actionText, depName, startTime.format('YYYY-MM-DD HH:mm:ss'));
225
227
  this.sockService.sendMessage({
226
228
  type: socketMessageType,
227
229
  message,
228
230
  references: depIds,
231
+ status,
229
232
  });
230
233
  this.updateLog(depIds, message);
231
234
  // 判断是否已经安装过依赖
@@ -254,11 +257,12 @@ let DependenceService = class DependenceService {
254
257
  isPythonDependence) &&
255
258
  (!depVersion || depInfo.includes(depVersion))) {
256
259
  const endTime = (0, dayjs_1.default)();
257
- const _message = `检测到已经安装 ${depName}\n\n${depInfo}\n\n跳过安装\n\n依赖${actionText}成功,结束时间 ${endTime.format('YYYY-MM-DD HH:mm:ss')},耗时 ${endTime.diff(startTime, 'second')} 秒`;
260
+ const _message = (0, i18n_1.tf)('检测到已经安装 %s\n\n%s\n\n跳过安装\n\n依赖%s成功,结束时间 %s,耗时 %s 秒', depName, depInfo, actionText, endTime.format('YYYY-MM-DD HH:mm:ss'), String(endTime.diff(startTime, 'second')));
258
261
  this.sockService.sendMessage({
259
262
  type: socketMessageType,
260
263
  message: _message,
261
264
  references: depIds,
265
+ status: dependence_1.DependenceStatus.installed,
262
266
  });
263
267
  this.updateLog(depIds, _message);
264
268
  await dependence_1.DependenceModel.update({ status: dependence_1.DependenceStatus.installed }, { where: { id: depIds } });
@@ -277,6 +281,7 @@ let DependenceService = class DependenceService {
277
281
  type: socketMessageType,
278
282
  message: data.toString(),
279
283
  references: depIds,
284
+ status,
280
285
  });
281
286
  this.updateLog(depIds, data.toString());
282
287
  });
@@ -285,6 +290,7 @@ let DependenceService = class DependenceService {
285
290
  type: socketMessageType,
286
291
  message: data.toString(),
287
292
  references: depIds,
293
+ status,
288
294
  });
289
295
  this.updateLog(depIds, data.toString());
290
296
  });
@@ -293,37 +299,32 @@ let DependenceService = class DependenceService {
293
299
  type: socketMessageType,
294
300
  message: JSON.stringify(err),
295
301
  references: depIds,
302
+ status,
296
303
  });
297
304
  this.updateLog(depIds, JSON.stringify(err));
298
305
  });
299
306
  cp.on('exit', async (code) => {
300
307
  const endTime = (0, dayjs_1.default)();
301
308
  const isSucceed = code === 0;
302
- const resultText = isSucceed ? '成功' : '失败';
303
- const message = `\n依赖${actionText}${resultText},结束时间 ${endTime.format('YYYY-MM-DD HH:mm:ss')},耗时 ${endTime.diff(startTime, 'second')} 秒`;
309
+ const resultText = isSucceed ? (0, i18n_1.t)('成功') : (0, i18n_1.t)('失败');
310
+ const message = '\n' +
311
+ (0, i18n_1.tf)('依赖%s%s,结束时间 %s,耗时 %s 秒', actionText, resultText, endTime.format('YYYY-MM-DD HH:mm:ss'), String(endTime.diff(startTime, 'second')));
312
+ const exitStatus = isSucceed
313
+ ? (isInstall ? dependence_1.DependenceStatus.installed : dependence_1.DependenceStatus.removed)
314
+ : (isInstall ? dependence_1.DependenceStatus.installFailed : dependence_1.DependenceStatus.removeFailed);
304
315
  this.sockService.sendMessage({
305
316
  type: socketMessageType,
306
317
  message,
307
318
  references: depIds,
319
+ status: exitStatus,
308
320
  });
309
321
  this.updateLog(depIds, message);
310
- let status;
311
- if (isSucceed) {
312
- status = isInstall
313
- ? dependence_1.DependenceStatus.installed
314
- : dependence_1.DependenceStatus.removed;
315
- }
316
- else {
317
- status = isInstall
318
- ? dependence_1.DependenceStatus.installFailed
319
- : dependence_1.DependenceStatus.removeFailed;
320
- }
321
322
  const docs = await dependence_1.DependenceModel.findAll({ where: { id: depIds } });
322
323
  const _docIds = docs
323
324
  .filter((x) => x.status !== dependence_1.DependenceStatus.cancelled)
324
325
  .map((x) => x.id);
325
326
  if (_docIds.length > 0) {
326
- await dependence_1.DependenceModel.update({ status }, { where: { id: _docIds } });
327
+ await dependence_1.DependenceModel.update({ status: exitStatus }, { where: { id: _docIds } });
327
328
  }
328
329
  // 如果删除依赖成功或者强制删除
329
330
  if ((isSucceed || force) && !isInstall) {
@@ -75,7 +75,7 @@ let GrpcServerService = class GrpcServerService {
75
75
  try {
76
76
  const address = this.formatGrpcAddress(host, grpcPort);
77
77
  await bindAsync(address, credentials);
78
- logger_1.default.debug(`✌️ gRPC service started successfully on ${address}`);
78
+ logger_1.default.debug(`[boot] gRPC service started successfully on ${address}`);
79
79
  metrics_1.metricsService.record('grpc_service_start', 1, {
80
80
  port: grpcPort.toString(),
81
81
  host
@@ -27,7 +27,7 @@ let HttpServerService = class HttpServerService {
27
27
  for (const host of hostsToTry) {
28
28
  try {
29
29
  const server = await this.tryListen(expressApp, port, host);
30
- logger_1.default.debug(`✌️ HTTP service started successfully on ${host}:${port}`);
30
+ logger_1.default.debug(`[boot] HTTP service started successfully on ${host}:${port}`);
31
31
  metrics_1.metricsService.record('http_service_start', 1, {
32
32
  port: port.toString(),
33
33
  host
@@ -55,6 +55,7 @@ let NotificationService = class NotificationService {
55
55
  ['chronocat', this.chronocat],
56
56
  ['ntfy', this.ntfy],
57
57
  ['wxPusherBot', this.wxPusherBot],
58
+ ['wxPusherSpt', this.wxPusherSpt],
58
59
  ['openiLink', this.openiLink],
59
60
  ]);
60
61
  this.title = '';
@@ -306,7 +307,7 @@ let NotificationService = class NotificationService {
306
307
  {
307
308
  title: `${this.title}`,
308
309
  thumb_media_id,
309
- author: `智能助手`,
310
+ author: (0, i18n_1.t)('智能助手'),
310
311
  content_source_url: ``,
311
312
  content: `${this.content.replace(/\n/g, '<br/>')}`,
312
313
  digest: `${this.content}`,
@@ -322,7 +323,7 @@ let NotificationService = class NotificationService {
322
323
  title: `${this.title}`,
323
324
  description: `${this.content}`,
324
325
  url: 'https://github.com/whyour/qinglong',
325
- btntxt: '更多',
326
+ btntxt: (0, i18n_1.t)('更多'),
326
327
  },
327
328
  };
328
329
  break;
@@ -361,7 +362,7 @@ let NotificationService = class NotificationService {
361
362
  roomName: `${aibotkName}`,
362
363
  message: {
363
364
  type: 1,
364
- content: `【青龙快讯】\n\n${this.title}\n${this.content}`,
365
+ content: `【${(0, i18n_1.t)('青龙快讯')}】\n\n${this.title}\n${this.content}`,
365
366
  },
366
367
  };
367
368
  break;
@@ -372,7 +373,7 @@ let NotificationService = class NotificationService {
372
373
  name: `${aibotkName}`,
373
374
  message: {
374
375
  type: 1,
375
- content: `【青龙快讯】\n\n${this.title}\n${this.content}`,
376
+ content: `【${(0, i18n_1.t)('青龙快讯')}】\n\n${this.title}\n${this.content}`,
376
377
  },
377
378
  };
378
379
  break;
@@ -507,7 +508,7 @@ let NotificationService = class NotificationService {
507
508
  },
508
509
  });
509
510
  const info = await transporter.sendMail({
510
- from: `"青龙快讯" <${emailUser}>`,
511
+ from: `"${(0, i18n_1.t)('青龙快讯')}" <${emailUser}>`,
511
512
  to: recipients,
512
513
  subject: `${this.title}`,
513
514
  html: `${this.content.replace(/\n/g, '<br/>')}`,
@@ -620,6 +621,47 @@ let NotificationService = class NotificationService {
620
621
  throw new Error(error.response ? error.response.body : error);
621
622
  }
622
623
  }
624
+ async wxPusherSpt() {
625
+ const { wxPusherSptList } = this.params;
626
+ // 处理 SPT,将逗号分隔的字符串转为数组
627
+ const spts = wxPusherSptList
628
+ ? wxPusherSptList
629
+ .split(',')
630
+ .map((spt) => spt.trim())
631
+ .filter((spt) => spt)
632
+ : [];
633
+ if (!spts.length) {
634
+ throw new Error((0, i18n_1.t)('wxPusher SPT 不能为空'));
635
+ }
636
+ if (spts.length > 10) {
637
+ throw new Error((0, i18n_1.t)('wxPusher SPT 最多支持 10 个'));
638
+ }
639
+ const url = `https://wxpusher.zjiecode.com/api/send/message/simple-push`;
640
+ const json = {
641
+ content: `<h1>${this.title}</h1><br/><div style='white-space: pre-wrap;'>${this.content}</div>`,
642
+ summary: this.title,
643
+ contentType: 2,
644
+ };
645
+ // 单个 SPT 用 spt,多个用 sptList
646
+ if (spts.length === 1) {
647
+ json.spt = spts[0];
648
+ }
649
+ else {
650
+ json.sptList = spts;
651
+ }
652
+ try {
653
+ const res = await http_1.httpClient.post(url, Object.assign(Object.assign({}, this.gotOption), { json }));
654
+ if (res.code === 1000) {
655
+ return true;
656
+ }
657
+ else {
658
+ throw new Error(JSON.stringify(res));
659
+ }
660
+ }
661
+ catch (error) {
662
+ throw new Error(error.response ? error.response.body : error);
663
+ }
664
+ }
623
665
  async chronocat() {
624
666
  var _a, _b;
625
667
  const { chronocatURL, chronocatQQ, chronocatToken } = this.params;
@@ -126,12 +126,12 @@ let SubscriptionService = class SubscriptionService {
126
126
  status: subscription_1.SubscriptionStatus.running,
127
127
  log_path: logPath,
128
128
  }, { where: { id: doc.id } });
129
- const absolutePath = await (0, util_1.handleLogPath)(logPath, `## 开始执行... ${startTime.format('YYYY-MM-DD HH:mm:ss')}\n`);
129
+ const absolutePath = await (0, util_1.handleLogPath)(logPath, (0, i18n_1.tf)('## 开始执行... %s\n', startTime.format('YYYY-MM-DD HH:mm:ss')));
130
130
  // 执行sub_before
131
131
  let beforeStr = '';
132
132
  try {
133
133
  if (doc.sub_before) {
134
- await logStreamManager_1.logStreamManager.write(absolutePath, `\n## 执行before命令...\n\n`);
134
+ await logStreamManager_1.logStreamManager.write(absolutePath, `\n## ${(0, i18n_1.t)('执行before命令...')}\n\n`);
135
135
  beforeStr = await (0, util_1.promiseExec)(doc.sub_before);
136
136
  }
137
137
  }
@@ -155,7 +155,7 @@ let SubscriptionService = class SubscriptionService {
155
155
  let afterStr = '';
156
156
  try {
157
157
  if (sub.sub_after) {
158
- await logStreamManager_1.logStreamManager.write(absolutePath, `\n\n## 执行after命令...\n\n`);
158
+ await logStreamManager_1.logStreamManager.write(absolutePath, `\n\n## ${(0, i18n_1.t)('执行after命令...')}\n\n`);
159
159
  afterStr = await (0, util_1.promiseExec)(sub.sub_after);
160
160
  }
161
161
  }
@@ -166,7 +166,9 @@ let SubscriptionService = class SubscriptionService {
166
166
  if (afterStr) {
167
167
  await logStreamManager_1.logStreamManager.write(absolutePath, `${afterStr}\n`);
168
168
  }
169
- await logStreamManager_1.logStreamManager.write(absolutePath, `\n## 执行结束... ${endTime.format('YYYY-MM-DD HH:mm:ss')} 耗时 ${diff} 秒${const_1.LOG_END_SYMBOL}`);
169
+ await logStreamManager_1.logStreamManager.write(absolutePath, '\n' +
170
+ (0, i18n_1.tf)('## 执行结束... %s 耗时 %s 秒', endTime.format('YYYY-MM-DD HH:mm:ss'), String(diff)) +
171
+ const_1.LOG_END_SYMBOL);
170
172
  // Close the stream after task completion
171
173
  await logStreamManager_1.logStreamManager.closeStream(absolutePath);
172
174
  await subscription_1.SubscriptionModel.update({ status: subscription_1.SubscriptionStatus.idle, pid: undefined }, { where: { id: sub.id } });
@@ -72,7 +72,7 @@ let SystemService = class SystemService {
72
72
  }
73
73
  async updateNotificationMode(notificationInfo) {
74
74
  const code = Math.random().toString().slice(-6);
75
- const isSuccess = await this.notificationService.testNotify(notificationInfo, '青龙', `【蛟龙】测试通知 https://t.me/jiao_long`);
75
+ const isSuccess = await this.notificationService.testNotify(notificationInfo, (0, i18n_1.t)('青龙'), (0, i18n_1.t)('【蛟龙】测试通知 https://t.me/jiao_long'));
76
76
  if (isSuccess) {
77
77
  const result = await this.updateAuthDb({
78
78
  type: system_1.AuthDataType.notification,
@@ -90,7 +90,7 @@ let SystemService = class SystemService {
90
90
  const result = await this.updateAuthDb(Object.assign(Object.assign({}, oDoc), { info: Object.assign(Object.assign({}, oDoc.info), info) }));
91
91
  const cron = {
92
92
  id: result.id,
93
- name: '删除日志',
93
+ name: (0, i18n_1.t)('删除日志'),
94
94
  command: `ql rmlog ${info.logRemoveFrequency}`,
95
95
  runOrigin: 'system',
96
96
  };
@@ -149,6 +149,7 @@ let SystemService = class SystemService {
149
149
  this.sockService.sendMessage({
150
150
  type: 'updateNodeMirror',
151
151
  message: 'update node mirror end',
152
+ status: 'completed',
152
153
  });
153
154
  },
154
155
  onError: async (message) => {
@@ -189,6 +190,7 @@ let SystemService = class SystemService {
189
190
  this.sockService.sendMessage({
190
191
  type: 'updateLinuxMirror',
191
192
  message: 'update linux mirror end',
193
+ status: 'completed',
192
194
  });
193
195
  onEnd === null || onEnd === void 0 ? void 0 : onEnd();
194
196
  if (!hasError) {
@@ -277,6 +279,14 @@ let SystemService = class SystemService {
277
279
  this.sockService.sendMessage({
278
280
  type: 'updateSystemVersion',
279
281
  message: JSON.stringify(err),
282
+ status: 'failed',
283
+ });
284
+ });
285
+ cp.on('exit', (code) => {
286
+ this.sockService.sendMessage({
287
+ type: 'updateSystemVersion',
288
+ message: '',
289
+ status: code === 0 ? 'success' : 'failed',
280
290
  });
281
291
  });
282
292
  return { code: 200 };