@whyour/qinglong 2.20.2-3 → 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 (238) hide show
  1. package/.env.example +5 -0
  2. package/AGENTS.md +43 -0
  3. package/README-en.md +11 -0
  4. package/README.md +11 -0
  5. package/back/protos/api.proto +3 -0
  6. package/deploy/kubernetes/README.md +81 -0
  7. package/deploy/kubernetes/base/kustomization.yaml +5 -0
  8. package/deploy/kubernetes/base/qinglong.yaml +95 -0
  9. package/deploy/kubernetes/overlays/example/kustomization.yaml +10 -0
  10. package/docker/Dockerfile +51 -69
  11. package/docker/Dockerfile.310 +94 -0
  12. package/docker/Dockerfile.debian +120 -0
  13. package/docker/{310.Dockerfile → Dockerfile.debian310} +13 -5
  14. package/docker/docker-entrypoint.sh +95 -3
  15. package/docs/PROJECT_ARCHITECTURE.md +550 -0
  16. package/package.json +29 -28
  17. package/sample/config.sample.sh +19 -3
  18. package/sample/notify.js +145 -9
  19. package/sample/notify.py +110 -11
  20. package/shell/api.sh +51 -21
  21. package/shell/bot.sh +30 -16
  22. package/shell/check.sh +21 -19
  23. package/shell/lang/en.sh +105 -0
  24. package/shell/lang/zh.sh +105 -0
  25. package/shell/otask.sh +117 -28
  26. package/shell/preload/client.js +20 -2
  27. package/shell/preload/esm-loader.mjs +41 -0
  28. package/shell/preload/sitecustomize.js +36 -0
  29. package/shell/pub.sh +8 -8
  30. package/shell/rmlog.sh +5 -5
  31. package/shell/share.sh +50 -31
  32. package/shell/start.sh +40 -27
  33. package/shell/task.sh +10 -2
  34. package/shell/update.sh +22 -22
  35. package/static/build/api/config.js +17 -7
  36. package/static/build/api/cron.js +40 -3
  37. package/static/build/api/dashboard.js +344 -0
  38. package/static/build/api/env.js +40 -2
  39. package/static/build/api/index.js +2 -0
  40. package/static/build/api/log.js +15 -5
  41. package/static/build/api/script.js +34 -9
  42. package/static/build/api/system.js +22 -8
  43. package/static/build/api/user.js +6 -5
  44. package/static/build/app.js +22 -19
  45. package/static/build/config/const.js +8 -6
  46. package/static/build/config/container.js +12 -0
  47. package/static/build/config/grpcCerts.js +150 -0
  48. package/static/build/config/index.js +6 -4
  49. package/static/build/config/util.js +69 -18
  50. package/static/build/data/cron.js +3 -1
  51. package/static/build/data/cronStats.js +59 -0
  52. package/static/build/data/env.js +2 -0
  53. package/static/build/data/notify.js +18 -1
  54. package/static/build/data/runningInstance.js +57 -0
  55. package/static/build/data/sock.js +1 -0
  56. package/static/build/loaders/app.js +7 -7
  57. package/static/build/loaders/db.js +8 -2
  58. package/static/build/loaders/deps.js +1 -22
  59. package/static/build/loaders/express.js +19 -15
  60. package/static/build/loaders/initData.js +20 -2
  61. package/static/build/loaders/initFile.js +3 -1
  62. package/static/build/loaders/initTask.js +3 -2
  63. package/static/build/loaders/server.js +1 -1
  64. package/static/build/protos/api.js +23 -1
  65. package/static/build/schedule/addCron.js +67 -10
  66. package/static/build/schedule/api.js +1 -1
  67. package/static/build/schedule/client.js +9 -1
  68. package/static/build/schedule/delCron.js +11 -1
  69. package/static/build/schedule/health.js +1 -1
  70. package/static/build/services/config.js +19 -5
  71. package/static/build/services/cron.js +198 -38
  72. package/static/build/services/dependence.js +19 -18
  73. package/static/build/services/env.js +31 -3
  74. package/static/build/services/grpc.js +32 -6
  75. package/static/build/services/http.js +33 -18
  76. package/static/build/services/notify.js +87 -8
  77. package/static/build/services/open.js +2 -1
  78. package/static/build/services/sshKey.js +6 -3
  79. package/static/build/services/subscription.js +8 -5
  80. package/static/build/services/system.js +34 -9
  81. package/static/build/services/user.js +39 -13
  82. package/static/build/shared/auth.js +7 -1
  83. package/static/build/shared/i18n.js +160 -0
  84. package/static/build/shared/pLimit.js +15 -3
  85. package/static/build/shared/runCron.js +5 -0
  86. package/static/build/shared/store.js +7 -0
  87. package/static/build/validation/schedule.js +10 -0
  88. package/static/dist/1089.0ecc5383.async.js +1 -0
  89. package/static/dist/1147.62793604.async.js +1 -0
  90. package/static/dist/1192.4e5740f2.async.js +1 -0
  91. package/static/dist/1214.c6469b53.async.js +1 -0
  92. package/static/dist/134.54df9e38.async.js +1 -0
  93. package/static/dist/1352.96a77e1d.async.js +1 -0
  94. package/static/dist/{1765.d8e002d7.async.js → 1765.5ac01a93.async.js} +1 -1
  95. package/static/dist/1836.f0b74cf1.async.js +1 -0
  96. package/static/dist/{1885.e0d00d2d.async.js → 1885.e1ea09f6.async.js} +1 -1
  97. package/static/dist/1967.3f3945d0.async.js +1 -0
  98. package/static/dist/{2096.383c1047.async.js → 2096.6d98fd12.async.js} +1 -1
  99. package/static/dist/2208.85285476.async.js +1 -0
  100. package/static/dist/2286.f371c743.async.js +1 -0
  101. package/static/dist/2476.4e9b0992.async.js +1 -0
  102. package/static/dist/2537.2b262ee0.async.js +1 -0
  103. package/static/dist/{255.12f03ab2.async.js → 255.8a80b983.async.js} +1 -1
  104. package/static/dist/2635.d4c59d23.async.js +1 -0
  105. package/static/dist/2808.cdc0995c.async.js +1 -0
  106. package/static/dist/{2821.be3dc88e.async.js → 2821.651f31e5.async.js} +1 -1
  107. package/static/dist/2986.4c49eef7.async.js +1 -0
  108. package/static/dist/300.08ac9875.async.js +1 -0
  109. package/static/dist/3191.545ab873.async.js +1 -0
  110. package/static/dist/3714.8d6dba9e.async.js +1 -0
  111. package/static/dist/3906.a5eee612.async.js +1 -0
  112. package/static/dist/{6541.a6d499de.async.js → 3948.4fe809fd.async.js} +1 -1
  113. package/static/dist/{5171.7fc6d0a2.async.js → 4573.16f19278.async.js} +1 -1
  114. package/static/dist/{4859.93e63ea6.async.js → 4859.6592cebb.async.js} +1 -1
  115. package/static/dist/4887.8e3ae573.async.js +1 -0
  116. package/static/dist/{4902.54ecbdb5.async.js → 4902.555f92ab.async.js} +1 -1
  117. package/static/dist/{4934.1ca6b6b0.async.js → 4934.f3539d08.async.js} +1 -1
  118. package/static/dist/5077.cbd111cd.async.js +1 -0
  119. package/static/dist/{6247.b550d996.async.js → 5157.7c5af144.async.js} +1 -1
  120. package/static/dist/{540.481d4708.async.js → 540.19ddf020.async.js} +1 -1
  121. package/static/dist/{5970.10ac4f16.async.js → 5970.d3a6e7bd.async.js} +1 -1
  122. package/static/dist/{6013.2d7bb12a.async.js → 6013.4e38de36.async.js} +1 -1
  123. package/static/dist/{6016.9c379049.async.js → 6016.1e6574b6.async.js} +1 -1
  124. package/static/dist/{6035.5889ddc7.async.js → 6035.32ebfad9.async.js} +1 -1
  125. package/static/dist/6339.ab305e96.async.js +1 -0
  126. package/static/dist/6569.55177f6a.async.js +1 -0
  127. package/static/dist/6610.c111af7e.async.js +1 -0
  128. package/static/dist/{3034.6413c38e.async.js → 6638.87a163d2.async.js} +1 -1
  129. package/static/dist/{6646.5fc37228.async.js → 6646.95e92533.async.js} +1 -1
  130. package/static/dist/6665.1d099ad1.async.js +1 -0
  131. package/static/dist/{7355.b7c0562f.async.js → 7355.f9f263d8.async.js} +1 -1
  132. package/static/dist/{7384.065ccae2.async.js → 7384.1bb449d5.async.js} +1 -1
  133. package/static/dist/7441.ee1bf2bb.async.js +1 -0
  134. package/static/dist/{7508.a31662a3.async.js → 7508.7dcee91c.async.js} +1 -1
  135. package/static/dist/{7802.6b73f16a.async.js → 7802.34255805.async.js} +1 -1
  136. package/static/dist/{7984.e6bb9378.async.js → 7984.594296a5.async.js} +1 -1
  137. package/static/dist/{5653.4fce7ce8.async.js → 8033.5cb31493.async.js} +1 -1
  138. package/static/dist/8147.2fb55202.async.js +1 -0
  139. package/static/dist/8187.48c130d6.async.js +1 -0
  140. package/static/dist/8495.d53e15ca.async.js +1 -0
  141. package/static/dist/8587.315b06c0.async.js +1 -0
  142. package/static/dist/8826.db34ad8d.async.js +1 -0
  143. package/static/dist/{2742.4852aac8.async.js → 8865.ed665d31.async.js} +1 -1
  144. package/static/dist/901.7ee5c6d3.async.js +1 -0
  145. package/static/dist/9271.231db2ce.async.js +1 -0
  146. package/static/dist/9323.a33f47da.async.js +1 -0
  147. package/static/dist/{9730.30083c91.async.js → 9730.801665a3.async.js} +1 -1
  148. package/static/dist/{9761.627ca3b5.async.js → 9761.360a19d8.async.js} +1 -1
  149. package/static/dist/index.html +2 -2
  150. package/static/dist/layouts__index.a7a2bfe0.async.js +1 -0
  151. package/static/dist/layouts__index.adf0692f.chunk.css +1 -0
  152. package/static/dist/preload_helper.3048fe5b.js +1 -0
  153. package/static/dist/{src__pages__config__index.622b6ee8.async.js → src__pages__config__index.a22ff7dc.async.js} +1 -1
  154. package/static/dist/{src__pages__crontab__const.323d5124.async.js → src__pages__crontab__const.aba07deb.async.js} +1 -1
  155. package/static/dist/src__pages__crontab__detail.b98f0084.async.js +1 -0
  156. package/static/dist/src__pages__crontab__index.68e78a33.async.js +1 -0
  157. package/static/dist/{src__pages__crontab__logModal.5e6a4bf2.async.js → src__pages__crontab__logModal.831d5e4b.async.js} +1 -1
  158. package/static/dist/src__pages__crontab__modal.f7041c7c.async.js +1 -0
  159. package/static/dist/src__pages__crontab__type.d7af36e5.async.js +1 -0
  160. package/static/dist/{src__pages__crontab__viewCreateModal.ffcf7a24.async.js → src__pages__crontab__viewCreateModal.4d589f66.async.js} +1 -1
  161. package/static/dist/{src__pages__crontab__viewManageModal.c2724575.async.js → src__pages__crontab__viewManageModal.0e317746.async.js} +1 -1
  162. package/static/dist/src__pages__dashboard__index.58177357.async.js +1 -0
  163. package/static/dist/src__pages__dependence__index.b7bbc795.async.js +1 -0
  164. package/static/dist/{src__pages__dependence__logModal.f123e2ac.async.js → src__pages__dependence__logModal.0681830b.async.js} +1 -1
  165. package/static/dist/src__pages__dependence__modal.f7bb47ba.async.js +1 -0
  166. package/static/dist/src__pages__env__editNameModal.48715576.async.js +1 -0
  167. package/static/dist/src__pages__env__index.452cdc47.async.js +1 -0
  168. package/static/dist/src__pages__env__modal.e1234834.async.js +1 -0
  169. package/static/dist/src__pages__error__index.dedb2b90.async.js +1 -0
  170. package/static/dist/src__pages__initialization__index.22620a77.async.js +1 -0
  171. package/static/dist/src__pages__log__index.9c66639f.async.js +1 -0
  172. package/static/dist/{src__pages__login__index.cd6e3152.async.js → src__pages__login__index.8c813eb1.async.js} +1 -1
  173. package/static/dist/{src__pages__script__components__UnsupportedFilePreview__index.39074c68.async.js → src__pages__script__components__UnsupportedFilePreview__index.c347a13a.async.js} +1 -1
  174. package/static/dist/src__pages__script__editNameModal.532a3044.async.js +1 -0
  175. package/static/dist/src__pages__script__index.1fc20f22.async.js +1 -0
  176. package/static/dist/src__pages__script__renameModal.5cdde919.async.js +1 -0
  177. package/static/dist/src__pages__script__saveModal.5ffed35f.async.js +1 -0
  178. package/static/dist/src__pages__script__setting.1a82c6d7.async.js +1 -0
  179. package/static/dist/src__pages__setting__appModal.43b7758a.async.js +1 -0
  180. package/static/dist/src__pages__setting__checkUpdate.34d773f2.async.js +1 -0
  181. package/static/dist/src__pages__setting__dependence.9c04c79a.async.js +1 -0
  182. package/static/dist/src__pages__setting__index.7e070838.async.js +1 -0
  183. package/static/dist/src__pages__setting__notification.bac481c2.async.js +1 -0
  184. package/static/dist/src__pages__setting__other.707cfafe.async.js +1 -0
  185. package/static/dist/src__pages__setting__security.b7235753.async.js +1 -0
  186. package/static/dist/src__pages__setting__systemLog.b81a8c1d.async.js +1 -0
  187. package/static/dist/{src__pages__subscription__logModal.0caa7283.async.js → src__pages__subscription__logModal.3864b37f.async.js} +1 -1
  188. package/static/dist/src__pages__subscription__modal.562448e2.async.js +1 -0
  189. package/static/dist/{umi.ef8199a4.js → umi.a54a59f6.js} +1 -1
  190. package/version.yaml +33 -4
  191. package/sample/notify.py.save +0 -1010
  192. package/static/dist/105.85a5c47a.async.js +0 -1
  193. package/static/dist/1083.f86ce804.async.js +0 -1
  194. package/static/dist/1147.32f41a88.async.js +0 -1
  195. package/static/dist/1352.ab6da08e.async.js +0 -1
  196. package/static/dist/1690.f0290540.async.js +0 -1
  197. package/static/dist/1742.6cbe5aca.async.js +0 -1
  198. package/static/dist/2208.8e3a7325.async.js +0 -1
  199. package/static/dist/3191.70bc19db.async.js +0 -1
  200. package/static/dist/5312.74b95311.async.js +0 -1
  201. package/static/dist/5691.931f59c5.async.js +0 -1
  202. package/static/dist/6159.55cb068a.async.js +0 -1
  203. package/static/dist/7025.f4080d63.async.js +0 -1
  204. package/static/dist/739.6be5552a.async.js +0 -1
  205. package/static/dist/7571.4f6240b1.async.js +0 -1
  206. package/static/dist/786.59fc381c.async.js +0 -1
  207. package/static/dist/8317.c44c1ebd.async.js +0 -1
  208. package/static/dist/8826.0291edfd.async.js +0 -1
  209. package/static/dist/955.3c9481f7.async.js +0 -1
  210. package/static/dist/layouts__index.1fce90e0.chunk.css +0 -1
  211. package/static/dist/layouts__index.8dcf1576.async.js +0 -1
  212. package/static/dist/preload_helper.116a62f6.js +0 -1
  213. package/static/dist/src__pages__crontab__detail.b07f0c0a.async.js +0 -1
  214. package/static/dist/src__pages__crontab__index.2e2e1096.async.js +0 -1
  215. package/static/dist/src__pages__crontab__modal.4d8c2a22.async.js +0 -1
  216. package/static/dist/src__pages__crontab__type.db7c1858.async.js +0 -1
  217. package/static/dist/src__pages__dependence__index.3d87ceac.async.js +0 -1
  218. package/static/dist/src__pages__dependence__modal.631ffb5b.async.js +0 -1
  219. package/static/dist/src__pages__env__editNameModal.ff85ef8c.async.js +0 -1
  220. package/static/dist/src__pages__env__index.a0a2fece.async.js +0 -1
  221. package/static/dist/src__pages__env__modal.d1004662.async.js +0 -1
  222. package/static/dist/src__pages__error__index.1bc3c90b.async.js +0 -1
  223. package/static/dist/src__pages__initialization__index.8b1cbaf9.async.js +0 -1
  224. package/static/dist/src__pages__log__index.cf00c9af.async.js +0 -1
  225. package/static/dist/src__pages__script__editNameModal.53424d49.async.js +0 -1
  226. package/static/dist/src__pages__script__index.e65df827.async.js +0 -1
  227. package/static/dist/src__pages__script__renameModal.4bbe7fb1.async.js +0 -1
  228. package/static/dist/src__pages__script__saveModal.cf449f3c.async.js +0 -1
  229. package/static/dist/src__pages__script__setting.b345d59a.async.js +0 -1
  230. package/static/dist/src__pages__setting__appModal.03faec89.async.js +0 -1
  231. package/static/dist/src__pages__setting__checkUpdate.46efe057.async.js +0 -1
  232. package/static/dist/src__pages__setting__dependence.4495c7b6.async.js +0 -1
  233. package/static/dist/src__pages__setting__index.6919c399.async.js +0 -1
  234. package/static/dist/src__pages__setting__notification.d6a3884f.async.js +0 -1
  235. package/static/dist/src__pages__setting__other.0d931d6f.async.js +0 -1
  236. package/static/dist/src__pages__setting__security.91cb545f.async.js +0 -1
  237. package/static/dist/src__pages__setting__systemLog.1d433a48.async.js +0 -1
  238. package/static/dist/src__pages__subscription__modal.0b84f6cf.async.js +0 -1
package/shell/otask.sh CHANGED
@@ -24,7 +24,13 @@ random_delay() {
24
24
  done
25
25
 
26
26
  local delay_second=$(($(gen_random_num "$random_delay_max") + 1))
27
- echo -e "任务随机延迟 $delay_second 秒,配置文件参数 RandomDelay 置空可取消延迟 \n"
27
+ local start_time
28
+ if [[ $is_macos -eq 1 ]]; then
29
+ start_time=$(date -v "+${delay_second}S" "+%Y-%m-%d %H:%M:%S")
30
+ else
31
+ start_time=$(date -d "+${delay_second} seconds" "+%Y-%m-%d %H:%M:%S")
32
+ fi
33
+ t '任务随机延迟 %s 秒,将于 %s 开始,配置文件参数 RandomDelay 置空可取消延迟\n' "$delay_second" "$start_time"
28
34
  sleep $delay_second
29
35
  fi
30
36
  }
@@ -83,6 +89,61 @@ clear_non_sh_env() {
83
89
  fi
84
90
  }
85
91
 
92
+ append_node_dependency_path() {
93
+ export PREV_NODE_PATH="${NODE_PATH:=}"
94
+
95
+ # 用户依赖目录加入 NODE_PATH,替代 symlink 到 node_modules 的方式
96
+ export NODE_PATH="${NODE_PATH:+${NODE_PATH}:}${dir_dep}"
97
+
98
+ local pnpm_global_path=$(pnpm root -g 2>/dev/null)
99
+ if [[ -n "$pnpm_global_path" ]]; then
100
+ export QL_NODE_GLOBAL_PATH="$pnpm_global_path"
101
+ export NODE_PATH="${NODE_PATH:+${NODE_PATH}:}${pnpm_global_path}"
102
+ fi
103
+ }
104
+
105
+ enter_script_workdir() {
106
+ local use_dot_prefix="$1"
107
+
108
+ # 如果定时任务显式指定了工作目录,优先使用
109
+ if [[ -n "${work_dir:=}" ]]; then
110
+ local _target_dir
111
+ if [[ "${work_dir}" == /* ]]; then
112
+ _target_dir="${work_dir}"
113
+ else
114
+ _target_dir="${dir_scripts}/${work_dir}"
115
+ fi
116
+ if [[ -d "${_target_dir}" ]]; then
117
+ cd "${_target_dir}"
118
+ if [[ ${file_param} =~ "/" ]]; then
119
+ local script_name="${file_param##*/}"
120
+ if [[ "${use_dot_prefix}" == "true" ]]; then
121
+ file_param="./${script_name}"
122
+ else
123
+ file_param="${script_name}"
124
+ fi
125
+ fi
126
+ return
127
+ fi
128
+ t '警告:工作目录不存在 %s' "${_target_dir}"
129
+ fi
130
+
131
+ cd $dir_scripts
132
+ if [[ ${file_param} =~ "/" ]]; then
133
+ local script_dir="${file_param%/*}"
134
+ local script_name="${file_param##*/}"
135
+
136
+ if [[ -d ${script_dir} ]]; then
137
+ cd ${script_dir}
138
+ if [[ "${use_dot_prefix}" == "true" ]]; then
139
+ file_param="./${script_name}"
140
+ else
141
+ file_param="${script_name}"
142
+ fi
143
+ fi
144
+ fi
145
+ }
146
+
86
147
  ## 正常运行单个脚本,$1:传入参数
87
148
  run_normal() {
88
149
  local file_param=$1
@@ -90,12 +151,7 @@ run_normal() {
90
151
  random_delay "$file_param"
91
152
  fi
92
153
 
93
- cd $dir_scripts
94
- local relative_path="${file_param%/*}"
95
- if [[ ${file_param} != /* ]] && [[ ! -z ${relative_path} ]] && [[ ${file_param} =~ "/" ]]; then
96
- cd ${relative_path}
97
- file_param=${file_param/$relative_path\//}
98
- fi
154
+ enter_script_workdir
99
155
 
100
156
  if [[ $isJsOrPythonFile == 'false' ]]; then
101
157
  clear_non_sh_env
@@ -120,7 +176,7 @@ run_concurrent() {
120
176
  local env_param="$2"
121
177
  local num_param=$(echo "$3" | perl -pe "s|.*$2(.*)|\1|" | awk '{$1=$1};1')
122
178
  if [[ ! $env_param ]]; then
123
- echo -e "\n 缺少并发运行的环境变量参数"
179
+ t '\n缺少并发运行的环境变量参数'
124
180
  exit 1
125
181
  fi
126
182
 
@@ -128,12 +184,7 @@ run_concurrent() {
128
184
  time=$(date "+$mtime_format")
129
185
  single_log_time=$(format_log_time "$mtime_format" "$time")
130
186
 
131
- cd $dir_scripts
132
- local relative_path="${file_param%/*}"
133
- if [[ ! -z ${relative_path} ]] && [[ ${file_param} =~ "/" ]]; then
134
- cd ${relative_path}
135
- file_param=${file_param/$relative_path\//}
136
- fi
187
+ enter_script_workdir
137
188
 
138
189
  local j=0
139
190
  for i in ${array_run[@]}; do
@@ -162,7 +213,7 @@ run_designated() {
162
213
  local env_param="$2"
163
214
  local num_param=$(echo "$3" | perl -pe "s|.*$2(.*)|\1|" | awk '{$1=$1};1')
164
215
  if [[ ! $env_param ]]; then
165
- echo -e "\n 缺少单独运行的参数 task xxx.js desi Test"
216
+ t '\n缺少单独运行的参数 task xxx.js desi Test'
166
217
  exit 1
167
218
  fi
168
219
 
@@ -182,12 +233,7 @@ run_designated() {
182
233
  clear_non_sh_env
183
234
  fi
184
235
 
185
- cd $dir_scripts
186
- local relative_path="${file_param%/*}"
187
- if [[ ! -z ${relative_path} ]] && [[ ${file_param} =~ "/" ]]; then
188
- cd ${relative_path}
189
- file_param=${file_param/$relative_path\//}
190
- fi
236
+ enter_script_workdir
191
237
 
192
238
  envParam="${env_param}" numParam="${num_param}" $timeoutCmd $which_program $file_param "${script_params[@]}"
193
239
  }
@@ -196,14 +242,53 @@ run_designated() {
196
242
  run_else() {
197
243
  local file_param="$1"
198
244
 
199
- cd $dir_scripts
200
- local relative_path="${file_param%/*}"
201
- if [[ ! -z ${relative_path} ]] && [[ ${file_param} =~ "/" ]]; then
202
- cd ${relative_path}
203
- file_param=${file_param/$relative_path\//.\/}
245
+ # 判断 file_param 本身是否是脚本文件
246
+ local is_file_script="false"
247
+ if [[ "$file_param" == *.js || "$file_param" == *.mjs ||
248
+ "$file_param" == *.py || "$file_param" == *.pyc ||
249
+ "$file_param" == *.sh || "$file_param" == *.ts ]]; then
250
+ is_file_script="true"
204
251
  fi
205
252
 
206
- shift
253
+ if [[ "$is_file_script" != "true" ]]; then
254
+ # file_param 不是脚本,从后续参数中查找脚本路径来确定工作目录
255
+ local script_for_dir=""
256
+ for arg in "$@"; do
257
+ if [[ "$arg" == *.js || "$arg" == *.mjs ||
258
+ "$arg" == *.py || "$arg" == *.pyc ||
259
+ "$arg" == *.sh || "$arg" == *.ts ]]; then
260
+ script_for_dir="$arg"
261
+ break
262
+ fi
263
+ done
264
+
265
+ if [[ -n "$script_for_dir" ]]; then
266
+ local saved_file_param="$file_param"
267
+ file_param="$script_for_dir"
268
+ enter_script_workdir true
269
+ local adjusted_script="$file_param"
270
+ file_param="$saved_file_param"
271
+
272
+ shift
273
+ local new_args=()
274
+ for arg in "$@"; do
275
+ if [[ "$arg" == "$script_for_dir" ]]; then
276
+ new_args+=("$adjusted_script")
277
+ else
278
+ new_args+=("$arg")
279
+ fi
280
+ done
281
+ set -- "${new_args[@]}"
282
+ else
283
+ # 没有找到脚本参数,只 cd 到 scripts 目录
284
+ enter_script_workdir true
285
+ shift
286
+ fi
287
+ else
288
+ # file_param 本身就是脚本,直接用 enter_script_workdir 处理
289
+ enter_script_workdir true
290
+ shift
291
+ fi
207
292
 
208
293
  clear_non_sh_env
209
294
  $timeoutCmd $which_program $file_param "$@"
@@ -242,7 +327,7 @@ check_nounset() {
242
327
  }
243
328
 
244
329
  main() {
245
- if [[ $1 == *.js ]] || [[ $1 == *.py ]] || [[ $1 == *.pyc ]] || [[ $1 == *.sh ]] || [[ $1 == *.ts ]]; then
330
+ if [[ $1 == *.js ]] || [[ $1 == *.mjs ]] || [[ $1 == *.py ]] || [[ $1 == *.pyc ]] || [[ $1 == *.sh ]] || [[ $1 == *.ts ]]; then
246
331
  if [[ $1 == *.sh ]]; then
247
332
  timeoutCmd=""
248
333
  fi
@@ -278,15 +363,19 @@ main() {
278
363
 
279
364
  handle_task_start "${task_shell_params[@]}"
280
365
  check_file "${task_shell_params[@]}"
366
+ append_node_dependency_path
281
367
  if [[ $isJsOrPythonFile == 'false' ]]; then
282
368
  run_task_before "${task_shell_params[@]}"
283
369
  fi
284
370
  set_u_on="false"
285
371
  check_nounset
286
372
  main "${task_shell_params[@]}"
373
+ _task_exit_code=$?
287
374
  if [[ "$set_u_on" == 'true' ]]; then
288
375
  set -u
289
376
  fi
377
+ export NODE_PATH="${PREV_NODE_PATH}"
378
+ unset QL_NODE_GLOBAL_PATH
290
379
  if [[ $isJsOrPythonFile == 'true' ]]; then
291
380
  export NODE_OPTIONS="${PREV_NODE_OPTIONS}"
292
381
  export PYTHONPATH="${PREV_PYTHONPATH}"
@@ -1,11 +1,29 @@
1
1
  const grpc = require('@grpc/grpc-js');
2
2
  const protoLoader = require('@grpc/proto-loader');
3
+ const { readFileSync } = require('fs');
3
4
  const { join } = require('path');
4
5
 
5
6
  class GrpcClient {
7
+ static #certDir = join(
8
+ process.env.QL_DATA_DIR || join(process.env.QL_DIR, 'data'),
9
+ 'config/grpc',
10
+ );
11
+
12
+ static #loadTlsCredentials() {
13
+ try {
14
+ return grpc.credentials.createSsl(
15
+ readFileSync(join(GrpcClient.#certDir, 'ca.crt')),
16
+ readFileSync(join(GrpcClient.#certDir, 'client.key')),
17
+ readFileSync(join(GrpcClient.#certDir, 'client.crt')),
18
+ );
19
+ } catch {
20
+ return grpc.credentials.createInsecure();
21
+ }
22
+ }
23
+
6
24
  static #config = {
7
25
  protoPath: join(process.env.QL_DIR, 'back/protos/api.proto'),
8
- serverAddress: `0.0.0.0:${process.env.GRPC_PORT || '5500'}`,
26
+ serverAddress: `localhost:${process.env.GRPC_PORT || '5500'}`,
9
27
  protoOptions: {
10
28
  keepCase: true,
11
29
  longs: String,
@@ -58,7 +76,7 @@ class GrpcClient {
58
76
 
59
77
  this.#client = new apiProto.Api(
60
78
  serverAddress,
61
- grpc.credentials.createInsecure(),
79
+ GrpcClient.#loadTlsCredentials(),
62
80
  grpcOptions,
63
81
  );
64
82
  } catch (error) {
@@ -0,0 +1,41 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+
4
+ const builtinModules = [
5
+ 'assert', 'buffer', 'child_process', 'cluster', 'crypto', 'dgram', 'dns',
6
+ 'events', 'fs', 'http', 'https', 'net', 'os', 'path', 'process', 'querystring',
7
+ 'readline', 'stream', 'string_decoder', 'timers', 'tls', 'tty', 'url', 'util',
8
+ 'v8', 'zlib',
9
+ ];
10
+
11
+ function isBareSpecifier(specifier) {
12
+ return !specifier.startsWith('.') &&
13
+ !specifier.startsWith('/') &&
14
+ !specifier.startsWith('file:') &&
15
+ !specifier.startsWith('node:') &&
16
+ !builtinModules.includes(specifier) &&
17
+ !builtinModules.includes(specifier.split('/')[0]);
18
+ }
19
+
20
+ export function resolve(specifier, context, nextResolve) {
21
+ if (!isBareSpecifier(specifier)) {
22
+ return nextResolve(specifier, context);
23
+ }
24
+
25
+ // 解析优先级:全局 pnpm > 系统全局
26
+ const bases = [
27
+ process.env.QL_NODE_GLOBAL_PATH,
28
+ '/usr/local/lib/node_modules',
29
+ ].filter(Boolean);
30
+
31
+ for (const base of bases) {
32
+ if (existsSync(join(base, specifier))) {
33
+ return nextResolve(specifier, {
34
+ ...context,
35
+ parentURL: new URL(`${join(base, specifier)}/`, 'file://').href,
36
+ });
37
+ }
38
+ }
39
+
40
+ return nextResolve(specifier, context);
41
+ }
@@ -1,7 +1,41 @@
1
1
  const { execSync } = require('child_process');
2
+ const Module = require('module');
3
+ const path = require('path');
2
4
  const client = require('./client.js');
3
5
  require(`./env.js`);
4
6
 
7
+ // 注册 ESM loader,使全局安装的包也可通过 import 导入
8
+ try {
9
+ Module.register(new URL('esm-loader.mjs', `file://${__dirname}/`).href);
10
+ } catch (_) {}
11
+
12
+ function preferGlobalNodeModules() {
13
+ const { QL_NODE_GLOBAL_PATH } = process.env;
14
+ if (!QL_NODE_GLOBAL_PATH || Module._qlGlobalPathPatched) {
15
+ return;
16
+ }
17
+
18
+ const originalResolveFilename = Module._resolveFilename;
19
+ Module._resolveFilename = function (request, parent, isMain, options) {
20
+ if (
21
+ !Module.builtinModules.includes(request) &&
22
+ !request.startsWith('node:') &&
23
+ !request.startsWith('.') &&
24
+ !path.isAbsolute(request)
25
+ ) {
26
+ try {
27
+ return originalResolveFilename.call(this, request, parent, isMain, {
28
+ ...options,
29
+ paths: [QL_NODE_GLOBAL_PATH],
30
+ });
31
+ } catch (error) {}
32
+ }
33
+
34
+ return originalResolveFilename.call(this, request, parent, isMain, options);
35
+ };
36
+ Module._qlGlobalPathPatched = true;
37
+ }
38
+
5
39
  function expandRange(rangeStr, max) {
6
40
  const tempRangeStr = rangeStr
7
41
  .trim()
@@ -113,6 +147,8 @@ try {
113
147
  return;
114
148
  }
115
149
 
150
+ preferGlobalNodeModules();
151
+
116
152
  process.on('SIGTERM', (code) => {
117
153
  process.exit(15);
118
154
  });
package/shell/pub.sh CHANGED
@@ -1,26 +1,26 @@
1
1
  #!/usr/bin/env bash
2
2
  echo -e "开始发布"
3
3
 
4
- echo -e "切换 debian 分支"
5
- git branch -D debian
6
- git checkout -b debian
7
- git push --set-upstream origin debian -f
4
+ echo -e "切换master分支"
5
+ git branch -D master
6
+ git checkout -b master
7
+ git push --set-upstream origin master -f
8
8
 
9
9
  echo -e "更新cdn文件"
10
10
  ts-node-transpile-only sample/tool.ts
11
11
 
12
12
  string=$(cat version.yaml | grep "version" | egrep "[^ ]*" -o | egrep "\d\.*")
13
13
  version="v$string"
14
- echo -e "当前版本$version-debian"
14
+ echo -e "当前版本$version"
15
15
 
16
16
  echo -e "删除已经存在的本地tag"
17
- git tag -d "$version-debian" &>/dev/null
17
+ git tag -d "$version" &>/dev/null
18
18
 
19
19
  echo -e "删除已经存在的远程tag"
20
- git push origin :refs/tags/$version-debian &>/dev/null
20
+ git push origin :refs/tags/$version &>/dev/null
21
21
 
22
22
  echo -e "创建新tag"
23
- git tag -a "$version-debian" -m "release $version-debian"
23
+ git tag -a "$version" -m "release $version"
24
24
 
25
25
  echo -e "提交tag"
26
26
  git push --tags
package/shell/rmlog.sh CHANGED
@@ -22,12 +22,12 @@ remove_js_log() {
22
22
  if [[ $diff_time -gt $((${days} * 86400)) ]]; then
23
23
  local log_path=$(echo "$log" | sed "s,${dir_log}/,,g")
24
24
  local result=$(find_cron_api "log_path=$log_path")
25
- echo -e "查询文件 $log_path"
25
+ t '查询文件 %s' "$log_path"
26
26
  if [[ -z $result ]]; then
27
- echo -e "删除中~"
27
+ t '删除中~'
28
28
  rm -vf $log
29
29
  else
30
- echo -e "正在被 $result 使用,跳过~"
30
+ t '正在被 %s 使用,跳过~' "$result"
31
31
  fi
32
32
  fi
33
33
  done
@@ -43,8 +43,8 @@ remove_empty_dir() {
43
43
  }
44
44
 
45
45
  if [[ ${days} ]]; then
46
- echo -e "查找旧日志文件中...\n"
46
+ t '查找旧日志文件中...\n'
47
47
  remove_js_log
48
48
  remove_empty_dir
49
- echo -e "删除旧日志执行完毕\n"
49
+ t '删除旧日志执行完毕\n'
50
50
  fi
package/shell/share.sh CHANGED
@@ -65,17 +65,7 @@ link_name=(
65
65
  )
66
66
 
67
67
  init_env() {
68
- local pnpm_global_path=$(pnpm root -g 2>/dev/null)
69
- export NODE_PATH="/usr/local/bin:/usr/local/lib/node_modules${pnpm_global_path:+:${pnpm_global_path}}"
70
-
71
- # 如果存在 pnpm 全局路径,创建软链接
72
- if [[ -n "$pnpm_global_path" ]]; then
73
- # 确保目标目录存在
74
- mkdir -p "${dir_root}/node_modules"
75
- # 链接全局模块到项目的 node_modules
76
- ln -sf "${pnpm_global_path}/"* "${dir_root}/node_modules/" 2>/dev/null || true
77
- fi
78
-
68
+ export NODE_PATH="/usr/local/bin:/usr/local/lib/node_modules"
79
69
  export PYTHONUNBUFFERED=1
80
70
  }
81
71
 
@@ -88,6 +78,16 @@ load_ql_envs() {
88
78
 
89
79
  import_config() {
90
80
  [[ -f $file_config_user ]] && . $file_config_user
81
+ [[ -f $dir_preload/lang_env.sh ]] && . $dir_preload/lang_env.sh
82
+
83
+ # 加载语言包(bash 4+ 支持 declare -A,不兼容时回退输出中文 key)
84
+ local lang=${QL_LANG:-zh}
85
+ local lang_file="$dir_shell/lang/${lang}.sh"
86
+ if [[ ${BASH_VERSINFO[0]} -ge 4 ]] && [[ -f $lang_file ]]; then
87
+ . $lang_file
88
+ elif [[ ${BASH_VERSINFO[0]} -ge 4 ]]; then
89
+ . "$dir_shell/lang/zh.sh"
90
+ fi
91
91
 
92
92
  load_ql_envs
93
93
  command_timeout_time=${CommandTimeoutTime:-""}
@@ -101,6 +101,18 @@ import_config() {
101
101
  fi
102
102
  }
103
103
 
104
+ t() {
105
+ local key="$1"
106
+ shift
107
+ local msg
108
+ if declare -p LANG_MESSAGES &>/dev/null; then
109
+ msg="${LANG_MESSAGES["$key"]}"
110
+ fi
111
+ [[ -z $msg ]] && msg="$key"
112
+ # shellcheck disable=SC2059
113
+ printf "$msg\n" "$@"
114
+ }
115
+
104
116
  set_proxy() {
105
117
  local proxy="$1"
106
118
  if [[ $proxy ]]; then
@@ -235,7 +247,7 @@ npm_install_2() {
235
247
  local dir_work=$1
236
248
 
237
249
  cd $dir_work
238
- echo -e "安装 $dir_work 依赖包...\n"
250
+ t '安装 %s 依赖包...\n' "$dir_work"
239
251
  npm_install_sub
240
252
  cd $dir_current
241
253
  }
@@ -254,7 +266,7 @@ git_clone_scripts() {
254
266
  local branch="$3"
255
267
  local proxy="$4"
256
268
  [[ $branch ]] && local part_cmd="-b $branch "
257
- echo -e "开始拉取仓库 ${uniq_path} $dir\n"
269
+ t '开始拉取仓库 %s 到 %s\n' "${uniq_path}" "$dir"
258
270
 
259
271
  set_proxy "$proxy"
260
272
 
@@ -287,18 +299,18 @@ reload_pm2() {
287
299
  return 0
288
300
  else
289
301
  local exit_code=$?
290
- echo "警告: PM2 启动失败 (退出码: $exit_code),可能是由于硬件不兼容"
291
- echo "正在尝试直接使用 Node.js 启动服务..."
292
-
302
+ t '警告: PM2 启动失败 (退出码: %s),可能是由于硬件不兼容' "$exit_code"
303
+ t '正在尝试直接使用 Node.js 启动服务...'
304
+
293
305
  # Kill any existing node processes for qinglong
294
306
  pkill -f "node.*static/build/app.js" 2>/dev/null || true
295
-
307
+
296
308
  # Start node directly in the background
297
309
  nohup node static/build/app.js > $dir_log/qinglong.log 2>&1 &
298
310
  local node_pid=$!
299
-
300
- echo "已使用 Node.js 直接启动服务 (PID: $node_pid)"
301
- echo "注意: 使用此模式时,部分 PM2 管理功能将不可用"
311
+
312
+ t '已使用 Node.js 直接启动服务 (PID: %s)' "$node_pid"
313
+ t '注意: 使用此模式时,部分 PM2 管理功能将不可用'
302
314
  return 0
303
315
  fi
304
316
  }
@@ -370,16 +382,16 @@ handle_task_start() {
370
382
  error_message=", 任务状态更新失败(${error})"
371
383
  fi
372
384
  fi
373
- echo -e "## 开始执行... ${begin_time}${error_message}\n"
385
+ t '## 开始执行... %s\n' "${begin_time}${error_message}"
374
386
  }
375
387
 
376
388
  run_task_before() {
377
389
  . $file_task_before "$@"
378
390
 
379
391
  if [[ ${task_before:=} ]]; then
380
- echo -e "执行前置命令\n"
392
+ t '执行前置命令\n'
381
393
  eval "${task_before%;}"
382
- echo -e "\n执行前置命令结束\n"
394
+ t '\n执行前置命令结束\n'
383
395
  fi
384
396
  }
385
397
 
@@ -387,9 +399,9 @@ run_task_after() {
387
399
  . $file_task_after "$@"
388
400
 
389
401
  if [[ ${task_after:=} ]]; then
390
- echo -e "\n执行后置命令\n"
402
+ t '\n执行后置命令\n'
391
403
  eval "${task_after%;}"
392
- echo -e "\n执行后置命令结束"
404
+ t '\n执行后置命令结束'
393
405
  fi
394
406
  }
395
407
 
@@ -398,18 +410,25 @@ handle_task_end() {
398
410
  local end_time=$(format_time "$time_format" "$etime")
399
411
  local end_timestamp=$(format_timestamp "$time_format" "$etime")
400
412
  local diff_time=$(($end_timestamp - $begin_timestamp))
401
- local suffix=""
402
- [[ "${MANUAL:=}" == "true" ]] && suffix="(手动停止)"
403
-
413
+ local exit_code="${_task_exit_code:-0}"
404
414
  [[ "$diff_time" == 0 ]] && diff_time=1
405
415
 
406
416
  if [[ $ID ]]; then
407
- local error=$(update_cron "\"$ID\"" "1" "$$" "$log_path" "$begin_timestamp" "$diff_time")
417
+ local error=$(update_cron "\"$ID\"" "1" "$$" "$log_path" "$begin_timestamp" "$diff_time" "$exit_code")
408
418
  if [[ $error ]]; then
409
- error_message=", 任务状态更新失败(${error})"
419
+ error_message=", 状态更新失败(${error})"
410
420
  fi
411
421
  fi
412
- echo -e "\n## 执行结束$suffix... $end_time 耗时 $diff_time 秒${error_message:=}     "
422
+
423
+ record_cron_stat "$ID" "${exit_code:-0}" "$diff_time"
424
+
425
+ if [[ "${MANUAL:=}" == "true" ]]; then
426
+ t '\n## 已停止 🛑... %s 耗时 %s 秒%s' "$end_time" "$diff_time" "${error_message:=}     "
427
+ elif [[ $exit_code -eq 0 ]]; then
428
+ t '\n## 完成 ✅... %s 耗时 %s 秒%s' "$end_time" "$diff_time" "${error_message:=}     "
429
+ else
430
+ t '\n## 失败 ❌(退出码 %s)... %s 耗时 %s 秒%s' "$exit_code" "$end_time" "$diff_time" "${error_message:=}     "
431
+ fi
413
432
  }
414
433
 
415
434
  init_env
package/shell/start.sh CHANGED
@@ -12,23 +12,23 @@ if [[ ! $QL_DIR ]]; then
12
12
  elif [[ -d "$pnpm_dir/@whyour/qinglong" ]]; then
13
13
  QL_DIR="$pnpm_dir/@whyour/qinglong"
14
14
  else
15
- echo -e "未找到 qinglong 模块,请先执行 npm i -g @whyour/qinglong 安装"
15
+ t '未找到 qinglong 模块,请先执行 npm i -g @whyour/qinglong 安装'
16
16
  fi
17
17
 
18
18
  if [[ $QL_DIR ]]; then
19
- echo -e "请先手动设置 export QL_DIR=$QL_DIR,环境变量,并手动添加到系统环境变量,然后再次执行命令 qinglong 启动服务"
19
+ t '请先手动设置 export QL_DIR=%s,环境变量,并手动添加到系统环境变量,然后再次执行命令 qinglong 启动服务' "$QL_DIR"
20
20
  fi
21
21
 
22
22
  exit 1
23
23
  fi
24
24
 
25
25
  if [[ ! $QL_DATA_DIR ]]; then
26
- echo -e "请先手动设置数据存储目录 export QL_DATA_DIR 环境变量,目录必须以斜杠开头的绝对路径,并且以 /data 结尾,例如 /ql/data 并手动添加到系统环境变量"
26
+ t '请先手动设置数据存储目录 export QL_DATA_DIR 环境变量,目录必须以斜杠开头的绝对路径,并且以 /data 结尾,例如 /ql/data 并手动添加到系统环境变量'
27
27
  exit 1
28
28
  fi
29
29
 
30
30
  if [[ $QL_DATA_DIR != */data ]]; then
31
- echo -e "QL_DATA_DIR 必须以 /data 结尾,例如 /ql/data,如果有历史数据,请新建 data 目录,把历史数据放到 data 目录中"
31
+ t 'QL_DATA_DIR 必须以 /data 结尾,例如 /ql/data,如果有历史数据,请新建 data 目录,把历史数据放到 data 目录中'
32
32
  exit 1
33
33
  fi
34
34
 
@@ -36,31 +36,44 @@ command="$1"
36
36
 
37
37
  if [[ $command != "reload" ]]; then
38
38
  # 安装依赖
39
- os_name=$(source /etc/os-release && echo "$ID")
40
-
41
- if [[ $os_name == 'alpine' ]]; then
42
- apk update
43
- apk add -f bash \
44
- coreutils \
45
- git \
46
- curl \
47
- wget \
48
- tzdata \
49
- perl \
50
- openssl \
51
- jq \
52
- nginx \
53
- openssh \
54
- procps \
55
- netcat-openbsd
56
- elif [[ $os_name == 'debian' ]] || [[ $os_name == 'ubuntu' ]]; then
57
- apt-get update
58
- apt-get install -y git curl wget tzdata perl openssl jq nginx procps netcat-openbsd openssh-client
59
- else
60
- echo -e "暂不支持此系统部署 $os_name"
61
- exit 1
39
+ os_name="${QL_OS_TYPE:-}"
40
+ if [ -z "$os_name" ]; then
41
+ os_name=$(source /etc/os-release && echo "$ID")
42
+ fi
43
+
44
+ # 非 root 用户使用 sudo
45
+ SUDO=""
46
+ if [ "$(id -u)" -ne 0 ]; then
47
+ SUDO="sudo"
62
48
  fi
63
49
 
50
+ case "$os_name" in
51
+ alpine)
52
+ $SUDO apk update
53
+ $SUDO apk add -f bash \
54
+ coreutils \
55
+ git \
56
+ curl \
57
+ wget \
58
+ tzdata \
59
+ perl \
60
+ openssl \
61
+ jq \
62
+ nginx \
63
+ openssh \
64
+ procps \
65
+ netcat-openbsd
66
+ ;;
67
+ debian|ubuntu)
68
+ $SUDO apt-get update
69
+ $SUDO apt-get install -y git curl wget tzdata perl openssl jq nginx procps netcat-openbsd openssh-client
70
+ ;;
71
+ *)
72
+ t '暂不支持此系统部署 %s' "$os_name"
73
+ exit 1
74
+ ;;
75
+ esac
76
+
64
77
  npm install -g pnpm@8.3.1 pm2 ts-node
65
78
  fi
66
79