@whyour/qinglong 2.20.2-3 → 2.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (218) 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 +114 -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 +38 -1
  33. package/static/build/api/dashboard.js +337 -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 -0
  45. package/static/build/config/util.js +66 -15
  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/loaders/app.js +1 -1
  52. package/static/build/loaders/db.js +6 -0
  53. package/static/build/loaders/express.js +2 -1
  54. package/static/build/loaders/initData.js +19 -2
  55. package/static/build/loaders/initFile.js +2 -0
  56. package/static/build/schedule/api.js +1 -1
  57. package/static/build/schedule/client.js +9 -1
  58. package/static/build/schedule/health.js +1 -1
  59. package/static/build/services/config.js +19 -5
  60. package/static/build/services/cron.js +118 -8
  61. package/static/build/services/env.js +31 -3
  62. package/static/build/services/grpc.js +32 -6
  63. package/static/build/services/http.js +33 -18
  64. package/static/build/services/notify.js +40 -3
  65. package/static/build/services/open.js +2 -1
  66. package/static/build/services/sshKey.js +6 -3
  67. package/static/build/services/subscription.js +2 -1
  68. package/static/build/services/system.js +22 -7
  69. package/static/build/services/user.js +8 -7
  70. package/static/build/shared/i18n.js +105 -0
  71. package/static/build/shared/pLimit.js +12 -1
  72. package/static/build/shared/runCron.js +5 -0
  73. package/static/build/validation/schedule.js +1 -0
  74. package/static/dist/1089.0ecc5383.async.js +1 -0
  75. package/static/dist/1147.c420132e.async.js +1 -0
  76. package/static/dist/1192.4e5740f2.async.js +1 -0
  77. package/static/dist/1214.c6469b53.async.js +1 -0
  78. package/static/dist/134.54df9e38.async.js +1 -0
  79. package/static/dist/1352.96a77e1d.async.js +1 -0
  80. package/static/dist/{1765.d8e002d7.async.js → 1765.5ac01a93.async.js} +1 -1
  81. package/static/dist/1836.f0b74cf1.async.js +1 -0
  82. package/static/dist/{1885.e0d00d2d.async.js → 1885.e1ea09f6.async.js} +1 -1
  83. package/static/dist/1967.3f3945d0.async.js +1 -0
  84. package/static/dist/{2096.383c1047.async.js → 2096.6d98fd12.async.js} +1 -1
  85. package/static/dist/2208.f7ba3dfa.async.js +1 -0
  86. package/static/dist/2286.164bb089.async.js +1 -0
  87. package/static/dist/2476.4e9b0992.async.js +1 -0
  88. package/static/dist/2537.2b262ee0.async.js +1 -0
  89. package/static/dist/{255.12f03ab2.async.js → 255.8a80b983.async.js} +1 -1
  90. package/static/dist/2635.d4c59d23.async.js +1 -0
  91. package/static/dist/2808.cdc0995c.async.js +1 -0
  92. package/static/dist/{2821.be3dc88e.async.js → 2821.651f31e5.async.js} +1 -1
  93. package/static/dist/2986.4c49eef7.async.js +1 -0
  94. package/static/dist/300.08ac9875.async.js +1 -0
  95. package/static/dist/{3191.70bc19db.async.js → 3191.63263871.async.js} +1 -1
  96. package/static/dist/3714.8d6dba9e.async.js +1 -0
  97. package/static/dist/3906.a5eee612.async.js +1 -0
  98. package/static/dist/{6541.a6d499de.async.js → 3948.4fe809fd.async.js} +1 -1
  99. package/static/dist/{5171.7fc6d0a2.async.js → 4573.16f19278.async.js} +1 -1
  100. package/static/dist/{4859.93e63ea6.async.js → 4859.6592cebb.async.js} +1 -1
  101. package/static/dist/4887.8e3ae573.async.js +1 -0
  102. package/static/dist/{4902.54ecbdb5.async.js → 4902.555f92ab.async.js} +1 -1
  103. package/static/dist/{4934.1ca6b6b0.async.js → 4934.f3539d08.async.js} +1 -1
  104. package/static/dist/5077.cbd111cd.async.js +1 -0
  105. package/static/dist/{6247.b550d996.async.js → 5157.7c5af144.async.js} +1 -1
  106. package/static/dist/{540.481d4708.async.js → 540.19ddf020.async.js} +1 -1
  107. package/static/dist/{5970.10ac4f16.async.js → 5970.d3a6e7bd.async.js} +1 -1
  108. package/static/dist/{6013.2d7bb12a.async.js → 6013.4e38de36.async.js} +1 -1
  109. package/static/dist/{6016.9c379049.async.js → 6016.1e6574b6.async.js} +1 -1
  110. package/static/dist/{6035.5889ddc7.async.js → 6035.32ebfad9.async.js} +1 -1
  111. package/static/dist/6339.ab305e96.async.js +1 -0
  112. package/static/dist/6569.55177f6a.async.js +1 -0
  113. package/static/dist/6610.c111af7e.async.js +1 -0
  114. package/static/dist/{3034.6413c38e.async.js → 6638.87a163d2.async.js} +1 -1
  115. package/static/dist/{6646.5fc37228.async.js → 6646.95e92533.async.js} +1 -1
  116. package/static/dist/6665.1d099ad1.async.js +1 -0
  117. package/static/dist/{7355.b7c0562f.async.js → 7355.f9f263d8.async.js} +1 -1
  118. package/static/dist/{7384.065ccae2.async.js → 7384.1bb449d5.async.js} +1 -1
  119. package/static/dist/7441.ee1bf2bb.async.js +1 -0
  120. package/static/dist/{7508.a31662a3.async.js → 7508.7dcee91c.async.js} +1 -1
  121. package/static/dist/{7802.6b73f16a.async.js → 7802.34255805.async.js} +1 -1
  122. package/static/dist/{7984.e6bb9378.async.js → 7984.594296a5.async.js} +1 -1
  123. package/static/dist/{5653.4fce7ce8.async.js → 8033.5cb31493.async.js} +1 -1
  124. package/static/dist/8147.2fb55202.async.js +1 -0
  125. package/static/dist/8187.48c130d6.async.js +1 -0
  126. package/static/dist/8495.d53e15ca.async.js +1 -0
  127. package/static/dist/8587.315b06c0.async.js +1 -0
  128. package/static/dist/8826.2447a104.async.js +1 -0
  129. package/static/dist/{2742.4852aac8.async.js → 8865.ed665d31.async.js} +1 -1
  130. package/static/dist/901.7ee5c6d3.async.js +1 -0
  131. package/static/dist/9271.231db2ce.async.js +1 -0
  132. package/static/dist/9323.a33f47da.async.js +1 -0
  133. package/static/dist/{9730.30083c91.async.js → 9730.801665a3.async.js} +1 -1
  134. package/static/dist/{9761.627ca3b5.async.js → 9761.360a19d8.async.js} +1 -1
  135. package/static/dist/index.html +2 -2
  136. package/static/dist/layouts__index.a7a2bfe0.async.js +1 -0
  137. package/static/dist/layouts__index.adf0692f.chunk.css +1 -0
  138. package/static/dist/preload_helper.0431c0f3.js +1 -0
  139. package/static/dist/{src__pages__config__index.622b6ee8.async.js → src__pages__config__index.a22ff7dc.async.js} +1 -1
  140. package/static/dist/{src__pages__crontab__const.323d5124.async.js → src__pages__crontab__const.aba07deb.async.js} +1 -1
  141. package/static/dist/src__pages__crontab__detail.b9c36808.async.js +1 -0
  142. package/static/dist/src__pages__crontab__index.fed99fd0.async.js +1 -0
  143. package/static/dist/{src__pages__crontab__logModal.5e6a4bf2.async.js → src__pages__crontab__logModal.0b8cce8c.async.js} +1 -1
  144. package/static/dist/src__pages__crontab__modal.f7041c7c.async.js +1 -0
  145. package/static/dist/src__pages__crontab__type.d7af36e5.async.js +1 -0
  146. package/static/dist/{src__pages__crontab__viewCreateModal.ffcf7a24.async.js → src__pages__crontab__viewCreateModal.4d589f66.async.js} +1 -1
  147. package/static/dist/{src__pages__crontab__viewManageModal.c2724575.async.js → src__pages__crontab__viewManageModal.0e317746.async.js} +1 -1
  148. package/static/dist/src__pages__dashboard__index.b30f2f47.async.js +1 -0
  149. package/static/dist/{src__pages__dependence__logModal.f123e2ac.async.js → src__pages__dependence__logModal.0681830b.async.js} +1 -1
  150. package/static/dist/src__pages__dependence__modal.11124896.async.js +1 -0
  151. package/static/dist/src__pages__env__editNameModal.5d264b25.async.js +1 -0
  152. package/static/dist/src__pages__env__index.baa27d4e.async.js +1 -0
  153. package/static/dist/src__pages__env__modal.7f2ef1bc.async.js +1 -0
  154. package/static/dist/src__pages__error__index.f156b45e.async.js +1 -0
  155. package/static/dist/src__pages__initialization__index.e96d4ba8.async.js +1 -0
  156. package/static/dist/{src__pages__log__index.cf00c9af.async.js → src__pages__log__index.e0978bae.async.js} +1 -1
  157. package/static/dist/{src__pages__login__index.cd6e3152.async.js → src__pages__login__index.8c813eb1.async.js} +1 -1
  158. package/static/dist/{src__pages__script__components__UnsupportedFilePreview__index.39074c68.async.js → src__pages__script__components__UnsupportedFilePreview__index.c347a13a.async.js} +1 -1
  159. package/static/dist/src__pages__script__editNameModal.3e7e100d.async.js +1 -0
  160. package/static/dist/src__pages__script__index.2765d1b8.async.js +1 -0
  161. package/static/dist/src__pages__script__renameModal.5e987ef5.async.js +1 -0
  162. package/static/dist/src__pages__script__saveModal.3f9d23d6.async.js +1 -0
  163. package/static/dist/src__pages__script__setting.a535793a.async.js +1 -0
  164. package/static/dist/src__pages__setting__appModal.251cd14f.async.js +1 -0
  165. package/static/dist/src__pages__setting__dependence.8a7a9529.async.js +1 -0
  166. package/static/dist/src__pages__setting__index.cc85fdfb.async.js +1 -0
  167. package/static/dist/src__pages__setting__notification.390fc905.async.js +1 -0
  168. package/static/dist/src__pages__setting__other.b22d2165.async.js +1 -0
  169. package/static/dist/src__pages__setting__security.598720a8.async.js +1 -0
  170. package/static/dist/src__pages__setting__systemLog.67406721.async.js +1 -0
  171. package/static/dist/{src__pages__subscription__logModal.0caa7283.async.js → src__pages__subscription__logModal.3864b37f.async.js} +1 -1
  172. package/static/dist/src__pages__subscription__modal.3562c670.async.js +1 -0
  173. package/static/dist/{umi.ef8199a4.js → umi.ca04a019.js} +1 -1
  174. package/version.yaml +33 -4
  175. package/sample/notify.py.save +0 -1010
  176. package/static/dist/105.85a5c47a.async.js +0 -1
  177. package/static/dist/1083.f86ce804.async.js +0 -1
  178. package/static/dist/1147.32f41a88.async.js +0 -1
  179. package/static/dist/1352.ab6da08e.async.js +0 -1
  180. package/static/dist/1690.f0290540.async.js +0 -1
  181. package/static/dist/1742.6cbe5aca.async.js +0 -1
  182. package/static/dist/2208.8e3a7325.async.js +0 -1
  183. package/static/dist/5312.74b95311.async.js +0 -1
  184. package/static/dist/5691.931f59c5.async.js +0 -1
  185. package/static/dist/6159.55cb068a.async.js +0 -1
  186. package/static/dist/7025.f4080d63.async.js +0 -1
  187. package/static/dist/739.6be5552a.async.js +0 -1
  188. package/static/dist/7571.4f6240b1.async.js +0 -1
  189. package/static/dist/786.59fc381c.async.js +0 -1
  190. package/static/dist/8317.c44c1ebd.async.js +0 -1
  191. package/static/dist/8826.0291edfd.async.js +0 -1
  192. package/static/dist/955.3c9481f7.async.js +0 -1
  193. package/static/dist/layouts__index.1fce90e0.chunk.css +0 -1
  194. package/static/dist/layouts__index.8dcf1576.async.js +0 -1
  195. package/static/dist/preload_helper.116a62f6.js +0 -1
  196. package/static/dist/src__pages__crontab__detail.b07f0c0a.async.js +0 -1
  197. package/static/dist/src__pages__crontab__index.2e2e1096.async.js +0 -1
  198. package/static/dist/src__pages__crontab__modal.4d8c2a22.async.js +0 -1
  199. package/static/dist/src__pages__crontab__type.db7c1858.async.js +0 -1
  200. package/static/dist/src__pages__dependence__modal.631ffb5b.async.js +0 -1
  201. package/static/dist/src__pages__env__editNameModal.ff85ef8c.async.js +0 -1
  202. package/static/dist/src__pages__env__index.a0a2fece.async.js +0 -1
  203. package/static/dist/src__pages__env__modal.d1004662.async.js +0 -1
  204. package/static/dist/src__pages__error__index.1bc3c90b.async.js +0 -1
  205. package/static/dist/src__pages__initialization__index.8b1cbaf9.async.js +0 -1
  206. package/static/dist/src__pages__script__editNameModal.53424d49.async.js +0 -1
  207. package/static/dist/src__pages__script__index.e65df827.async.js +0 -1
  208. package/static/dist/src__pages__script__renameModal.4bbe7fb1.async.js +0 -1
  209. package/static/dist/src__pages__script__saveModal.cf449f3c.async.js +0 -1
  210. package/static/dist/src__pages__script__setting.b345d59a.async.js +0 -1
  211. package/static/dist/src__pages__setting__appModal.03faec89.async.js +0 -1
  212. package/static/dist/src__pages__setting__dependence.4495c7b6.async.js +0 -1
  213. package/static/dist/src__pages__setting__index.6919c399.async.js +0 -1
  214. package/static/dist/src__pages__setting__notification.d6a3884f.async.js +0 -1
  215. package/static/dist/src__pages__setting__other.0d931d6f.async.js +0 -1
  216. package/static/dist/src__pages__setting__security.91cb545f.async.js +0 -1
  217. package/static/dist/src__pages__setting__systemLog.1d433a48.async.js +0 -1
  218. package/static/dist/src__pages__subscription__modal.0b84f6cf.async.js +0 -1
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@whyour/qinglong",
3
3
  "packageManager": "pnpm@8.3.1",
4
- "version": "2.20.2-3",
4
+ "version": "2.21.0",
5
5
  "description": "Timed task management platform supporting Python3, JavaScript, Shell, Typescript",
6
6
  "repository": {
7
7
  "type": "git",
@@ -67,16 +67,22 @@
67
67
  }
68
68
  },
69
69
  "overrides": {
70
- "sqlite3": "git+https://github.com/whyour/node-sqlite3.git#v1.0.3"
70
+ "sqlite3": "git+https://github.com/whyour/node-sqlite3.git#v1.0.3",
71
+ "@codemirror/state": "6.5.4",
72
+ "@codemirror/view": "6.39.16"
71
73
  }
72
74
  },
73
75
  "dependencies": {
76
+ "@ant-design/plots": "^2.6.8",
77
+ "@bufbuild/protobuf": "^2.10.0",
74
78
  "@grpc/grpc-js": "^1.14.0",
75
79
  "@grpc/proto-loader": "^0.8.0",
80
+ "@keyv/sqlite": "^4.0.1",
76
81
  "@otplib/preset-default": "^12.0.1",
77
82
  "body-parser": "^1.20.3",
78
83
  "celebrate": "^15.0.3",
79
84
  "chokidar": "^4.0.1",
85
+ "compression": "^1.7.4",
80
86
  "cors": "^2.8.5",
81
87
  "cron-parser": "^5.4.0",
82
88
  "cross-spawn": "^7.0.6",
@@ -86,51 +92,48 @@
86
92
  "express-jwt": "^8.4.1",
87
93
  "express-rate-limit": "^7.4.1",
88
94
  "express-urlrewrite": "^2.0.3",
89
- "undici": "^7.9.0",
95
+ "helmet": "^8.1.0",
90
96
  "hpagent": "^1.2.0",
91
97
  "http-proxy-middleware": "^3.0.3",
92
98
  "iconv-lite": "^0.6.3",
99
+ "ip2region": "2.3.0",
93
100
  "js-yaml": "^4.1.0",
94
101
  "jsonwebtoken": "^9.0.2",
102
+ "keyv": "^5.2.3",
95
103
  "lodash": "^4.17.21",
96
- "multer": "1.4.5-lts.1",
104
+ "multer": "2.1.1",
97
105
  "node-schedule": "^2.1.0",
98
- "nodemailer": "^6.9.16",
106
+ "nodemailer": "^8.0.1",
99
107
  "p-queue-cjs": "7.3.4",
100
- "@bufbuild/protobuf": "^2.10.0",
108
+ "proper-lockfile": "^4.1.2",
101
109
  "ps-tree": "^1.2.0",
102
110
  "reflect-metadata": "^0.2.2",
111
+ "request-ip": "3.3.0",
103
112
  "sequelize": "^6.37.5",
104
113
  "sockjs": "^0.3.24",
105
114
  "sqlite3": "git+https://github.com/whyour/node-sqlite3.git#v1.0.3",
106
115
  "toad-scheduler": "^3.0.1",
107
116
  "typedi": "^0.10.0",
117
+ "undici": "^7.9.0",
108
118
  "uuid": "^11.0.3",
109
119
  "winston": "^3.17.0",
110
- "winston-daily-rotate-file": "^5.0.0",
111
- "request-ip": "3.3.0",
112
- "ip2region": "2.3.0",
113
- "keyv": "^5.2.3",
114
- "@keyv/sqlite": "^4.0.1",
115
- "proper-lockfile": "^4.1.2",
116
- "compression": "^1.7.4",
117
- "helmet": "^8.1.0"
120
+ "winston-daily-rotate-file": "^5.0.0"
118
121
  },
119
122
  "devDependencies": {
120
- "moment": "2.30.1",
121
123
  "@ant-design/icons": "^5.0.1",
122
124
  "@ant-design/pro-layout": "6.38.22",
123
- "@codemirror/view": "^6.34.1",
124
- "@codemirror/state": "^6.4.1",
125
+ "@codemirror/state": "6.5.4",
126
+ "@codemirror/view": "6.39.16",
125
127
  "@monaco-editor/react": "4.2.1",
126
128
  "@react-hook/resize-observer": "^2.0.2",
127
- "react-router-dom": "6.26.1",
128
129
  "@types/body-parser": "^1.19.2",
130
+ "@types/compression": "^1.7.2",
129
131
  "@types/cors": "^2.8.12",
130
132
  "@types/cross-spawn": "^6.0.2",
131
133
  "@types/express": "^4.17.13",
132
134
  "@types/express-jwt": "^6.0.4",
133
135
  "@types/file-saver": "2.0.2",
136
+ "@types/helmet": "^4.0.0",
134
137
  "@types/js-yaml": "^4.0.5",
135
138
  "@types/jsonwebtoken": "^8.5.8",
136
139
  "@types/lodash": "^4.14.185",
@@ -138,17 +141,17 @@
138
141
  "@types/node": "^17.0.21",
139
142
  "@types/node-schedule": "^1.3.2",
140
143
  "@types/nodemailer": "^6.4.4",
144
+ "@types/proper-lockfile": "^4.1.4",
145
+ "@types/ps-tree": "^1.1.6",
141
146
  "@types/qrcode.react": "^1.0.2",
142
147
  "@types/react": "^18.0.20",
143
148
  "@types/react-copy-to-clipboard": "^5.0.4",
144
149
  "@types/react-dom": "^18.0.6",
150
+ "@types/request-ip": "0.0.41",
145
151
  "@types/serve-handler": "^6.1.1",
146
152
  "@types/sockjs": "^0.3.33",
147
153
  "@types/sockjs-client": "^1.5.1",
148
154
  "@types/uuid": "^8.3.4",
149
- "@types/request-ip": "0.0.41",
150
- "@types/proper-lockfile": "^4.1.4",
151
- "@types/ps-tree": "^1.1.6",
152
155
  "@uiw/codemirror-extensions-langs": "^4.21.9",
153
156
  "@uiw/react-codemirror": "^4.21.9",
154
157
  "@umijs/max": "^4.4.4",
@@ -160,9 +163,9 @@
160
163
  "axios": "^1.4.0",
161
164
  "compression-webpack-plugin": "9.2.0",
162
165
  "concurrently": "^7.0.0",
163
- "react-hotkeys-hook": "^4.6.1",
164
166
  "file-saver": "2.0.2",
165
167
  "lint-staged": "^13.0.3",
168
+ "moment": "2.30.1",
166
169
  "monaco-editor": "0.33.0",
167
170
  "nodemon": "^3.0.1",
168
171
  "prettier": "^2.5.1",
@@ -178,7 +181,9 @@
178
181
  "react-dnd": "^16.0.1",
179
182
  "react-dnd-html5-backend": "^16.0.1",
180
183
  "react-dom": "18.3.1",
184
+ "react-hotkeys-hook": "^4.6.1",
181
185
  "react-intl-universal": "^2.12.0",
186
+ "react-router-dom": "6.26.1",
182
187
  "react-split-pane": "^0.1.92",
183
188
  "sockjs-client": "^1.6.0",
184
189
  "ts-node": "^10.9.2",
@@ -186,8 +191,6 @@
186
191
  "tslib": "^2.4.0",
187
192
  "typescript": "5.2.2",
188
193
  "vh-check": "^2.0.5",
189
- "virtualizedtableforantd4": "1.3.0",
190
- "@types/compression": "^1.7.2",
191
- "@types/helmet": "^4.0.0"
194
+ "virtualizedtableforantd4": "1.3.0"
192
195
  }
193
196
  }
@@ -195,12 +195,14 @@ export SMTP_SERVER=""
195
195
  ## SMTP 发送邮件服务器是否使用 SSL,填写 true 或 false
196
196
  export SMTP_SSL=""
197
197
 
198
- ## smtp_email 填写 SMTP 收发件邮箱,通知将会由自己发给自己
198
+ ## smtp_email 填写 SMTP 发件邮箱
199
199
  export SMTP_EMAIL=""
200
200
  ## smtp_password 填写 SMTP 登录密码,也可能为特殊口令,视具体邮件服务商说明而定
201
201
  export SMTP_PASSWORD=""
202
202
  ## smtp_name 填写 SMTP 收发件人姓名,可随意填写
203
203
  export SMTP_NAME=""
204
+ ## smtp_email_to 填写 SMTP 收件邮箱,多个用英文;分隔,不填默认发给发件邮箱
205
+ export SMTP_EMAIL_TO=""
204
206
 
205
207
  ## 17. PushMe
206
208
  ## 官方说明文档:https://push.i-i.me/
@@ -259,4 +261,13 @@ export WEBHOOK_METHOD=""
259
261
  ## 支持 text/plain、application/json、multipart/form-data、application/x-www-form-urlencoded
260
262
  export WEBHOOK_CONTENT_TYPE=""
261
263
 
264
+ ## 23. OpeniLink
265
+ ## 官方文档: https://openilink.com/docs/hub/apps
266
+ ## 在 OpeniLink Hub 后台安装 App 后获取 app_token
267
+ export OPENILINK_APP_TOKEN=""
268
+ ## OpeniLink Hub 地址,默认为 https://hub.openilink.com,自建 Hub 时填写自己的地址
269
+ export OPENILINK_HUB_URL=""
270
+ ## OpeniLink 的 context_token,用于标识消息会话上下文,可从消息事件中获取
271
+ export OPENILINK_CONTEXT_TOKEN=""
272
+
262
273
  ## 其他需要的变量,脚本中需要的变量使用 export 变量名= 声明即可
package/sample/notify.js CHANGED
@@ -108,8 +108,8 @@ const push_config = {
108
108
 
109
109
  QYWX_KEY: '', // 企业微信机器人的 webhook(详见文档 https://work.weixin.qq.com/api/doc/90000/90136/91770),例如:693a91f6-7xxx-4bc4-97a0-0ec2sifa5aaa
110
110
 
111
- TG_BOT_TOKEN: '', // tg 机器人的 TG_BOT_TOKEN,例:1407203283:AAG9rt-6RDaaX0HBLZQq0laNOh898iFYaRQ
112
- TG_USER_ID: '', // tg 机器人的 TG_USER_ID,例:1434078534
111
+ TG_BOT_TOKEN: '', // tg 机器人的 TG_BOT_TOKEN,例:1234567890:ABCdefGHIjklMNOpqrsTUVwxyz
112
+ TG_USER_ID: '', // tg 机器人的 TG_USER_ID,例:1234567890
113
113
  TG_API_HOST: 'https://api.telegram.org', // tg 代理 api
114
114
  TG_PROXY_AUTH: '', // tg 代理认证参数
115
115
  TG_PROXY_HOST: '', // tg 机器人的 TG_PROXY_HOST
@@ -121,7 +121,8 @@ const push_config = {
121
121
 
122
122
  SMTP_SERVICE: '', // 邮箱服务名称,比如 126、163、Gmail、QQ 等,支持列表 https://github.com/nodemailer/nodemailer/blob/master/lib/well-known/services.json
123
123
  SMTP_EMAIL: '', // SMTP 发件邮箱
124
- SMTP_TO: '', // SMTP 收件邮箱,默认通知将会发给发件邮箱
124
+ SMTP_TO: '', // SMTP 收件邮箱,兼容旧参数名,默认通知将会发给发件邮箱
125
+ SMTP_EMAIL_TO: '', // SMTP 收件邮箱,多个分号分隔,默认发给发件邮箱
125
126
  SMTP_PASSWORD: '', // SMTP 登录密码,也可能为特殊口令,视具体邮件服务商说明而定
126
127
  SMTP_NAME: '', // SMTP 收发件人姓名,可随意填写
127
128
 
@@ -151,6 +152,11 @@ const push_config = {
151
152
  WXPUSHER_APP_TOKEN: '', // wxpusher 的 appToken
152
153
  WXPUSHER_TOPIC_IDS: '', // wxpusher 的 主题ID,多个用英文分号;分隔 topic_ids 与 uids 至少配置一个才行
153
154
  WXPUSHER_UIDS: '', // wxpusher 的 用户ID,多个用英文分号;分隔 topic_ids 与 uids 至少配置一个才行
155
+
156
+ // 官方文档: https://openilink.com/docs/hub/apps
157
+ OPENILINK_APP_TOKEN: '', // OpeniLink 的 app_token,在 OpeniLink Hub 后台安装 App 后获取
158
+ OPENILINK_HUB_URL: '', // OpeniLink Hub 地址,默认为 https://hub.openilink.com,自建 Hub 时填写自己的地址
159
+ OPENILINK_CONTEXT_TOKEN: '', // OpeniLink 的 context_token,用于标识消息会话上下文,可从消息事件中获取
154
160
  };
155
161
 
156
162
  for (const key in push_config) {
@@ -301,7 +307,7 @@ function serverNotify(text, desp) {
301
307
  console.log('Server 酱发送通知调用API失败😞\n', err);
302
308
  } else {
303
309
  // server酱和Server酱·Turbo版的返回json格式不太一样
304
- if (data.errno === 0 || data.data.errno === 0) {
310
+ if (data.errno === 0 || data.code === 0) {
305
311
  console.log('Server 酱发送通知消息成功🎉\n');
306
312
  } else if (data.errno === 1024) {
307
313
  // 一分钟内发送相同的内容会触发
@@ -1046,8 +1052,14 @@ function fsBotNotify(text, desp) {
1046
1052
  }
1047
1053
 
1048
1054
  async function smtpNotify(text, desp) {
1049
- const { SMTP_EMAIL, SMTP_TO, SMTP_PASSWORD, SMTP_SERVICE, SMTP_NAME } =
1050
- push_config;
1055
+ const {
1056
+ SMTP_EMAIL,
1057
+ SMTP_TO,
1058
+ SMTP_EMAIL_TO,
1059
+ SMTP_PASSWORD,
1060
+ SMTP_SERVICE,
1061
+ SMTP_NAME,
1062
+ } = push_config;
1051
1063
  if (![SMTP_EMAIL, SMTP_PASSWORD].every(Boolean) || !SMTP_SERVICE) {
1052
1064
  return;
1053
1065
  }
@@ -1063,9 +1075,20 @@ async function smtpNotify(text, desp) {
1063
1075
  });
1064
1076
 
1065
1077
  const addr = SMTP_NAME ? `"${SMTP_NAME}" <${SMTP_EMAIL}>` : SMTP_EMAIL;
1078
+ const recipients = [SMTP_EMAIL_TO, SMTP_TO].reduce((list, value) => {
1079
+ if (!value) {
1080
+ return list;
1081
+ }
1082
+ return list.concat(
1083
+ value
1084
+ .split(/[;;]/)
1085
+ .map((item) => item.trim())
1086
+ .filter(Boolean),
1087
+ );
1088
+ }, []);
1066
1089
  const info = await transporter.sendMail({
1067
1090
  from: addr,
1068
- to: SMTP_TO ? SMTP_TO.split(';') : addr,
1091
+ to: recipients.length ? recipients : SMTP_EMAIL,
1069
1092
  subject: text,
1070
1093
  html: `${desp.replace(/\n/g, '<br/>')}`,
1071
1094
  });
@@ -1408,6 +1431,54 @@ function wxPusherNotify(text, desp) {
1408
1431
  });
1409
1432
  }
1410
1433
 
1434
+ function openiLinkNotify(text, desp) {
1435
+ return new Promise((resolve) => {
1436
+ const { OPENILINK_APP_TOKEN, OPENILINK_HUB_URL, OPENILINK_CONTEXT_TOKEN } =
1437
+ push_config;
1438
+ if (OPENILINK_APP_TOKEN) {
1439
+ const baseUrl = OPENILINK_HUB_URL
1440
+ ? OPENILINK_HUB_URL.replace(/\/$/, '')
1441
+ : 'https://hub.openilink.com';
1442
+ const body = {
1443
+ type: 'text',
1444
+ content: `${text}\n\n${desp}`,
1445
+ };
1446
+ if (OPENILINK_CONTEXT_TOKEN) {
1447
+ body.context_token = OPENILINK_CONTEXT_TOKEN;
1448
+ }
1449
+ const options = {
1450
+ url: `${baseUrl}/bot/v1/message/send`,
1451
+ body: JSON.stringify(body),
1452
+ headers: {
1453
+ 'Content-Type': 'application/json',
1454
+ Authorization: `Bearer ${OPENILINK_APP_TOKEN}`,
1455
+ },
1456
+ timeout,
1457
+ };
1458
+
1459
+ $.post(options, (err, resp, data) => {
1460
+ try {
1461
+ if (err) {
1462
+ console.log('OpeniLink 发送通知消息失败!\n', err);
1463
+ } else {
1464
+ if (data.ok) {
1465
+ console.log('OpeniLink 发送通知消息成功!');
1466
+ } else {
1467
+ console.log(`OpeniLink 发送通知消息异常:${data.error}`);
1468
+ }
1469
+ }
1470
+ } catch (e) {
1471
+ $.logErr(e, resp);
1472
+ } finally {
1473
+ resolve(data);
1474
+ }
1475
+ });
1476
+ } else {
1477
+ resolve();
1478
+ }
1479
+ });
1480
+ }
1481
+
1411
1482
  function parseString(input, valueFormatFn) {
1412
1483
  const regex = /(\w+):\s*((?:(?!\n\w+:).)*)/g;
1413
1484
  const matches = {};
@@ -1511,7 +1582,7 @@ async function sendNotify(text, desp, params = {}) {
1511
1582
  }
1512
1583
  }
1513
1584
 
1514
- if (push_config.HITOKOTO !== 'false') {
1585
+ if (![false, 'false'].includes(push_config.HITOKOTO)) {
1515
1586
  desp += '\n\n' + (await one());
1516
1587
  }
1517
1588
 
@@ -1527,7 +1598,7 @@ async function sendNotify(text, desp, params = {}) {
1527
1598
  iGotNotify(text, desp, params), // iGot
1528
1599
  gobotNotify(text, desp), // go-cqhttp
1529
1600
  gotifyNotify(text, desp), // gotify
1530
- chatNotify(text, desp), // synolog chat
1601
+ chatNotify(text, desp), // synology chat
1531
1602
  pushDeerNotify(text, desp), // PushDeer
1532
1603
  aibotkNotify(text, desp), // 智能微秘书
1533
1604
  fsBotNotify(text, desp), // 飞书机器人
@@ -1538,6 +1609,7 @@ async function sendNotify(text, desp, params = {}) {
1538
1609
  qmsgNotify(text, desp), // 自定义通知
1539
1610
  ntfyNotify(text, desp), // Ntfy
1540
1611
  wxPusherNotify(text, desp), // wxpusher
1612
+ openiLinkNotify(text, desp), // OpeniLink
1541
1613
  ]);
1542
1614
  }
1543
1615
 
package/sample/notify.py CHANGED
@@ -94,8 +94,8 @@ push_config = {
94
94
 
95
95
  'QYWX_KEY': '', # 企业微信机器人
96
96
 
97
- 'TG_BOT_TOKEN': '', # tg 机器人的 TG_BOT_TOKEN,例:1407203283:AAG9rt-6RDaaX0HBLZQq0laNOh898iFYaRQ
98
- 'TG_USER_ID': '', # tg 机器人的 TG_USER_ID,例:1434078534
97
+ 'TG_BOT_TOKEN': '', # tg 机器人的 TG_BOT_TOKEN,例:1234567890:ABCdefGHIjklMNOpqrsTUVwxyz
98
+ 'TG_USER_ID': '', # tg 机器人的 TG_USER_ID,例:1234567890
99
99
  'TG_API_HOST': '', # tg 代理 api
100
100
  'TG_PROXY_AUTH': '', # tg 代理认证参数
101
101
  'TG_PROXY_HOST': '', # tg 机器人的 TG_PROXY_HOST
@@ -107,7 +107,8 @@ push_config = {
107
107
 
108
108
  'SMTP_SERVER': '', # SMTP 发送邮件服务器,形如 smtp.exmail.qq.com:465
109
109
  'SMTP_SSL': 'false', # SMTP 发送邮件服务器是否使用 SSL,填写 true 或 false
110
- 'SMTP_EMAIL': '', # SMTP 收发件邮箱,通知将会由自己发给自己
110
+ 'SMTP_EMAIL': '', # SMTP 发件邮箱
111
+ 'SMTP_EMAIL_TO': '', # SMTP 收件邮箱,多个分号分隔,默认发给发件邮箱
111
112
  'SMTP_PASSWORD': '', # SMTP 登录密码,也可能为特殊口令,视具体邮件服务商说明而定
112
113
  'SMTP_NAME': '', # SMTP 收发件人姓名,可随意填写
113
114
 
@@ -135,6 +136,10 @@ push_config = {
135
136
  'WXPUSHER_APP_TOKEN': '', # wxpusher 的 appToken 官方文档: https://wxpusher.zjiecode.com/docs/ 管理后台: https://wxpusher.zjiecode.com/admin/
136
137
  'WXPUSHER_TOPIC_IDS': '', # wxpusher 的 主题ID,多个用英文分号;分隔 topic_ids 与 uids 至少配置一个才行
137
138
  'WXPUSHER_UIDS': '', # wxpusher 的 用户ID,多个用英文分号;分隔 topic_ids 与 uids 至少配置一个才行
139
+
140
+ 'OPENILINK_APP_TOKEN': '', # OpeniLink 的 app_token,在 OpeniLink Hub 后台安装 App 后获取 官方文档: https://openilink.com/docs/hub/apps
141
+ 'OPENILINK_HUB_URL': '', # OpeniLink Hub 地址,默认为 https://hub.openilink.com,自建 Hub 时填写自己的地址
142
+ 'OPENILINK_CONTEXT_TOKEN': '', # OpeniLink 的 context_token,用于标识消息会话上下文,可从消息事件中获取
138
143
  }
139
144
  # fmt: on
140
145
 
@@ -690,6 +695,10 @@ def smtp(title: str, content: str) -> None:
690
695
  return
691
696
  print("SMTP 邮件 服务启动")
692
697
 
698
+ email_to = push_config.get("SMTP_EMAIL_TO") or push_config.get("SMTP_EMAIL")
699
+ email_to_list = [
700
+ item.strip() for item in re.split(r"[;;]", email_to) if item.strip()
701
+ ]
693
702
  message = MIMEText(content, "plain", "utf-8")
694
703
  message["From"] = formataddr(
695
704
  (
@@ -697,12 +706,7 @@ def smtp(title: str, content: str) -> None:
697
706
  push_config.get("SMTP_EMAIL"),
698
707
  )
699
708
  )
700
- message["To"] = formataddr(
701
- (
702
- Header(push_config.get("SMTP_NAME"), "utf-8").encode(),
703
- push_config.get("SMTP_EMAIL"),
704
- )
705
- )
709
+ message["To"] = ",".join(email_to_list)
706
710
  message["Subject"] = Header(title, "utf-8")
707
711
 
708
712
  try:
@@ -716,7 +720,7 @@ def smtp(title: str, content: str) -> None:
716
720
  )
717
721
  smtp_server.sendmail(
718
722
  push_config.get("SMTP_EMAIL"),
719
- push_config.get("SMTP_EMAIL"),
723
+ email_to_list,
720
724
  message.as_bytes(),
721
725
  )
722
726
  smtp_server.close()
@@ -898,6 +902,43 @@ def wxpusher_bot(title: str, content: str) -> None:
898
902
  print(f"wxpusher 推送失败!错误信息:{response.get('msg')}")
899
903
 
900
904
 
905
+ def openilink(title: str, content: str) -> None:
906
+ """
907
+ 通过 OpeniLink 推送消息。
908
+ 支持的环境变量:
909
+ - OPENILINK_APP_TOKEN: 在 OpeniLink Hub 后台安装 App 后获取的 app_token
910
+ - OPENILINK_HUB_URL: OpeniLink Hub 地址,默认为 https://hub.openilink.com
911
+ - OPENILINK_CONTEXT_TOKEN: 消息会话上下文 token,可从消息事件中获取
912
+ """
913
+ if not push_config.get("OPENILINK_APP_TOKEN"):
914
+ return
915
+
916
+ print("OpeniLink 服务启动")
917
+
918
+ base_url = (
919
+ push_config.get("OPENILINK_HUB_URL", "").rstrip("/")
920
+ or "https://hub.openilink.com"
921
+ )
922
+ url = f"{base_url}/bot/v1/message/send"
923
+ headers = {
924
+ "Content-Type": "application/json",
925
+ "Authorization": f'Bearer {push_config.get("OPENILINK_APP_TOKEN")}',
926
+ }
927
+ data = {
928
+ "type": "text",
929
+ "content": f"{title}\n\n{content}",
930
+ }
931
+ if push_config.get("OPENILINK_CONTEXT_TOKEN"):
932
+ data["context_token"] = push_config.get("OPENILINK_CONTEXT_TOKEN")
933
+
934
+ response = requests.post(url=url, json=data, headers=headers).json()
935
+
936
+ if response.get("ok"):
937
+ print("OpeniLink 推送成功!")
938
+ else:
939
+ print(f'OpeniLink 推送失败!错误信息:{response.get("error")}')
940
+
941
+
901
942
  def parse_headers(headers):
902
943
  if not headers:
903
944
  return {}
@@ -1063,6 +1104,8 @@ def add_notify_function():
1063
1104
  push_config.get("WXPUSHER_TOPIC_IDS") or push_config.get("WXPUSHER_UIDS")
1064
1105
  ):
1065
1106
  notify_function.append(wxpusher_bot)
1107
+ if push_config.get("OPENILINK_APP_TOKEN"):
1108
+ notify_function.append(openilink)
1066
1109
  if not notify_function:
1067
1110
  print(f"无推送渠道,请检查通知变量是否正确")
1068
1111
  return notify_function
@@ -1088,7 +1131,7 @@ def send(title: str, content: str, ignore_default_config: bool = False, **kwargs
1088
1131
  return
1089
1132
 
1090
1133
  hitokoto = push_config.get("HITOKOTO")
1091
- content += "\n\n" + one() if hitokoto != "false" else ""
1134
+ content += "\n\n" + one() if hitokoto not in [False, "false"] else ""
1092
1135
 
1093
1136
  notify_function = add_notify_function()
1094
1137
  ts = [
package/shell/api.sh CHANGED
@@ -41,7 +41,7 @@ add_cron_api() {
41
41
  fi
42
42
 
43
43
  local api=$(
44
- curl -s --noproxy "*" "http://0.0.0.0:${ql_port}/open/crons?t=$currentTimeStamp" \
44
+ curl -s --noproxy "*" "http://localhost:${ql_port}/open/crons?t=$currentTimeStamp" \
45
45
  -H "Authorization: Bearer ${__ql_token__}" \
46
46
  -H "Content-Type: application/json;charset=UTF-8" \
47
47
  --data-raw "{\"name\":\"${name//\"/\\\"}\",\"command\":\"${command//\"/\\\"}\",\"schedule\":\"$schedule\",\"sub_id\":$sub_id}" \
@@ -50,9 +50,9 @@ add_cron_api() {
50
50
  code=$(echo "$api" | jq -r .code)
51
51
  message=$(echo "$api" | jq -r .message)
52
52
  if [[ $code == 200 ]]; then
53
- echo -e "$name -> 添加成功"
53
+ t '%s -> 添加成功' "$name"
54
54
  else
55
- echo -e "$name -> 添加失败(${message})"
55
+ t '%s -> 添加失败(%s)' "$name" "$message"
56
56
  fi
57
57
  }
58
58
 
@@ -71,7 +71,7 @@ update_cron_api() {
71
71
  fi
72
72
 
73
73
  local api=$(
74
- curl -s --noproxy "*" "http://0.0.0.0:${ql_port}/open/crons?t=$currentTimeStamp" \
74
+ curl -s --noproxy "*" "http://localhost:${ql_port}/open/crons?t=$currentTimeStamp" \
75
75
  -X 'PUT' \
76
76
  -H "Authorization: Bearer ${__ql_token__}" \
77
77
  -H "Content-Type: application/json;charset=UTF-8" \
@@ -81,9 +81,9 @@ update_cron_api() {
81
81
  code=$(echo "$api" | jq -r .code)
82
82
  message=$(echo "$api" | jq -r .message)
83
83
  if [[ $code == 200 ]]; then
84
- echo -e "$name -> 更新成功"
84
+ t '%s -> 更新成功' "$name"
85
85
  else
86
- echo -e "$name -> 更新失败(${message})"
86
+ t '%s -> 更新失败(%s)' "$name" "$message"
87
87
  fi
88
88
  }
89
89
 
@@ -98,7 +98,7 @@ update_cron_command_api() {
98
98
  fi
99
99
 
100
100
  local api=$(
101
- curl -s --noproxy "*" "http://0.0.0.0:${ql_port}/open/crons?t=$currentTimeStamp" \
101
+ curl -s --noproxy "*" "http://localhost:${ql_port}/open/crons?t=$currentTimeStamp" \
102
102
  -X 'PUT' \
103
103
  -H "Authorization: Bearer ${__ql_token__}" \
104
104
  -H "Content-Type: application/json;charset=UTF-8" \
@@ -108,9 +108,9 @@ update_cron_command_api() {
108
108
  code=$(echo "$api" | jq -r .code)
109
109
  message=$(echo "$api" | jq -r .message)
110
110
  if [[ $code == 200 ]]; then
111
- echo -e "$command -> 更新成功"
111
+ t '%s -> 更新成功' "$command"
112
112
  else
113
- echo -e "$command -> 更新失败(${message})"
113
+ t '%s -> 更新失败(%s)' "$command" "$message"
114
114
  fi
115
115
  }
116
116
 
@@ -118,7 +118,7 @@ del_cron_api() {
118
118
  local ids="$1"
119
119
  local currentTimeStamp=$(date +%s)
120
120
  local api=$(
121
- curl -s --noproxy "*" "http://0.0.0.0:${ql_port}/open/crons?t=$currentTimeStamp" \
121
+ curl -s --noproxy "*" "http://localhost:${ql_port}/open/crons?t=$currentTimeStamp" \
122
122
  -X 'DELETE' \
123
123
  -H "Authorization: Bearer ${__ql_token__}" \
124
124
  -H "Content-Type: application/json;charset=UTF-8" \
@@ -128,9 +128,9 @@ del_cron_api() {
128
128
  code=$(echo "$api" | jq -r .code)
129
129
  message=$(echo "$api" | jq -r .message)
130
130
  if [[ $code == 200 ]]; then
131
- echo -e "成功"
131
+ t '成功'
132
132
  else
133
- echo -e "失败(${message})"
133
+ t '失败(%s)' "$message"
134
134
  fi
135
135
  }
136
136
 
@@ -143,7 +143,7 @@ update_cron() {
143
143
  local runningTime="${6:-0}"
144
144
  local currentTimeStamp=$(date +%s)
145
145
  local api=$(
146
- curl -s --noproxy "*" "http://0.0.0.0:${ql_port}/open/crons/status?t=$currentTimeStamp" \
146
+ curl -s --noproxy "*" "http://localhost:${ql_port}/open/crons/status?t=$currentTimeStamp" \
147
147
  -X 'PUT' \
148
148
  -H "Authorization: Bearer ${__ql_token__}" \
149
149
  -H "Content-Type: application/json;charset=UTF-8" \
@@ -165,7 +165,7 @@ notify_api() {
165
165
  local content="$2"
166
166
  local currentTimeStamp=$(date +%s)
167
167
  local api=$(
168
- curl -s --noproxy "*" "http://0.0.0.0:${ql_port}/open/system/notify?t=$currentTimeStamp" \
168
+ curl -s --noproxy "*" "http://localhost:${ql_port}/open/system/notify?t=$currentTimeStamp" \
169
169
  -X 'PUT' \
170
170
  -H "Authorization: Bearer ${__ql_token__}" \
171
171
  -H "Content-Type: application/json;charset=UTF-8" \
@@ -175,9 +175,9 @@ notify_api() {
175
175
  code=$(echo "$api" | jq -r .code)
176
176
  message=$(echo "$api" | jq -r .message)
177
177
  if [[ $code == 200 ]]; then
178
- echo -e "通知发送成功🎉"
178
+ t '通知发送成功🎉'
179
179
  else
180
- echo -e "通知失败(${message})"
180
+ t '通知失败(%s)' "$message"
181
181
  fi
182
182
  }
183
183
 
@@ -185,7 +185,7 @@ find_cron_api() {
185
185
  local params="$1"
186
186
  local currentTimeStamp=$(date +%s)
187
187
  local api=$(
188
- curl -s --noproxy "*" "http://0.0.0.0:${ql_port}/open/crons/detail?$params&t=$currentTimeStamp" \
188
+ curl -s --noproxy "*" "http://localhost:${ql_port}/open/crons/detail?$params&t=$currentTimeStamp" \
189
189
  -H "Authorization: Bearer ${__ql_token__}" \
190
190
  -H "Content-Type: application/json;charset=UTF-8" \
191
191
  --compressed
@@ -204,7 +204,7 @@ update_auth_config() {
204
204
  local tip="$2"
205
205
  local currentTimeStamp=$(date +%s)
206
206
  local api=$(
207
- curl -s --noproxy "*" "http://0.0.0.0:${ql_port}/open/system/auth/reset?t=$currentTimeStamp" \
207
+ curl -s --noproxy "*" "http://localhost:${ql_port}/open/system/auth/reset?t=$currentTimeStamp" \
208
208
  -X 'PUT' \
209
209
  -H "Authorization: Bearer ${__ql_token__}" \
210
210
  -H "Content-Type: application/json;charset=UTF-8" \
@@ -214,9 +214,33 @@ update_auth_config() {
214
214
  code=$(echo "$api" | jq -r .code)
215
215
  message=$(echo "$api" | jq -r .message)
216
216
  if [[ $code == 200 ]]; then
217
- echo -e "${tip}成功🎉"
217
+ t '%s成功🎉' "$tip"
218
218
  else
219
- echo -e "${tip}失败(${message})"
219
+ t '%s失败(%s)' "$tip" "$message"
220
+ fi
221
+ }
222
+
223
+ record_cron_stat() {
224
+ local ref_id="$1"
225
+ local exit_code="${2:-0}"
226
+ local elapsed="${3:-0}"
227
+ [[ $ref_id ]] && [[ $ref_id -gt 0 ]] 2>/dev/null || return
228
+
229
+ local api=$(
230
+ curl -s --noproxy "*" "http://localhost:${ql_port:-5700}/open/dashboard/record" \
231
+ -X POST \
232
+ -H "Authorization: Bearer ${__ql_token__}" \
233
+ -H "Content-Type: application/json;charset=UTF-8" \
234
+ --data-raw "{\"ref_id\":$ref_id,\"code\":$exit_code,\"elapsed\":$elapsed}" \
235
+ --compressed
236
+ )
237
+ code=$(echo "$api" | jq -r .code)
238
+ message=$(echo "$api" | jq -r .message)
239
+ if [[ $code != 200 ]]; then
240
+ if [[ ! $message ]]; then
241
+ message="$api"
242
+ fi
243
+ echo -e "${message}"
220
244
  fi
221
245
  }
222
246