@whyour/qinglong 2.20.2-3 → 2.21.0-2

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 (227) hide show
  1. package/.env.example +5 -0
  2. package/AGENTS.md +43 -0
  3. package/CLAUDE.md +43 -0
  4. package/README-en.md +11 -0
  5. package/README.md +11 -0
  6. package/docker/Dockerfile +51 -69
  7. package/docker/Dockerfile.310 +94 -0
  8. package/docker/Dockerfile.debian +120 -0
  9. package/docker/{310.Dockerfile → Dockerfile.debian310} +13 -5
  10. package/docker/docker-entrypoint.sh +95 -3
  11. package/docs/PROJECT_ARCHITECTURE.md +550 -0
  12. package/package.json +28 -25
  13. package/sample/config.sample.sh +12 -1
  14. package/sample/notify.js +81 -9
  15. package/sample/notify.py +54 -11
  16. package/shell/api.sh +44 -20
  17. package/shell/bot.sh +30 -16
  18. package/shell/check.sh +21 -19
  19. package/shell/lang/en.sh +105 -0
  20. package/shell/lang/zh.sh +105 -0
  21. package/shell/otask.sh +117 -28
  22. package/shell/preload/client.js +20 -2
  23. package/shell/preload/esm-loader.mjs +41 -0
  24. package/shell/preload/sitecustomize.js +36 -0
  25. package/shell/pub.sh +8 -8
  26. package/shell/rmlog.sh +5 -5
  27. package/shell/share.sh +49 -30
  28. package/shell/start.sh +40 -27
  29. package/shell/task.sh +10 -2
  30. package/shell/update.sh +22 -22
  31. package/static/build/api/config.js +17 -7
  32. package/static/build/api/cron.js +40 -3
  33. package/static/build/api/dashboard.js +344 -0
  34. package/static/build/api/env.js +40 -2
  35. package/static/build/api/index.js +2 -0
  36. package/static/build/api/log.js +5 -4
  37. package/static/build/api/script.js +34 -9
  38. package/static/build/api/system.js +16 -1
  39. package/static/build/api/user.js +6 -5
  40. package/static/build/app.js +3 -0
  41. package/static/build/config/const.js +7 -6
  42. package/static/build/config/container.js +12 -0
  43. package/static/build/config/grpcCerts.js +150 -0
  44. package/static/build/config/index.js +6 -4
  45. package/static/build/config/util.js +69 -18
  46. package/static/build/data/cron.js +3 -1
  47. package/static/build/data/cronStats.js +59 -0
  48. package/static/build/data/env.js +2 -0
  49. package/static/build/data/notify.js +11 -1
  50. package/static/build/data/runningInstance.js +57 -0
  51. package/static/build/data/sock.js +1 -0
  52. package/static/build/loaders/app.js +1 -1
  53. package/static/build/loaders/db.js +6 -0
  54. package/static/build/loaders/deps.js +1 -22
  55. package/static/build/loaders/express.js +2 -1
  56. package/static/build/loaders/initData.js +19 -2
  57. package/static/build/loaders/initFile.js +2 -0
  58. package/static/build/loaders/initTask.js +3 -2
  59. package/static/build/schedule/api.js +1 -1
  60. package/static/build/schedule/client.js +9 -1
  61. package/static/build/schedule/health.js +1 -1
  62. package/static/build/services/config.js +19 -5
  63. package/static/build/services/cron.js +126 -13
  64. package/static/build/services/dependence.js +19 -18
  65. package/static/build/services/env.js +31 -3
  66. package/static/build/services/grpc.js +32 -6
  67. package/static/build/services/http.js +33 -18
  68. package/static/build/services/notify.js +45 -8
  69. package/static/build/services/open.js +2 -1
  70. package/static/build/services/sshKey.js +6 -3
  71. package/static/build/services/subscription.js +2 -1
  72. package/static/build/services/system.js +34 -9
  73. package/static/build/services/user.js +36 -13
  74. package/static/build/shared/i18n.js +145 -0
  75. package/static/build/shared/pLimit.js +15 -3
  76. package/static/build/shared/runCron.js +5 -0
  77. package/static/build/validation/schedule.js +1 -0
  78. package/static/dist/1089.0ecc5383.async.js +1 -0
  79. package/static/dist/1147.c420132e.async.js +1 -0
  80. package/static/dist/1192.4e5740f2.async.js +1 -0
  81. package/static/dist/1214.c6469b53.async.js +1 -0
  82. package/static/dist/134.54df9e38.async.js +1 -0
  83. package/static/dist/1352.96a77e1d.async.js +1 -0
  84. package/static/dist/{1765.d8e002d7.async.js → 1765.5ac01a93.async.js} +1 -1
  85. package/static/dist/1836.f0b74cf1.async.js +1 -0
  86. package/static/dist/{1885.e0d00d2d.async.js → 1885.e1ea09f6.async.js} +1 -1
  87. package/static/dist/1967.3f3945d0.async.js +1 -0
  88. package/static/dist/{2096.383c1047.async.js → 2096.6d98fd12.async.js} +1 -1
  89. package/static/dist/2208.f7ba3dfa.async.js +1 -0
  90. package/static/dist/2286.164bb089.async.js +1 -0
  91. package/static/dist/2476.4e9b0992.async.js +1 -0
  92. package/static/dist/2537.2b262ee0.async.js +1 -0
  93. package/static/dist/{255.12f03ab2.async.js → 255.8a80b983.async.js} +1 -1
  94. package/static/dist/2635.d4c59d23.async.js +1 -0
  95. package/static/dist/2808.cdc0995c.async.js +1 -0
  96. package/static/dist/{2821.be3dc88e.async.js → 2821.651f31e5.async.js} +1 -1
  97. package/static/dist/2986.4c49eef7.async.js +1 -0
  98. package/static/dist/300.08ac9875.async.js +1 -0
  99. package/static/dist/{3191.70bc19db.async.js → 3191.63263871.async.js} +1 -1
  100. package/static/dist/3714.8d6dba9e.async.js +1 -0
  101. package/static/dist/3906.a5eee612.async.js +1 -0
  102. package/static/dist/{6541.a6d499de.async.js → 3948.4fe809fd.async.js} +1 -1
  103. package/static/dist/{5171.7fc6d0a2.async.js → 4573.16f19278.async.js} +1 -1
  104. package/static/dist/{4859.93e63ea6.async.js → 4859.6592cebb.async.js} +1 -1
  105. package/static/dist/4887.8e3ae573.async.js +1 -0
  106. package/static/dist/{4902.54ecbdb5.async.js → 4902.555f92ab.async.js} +1 -1
  107. package/static/dist/{4934.1ca6b6b0.async.js → 4934.f3539d08.async.js} +1 -1
  108. package/static/dist/5077.cbd111cd.async.js +1 -0
  109. package/static/dist/{6247.b550d996.async.js → 5157.7c5af144.async.js} +1 -1
  110. package/static/dist/{540.481d4708.async.js → 540.19ddf020.async.js} +1 -1
  111. package/static/dist/{5970.10ac4f16.async.js → 5970.d3a6e7bd.async.js} +1 -1
  112. package/static/dist/{6013.2d7bb12a.async.js → 6013.4e38de36.async.js} +1 -1
  113. package/static/dist/{6016.9c379049.async.js → 6016.1e6574b6.async.js} +1 -1
  114. package/static/dist/{6035.5889ddc7.async.js → 6035.32ebfad9.async.js} +1 -1
  115. package/static/dist/6339.ab305e96.async.js +1 -0
  116. package/static/dist/6569.55177f6a.async.js +1 -0
  117. package/static/dist/6610.c111af7e.async.js +1 -0
  118. package/static/dist/{3034.6413c38e.async.js → 6638.87a163d2.async.js} +1 -1
  119. package/static/dist/{6646.5fc37228.async.js → 6646.95e92533.async.js} +1 -1
  120. package/static/dist/6665.1d099ad1.async.js +1 -0
  121. package/static/dist/{7355.b7c0562f.async.js → 7355.f9f263d8.async.js} +1 -1
  122. package/static/dist/{7384.065ccae2.async.js → 7384.1bb449d5.async.js} +1 -1
  123. package/static/dist/7441.ee1bf2bb.async.js +1 -0
  124. package/static/dist/{7508.a31662a3.async.js → 7508.7dcee91c.async.js} +1 -1
  125. package/static/dist/{7802.6b73f16a.async.js → 7802.34255805.async.js} +1 -1
  126. package/static/dist/{7984.e6bb9378.async.js → 7984.594296a5.async.js} +1 -1
  127. package/static/dist/{5653.4fce7ce8.async.js → 8033.5cb31493.async.js} +1 -1
  128. package/static/dist/8147.2fb55202.async.js +1 -0
  129. package/static/dist/8187.48c130d6.async.js +1 -0
  130. package/static/dist/8495.d53e15ca.async.js +1 -0
  131. package/static/dist/8587.315b06c0.async.js +1 -0
  132. package/static/dist/8826.6879b271.async.js +1 -0
  133. package/static/dist/{2742.4852aac8.async.js → 8865.ed665d31.async.js} +1 -1
  134. package/static/dist/901.7ee5c6d3.async.js +1 -0
  135. package/static/dist/9271.231db2ce.async.js +1 -0
  136. package/static/dist/9323.a33f47da.async.js +1 -0
  137. package/static/dist/{9730.30083c91.async.js → 9730.801665a3.async.js} +1 -1
  138. package/static/dist/{9761.627ca3b5.async.js → 9761.360a19d8.async.js} +1 -1
  139. package/static/dist/index.html +2 -2
  140. package/static/dist/layouts__index.a7a2bfe0.async.js +1 -0
  141. package/static/dist/layouts__index.adf0692f.chunk.css +1 -0
  142. package/static/dist/preload_helper.c1c91aaa.js +1 -0
  143. package/static/dist/{src__pages__config__index.622b6ee8.async.js → src__pages__config__index.a22ff7dc.async.js} +1 -1
  144. package/static/dist/{src__pages__crontab__const.323d5124.async.js → src__pages__crontab__const.aba07deb.async.js} +1 -1
  145. package/static/dist/src__pages__crontab__detail.cf4a6aaf.async.js +1 -0
  146. package/static/dist/src__pages__crontab__index.5b67b1a2.async.js +1 -0
  147. package/static/dist/{src__pages__crontab__logModal.5e6a4bf2.async.js → src__pages__crontab__logModal.831d5e4b.async.js} +1 -1
  148. package/static/dist/src__pages__crontab__modal.f7041c7c.async.js +1 -0
  149. package/static/dist/src__pages__crontab__type.d7af36e5.async.js +1 -0
  150. package/static/dist/{src__pages__crontab__viewCreateModal.ffcf7a24.async.js → src__pages__crontab__viewCreateModal.4d589f66.async.js} +1 -1
  151. package/static/dist/{src__pages__crontab__viewManageModal.c2724575.async.js → src__pages__crontab__viewManageModal.0e317746.async.js} +1 -1
  152. package/static/dist/src__pages__dashboard__index.58177357.async.js +1 -0
  153. package/static/dist/src__pages__dependence__index.b7bbc795.async.js +1 -0
  154. package/static/dist/{src__pages__dependence__logModal.f123e2ac.async.js → src__pages__dependence__logModal.0681830b.async.js} +1 -1
  155. package/static/dist/src__pages__dependence__modal.11124896.async.js +1 -0
  156. package/static/dist/src__pages__env__editNameModal.5d264b25.async.js +1 -0
  157. package/static/dist/src__pages__env__index.9a565d2b.async.js +1 -0
  158. package/static/dist/src__pages__env__modal.7f2ef1bc.async.js +1 -0
  159. package/static/dist/src__pages__error__index.f156b45e.async.js +1 -0
  160. package/static/dist/src__pages__initialization__index.e96d4ba8.async.js +1 -0
  161. package/static/dist/src__pages__log__index.9c66639f.async.js +1 -0
  162. package/static/dist/{src__pages__login__index.cd6e3152.async.js → src__pages__login__index.8c813eb1.async.js} +1 -1
  163. package/static/dist/{src__pages__script__components__UnsupportedFilePreview__index.39074c68.async.js → src__pages__script__components__UnsupportedFilePreview__index.c347a13a.async.js} +1 -1
  164. package/static/dist/src__pages__script__editNameModal.3e7e100d.async.js +1 -0
  165. package/static/dist/src__pages__script__index.e869812f.async.js +1 -0
  166. package/static/dist/src__pages__script__renameModal.5e987ef5.async.js +1 -0
  167. package/static/dist/src__pages__script__saveModal.3f9d23d6.async.js +1 -0
  168. package/static/dist/src__pages__script__setting.a535793a.async.js +1 -0
  169. package/static/dist/src__pages__setting__appModal.251cd14f.async.js +1 -0
  170. package/static/dist/src__pages__setting__checkUpdate.34d773f2.async.js +1 -0
  171. package/static/dist/src__pages__setting__dependence.29f13e8c.async.js +1 -0
  172. package/static/dist/src__pages__setting__index.7a39d415.async.js +1 -0
  173. package/static/dist/src__pages__setting__notification.390fc905.async.js +1 -0
  174. package/static/dist/src__pages__setting__other.707cfafe.async.js +1 -0
  175. package/static/dist/src__pages__setting__security.598720a8.async.js +1 -0
  176. package/static/dist/src__pages__setting__systemLog.1a5c855f.async.js +1 -0
  177. package/static/dist/{src__pages__subscription__logModal.0caa7283.async.js → src__pages__subscription__logModal.3864b37f.async.js} +1 -1
  178. package/static/dist/src__pages__subscription__modal.3562c670.async.js +1 -0
  179. package/static/dist/{umi.ef8199a4.js → umi.97fb5a40.js} +1 -1
  180. package/version.yaml +33 -4
  181. package/sample/notify.py.save +0 -1010
  182. package/static/dist/105.85a5c47a.async.js +0 -1
  183. package/static/dist/1083.f86ce804.async.js +0 -1
  184. package/static/dist/1147.32f41a88.async.js +0 -1
  185. package/static/dist/1352.ab6da08e.async.js +0 -1
  186. package/static/dist/1690.f0290540.async.js +0 -1
  187. package/static/dist/1742.6cbe5aca.async.js +0 -1
  188. package/static/dist/2208.8e3a7325.async.js +0 -1
  189. package/static/dist/5312.74b95311.async.js +0 -1
  190. package/static/dist/5691.931f59c5.async.js +0 -1
  191. package/static/dist/6159.55cb068a.async.js +0 -1
  192. package/static/dist/7025.f4080d63.async.js +0 -1
  193. package/static/dist/739.6be5552a.async.js +0 -1
  194. package/static/dist/7571.4f6240b1.async.js +0 -1
  195. package/static/dist/786.59fc381c.async.js +0 -1
  196. package/static/dist/8317.c44c1ebd.async.js +0 -1
  197. package/static/dist/8826.0291edfd.async.js +0 -1
  198. package/static/dist/955.3c9481f7.async.js +0 -1
  199. package/static/dist/layouts__index.1fce90e0.chunk.css +0 -1
  200. package/static/dist/layouts__index.8dcf1576.async.js +0 -1
  201. package/static/dist/preload_helper.116a62f6.js +0 -1
  202. package/static/dist/src__pages__crontab__detail.b07f0c0a.async.js +0 -1
  203. package/static/dist/src__pages__crontab__index.2e2e1096.async.js +0 -1
  204. package/static/dist/src__pages__crontab__modal.4d8c2a22.async.js +0 -1
  205. package/static/dist/src__pages__crontab__type.db7c1858.async.js +0 -1
  206. package/static/dist/src__pages__dependence__index.3d87ceac.async.js +0 -1
  207. package/static/dist/src__pages__dependence__modal.631ffb5b.async.js +0 -1
  208. package/static/dist/src__pages__env__editNameModal.ff85ef8c.async.js +0 -1
  209. package/static/dist/src__pages__env__index.a0a2fece.async.js +0 -1
  210. package/static/dist/src__pages__env__modal.d1004662.async.js +0 -1
  211. package/static/dist/src__pages__error__index.1bc3c90b.async.js +0 -1
  212. package/static/dist/src__pages__initialization__index.8b1cbaf9.async.js +0 -1
  213. package/static/dist/src__pages__log__index.cf00c9af.async.js +0 -1
  214. package/static/dist/src__pages__script__editNameModal.53424d49.async.js +0 -1
  215. package/static/dist/src__pages__script__index.e65df827.async.js +0 -1
  216. package/static/dist/src__pages__script__renameModal.4bbe7fb1.async.js +0 -1
  217. package/static/dist/src__pages__script__saveModal.cf449f3c.async.js +0 -1
  218. package/static/dist/src__pages__script__setting.b345d59a.async.js +0 -1
  219. package/static/dist/src__pages__setting__appModal.03faec89.async.js +0 -1
  220. package/static/dist/src__pages__setting__checkUpdate.46efe057.async.js +0 -1
  221. package/static/dist/src__pages__setting__dependence.4495c7b6.async.js +0 -1
  222. package/static/dist/src__pages__setting__index.6919c399.async.js +0 -1
  223. package/static/dist/src__pages__setting__notification.d6a3884f.async.js +0 -1
  224. package/static/dist/src__pages__setting__other.0d931d6f.async.js +0 -1
  225. package/static/dist/src__pages__setting__security.91cb545f.async.js +0 -1
  226. package/static/dist/src__pages__setting__systemLog.1d433a48.async.js +0 -1
  227. package/static/dist/src__pages__subscription__modal.0b84f6cf.async.js +0 -1
@@ -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) {
@@ -33,7 +33,7 @@ let EnvService = class EnvService {
33
33
  if (envs &&
34
34
  envs.length > 0 &&
35
35
  typeof envs[envs.length - 1].position === 'number') {
36
- position = envs[envs.length - 1].position;
36
+ position = this.getPrecisionPosition(envs[envs.length - 1].position);
37
37
  }
38
38
  const tabs = payloads.map((x) => {
39
39
  position = position - env_1.stepPosition;
@@ -90,7 +90,7 @@ let EnvService = class EnvService {
90
90
  return newDoc;
91
91
  }
92
92
  async checkPosition(position, edge = 0) {
93
- const precisionPosition = parseFloat(position.toPrecision(16));
93
+ const precisionPosition = this.getPrecisionPosition(position);
94
94
  if (precisionPosition < env_1.minPosition ||
95
95
  precisionPosition > env_1.maxPosition ||
96
96
  Math.abs(precisionPosition - edge) < env_1.minPosition) {
@@ -103,7 +103,7 @@ let EnvService = class EnvService {
103
103
  }
104
104
  }
105
105
  getPrecisionPosition(position) {
106
- return parseFloat(position.toPrecision(16));
106
+ return Math.trunc(parseFloat(position.toPrecision(16)));
107
107
  }
108
108
  async envs(searchText = '', query = {}) {
109
109
  let condition = Object.assign({}, query);
@@ -171,6 +171,34 @@ let EnvService = class EnvService {
171
171
  async unPin(ids) {
172
172
  await env_1.EnvModel.update({ isPinned: 0 }, { where: { id: ids } });
173
173
  }
174
+ async addLabels(ids, labels) {
175
+ await data_1.sequelize.transaction(async (transaction) => {
176
+ const docs = await env_1.EnvModel.findAll({
177
+ where: { id: ids },
178
+ transaction,
179
+ });
180
+ for (const doc of docs) {
181
+ const env = doc.get({ plain: true });
182
+ await env_1.EnvModel.update({ labels: Array.from(new Set([...(env.labels || []), ...labels])) }, { where: { id: env.id }, transaction });
183
+ }
184
+ });
185
+ return await this.find({ id: ids });
186
+ }
187
+ async removeLabels(ids, labels) {
188
+ await data_1.sequelize.transaction(async (transaction) => {
189
+ const docs = await env_1.EnvModel.findAll({
190
+ where: { id: ids },
191
+ transaction,
192
+ });
193
+ for (const doc of docs) {
194
+ const env = doc.get({ plain: true });
195
+ await env_1.EnvModel.update({
196
+ labels: (env.labels || []).filter((label) => !labels.includes(label)),
197
+ }, { where: { id: env.id }, transaction });
198
+ }
199
+ });
200
+ return await this.find({ id: ids });
201
+ }
174
202
  async set_envs() {
175
203
  const envs = await this.envs('', {
176
204
  name: { [sequelize_1.Op.not]: null },
@@ -46,23 +46,49 @@ const util_1 = require("util");
46
46
  const config_1 = __importDefault(require("../config"));
47
47
  const metrics_1 = require("./metrics");
48
48
  const typedi_1 = require("typedi");
49
+ const grpcCerts_1 = require("../config/grpcCerts");
49
50
  let GrpcServerService = class GrpcServerService {
50
51
  constructor() {
51
52
  this.server = new grpc_js_1.Server({ 'grpc.enable_http_proxy': 0 });
52
53
  }
54
+ formatGrpcAddress(host, port) {
55
+ if (host === '::') {
56
+ return `[::]:${port}`;
57
+ }
58
+ return `${host}:${port}`;
59
+ }
53
60
  async initialize() {
54
61
  try {
55
62
  this.server.addService(health_1.HealthService, { check: health_2.check });
56
63
  this.server.addService(cron_1.CronService, { addCron: addCron_1.addCron, delCron: delCron_1.delCron });
57
64
  this.server.addService(api_1.ApiService, Api);
65
+ const tlsConfig = await (0, grpcCerts_1.initGrpcCerts)();
66
+ const credentials = grpc_js_1.ServerCredentials.createSsl(Buffer.from(tlsConfig.caCert), [{ cert_chain: Buffer.from(tlsConfig.serverCert), private_key: Buffer.from(tlsConfig.serverKey) }], true);
58
67
  const grpcPort = config_1.default.grpcPort;
68
+ const hostsToTry = [
69
+ config_1.default.bindHostGrpc,
70
+ ...(config_1.default.bindHostGrpc !== '0.0.0.0' ? ['0.0.0.0'] : [])
71
+ ];
59
72
  const bindAsync = (0, util_1.promisify)(this.server.bindAsync).bind(this.server);
60
- await bindAsync(`0.0.0.0:${grpcPort}`, grpc_js_1.ServerCredentials.createInsecure());
61
- logger_1.default.debug(`✌️ gRPC service started successfully`);
62
- metrics_1.metricsService.record('grpc_service_start', 1, {
63
- port: grpcPort.toString(),
64
- });
65
- return grpcPort;
73
+ let lastError = null;
74
+ for (const host of hostsToTry) {
75
+ try {
76
+ const address = this.formatGrpcAddress(host, grpcPort);
77
+ await bindAsync(address, credentials);
78
+ logger_1.default.debug(`✌️ gRPC service started successfully on ${address}`);
79
+ metrics_1.metricsService.record('grpc_service_start', 1, {
80
+ port: grpcPort.toString(),
81
+ host
82
+ });
83
+ return grpcPort;
84
+ }
85
+ catch (err) {
86
+ lastError = err;
87
+ logger_1.default.warn(`Failed to bind gRPC on ${host}:${grpcPort}, trying next...`, err);
88
+ }
89
+ }
90
+ logger_1.default.error('Failed to start gRPC service on all hosts');
91
+ throw lastError || new Error('Failed to start gRPC service');
66
92
  }
67
93
  catch (err) {
68
94
  logger_1.default.error('Failed to start gRPC service:', err);
@@ -13,31 +13,46 @@ exports.HttpServerService = void 0;
13
13
  const logger_1 = __importDefault(require("../loaders/logger"));
14
14
  const metrics_1 = require("./metrics");
15
15
  const typedi_1 = require("typedi");
16
+ const config_1 = __importDefault(require("../config"));
16
17
  let HttpServerService = class HttpServerService {
17
18
  constructor() {
18
19
  this.server = undefined;
19
20
  }
20
21
  async initialize(expressApp, port) {
21
- try {
22
- return new Promise((resolve, reject) => {
23
- var _a;
24
- this.server = expressApp.listen(port, '0.0.0.0', () => {
25
- logger_1.default.debug(`✌️ HTTP service started successfully`);
26
- metrics_1.metricsService.record('http_service_start', 1, {
27
- port: port.toString(),
28
- });
29
- resolve(this.server);
30
- });
31
- (_a = this.server) === null || _a === void 0 ? void 0 : _a.on('error', (err) => {
32
- logger_1.default.error('Failed to start HTTP service:', err);
33
- reject(err);
22
+ const hostsToTry = [
23
+ config_1.default.bindHost,
24
+ ...(config_1.default.bindHost !== '0.0.0.0' ? ['0.0.0.0'] : [])
25
+ ];
26
+ let lastError = null;
27
+ for (const host of hostsToTry) {
28
+ try {
29
+ const server = await this.tryListen(expressApp, port, host);
30
+ logger_1.default.debug(`✌️ HTTP service started successfully on ${host}:${port}`);
31
+ metrics_1.metricsService.record('http_service_start', 1, {
32
+ port: port.toString(),
33
+ host
34
34
  });
35
- });
36
- }
37
- catch (err) {
38
- logger_1.default.error('Failed to start HTTP service:', err);
39
- throw err;
35
+ this.server = server;
36
+ return server;
37
+ }
38
+ catch (err) {
39
+ lastError = err;
40
+ logger_1.default.warn(`Failed to bind HTTP on ${host}:${port}, trying next...`, err);
41
+ }
40
42
  }
43
+ logger_1.default.error('Failed to start HTTP service on all hosts');
44
+ throw lastError || new Error('Failed to start HTTP service');
45
+ }
46
+ async tryListen(expressApp, port, host) {
47
+ return new Promise((resolve, reject) => {
48
+ const server = expressApp.listen(port, host, () => {
49
+ resolve(server);
50
+ });
51
+ server.on('error', (err) => {
52
+ server.close();
53
+ reject(err);
54
+ });
55
+ });
41
56
  }
42
57
  async shutdown() {
43
58
  try {
@@ -27,6 +27,7 @@ const crypto_1 = __importDefault(require("crypto"));
27
27
  const nodemailer_1 = __importDefault(require("nodemailer"));
28
28
  const typedi_1 = require("typedi");
29
29
  const util_1 = require("../config/util");
30
+ const i18n_1 = require("../shared/i18n");
30
31
  const user_1 = __importDefault(require("./user"));
31
32
  const http_1 = require("../config/http");
32
33
  const undici_1 = require("undici");
@@ -54,6 +55,7 @@ let NotificationService = class NotificationService {
54
55
  ['chronocat', this.chronocat],
55
56
  ['ntfy', this.ntfy],
56
57
  ['wxPusherBot', this.wxPusherBot],
58
+ ['openiLink', this.openiLink],
57
59
  ]);
58
60
  this.title = '';
59
61
  this.content = '';
@@ -97,6 +99,13 @@ let NotificationService = class NotificationService {
97
99
  }
98
100
  return true;
99
101
  }
102
+ parseMailRecipients(value) {
103
+ const recipients = (value || '')
104
+ .split(/[;;]/)
105
+ .map((item) => item.trim())
106
+ .filter(Boolean);
107
+ return recipients.length > 0 ? recipients : undefined;
108
+ }
100
109
  async gotify() {
101
110
  const { gotifyUrl, gotifyToken, gotifyPriority = 1 } = this.params;
102
111
  try {
@@ -297,7 +306,7 @@ let NotificationService = class NotificationService {
297
306
  {
298
307
  title: `${this.title}`,
299
308
  thumb_media_id,
300
- author: `智能助手`,
309
+ author: (0, i18n_1.t)('智能助手'),
301
310
  content_source_url: ``,
302
311
  content: `${this.content.replace(/\n/g, '<br/>')}`,
303
312
  digest: `${this.content}`,
@@ -313,7 +322,7 @@ let NotificationService = class NotificationService {
313
322
  title: `${this.title}`,
314
323
  description: `${this.content}`,
315
324
  url: 'https://github.com/whyour/qinglong',
316
- btntxt: '更多',
325
+ btntxt: (0, i18n_1.t)('更多'),
317
326
  },
318
327
  };
319
328
  break;
@@ -352,7 +361,7 @@ let NotificationService = class NotificationService {
352
361
  roomName: `${aibotkName}`,
353
362
  message: {
354
363
  type: 1,
355
- content: `【青龙快讯】\n\n${this.title}\n${this.content}`,
364
+ content: `【${(0, i18n_1.t)('青龙快讯')}】\n\n${this.title}\n${this.content}`,
356
365
  },
357
366
  };
358
367
  break;
@@ -363,7 +372,7 @@ let NotificationService = class NotificationService {
363
372
  name: `${aibotkName}`,
364
373
  message: {
365
374
  type: 1,
366
- content: `【青龙快讯】\n\n${this.title}\n${this.content}`,
375
+ content: `【${(0, i18n_1.t)('青龙快讯')}】\n\n${this.title}\n${this.content}`,
367
376
  },
368
377
  };
369
378
  break;
@@ -488,6 +497,7 @@ let NotificationService = class NotificationService {
488
497
  }
489
498
  async email() {
490
499
  const { emailPass, emailService, emailUser, emailTo } = this.params;
500
+ const recipients = this.parseMailRecipients(emailTo) || emailUser;
491
501
  try {
492
502
  const transporter = nodemailer_1.default.createTransport({
493
503
  service: emailService,
@@ -497,8 +507,8 @@ let NotificationService = class NotificationService {
497
507
  },
498
508
  });
499
509
  const info = await transporter.sendMail({
500
- from: `"青龙快讯" <${emailUser}>`,
501
- to: emailTo ? emailTo.split(';') : emailUser,
510
+ from: `"${(0, i18n_1.t)('青龙快讯')}" <${emailUser}>`,
511
+ to: recipients,
502
512
  subject: `${this.title}`,
503
513
  html: `${this.content.replace(/\n/g, '<br/>')}`,
504
514
  });
@@ -586,7 +596,7 @@ let NotificationService = class NotificationService {
586
596
  : [];
587
597
  // topic_ids 和 uids 至少要有一个
588
598
  if (!topicIds.length && !uids.length) {
589
- throw new Error('wxPusher 服务的 TopicIds 和 Uids 至少配置一个才行');
599
+ throw new Error((0, i18n_1.t)('wxPusher 服务的 TopicIds 和 Uids 至少配置一个才行'));
590
600
  }
591
601
  const url = `https://wxpusher.zjiecode.com/api/send/message`;
592
602
  try {
@@ -665,7 +675,7 @@ let NotificationService = class NotificationService {
665
675
  var _a;
666
676
  const { webhookUrl, webhookBody, webhookHeaders, webhookMethod, webhookContentType, } = this.params;
667
677
  if (!(webhookUrl === null || webhookUrl === void 0 ? void 0 : webhookUrl.includes('$title')) && !(webhookBody === null || webhookBody === void 0 ? void 0 : webhookBody.includes('$title'))) {
668
- throw new Error('Url 或者 Body 中必须包含 $title');
678
+ throw new Error((0, i18n_1.t)('Url 或者 Body 中必须包含 $title'));
669
679
  }
670
680
  const headers = (0, util_1.parseHeaders)(webhookHeaders);
671
681
  const body = (0, util_1.parseBody)(webhookBody, webhookContentType, (v) => { var _a; return (_a = v === null || v === void 0 ? void 0 : v.replaceAll('$title', this.title)) === null || _a === void 0 ? void 0 : _a.replaceAll('$content', this.content); });
@@ -700,6 +710,33 @@ let NotificationService = class NotificationService {
700
710
  }
701
711
  return {};
702
712
  }
713
+ async openiLink() {
714
+ const { openiLinkAppToken, openiLinkHubUrl, openiLinkContextToken } = this.params;
715
+ const baseUrl = (openiLinkHubUrl === null || openiLinkHubUrl === void 0 ? void 0 : openiLinkHubUrl.replace(/\/$/, '')) || 'https://hub.openilink.com';
716
+ const url = `${baseUrl}/bot/v1/message/send`;
717
+ const body = {
718
+ type: 'text',
719
+ content: `${this.title}\n\n${this.content}`,
720
+ };
721
+ if (openiLinkContextToken) {
722
+ body.context_token = openiLinkContextToken;
723
+ }
724
+ try {
725
+ const res = await http_1.httpClient.post(url, Object.assign(Object.assign({}, this.gotOption), { json: body, headers: {
726
+ 'Content-Type': 'application/json',
727
+ Authorization: `Bearer ${openiLinkAppToken}`,
728
+ } }));
729
+ if (res.ok) {
730
+ return true;
731
+ }
732
+ else {
733
+ throw new Error(JSON.stringify(res));
734
+ }
735
+ }
736
+ catch (error) {
737
+ throw new Error(error.response ? error.response.body : error);
738
+ }
739
+ }
703
740
  };
704
741
  __decorate([
705
742
  (0, typedi_1.Inject)((type) => user_1.default),
@@ -22,6 +22,7 @@ const open_1 = require("../data/open");
22
22
  const uuid_1 = require("uuid");
23
23
  const sequelize_1 = require("sequelize");
24
24
  const store_1 = require("../shared/store");
25
+ const i18n_1 = require("../shared/i18n");
25
26
  let OpenService = class OpenService {
26
27
  constructor(logger) {
27
28
  this.logger = logger;
@@ -141,7 +142,7 @@ let OpenService = class OpenService {
141
142
  };
142
143
  }
143
144
  else {
144
- return { code: 400, message: 'client_id 或 client_seret 有误' };
145
+ return { code: 400, message: (0, i18n_1.t)('client_id 或 client_seret 有误') };
145
146
  }
146
147
  }
147
148
  async generateSystemToken() {
@@ -48,9 +48,12 @@ let SshKeyService = class SshKeyService {
48
48
  }
49
49
  async generatePrivateKeyFile(alias, key) {
50
50
  try {
51
- await (0, utils_1.writeFileWithLock)(path_1.default.join(this.sshPath, alias), `${key}${os_1.default.EOL}`, {
52
- mode: '400',
53
- });
51
+ const filePath = path_1.default.join(this.sshPath, alias);
52
+ try {
53
+ await (0, util_1.rmPath)(filePath);
54
+ }
55
+ catch (_a) { }
56
+ await (0, utils_1.writeFileWithLock)(filePath, `${key}${os_1.default.EOL}`, { mode: '400' });
54
57
  }
55
58
  catch (error) {
56
59
  this.logger.error('生成私钥文件失败', error);
@@ -48,6 +48,7 @@ const sequelize_1 = require("sequelize");
48
48
  const path_1 = __importStar(require("path"));
49
49
  const schedule_1 = __importDefault(require("./schedule"));
50
50
  const sock_1 = __importDefault(require("./sock"));
51
+ const i18n_1 = require("../shared/i18n");
51
52
  const sshKey_1 = __importDefault(require("./sshKey"));
52
53
  const const_1 = require("../config/const");
53
54
  const subscription_2 = require("../config/subscription");
@@ -171,7 +172,7 @@ let SubscriptionService = class SubscriptionService {
171
172
  await subscription_1.SubscriptionModel.update({ status: subscription_1.SubscriptionStatus.idle, pid: undefined }, { where: { id: sub.id } });
172
173
  this.sockService.sendMessage({
173
174
  type: 'runSubscriptionEnd',
174
- message: '订阅执行完成',
175
+ message: (0, i18n_1.t)('订阅执行完成'),
175
176
  references: [doc.id],
176
177
  });
177
178
  },
@@ -44,6 +44,7 @@ const schedule_1 = __importDefault(require("./schedule"));
44
44
  const sock_1 = __importDefault(require("./sock"));
45
45
  const os_1 = __importDefault(require("os"));
46
46
  const dayjs_1 = __importDefault(require("dayjs"));
47
+ const i18n_1 = require("../shared/i18n");
47
48
  const util_2 = require("../config/util");
48
49
  let SystemService = class SystemService {
49
50
  constructor(logger, scheduleService, sockService) {
@@ -71,7 +72,7 @@ let SystemService = class SystemService {
71
72
  }
72
73
  async updateNotificationMode(notificationInfo) {
73
74
  const code = Math.random().toString().slice(-6);
74
- 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'));
75
76
  if (isSuccess) {
76
77
  const result = await this.updateAuthDb({
77
78
  type: system_1.AuthDataType.notification,
@@ -80,7 +81,7 @@ let SystemService = class SystemService {
80
81
  return { code: 200, data: Object.assign(Object.assign({}, result), { code }) };
81
82
  }
82
83
  else {
83
- return { code: 400, message: '通知发送失败,请检查参数' };
84
+ return { code: 400, message: (0, i18n_1.t)('通知发送失败,请检查参数') };
84
85
  }
85
86
  }
86
87
  async updateLogRemoveFrequency(info) {
@@ -89,7 +90,7 @@ let SystemService = class SystemService {
89
90
  const result = await this.updateAuthDb(Object.assign(Object.assign({}, oDoc), { info: Object.assign(Object.assign({}, oDoc.info), info) }));
90
91
  const cron = {
91
92
  id: result.id,
92
- name: '删除日志',
93
+ name: (0, i18n_1.t)('删除日志'),
93
94
  command: `ql rmlog ${info.logRemoveFrequency}`,
94
95
  runOrigin: 'system',
95
96
  };
@@ -148,6 +149,7 @@ let SystemService = class SystemService {
148
149
  this.sockService.sendMessage({
149
150
  type: 'updateNodeMirror',
150
151
  message: 'update node mirror end',
152
+ status: 'completed',
151
153
  });
152
154
  },
153
155
  onError: async (message) => {
@@ -188,6 +190,7 @@ let SystemService = class SystemService {
188
190
  this.sockService.sendMessage({
189
191
  type: 'updateLinuxMirror',
190
192
  message: 'update linux mirror end',
193
+ status: 'completed',
191
194
  });
192
195
  onEnd === null || onEnd === void 0 ? void 0 : onEnd();
193
196
  if (!hasError) {
@@ -276,6 +279,14 @@ let SystemService = class SystemService {
276
279
  this.sockService.sendMessage({
277
280
  type: 'updateSystemVersion',
278
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',
279
290
  });
280
291
  });
281
292
  return { code: 200 };
@@ -302,10 +313,10 @@ let SystemService = class SystemService {
302
313
  }
303
314
  const isSuccess = await this.notificationService.notify(title, content, notificationInfo);
304
315
  if (isSuccess) {
305
- return { code: 200, message: '通知发送成功' };
316
+ return { code: 200, message: (0, i18n_1.t)('通知发送成功') };
306
317
  }
307
318
  else {
308
- return { code: 400, message: '通知发送失败,请检查系统设置/通知配置' };
319
+ return { code: 400, message: (0, i18n_1.t)('通知发送失败,请检查系统设置/通知配置') };
309
320
  }
310
321
  }
311
322
  async run({ command, logPath }, callback) {
@@ -321,7 +332,7 @@ let SystemService = class SystemService {
321
332
  }
322
333
  async stop({ command, pid }) {
323
334
  if (!pid && !command) {
324
- return { code: 400, message: '参数错误' };
335
+ return { code: 400, message: (0, i18n_1.t)('参数错误') };
325
336
  }
326
337
  if (pid) {
327
338
  await (0, util_1.killTask)(pid);
@@ -336,7 +347,7 @@ let SystemService = class SystemService {
336
347
  return { code: 200 };
337
348
  }
338
349
  else {
339
- return { code: 400, message: '任务未找到' };
350
+ return { code: 400, message: (0, i18n_1.t)('任务未找到') };
340
351
  }
341
352
  }
342
353
  async exportData(res, type) {
@@ -411,8 +422,22 @@ let SystemService = class SystemService {
411
422
  return { code: 200, data: info };
412
423
  }
413
424
  else {
414
- return { code: 400, message: '设置时区失败' };
425
+ return { code: 400, message: (0, i18n_1.t)('设置时区失败') };
426
+ }
427
+ }
428
+ async updateLanguage(info) {
429
+ const oDoc = await this.getSystemConfig();
430
+ const lang = info.lang || 'zh';
431
+ await this.updateAuthDb(Object.assign(Object.assign({}, oDoc), { info: Object.assign(Object.assign({}, oDoc.info), { lang }) }));
432
+ // Write to standalone lang_env.sh, sourced by shell scripts
433
+ try {
434
+ await fs_1.default.promises.writeFile(config_1.default.langEnvFile, `export QL_LANG='${lang}'\n`);
435
+ }
436
+ catch (error) {
437
+ this.logger.error(`Failed to write lang_env.sh: ${error}`);
415
438
  }
439
+ (0, i18n_1.setLang)(lang);
440
+ return { code: 200, data: { lang } };
416
441
  }
417
442
  async updateGlobalSshKey(info) {
418
443
  const oDoc = await this.getSystemConfig();
@@ -431,7 +456,7 @@ let SystemService = class SystemService {
431
456
  }
432
457
  async cleanDependence(type) {
433
458
  if (!type || !['node', 'python3'].includes(type)) {
434
- return { code: 400, message: '参数错误' };
459
+ return { code: 400, message: (0, i18n_1.t)('参数错误') };
435
460
  }
436
461
  try {
437
462
  const finalPath = path_1.default.join(config_1.default.dependenceCachePath, type);