@yinuo-ngm/server 1.0.15 → 1.0.16

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 (326) hide show
  1. package/lib/plugins/error-handler.plugin.js +5 -0
  2. package/lib/routes/api-client/hub-token.routes.js +22 -0
  3. package/lib/routes/index.js +2 -0
  4. package/lib/routes/node-version.routes.d.ts +2 -0
  5. package/lib/routes/node-version.routes.js +56 -0
  6. package/lib/routes/project.routes.js +5 -0
  7. package/lib/routes/sprite-browse.routes.js +19 -5
  8. package/lib/routes/sprite.routes.js +17 -11
  9. package/lib/routes/static-files.routes.js +16 -0
  10. package/lib/routes/task.routes.js +7 -0
  11. package/package.json +3 -3
  12. package/www/3rdpartylicenses.txt +14 -66
  13. package/www/browser/chunk-2BZRE4G7.js +1 -0
  14. package/www/browser/chunk-2X3MRS27.js +1 -0
  15. package/www/browser/chunk-4KETC6EB.js +1 -0
  16. package/www/browser/chunk-5T5KA5PG.js +1 -0
  17. package/www/browser/chunk-6CGHNKJI.js +1 -0
  18. package/www/browser/chunk-76TVIB33.js +63 -0
  19. package/www/browser/chunk-7J24TP36.js +1 -0
  20. package/www/browser/chunk-ACAZUX6C.js +1 -0
  21. package/www/browser/chunk-B6MBYCXI.js +1 -0
  22. package/www/browser/chunk-BYEU6KGP.js +2 -0
  23. package/www/browser/chunk-CZ5AZ3VW.js +1 -0
  24. package/www/browser/chunk-DW7F5PEA.js +1 -0
  25. package/www/browser/chunk-DXF7BVK5.js +1 -0
  26. package/www/browser/chunk-EEVPZGEY.js +1 -0
  27. package/www/browser/chunk-G2W3B7TJ.js +1 -0
  28. package/www/browser/chunk-GDWS2L66.js +1 -0
  29. package/www/browser/chunk-HFZLJHYR.js +1 -0
  30. package/www/browser/chunk-HGONFYP6.js +1 -0
  31. package/www/browser/chunk-HHBPULJW.js +2 -0
  32. package/www/browser/chunk-HRXCR3IN.js +1 -0
  33. package/www/browser/chunk-INL2PELS.js +1 -0
  34. package/www/browser/chunk-JHMEKUZ5.js +1 -0
  35. package/www/browser/chunk-JU3TEDBV.js +37 -0
  36. package/www/browser/chunk-JZULA5JV.js +1 -0
  37. package/www/browser/chunk-KVFR7GFV.js +20 -0
  38. package/www/browser/chunk-L5D75AMV.js +1 -0
  39. package/www/browser/chunk-L7TMCSHV.js +1 -0
  40. package/www/browser/chunk-LQ5OXSW7.js +3 -0
  41. package/www/browser/chunk-MVQTKINJ.js +1 -0
  42. package/www/browser/chunk-N4LRZJBP.js +1 -0
  43. package/www/browser/chunk-O2EYEF7J.js +1 -0
  44. package/www/browser/chunk-OMDHJIUB.js +1 -0
  45. package/www/browser/chunk-OWUAAOHW.js +2 -0
  46. package/www/browser/chunk-PIK5YPIB.js +1 -0
  47. package/www/browser/chunk-RHLQRQDK.js +9 -0
  48. package/www/browser/chunk-RW2JPJV7.js +1 -0
  49. package/www/browser/chunk-S3SJ4VVM.js +11 -0
  50. package/www/browser/chunk-SIVPP74B.js +0 -0
  51. package/www/browser/chunk-SPRWNZHF.js +15 -0
  52. package/www/browser/chunk-SQQNR223.js +1 -0
  53. package/www/browser/chunk-SVQWPHF5.js +1 -0
  54. package/www/browser/chunk-SYCNSLAW.js +4 -0
  55. package/www/browser/chunk-T3KK7ZXB.js +2 -0
  56. package/www/browser/chunk-TMX5TTV3.js +1 -0
  57. package/www/browser/chunk-UFY3FLDK.js +1 -0
  58. package/www/browser/chunk-UJOHBN2Y.js +1 -0
  59. package/www/browser/chunk-UQGCUFNM.js +1 -0
  60. package/www/browser/chunk-UXXWRMM6.js +1 -0
  61. package/www/browser/chunk-UZRJGJTD.js +2 -0
  62. package/www/browser/chunk-WD4IAQR3.js +1 -0
  63. package/www/browser/chunk-WF2QTF5L.js +1 -0
  64. package/www/browser/chunk-WI67LAOV.js +4 -0
  65. package/www/browser/chunk-WNCM6QKB.js +1 -0
  66. package/www/browser/chunk-XJ5KZQNN.js +1 -0
  67. package/www/browser/chunk-YETDFSQE.js +1 -0
  68. package/www/browser/chunk-YMTC5GZK.js +1 -0
  69. package/www/browser/chunk-YVZHJ76K.js +1 -0
  70. package/www/browser/chunk-ZNTJRLVH.js +1 -0
  71. package/www/browser/dark.css +1 -30433
  72. package/www/browser/default.css +1 -30149
  73. package/www/browser/index.html +3 -3
  74. package/www/browser/main-6LN5C22E.js +34 -0
  75. package/www/browser/scripts-U25HCVEI.js +6 -0
  76. package/www/browser/styles-ROAHD7MY.css +1 -0
  77. package/www/browser/add-widget-drawer.component.css.map +0 -7
  78. package/www/browser/advanced-editor.component.css.map +0 -7
  79. package/www/browser/api-client.component.css.map +0 -7
  80. package/www/browser/api-history-drawer.component.css.map +0 -7
  81. package/www/browser/app.css.map +0 -7
  82. package/www/browser/attachment-viewer.component.css.map +0 -7
  83. package/www/browser/auth-editor.component.css.map +0 -7
  84. package/www/browser/body-editor.component.css.map +0 -7
  85. package/www/browser/chunk-2YSUXBGC.js +0 -627
  86. package/www/browser/chunk-2YSUXBGC.js.map +0 -1
  87. package/www/browser/chunk-3G2GEOPV.js +0 -456
  88. package/www/browser/chunk-3G2GEOPV.js.map +0 -1
  89. package/www/browser/chunk-3L72DF3R.js +0 -1683
  90. package/www/browser/chunk-3L72DF3R.js.map +0 -1
  91. package/www/browser/chunk-3MCIETCQ.js +0 -8279
  92. package/www/browser/chunk-3MCIETCQ.js.map +0 -1
  93. package/www/browser/chunk-665WC4RU.js +0 -5383
  94. package/www/browser/chunk-665WC4RU.js.map +0 -1
  95. package/www/browser/chunk-6QMSEZPT.js +0 -10044
  96. package/www/browser/chunk-6QMSEZPT.js.map +0 -1
  97. package/www/browser/chunk-6TBQERYX.js +0 -280
  98. package/www/browser/chunk-6TBQERYX.js.map +0 -1
  99. package/www/browser/chunk-6UW4MRJZ.js +0 -43
  100. package/www/browser/chunk-6UW4MRJZ.js.map +0 -7
  101. package/www/browser/chunk-6XUMIL3W.js +0 -5495
  102. package/www/browser/chunk-6XUMIL3W.js.map +0 -1
  103. package/www/browser/chunk-724YOJI3.js +0 -903
  104. package/www/browser/chunk-724YOJI3.js.map +0 -1
  105. package/www/browser/chunk-766T7YES.js +0 -437
  106. package/www/browser/chunk-766T7YES.js.map +0 -1
  107. package/www/browser/chunk-7FC7DN65.js +0 -728
  108. package/www/browser/chunk-7FC7DN65.js.map +0 -1
  109. package/www/browser/chunk-APGQRYWX.js +0 -156
  110. package/www/browser/chunk-APGQRYWX.js.map +0 -7
  111. package/www/browser/chunk-AQHSGTHS.js +0 -1960
  112. package/www/browser/chunk-AQHSGTHS.js.map +0 -1
  113. package/www/browser/chunk-CTOEAJQK.js +0 -40
  114. package/www/browser/chunk-CTOEAJQK.js.map +0 -7
  115. package/www/browser/chunk-DEGQJKKZ.js +0 -409
  116. package/www/browser/chunk-DEGQJKKZ.js.map +0 -1
  117. package/www/browser/chunk-DIJX2663.js +0 -1166
  118. package/www/browser/chunk-DIJX2663.js.map +0 -1
  119. package/www/browser/chunk-E4XD4GLT.js +0 -427
  120. package/www/browser/chunk-E4XD4GLT.js.map +0 -1
  121. package/www/browser/chunk-EQOY6A3M.js +0 -212
  122. package/www/browser/chunk-EQOY6A3M.js.map +0 -1
  123. package/www/browser/chunk-ETTBRXVA.js +0 -2643
  124. package/www/browser/chunk-ETTBRXVA.js.map +0 -1
  125. package/www/browser/chunk-FLSUSPBD.js +0 -5569
  126. package/www/browser/chunk-FLSUSPBD.js.map +0 -1
  127. package/www/browser/chunk-FZNHBEAZ.js +0 -102
  128. package/www/browser/chunk-FZNHBEAZ.js.map +0 -1
  129. package/www/browser/chunk-GJC7CZIJ.js +0 -361
  130. package/www/browser/chunk-GJC7CZIJ.js.map +0 -7
  131. package/www/browser/chunk-GMCNMOTD.js +0 -844
  132. package/www/browser/chunk-GMCNMOTD.js.map +0 -7
  133. package/www/browser/chunk-H43HGCRW.js +0 -198
  134. package/www/browser/chunk-H43HGCRW.js.map +0 -7
  135. package/www/browser/chunk-J447GBHM.js +0 -160
  136. package/www/browser/chunk-J447GBHM.js.map +0 -1
  137. package/www/browser/chunk-JN3VOWP7.js +0 -384
  138. package/www/browser/chunk-JN3VOWP7.js.map +0 -1
  139. package/www/browser/chunk-KSHAGY2M.js +0 -399
  140. package/www/browser/chunk-KSHAGY2M.js.map +0 -7
  141. package/www/browser/chunk-KV72AKOD.js +0 -59
  142. package/www/browser/chunk-KV72AKOD.js.map +0 -7
  143. package/www/browser/chunk-LAVMY2SJ.js +0 -26
  144. package/www/browser/chunk-LAVMY2SJ.js.map +0 -7
  145. package/www/browser/chunk-LLRVGHG5.js +0 -73
  146. package/www/browser/chunk-LLRVGHG5.js.map +0 -7
  147. package/www/browser/chunk-LPME4AJ3.js +0 -196
  148. package/www/browser/chunk-LPME4AJ3.js.map +0 -1
  149. package/www/browser/chunk-LVPSURVU.js +0 -4615
  150. package/www/browser/chunk-LVPSURVU.js.map +0 -1
  151. package/www/browser/chunk-MMPO7QLO.js +0 -4146
  152. package/www/browser/chunk-MMPO7QLO.js.map +0 -7
  153. package/www/browser/chunk-O7LB6VFM.js +0 -1953
  154. package/www/browser/chunk-O7LB6VFM.js.map +0 -1
  155. package/www/browser/chunk-P5CWA4FO.js +0 -685
  156. package/www/browser/chunk-P5CWA4FO.js.map +0 -1
  157. package/www/browser/chunk-RBWIWNTL.js +0 -203
  158. package/www/browser/chunk-RBWIWNTL.js.map +0 -7
  159. package/www/browser/chunk-RUXJCM3V.js +0 -26199
  160. package/www/browser/chunk-RUXJCM3V.js.map +0 -1
  161. package/www/browser/chunk-S77VVYUZ.js +0 -168
  162. package/www/browser/chunk-S77VVYUZ.js.map +0 -1
  163. package/www/browser/chunk-SCXPYFOB.js +0 -4316
  164. package/www/browser/chunk-SCXPYFOB.js.map +0 -1
  165. package/www/browser/chunk-SS73S33S.js +0 -471
  166. package/www/browser/chunk-SS73S33S.js.map +0 -7
  167. package/www/browser/chunk-T6UAOWNP.js +0 -600
  168. package/www/browser/chunk-T6UAOWNP.js.map +0 -1
  169. package/www/browser/chunk-TEW4MY4J.js +0 -148
  170. package/www/browser/chunk-TEW4MY4J.js.map +0 -7
  171. package/www/browser/chunk-TVORF3SF.js +0 -82
  172. package/www/browser/chunk-TVORF3SF.js.map +0 -7
  173. package/www/browser/chunk-UAN42EUZ.js +0 -2147
  174. package/www/browser/chunk-UAN42EUZ.js.map +0 -1
  175. package/www/browser/chunk-ULOHDK7Y.js +0 -1
  176. package/www/browser/chunk-ULOHDK7Y.js.map +0 -7
  177. package/www/browser/chunk-UYJ3FVRV.js +0 -3978
  178. package/www/browser/chunk-UYJ3FVRV.js.map +0 -1
  179. package/www/browser/chunk-VXHZMSDM.js +0 -5509
  180. package/www/browser/chunk-VXHZMSDM.js.map +0 -1
  181. package/www/browser/chunk-VYNQPZQO.js +0 -281
  182. package/www/browser/chunk-VYNQPZQO.js.map +0 -1
  183. package/www/browser/chunk-W3J23PZI.js +0 -9213
  184. package/www/browser/chunk-W3J23PZI.js.map +0 -1
  185. package/www/browser/chunk-W5HGHCQT.js +0 -1642
  186. package/www/browser/chunk-W5HGHCQT.js.map +0 -7
  187. package/www/browser/chunk-XF5H7E4L.js +0 -4723
  188. package/www/browser/chunk-XF5H7E4L.js.map +0 -1
  189. package/www/browser/chunk-XNFNEBMY.js +0 -398
  190. package/www/browser/chunk-XNFNEBMY.js.map +0 -7
  191. package/www/browser/chunk-XQY5SPGI.js +0 -30811
  192. package/www/browser/chunk-XQY5SPGI.js.map +0 -1
  193. package/www/browser/chunk-Y7JBZHNK.js +0 -514
  194. package/www/browser/chunk-Y7JBZHNK.js.map +0 -1
  195. package/www/browser/chunk-YWHRP4X3.js +0 -406
  196. package/www/browser/chunk-YWHRP4X3.js.map +0 -1
  197. package/www/browser/chunk-Z7FJIV62.js +0 -95
  198. package/www/browser/chunk-Z7FJIV62.js.map +0 -7
  199. package/www/browser/chunk-ZBZHXS46.js +0 -1674
  200. package/www/browser/chunk-ZBZHXS46.js.map +0 -1
  201. package/www/browser/chunk-ZZA5NVAI.js +0 -735
  202. package/www/browser/chunk-ZZA5NVAI.js.map +0 -1
  203. package/www/browser/collection-modal.component.css.map +0 -7
  204. package/www/browser/collection-tree-item.component.css.map +0 -7
  205. package/www/browser/collection-tree.component.css.map +0 -7
  206. package/www/browser/config-change-bar-component.css.map +0 -7
  207. package/www/browser/config-item-component.css.map +0 -7
  208. package/www/browser/config-nav-component.css.map +0 -7
  209. package/www/browser/config-section-component.css.map +0 -7
  210. package/www/browser/create-summary-aside.component.css.map +0 -7
  211. package/www/browser/curl-actions.component.css.map +0 -7
  212. package/www/browser/dark.css.map +0 -7
  213. package/www/browser/dashboard-canvas.component.css.map +0 -7
  214. package/www/browser/dashboard.component.css.map +0 -7
  215. package/www/browser/default.css.map +0 -7
  216. package/www/browser/detail-item-card.component.css.map +0 -7
  217. package/www/browser/ellipsis-text.component.css.map +0 -7
  218. package/www/browser/env-modal.component.css.map +0 -7
  219. package/www/browser/env-picker.component.css.map +0 -7
  220. package/www/browser/feedback.component.css.map +0 -7
  221. package/www/browser/fs-explorer.component.css.map +0 -7
  222. package/www/browser/git-import-modal.component.css.map +0 -7
  223. package/www/browser/hub-v2-personal-token-modal.component.css.map +0 -7
  224. package/www/browser/issue-action-area.component.css.map +0 -7
  225. package/www/browser/issue-add-participants-dialog.component.css.map +0 -7
  226. package/www/browser/issue-assign-dialog.component.css.map +0 -7
  227. package/www/browser/issue-attachment-area.component.css.map +0 -7
  228. package/www/browser/issue-base-info-area.component.css.map +0 -7
  229. package/www/browser/issue-branches.component.css.map +0 -7
  230. package/www/browser/issue-close-dialog.component.css.map +0 -7
  231. package/www/browser/issue-collaborators-panel.component.css.map +0 -7
  232. package/www/browser/issue-comment-editor.component.css.map +0 -7
  233. package/www/browser/issue-create-branch-dialog.component.css.map +0 -7
  234. package/www/browser/issue-description-area.component.css.map +0 -7
  235. package/www/browser/issue-detail.component.css.map +0 -7
  236. package/www/browser/issue-resolve-dialog.component.css.map +0 -7
  237. package/www/browser/issue-start-own-branch-dialog.component.css.map +0 -7
  238. package/www/browser/issues-list-table.component.css.map +0 -7
  239. package/www/browser/issues.component.css.map +0 -7
  240. package/www/browser/kill-port-widget.component.css.map +0 -7
  241. package/www/browser/kv-table.component.css.map +0 -7
  242. package/www/browser/layout-footer.component.css.map +0 -7
  243. package/www/browser/layout-header.component.css.map +0 -7
  244. package/www/browser/layout-menu.component.css.map +0 -7
  245. package/www/browser/layout-project-nav.component.css.map +0 -7
  246. package/www/browser/layout-sidebar.component.css.map +0 -7
  247. package/www/browser/layout.component.css.map +0 -7
  248. package/www/browser/less-viewport-component.css.map +0 -7
  249. package/www/browser/main.js +0 -1872
  250. package/www/browser/main.js.map +0 -1
  251. package/www/browser/markdown-viewer.component.css.map +0 -7
  252. package/www/browser/news-feed-widget.component.css.map +0 -7
  253. package/www/browser/ng-devtool.component.css.map +0 -7
  254. package/www/browser/nginx-config-editor.component.css.map +0 -7
  255. package/www/browser/nginx-log-viewer.component.css.map +0 -7
  256. package/www/browser/nginx-secondary-logs-tab.component.css.map +0 -7
  257. package/www/browser/nginx-secondary-perf-tab.component.css.map +0 -7
  258. package/www/browser/nginx-secondary-settings-tab.component.css.map +0 -7
  259. package/www/browser/nginx-secondary-ssl-tab.component.css.map +0 -7
  260. package/www/browser/nginx-secondary-test-tab.component.css.map +0 -7
  261. package/www/browser/nginx-secondary-traffic-tab.component.css.map +0 -7
  262. package/www/browser/nginx-secondary-upstream-tab.component.css.map +0 -7
  263. package/www/browser/nginx-section-card.component.css.map +0 -7
  264. package/www/browser/nginx-server-drawer.component.css.map +0 -7
  265. package/www/browser/nginx-server-list.component.css.map +0 -7
  266. package/www/browser/nginx-stat-card.component.css.map +0 -7
  267. package/www/browser/nginx.component.css.map +0 -7
  268. package/www/browser/page-layout.component.css.map +0 -7
  269. package/www/browser/pick-workspace-modal.component.css.map +0 -7
  270. package/www/browser/polyfills.js +0 -26584
  271. package/www/browser/polyfills.js.map +0 -7
  272. package/www/browser/project-conf.component.css.map +0 -7
  273. package/www/browser/project-create-modal.component.css.map +0 -7
  274. package/www/browser/project-create.component.css.map +0 -7
  275. package/www/browser/project-deps.component.css.map +0 -7
  276. package/www/browser/project-edit-modal.component.css.map +0 -7
  277. package/www/browser/project-import.component.css.map +0 -7
  278. package/www/browser/project-item-popover.component.css.map +0 -7
  279. package/www/browser/project-item.component.css.map +0 -7
  280. package/www/browser/project-list.component.css.map +0 -7
  281. package/www/browser/projects.component.css.map +0 -7
  282. package/www/browser/quick-task-widget.component.css.map +0 -7
  283. package/www/browser/rd-action-area.component.css.map +0 -7
  284. package/www/browser/rd-advance-stage-dialog.component.css.map +0 -7
  285. package/www/browser/rd-block-dialog.component.css.map +0 -7
  286. package/www/browser/rd-create-dialog.component.css.map +0 -7
  287. package/www/browser/rd-detail.component.css.map +0 -7
  288. package/www/browser/rd-item-card.component.css.map +0 -7
  289. package/www/browser/rd-list-board.component.css.map +0 -7
  290. package/www/browser/rd-list-table.component.css.map +0 -7
  291. package/www/browser/rd.component.css.map +0 -7
  292. package/www/browser/request-collections.component.css.map +0 -7
  293. package/www/browser/request-editor.component.css.map +0 -7
  294. package/www/browser/request-list-item.component.css.map +0 -7
  295. package/www/browser/request-list.component.css.map +0 -7
  296. package/www/browser/request-tabs-bar.component.css.map +0 -7
  297. package/www/browser/request-tabs.component.css.map +0 -7
  298. package/www/browser/request-urlbar.component.css.map +0 -7
  299. package/www/browser/response-viewer.component.css.map +0 -7
  300. package/www/browser/scripts.js +0 -245
  301. package/www/browser/scripts.js.map +0 -7
  302. package/www/browser/setting.component.css.map +0 -7
  303. package/www/browser/sprite-conf-modal.component.css.map +0 -7
  304. package/www/browser/sprite-icons-panel-component.css.map +0 -7
  305. package/www/browser/sprite-images-panel-component.css.map +0 -7
  306. package/www/browser/sprite-result-tabs.component.css.map +0 -7
  307. package/www/browser/sprite-viewport-component.css.map +0 -7
  308. package/www/browser/sprite.component.css.map +0 -7
  309. package/www/browser/step-advance.component.css.map +0 -7
  310. package/www/browser/step-basic.component.css.map +0 -7
  311. package/www/browser/step-config.component.css.map +0 -7
  312. package/www/browser/step-features.component.css.map +0 -7
  313. package/www/browser/step-preset.component.css.map +0 -7
  314. package/www/browser/step-summary-aside.component.css.map +0 -7
  315. package/www/browser/styles.css +0 -258
  316. package/www/browser/styles.css.map +0 -7
  317. package/www/browser/system-log.component.css.map +0 -7
  318. package/www/browser/task-actions.component.css.map +0 -7
  319. package/www/browser/task-console.component.css.map +0 -7
  320. package/www/browser/task-header.component.css.map +0 -7
  321. package/www/browser/task-list.component.css.map +0 -7
  322. package/www/browser/tasks.component.css.map +0 -7
  323. package/www/browser/terminal-view.component.css.map +0 -7
  324. package/www/browser/welcome-widget.component.css.map +0 -7
  325. package/www/browser/widget-base.component.css.map +0 -7
  326. package/www/browser/widget-host.component.css.map +0 -7
@@ -1,4146 +0,0 @@
1
- import {
2
- NzCodeEditorModule
3
- } from "./chunk-E4XD4GLT.js";
4
- import {
5
- NzLayoutModule
6
- } from "./chunk-YWHRP4X3.js";
7
- import {
8
- NzPopconfirmModule
9
- } from "./chunk-Y7JBZHNK.js";
10
- import {
11
- NzTabsModule
12
- } from "./chunk-O7LB6VFM.js";
13
- import {
14
- NzFormModule
15
- } from "./chunk-P5CWA4FO.js";
16
- import {
17
- NzSwitchModule
18
- } from "./chunk-6TBQERYX.js";
19
- import {
20
- NzDrawerModule
21
- } from "./chunk-7FC7DN65.js";
22
- import {
23
- NzModalModule,
24
- NzModalService
25
- } from "./chunk-FLSUSPBD.js";
26
- import "./chunk-S77VVYUZ.js";
27
- import "./chunk-ULOHDK7Y.js";
28
- import {
29
- NzSpinModule
30
- } from "./chunk-EQOY6A3M.js";
31
- import "./chunk-ZBZHXS46.js";
32
- import "./chunk-KSHAGY2M.js";
33
- import "./chunk-VXHZMSDM.js";
34
- import {
35
- NzEmptyModule,
36
- NzSelectModule
37
- } from "./chunk-ETTBRXVA.js";
38
- import {
39
- PageLayoutComponent
40
- } from "./chunk-6QMSEZPT.js";
41
- import {
42
- WsClientService
43
- } from "./chunk-APGQRYWX.js";
44
- import {
45
- ApiClient,
46
- NzMessageService
47
- } from "./chunk-2YSUXBGC.js";
48
- import {
49
- FormsModule,
50
- NzButtonModule,
51
- NzIconModule,
52
- NzInputModule,
53
- NzToolTipModule,
54
- NzTooltipModule
55
- } from "./chunk-RUXJCM3V.js";
56
- import "./chunk-VYNQPZQO.js";
57
- import "./chunk-665WC4RU.js";
58
- import {
59
- CommonModule,
60
- HttpParams
61
- } from "./chunk-3MCIETCQ.js";
62
- import {
63
- ChangeDetectorRef,
64
- Component,
65
- EventEmitter,
66
- Injectable,
67
- Input,
68
- Output,
69
- ViewChild,
70
- __decorate,
71
- __spreadProps,
72
- __spreadValues,
73
- filter,
74
- firstValueFrom,
75
- inject,
76
- signal
77
- } from "./chunk-XQY5SPGI.js";
78
-
79
- // angular:jit:template:src\app\pages\nginx\nginx.component.html
80
- var nginx_component_default = `<app-page-layout [title]="'Nginx \u7BA1\u7406'" [loading]="loading()" [isFullscreen]="true" [isOverflowYAuto]="false">\r
81
- <ng-container ngProjectAs="actions">\r
82
- @if (instance()) {\r
83
- <span class="top-status" [class.running]="status()?.isRunning">\r
84
- <span class="status-dot"></span>\r
85
- {{ instance()?.version || 'unknown' }} \xB7 {{ pidText }}\r
86
- </span>\r
87
- \r
88
- <button nz-button nzType="default" (click)="refreshAll()" [nzLoading]="configLoading()">\r
89
- <nz-icon nzType="reload" nzTheme="outline"></nz-icon>\r
90
- \u5237\u65B0\r
91
- </button>\r
92
- \r
93
- <button nz-button nzType="text" nz-tooltip nzTooltipTitle="\u8BBE\u7F6E" (click)="switchSecondaryTab('settings')">\r
94
- <nz-icon nzType="setting" nzTheme="outline"></nz-icon>\r
95
- </button>\r
96
- }\r
97
- </ng-container>\r
98
- \r
99
- @if (!instance()) {\r
100
- <nz-layout class="console-shell">\r
101
- <nz-content class="unbound-content">\r
102
- <nz-empty\r
103
- nzNotFoundImage="simple"\r
104
- [nzNotFoundContent]="'\u8BF7\u5148\u7ED1\u5B9A\u672C\u5730 Nginx \u5B9E\u4F8B'"\r
105
- [nzNotFoundFooter]="bindFooter"\r
106
- >\r
107
- <ng-template #bindFooter>\r
108
- <button nz-button nzType="primary" (click)="showBindModal()">\u7ED1\u5B9A Nginx</button>\r
109
- </ng-template>\r
110
- </nz-empty>\r
111
- </nz-content>\r
112
- </nz-layout>\r
113
- } @else {\r
114
- <nz-layout class="console-shell">\r
115
- <nz-content class="console-content">\r
116
- <div class="stats-row">\r
117
- <app-nginx-stat-card\r
118
- label="\u670D\u52A1\u72B6\u6001"\r
119
- [value]="serviceStatusText"\r
120
- [toneClass]="status()?.isRunning ? 'green' : 'red'"\r
121
- [sub]="pidText"\r
122
- ></app-nginx-stat-card>\r
123
- \r
124
- <app-nginx-stat-card
125
- label="\u8FD0\u884C\u65F6\u957F"
126
- [value]="uptimeText"
127
- toneClass="blue"
128
- [sub]="uptimeSubText"
129
- ></app-nginx-stat-card>
130
- \r
131
- <app-nginx-stat-card\r
132
- label="\u6D3B\u8DC3\u8FDE\u63A5"\r
133
- [value]="activeConnectionText"\r
134
- toneClass="purple"\r
135
- [sub]="activeConnectionSubText"\r
136
- ></app-nginx-stat-card>\r
137
- \r
138
- <app-nginx-stat-card\r
139
- label="Server \u5757"\r
140
- [value]="serverSummary().total"\r
141
- toneClass="orange"\r
142
- [sub]="enabledServerText"\r
143
- ></app-nginx-stat-card>\r
144
- </div>\r
145
- \r
146
- <div class="service-control-row">\r
147
- <div class="service-control-left">\r
148
- <span class="service-control-label">\u670D\u52A1\u63A7\u5236</span>\r
149
- <button nz-button nzType="default" (click)="startNginx()" [disabled]="controlling() || status()?.isRunning">\r
150
- <nz-icon nzType="caret-right" nzTheme="outline"></nz-icon>\r
151
- \u542F\u52A8\r
152
- </button>\r
153
- <button nz-button nzType="default" (click)="stopNginx()" [disabled]="controlling() || !status()?.isRunning">\r
154
- <nz-icon nzType="pause" nzTheme="outline"></nz-icon>\r
155
- \u505C\u6B62\r
156
- </button>\r
157
- <button nz-button nzType="default" (click)="reloadNginx()" [disabled]="controlling() || !status()?.isRunning">\r
158
- <nz-icon nzType="reload" nzTheme="outline"></nz-icon>\r
159
- \u91CD\u8F7D\r
160
- </button>\r
161
- <button nz-button nzType="default" (click)="testConfig()">\r
162
- <nz-icon nzType="check-circle" nzTheme="outline"></nz-icon>\r
163
- \u68C0\u6D4B\r
164
- </button>\r
165
- </div>\r
166
- <span class="service-control-path mono">{{ instance()?.path || '-' }}</span>\r
167
- </div>\r
168
- \r
169
- <app-nginx-section-card\r
170
- accent="blue"\r
171
- [showHeader]="true"\r
172
- title="\u57FA\u7840\u914D\u7F6E"\r
173
- subtitle="Server \u7BA1\u7406"\r
174
- [noBodyPadding]="true"\r
175
- >\r
176
- <ng-container nginxCardActions>\r
177
- <button nz-button nzType="default" (click)="importServer()">\r
178
- <nz-icon nzType="upload" nzTheme="outline"></nz-icon>\r
179
- \u5BFC\u5165\r
180
- </button>\r
181
- <button nz-button nzType="primary" (click)="requestCreateServer()">\r
182
- <nz-icon nzType="plus" nzTheme="outline"></nz-icon>\r
183
- \u65B0\u589E Server\r
184
- </button>\r
185
- </ng-container>\r
186
- <app-nginx-server-list\r
187
- [showToolbar]="false"\r
188
- [openCreateToken]="openServerDrawerToken()"\r
189
- (summaryChange)="onServerSummaryChange($event)"\r
190
- (serverListMutated)="onServerListMutated()"\r
191
- ></app-nginx-server-list>\r
192
- </app-nginx-section-card>\r
193
- \r
194
- <app-nginx-section-card accent="green" [noBodyPadding]="true">\r
195
- <button type="button" class="config-summary" [class.open]="configExpanded()" (click)="toggleConfigExpanded()">\r
196
- <div class="config-summary-left">\r
197
- <span class="config-title">\u914D\u7F6E\u6587\u4EF6</span>\r
198
- </div>\r
199
- <nz-icon nzType="down" nzTheme="outline" class="expand-icon"></nz-icon>\r
200
- </button>\r
201
- \r
202
- @if (configExpanded()) {\r
203
- <div class="config-expanded">\r
204
- <app-nginx-config-editor [refreshToken]="configEditorRefreshToken()"></app-nginx-config-editor>\r
205
- </div>\r
206
- }\r
207
- </app-nginx-section-card>\r
208
- \r
209
- <app-nginx-section-card accent="orange" [noBodyPadding]="true">\r
210
- <nz-tabs\r
211
- class="secondary-tabset"\r
212
- [nzSelectedIndex]="secondaryTabIndex()"\r
213
- (nzSelectedIndexChange)="onSecondaryTabIndexChange($event)"\r
214
- >\r
215
- <nz-tab nzTitle="Upstream \u7BA1\u7406">\r
216
- @if (secondaryTab() === 'upstream') {\r
217
- <app-nginx-secondary-upstream-tab></app-nginx-secondary-upstream-tab>\r
218
- }\r
219
- </nz-tab>\r
220
- <nz-tab nzTitle="SSL \u8BC1\u4E66">\r
221
- @if (secondaryTab() === 'ssl') {\r
222
- <app-nginx-secondary-ssl-tab></app-nginx-secondary-ssl-tab>\r
223
- }\r
224
- </nz-tab>\r
225
- <nz-tab nzTitle="\u6D41\u91CF\u63A7\u5236">\r
226
- @if (secondaryTab() === 'traffic') {\r
227
- <app-nginx-secondary-traffic-tab></app-nginx-secondary-traffic-tab>\r
228
- }\r
229
- </nz-tab>\r
230
- <nz-tab nzTitle="\u6027\u80FD\u4F18\u5316">\r
231
- @if (secondaryTab() === 'perf') {\r
232
- <app-nginx-secondary-perf-tab></app-nginx-secondary-perf-tab>\r
233
- }\r
234
- </nz-tab>\r
235
- <nz-tab nzTitle="\u65E5\u5FD7">\r
236
- @if (secondaryTab() === 'logs') {\r
237
- <app-nginx-secondary-logs-tab></app-nginx-secondary-logs-tab>\r
238
- }\r
239
- </nz-tab>\r
240
- <nz-tab nzTitle="\u914D\u7F6E\u68C0\u6D4B">\r
241
- @if (secondaryTab() === 'test') {\r
242
- <app-nginx-secondary-test-tab\r
243
- [loading]="loading()"\r
244
- [logs]="recentLogs()"\r
245
- (runTest)="testConfig()"\r
246
- ></app-nginx-secondary-test-tab>\r
247
- }\r
248
- </nz-tab>\r
249
- <nz-tab nzTitle="\u8BBE\u7F6E">\r
250
- @if (secondaryTab() === 'settings') {\r
251
- <app-nginx-secondary-settings-tab\r
252
- [instance]="instance()"\r
253
- [configFileCount]="configFiles().length"\r
254
- (unbind)="unbind()"\r
255
- ></app-nginx-secondary-settings-tab>\r
256
- }\r
257
- </nz-tab>\r
258
- </nz-tabs>\r
259
- </app-nginx-section-card>\r
260
- </nz-content>\r
261
- </nz-layout>\r
262
- }\r
263
- \r
264
- <nz-modal\r
265
- [(nzVisible)]="bindModalVisible"\r
266
- (nzOnCancel)="bindModalVisible = false"\r
267
- [nzFooter]="null"\r
268
- [nzWidth]="560"\r
269
- [nzCentered]="true"\r
270
- [nzBodyStyle]="{ padding: '0' }"\r
271
- >\r
272
- <ng-container *nzModalContent>\r
273
- <div class="bind-modal">\r
274
- <div class="bind-icon">\r
275
- <nz-icon nzType="link" nzTheme="outline"></nz-icon>\r
276
- </div>\r
277
- \r
278
- <div class="bind-title">\u7ED1\u5B9A\u672C\u5730 Nginx</div>\r
279
- <div class="bind-desc">\r
280
- \u6307\u5B9A\u672C\u673A Nginx \u53EF\u6267\u884C\u6587\u4EF6\u8DEF\u5F84\uFF0C\u5B8C\u6210\u5B9E\u4F8B\u7ED1\u5B9A\u540E\u5373\u53EF\u4F7F\u7528\u53EF\u89C6\u5316\u7BA1\u7406\u529F\u80FD\r
281
- </div>\r
282
- \r
283
- <div class="bind-input-group">\r
284
- <input\r
285
- nz-input\r
286
- class="mono bind-path-input"\r
287
- [(ngModel)]="bindPath"\r
288
- placeholder="/usr/local/nginx/sbin/nginx"\r
289
- (keyup.enter)="bindNginx()"\r
290
- />\r
291
- <button nz-button nzType="default" (click)="showPathHint()">\r
292
- <nz-icon nzType="folder-open" nzTheme="outline"></nz-icon>\r
293
- \u6D4F\u89C8\r
294
- </button>\r
295
- </div>\r
296
- \r
297
- <!-- <div class="bind-or">\u2014 \u6216 \u2014</div>\r
298
- \r
299
- <button type="button" class="bind-auto-detect" (click)="autoDetectBindPath()">\r
300
- <nz-icon nzType="search" nzTheme="outline"></nz-icon>\r
301
- <div class="bind-auto-detect-text">\r
302
- <div class="bind-auto-detect-title">\u81EA\u52A8\u68C0\u6D4B</div>\r
303
- <div class="bind-auto-detect-hint">\u586B\u5145\u5E38\u89C1\u5B89\u88C5\u8DEF\u5F84\uFF0C\u786E\u8BA4\u540E\u5373\u53EF\u7ED1\u5B9A</div>\r
304
- </div>\r
305
- </button> -->\r
306
- \r
307
- <button\r
308
- nz-button\r
309
- nzType="primary"\r
310
- class="bind-submit-btn"\r
311
- (click)="bindNginx()"\r
312
- [nzLoading]="binding()"\r
313
- [disabled]="!bindPath.trim()"\r
314
- >\r
315
- <nz-icon nzType="link" nzTheme="outline"></nz-icon>\r
316
- \u7ED1\u5B9A Nginx\r
317
- </button>\r
318
- </div>\r
319
- </ng-container>\r
320
- </nz-modal>\r
321
- </app-page-layout>\r
322
- `;
323
-
324
- // angular:jit:style:src\app\pages\nginx\nginx.component.less
325
- var nginx_component_default2 = '/* src/app/pages/nginx/nginx.component.less */\n:host {\n --nginx-font-size-sm: 12px;\n --nginx-font-size-base: 14px;\n --nginx-font-size-kpi: 22px;\n --nginx-font-family-mono:\n ui-monospace,\n SFMono-Regular,\n Menlo,\n Monaco,\n Consolas,\n "Liberation Mono",\n monospace;\n --bg-page: #f5f7fa;\n --bg-white: #ffffff;\n --bg-hover: #f8f9fc;\n --bg-input: #f7f8fa;\n --border: #e8ebf0;\n --border-light: #eef1f5;\n --text-1: #1d2129;\n --text-2: #4e5969;\n --text-3: #86909c;\n --text-4: #c9cdd4;\n --blue: #165dff;\n --blue-bg: #f2f6ff;\n --blue-border: rgba(22, 93, 255, 0.15);\n --green: #00b42a;\n --green-bg: #f2fff6;\n --green-border: rgba(0, 180, 42, 0.15);\n --red: #f53f3f;\n --red-bg: #fff2f2;\n --orange: #ff7d00;\n --orange-bg: #fff7e8;\n --orange-border: rgba(255, 125, 0, 0.15);\n --purple: #722ed1;\n --shadow-card: 0 1px 4px rgba(0, 0, 0, 0.04);\n --shadow-hover: 0 4px 12px rgba(0, 0, 0, 0.06);\n}\n.console-shell {\n height: 100%;\n background: var(--bg-page);\n}\n.console-content {\n padding: 18px 20px 30px;\n overflow-y: auto;\n}\n.unbound-content {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n}\n.stats-row {\n display: grid;\n grid-template-columns: repeat(4, minmax(0, 1fr));\n gap: 12px;\n margin-bottom: 14px;\n}\n.service-control-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 10px;\n background: #f2f3f5;\n border: 1px solid #e5e6eb;\n border-radius: 6px;\n padding: 10px 12px;\n margin-bottom: 14px;\n}\n.service-control-left {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n}\n.service-control-label {\n font-size: var(--nginx-font-size-sm, 12px);\n color: var(--text-2);\n font-weight: 600;\n margin-right: 2px;\n}\n.service-control-path {\n font-size: var(--nginx-font-size-sm, 12px);\n color: var(--text-3);\n padding: 4px 8px;\n border: 1px solid #dcdfe5;\n border-radius: 4px;\n background: #f7f8fa;\n}\n.config-title {\n font-size: var(--nginx-font-size-base, 14px);\n font-weight: 700;\n color: var(--text-1);\n}\n.config-summary {\n width: 100%;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 16px 10px 20px;\n border: none;\n background: #fff;\n cursor: pointer;\n text-align: left;\n transition: background 120ms ease;\n}\n.config-summary:hover {\n background: var(--bg-hover);\n}\n.config-summary.open .expand-icon {\n transform: rotate(180deg);\n}\n.config-summary-left {\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n}\n.expand-icon {\n color: var(--text-3);\n transition: transform 200ms ease;\n}\n.config-expanded {\n border-top: 1px solid var(--border-light);\n}\n.mono {\n font-family: var(--nginx-font-family-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace);\n}\n.mono.strong {\n color: var(--text-1);\n font-weight: 600;\n}\n.mono.dim {\n color: var(--text-2);\n font-size: var(--nginx-font-size-sm, 12px);\n}\n.bind-modal {\n padding: 32px;\n text-align: center;\n}\n.bind-icon {\n width: 56px;\n height: 56px;\n margin: 0 auto 18px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--blue);\n background: var(--blue-bg);\n border: 1px solid var(--blue-border);\n font-size: 24px;\n}\n.bind-title {\n font-size: 18px;\n font-weight: 700;\n color: var(--text-1);\n margin-bottom: 8px;\n}\n.bind-desc {\n font-size: var(--nginx-font-size-sm, 12px);\n line-height: 1.7;\n color: var(--text-3);\n margin-bottom: 22px;\n}\n.bind-input-group {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 12px;\n}\n.bind-path-input {\n flex: 1;\n}\n.bind-or {\n font-size: var(--nginx-font-size-sm, 12px);\n color: var(--text-3);\n margin-bottom: 12px;\n}\n.bind-auto-detect {\n width: 100%;\n margin-bottom: 16px;\n border: 1px solid var(--border);\n border-radius: 6px;\n background: var(--bg-input);\n cursor: pointer;\n padding: 10px 12px;\n display: flex;\n align-items: center;\n gap: 10px;\n transition: all 120ms ease;\n text-align: left;\n}\n.bind-auto-detect:hover {\n border-color: var(--blue);\n box-shadow: 0 0 0 2px var(--blue-border);\n background: #fff;\n}\n.bind-auto-detect .anticon {\n font-size: 16px;\n color: var(--blue);\n}\n.bind-auto-detect-text {\n min-width: 0;\n}\n.bind-auto-detect-title {\n color: var(--text-1);\n font-size: var(--nginx-font-size-base, 14px);\n font-weight: 600;\n}\n.bind-auto-detect-hint {\n margin-top: 2px;\n color: var(--text-3);\n font-size: var(--nginx-font-size-sm, 12px);\n}\n.bind-submit-btn {\n width: 100%;\n height: 36px;\n font-size: var(--nginx-font-size-base, 14px);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n}\n@media (max-width: 768px) {\n .bind-modal {\n padding: 20px 16px;\n }\n .bind-input-group {\n flex-direction: column;\n align-items: stretch;\n }\n}\n:host ::ng-deep .top-status {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-size: var(--nginx-font-size-sm, 12px);\n font-family: var(--nginx-font-family-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace);\n color: var(--red);\n background: var(--red-bg);\n border: 1px solid rgba(245, 63, 63, 0.2);\n border-radius: 16px;\n padding: 4px 10px;\n}\n:host ::ng-deep .top-status.running {\n color: var(--green);\n background: var(--green-bg);\n border-color: var(--green-border);\n}\n:host ::ng-deep .top-status .status-dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: currentColor;\n animation: pulse 2s ease-in-out infinite;\n}\n:host ::ng-deep .secondary-tabset .ant-tabs-nav {\n margin: 0;\n padding: 0 16px;\n border-bottom: 1px solid var(--border-light);\n}\n:host ::ng-deep .secondary-tabset .ant-tabs-content-holder {\n padding: 14px 18px;\n}\n@keyframes pulse {\n 0%, 100% {\n opacity: 1;\n }\n 50% {\n opacity: 0.35;\n }\n}\n@media (max-width: 1280px) {\n .stats-row {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n }\n}\n@media (max-width: 992px) {\n .service-control-row {\n flex-direction: column;\n align-items: flex-start;\n }\n}\n@media (max-width: 768px) {\n .console-content {\n padding: 12px;\n }\n .stats-row {\n grid-template-columns: 1fr;\n }\n .config-summary-left {\n flex-direction: column;\n align-items: flex-start;\n }\n}\n/*# sourceMappingURL=nginx.component.css.map */\n';
326
-
327
- // angular:jit:style:inline:src\app\pages\nginx\components\nginx-config-editor\nginx-config-editor.component.ts;CiAgICAuY29uZmlnLWVkaXRvciB7CiAgICAgIGRpc3BsYXk6IGZsZXg7CiAgICAgIGZsZXgtZGlyZWN0aW9uOiBjb2x1bW47CiAgICAgIGhlaWdodDogMTAwJTsKICAgICAgYm9yZGVyOiAxcHggc29saWQgI2YwZjBmMDsKICAgICAgYm9yZGVyLXJhZGl1czogOHB4OwogICAgICBvdmVyZmxvdzogaGlkZGVuOwogICAgfQoKICAgIC5lZGl0b3ItdG9vbGJhciB7CiAgICAgIGRpc3BsYXk6IGZsZXg7CiAgICAgIGp1c3RpZnktY29udGVudDogc3BhY2UtYmV0d2VlbjsKICAgICAgYWxpZ24taXRlbXM6IGZsZXgtc3RhcnQ7CiAgICAgIGdhcDogMTJweDsKICAgICAgcGFkZGluZzogMTJweCAxNnB4OwogICAgICBiYWNrZ3JvdW5kOiAjZmFmYWZhOwogICAgICBib3JkZXItYm90dG9tOiAxcHggc29saWQgI2YwZjBmMDsKCiAgICAgIC50b29sYmFyLWxlZnQgewogICAgICAgIG1pbi13aWR0aDogMDsKICAgICAgICBmbGV4OiAxOwogICAgICAgIGRpc3BsYXk6IGZsZXg7CiAgICAgICAgZmxleC1kaXJlY3Rpb246IHJvdzsKICAgICAgICBhbGlnbi1pdGVtczogY2VudGVyOwogICAgICAgIGZsZXgtd3JhcDogd3JhcDsKICAgICAgICBnYXA6IDEwcHg7CiAgICAgIH0KCiAgICAgIC5maWxlLWluZm8gewogICAgICAgIGRpc3BsYXk6IGZsZXg7CiAgICAgICAgYWxpZ24taXRlbXM6IGNlbnRlcjsKICAgICAgICBnYXA6IDhweDsKICAgICAgICBmb250LXNpemU6IHZhcigtLW5naW54LWZvbnQtc2l6ZS1iYXNlLCAxNHB4KTsKICAgICAgICBtaW4td2lkdGg6IDA7CgogICAgICAgIC5maWxlLXBhdGggewogICAgICAgICAgZm9udC1mYW1pbHk6IHZhcigKICAgICAgICAgICAgLS1uZ2lueC1mb250LWZhbWlseS1tb25vLAogICAgICAgICAgICB1aS1tb25vc3BhY2UsCiAgICAgICAgICAgIFNGTW9uby1SZWd1bGFyLAogICAgICAgICAgICBNZW5sbywKICAgICAgICAgICAgTW9uYWNvLAogICAgICAgICAgICBDb25zb2xhcywKICAgICAgICAgICAgJ0xpYmVyYXRpb24gTW9ubycsCiAgICAgICAgICAgIG1vbm9zcGFjZQogICAgICAgICAgKTsKICAgICAgICAgIGNvbG9yOiByZ2JhKDAsIDAsIDAsIDAuNjUpOwogICAgICAgICAgb3ZlcmZsb3c6IGhpZGRlbjsKICAgICAgICAgIHRleHQtb3ZlcmZsb3c6IGVsbGlwc2lzOwogICAgICAgICAgd2hpdGUtc3BhY2U6IG5vd3JhcDsKICAgICAgICB9CiAgICAgIH0KCiAgICAgIC5maWxlLXNlbGVjdG9yIHsKICAgICAgICB3aWR0aDogbWluKDU2MHB4LCAxMDAlKTsKICAgICAgfQoKICAgICAgLmVkaXRvci1hY3Rpb25zIHsKICAgICAgICBkaXNwbGF5OiBmbGV4OwogICAgICAgIGdhcDogOHB4OwogICAgICAgIGZsZXgtc2hyaW5rOiAwOwogICAgICB9CiAgICB9CgogICAgLmVkaXRvci1jb250YWluZXIgewogICAgICBmbGV4OiAxOwogICAgICBtaW4taGVpZ2h0OiA1MjBweDsKICAgICAgcG9zaXRpb246IHJlbGF0aXZlOwogICAgfQoKICAgIC5jb2RlLWVkaXRvciB7CiAgICAgIGRpc3BsYXk6IGJsb2NrOwogICAgICBtaW4taGVpZ2h0OiA1MjBweDsKICAgICAgd2lkdGg6IDEwMCU7CiAgICAgIGJvcmRlcjogMXB4IHNvbGlkICNkOWQ5ZDk7CiAgICB9CgogICAgLnZhbGlkYXRpb24tcmVzdWx0IHsKICAgICAgcGFkZGluZzogMTJweCAxNnB4OwogICAgICBiYWNrZ3JvdW5kOiAjZjZmZmVkOwogICAgICBib3JkZXItdG9wOiAxcHggc29saWQgI2I3ZWI4ZjsKICAgICAgZGlzcGxheTogZmxleDsKICAgICAgYWxpZ24taXRlbXM6IGNlbnRlcjsKICAgICAgZ2FwOiA4cHg7CgogICAgICAmLmVycm9yIHsKICAgICAgICBiYWNrZ3JvdW5kOiAjZmZmMmYwOwogICAgICAgIGJvcmRlci10b3AtY29sb3I6ICNmZmNjYzc7CiAgICAgIH0KCiAgICAgIC5zdWNjZXNzLWljb24gewogICAgICAgIGNvbG9yOiAjNTJjNDFhOwogICAgICB9CgogICAgICAuZXJyb3ItaWNvbiB7CiAgICAgICAgY29sb3I6ICNmZjRkNGY7CiAgICAgIH0KCiAgICAgIC5lcnJvci1saXN0LAogICAgICAud2FybmluZy1saXN0IHsKICAgICAgICBtYXJnaW4tdG9wOiA4cHg7CiAgICAgICAgd2lkdGg6IDEwMCU7CgogICAgICAgIC5lcnJvci1pdGVtIHsKICAgICAgICAgIGNvbG9yOiAjZmY0ZDRmOwogICAgICAgICAgZm9udC1zaXplOiB2YXIoLS1uZ2lueC1mb250LXNpemUtc20sIDEycHgpOwogICAgICAgICAgcGFkZGluZzogNHB4IDA7CiAgICAgICAgICBmb250LWZhbWlseTogdmFyKAogICAgICAgICAgICAtLW5naW54LWZvbnQtZmFtaWx5LW1vbm8sCiAgICAgICAgICAgIHVpLW1vbm9zcGFjZSwKICAgICAgICAgICAgU0ZNb25vLVJlZ3VsYXIsCiAgICAgICAgICAgIE1lbmxvLAogICAgICAgICAgICBNb25hY28sCiAgICAgICAgICAgIENvbnNvbGFzLAogICAgICAgICAgICAnTGliZXJhdGlvbiBNb25vJywKICAgICAgICAgICAgbW9ub3NwYWNlCiAgICAgICAgICApOwogICAgICAgIH0KCiAgICAgICAgLndhcm5pbmctaXRlbSB7CiAgICAgICAgICBjb2xvcjogI2ZhYWQxNDsKICAgICAgICAgIGZvbnQtc2l6ZTogdmFyKC0tbmdpbngtZm9udC1zaXplLXNtLCAxMnB4KTsKICAgICAgICAgIHBhZGRpbmc6IDRweCAwOwogICAgICAgICAgZm9udC1mYW1pbHk6IHZhcigKICAgICAgICAgICAgLS1uZ2lueC1mb250LWZhbWlseS1tb25vLAogICAgICAgICAgICB1aS1tb25vc3BhY2UsCiAgICAgICAgICAgIFNGTW9uby1SZWd1bGFyLAogICAgICAgICAgICBNZW5sbywKICAgICAgICAgICAgTW9uYWNvLAogICAgICAgICAgICBDb25zb2xhcywKICAgICAgICAgICAgJ0xpYmVyYXRpb24gTW9ubycsCiAgICAgICAgICAgIG1vbm9zcGFjZQogICAgICAgICAgKTsKICAgICAgICB9CiAgICAgIH0KICAgIH0KCiAgICA6aG9zdCA6Om5nLWRlZXAgbnotY29kZS1lZGl0b3IuY29kZS1lZGl0b3IuYW50LWNvZGUtZWRpdG9yIHsKICAgICAgd2lkdGg6IDEwMCU7CiAgICAgIG1pbi1oZWlnaHQ6IDUyMHB4ICFpbXBvcnRhbnQ7CiAgICAgIGRpc3BsYXk6IGJsb2NrOwogICAgfQoKICAgIDpob3N0IDo6bmctZGVlcCBuei1jb2RlLWVkaXRvci5jb2RlLWVkaXRvciAubW9uYWNvLWRpZmYtZWRpdG9yIHsKICAgICAgbWluLWhlaWdodDogNTIwcHg7CiAgICB9CiAg
328
- var nginx_config_editor_component_default = '/* angular:styles/component:less;f674c0a165c5f39b31152e289fc86966a5f30c517d213b82b5ab60d88ac38960;D:\\ng-manager\\webapp\\src\\app\\pages\\nginx\\components\\nginx-config-editor\\nginx-config-editor.component.ts */\n.config-editor {\n display: flex;\n flex-direction: column;\n height: 100%;\n border: 1px solid #f0f0f0;\n border-radius: 8px;\n overflow: hidden;\n}\n.editor-toolbar {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n gap: 12px;\n padding: 12px 16px;\n background: #fafafa;\n border-bottom: 1px solid #f0f0f0;\n}\n.editor-toolbar .toolbar-left {\n min-width: 0;\n flex: 1;\n display: flex;\n flex-direction: row;\n align-items: center;\n flex-wrap: wrap;\n gap: 10px;\n}\n.editor-toolbar .file-info {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: var(--nginx-font-size-base, 14px);\n min-width: 0;\n}\n.editor-toolbar .file-info .file-path {\n font-family: var(--nginx-font-family-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace);\n color: rgba(0, 0, 0, 0.65);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.editor-toolbar .file-selector {\n width: min(560px, 100%);\n}\n.editor-toolbar .editor-actions {\n display: flex;\n gap: 8px;\n flex-shrink: 0;\n}\n.editor-container {\n flex: 1;\n min-height: 520px;\n position: relative;\n}\n.code-editor {\n display: block;\n min-height: 520px;\n width: 100%;\n border: 1px solid #d9d9d9;\n}\n.validation-result {\n padding: 12px 16px;\n background: #f6ffed;\n border-top: 1px solid #b7eb8f;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n.validation-result.error {\n background: #fff2f0;\n border-top-color: #ffccc7;\n}\n.validation-result .success-icon {\n color: #52c41a;\n}\n.validation-result .error-icon {\n color: #ff4d4f;\n}\n.validation-result .error-list,\n.validation-result .warning-list {\n margin-top: 8px;\n width: 100%;\n}\n.validation-result .error-list .error-item,\n.validation-result .warning-list .error-item {\n color: #ff4d4f;\n font-size: var(--nginx-font-size-sm, 12px);\n padding: 4px 0;\n font-family: var(--nginx-font-family-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace);\n}\n.validation-result .error-list .warning-item,\n.validation-result .warning-list .warning-item {\n color: #faad14;\n font-size: var(--nginx-font-size-sm, 12px);\n padding: 4px 0;\n font-family: var(--nginx-font-family-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace);\n}\n:host ::ng-deep nz-code-editor.code-editor.ant-code-editor {\n width: 100%;\n min-height: 520px !important;\n display: block;\n}\n:host ::ng-deep nz-code-editor.code-editor .monaco-diff-editor {\n min-height: 520px;\n}\n/*# sourceMappingURL=nginx-config-editor.component.css.map */\n';
329
-
330
- // src/app/pages/nginx/services/nginx.service.ts
331
- var NginxService = class NginxService2 {
332
- http = inject(ApiClient);
333
- baseUrl = "/api/nginx";
334
- // ========== 实例管理 ==========
335
- /**
336
- * 获取 Nginx 状态和实例信息
337
- */
338
- async getStatus() {
339
- return await firstValueFrom(this.http.get(`${this.baseUrl}/status`));
340
- }
341
- /**
342
- * 获取首页统计信息(状态 + server 汇总)
343
- */
344
- async getStats() {
345
- return await firstValueFrom(this.http.get(`${this.baseUrl}/stats`));
346
- }
347
- /**
348
- * 绑定 Nginx 实例
349
- */
350
- async bind(path) {
351
- return await firstValueFrom(this.http.post(`${this.baseUrl}/bind`, { path }));
352
- }
353
- /**
354
- * 解绑 Nginx 实例
355
- */
356
- async unbind() {
357
- return await firstValueFrom(this.http.post(`${this.baseUrl}/unbind`, {}));
358
- }
359
- // ========== 服务控制 ==========
360
- /**
361
- * 启动 Nginx
362
- */
363
- async start() {
364
- return await firstValueFrom(this.http.post(`${this.baseUrl}/start`, {}));
365
- }
366
- /**
367
- * 停止 Nginx
368
- */
369
- async stop() {
370
- return await firstValueFrom(this.http.post(`${this.baseUrl}/stop`, {}));
371
- }
372
- /**
373
- * 重载配置
374
- */
375
- async reload() {
376
- return await firstValueFrom(this.http.post(`${this.baseUrl}/reload`, {}));
377
- }
378
- /**
379
- * 测试配置
380
- */
381
- async test() {
382
- return await firstValueFrom(this.http.post(`${this.baseUrl}/test`, {}));
383
- }
384
- // ========== 配置管理 ==========
385
- /**
386
- * 读取主配置
387
- */
388
- async getConfig() {
389
- return await firstValueFrom(this.http.get(`${this.baseUrl}/config`));
390
- }
391
- /**
392
- * 更新主配置
393
- */
394
- async updateConfig(content) {
395
- return await firstValueFrom(this.http.put(`${this.baseUrl}/config`, { content }));
396
- }
397
- /**
398
- * 验证配置
399
- */
400
- async validateConfig(content) {
401
- return await firstValueFrom(this.http.post(`${this.baseUrl}/config/validate`, { content }));
402
- }
403
- /**
404
- * 获取包含的配置文件列表
405
- */
406
- async getConfigFiles() {
407
- return await firstValueFrom(this.http.get(`${this.baseUrl}/config/files`));
408
- }
409
- /**
410
- * 读取指定配置文件
411
- */
412
- async getConfigFile(filePath) {
413
- const params = new HttpParams().set("filePath", filePath);
414
- return await firstValueFrom(this.http.get(`${this.baseUrl}/config/file`, params));
415
- }
416
- /**
417
- * 保存指定配置文件
418
- */
419
- async updateConfigFile(filePath, content) {
420
- return await firstValueFrom(this.http.put(`${this.baseUrl}/config/file`, {
421
- filePath,
422
- content
423
- }));
424
- }
425
- // ========== Server 管理 ==========
426
- /**
427
- * 获取所有 server
428
- */
429
- async getServers() {
430
- return await firstValueFrom(this.http.get(`${this.baseUrl}/servers`));
431
- }
432
- /**
433
- * 获取单个 server
434
- */
435
- async getServer(id) {
436
- return await firstValueFrom(this.http.get(`${this.baseUrl}/servers/${id}`));
437
- }
438
- /**
439
- * 创建 server
440
- */
441
- async createServer(request) {
442
- return await firstValueFrom(this.http.post(`${this.baseUrl}/servers`, request));
443
- }
444
- /**
445
- * 更新 server
446
- */
447
- async updateServer(id, request) {
448
- return await firstValueFrom(this.http.put(`${this.baseUrl}/servers/${id}`, request));
449
- }
450
- /**
451
- * 删除 server
452
- */
453
- async deleteServer(id) {
454
- return await firstValueFrom(this.http.delete(`${this.baseUrl}/servers/${id}`));
455
- }
456
- /**
457
- * 启用 server
458
- */
459
- async enableServer(id) {
460
- return await firstValueFrom(this.http.patch(`${this.baseUrl}/servers/${id}/enable`, {}));
461
- }
462
- /**
463
- * 禁用 server
464
- */
465
- async disableServer(id) {
466
- return await firstValueFrom(this.http.patch(`${this.baseUrl}/servers/${id}/disable`, {}));
467
- }
468
- // ========== Phase2:Upstream / SSL / 流量 / 性能 ==========
469
- async getUpstreams() {
470
- return await firstValueFrom(this.http.get(`${this.baseUrl}/upstreams`));
471
- }
472
- async saveUpstreams(upstreams) {
473
- return await firstValueFrom(this.http.put(`${this.baseUrl}/upstreams`, { upstreams }));
474
- }
475
- async getSslCertificates() {
476
- return await firstValueFrom(this.http.get(`${this.baseUrl}/ssl/certificates`));
477
- }
478
- async saveSslCertificates(certificates) {
479
- return await firstValueFrom(this.http.put(`${this.baseUrl}/ssl/certificates`, {
480
- certificates
481
- }));
482
- }
483
- async getTrafficConfig() {
484
- return await firstValueFrom(this.http.get(`${this.baseUrl}/traffic`));
485
- }
486
- async saveTrafficConfig(traffic) {
487
- return await firstValueFrom(this.http.put(`${this.baseUrl}/traffic`, traffic));
488
- }
489
- async getPerformanceConfig() {
490
- return await firstValueFrom(this.http.get(`${this.baseUrl}/performance`));
491
- }
492
- async savePerformanceConfig(performance) {
493
- return await firstValueFrom(this.http.put(`${this.baseUrl}/performance`, performance));
494
- }
495
- async getModuleSettings() {
496
- return await firstValueFrom(this.http.get(`${this.baseUrl}/module/settings`));
497
- }
498
- async saveModuleSettings(settings) {
499
- return await firstValueFrom(this.http.put(`${this.baseUrl}/module/settings`, settings));
500
- }
501
- // ========== 日志管理 ==========
502
- /**
503
- * 获取错误日志尾部
504
- */
505
- async getErrorLogs(tail = 100) {
506
- const params = new HttpParams().set("tail", tail.toString());
507
- return await firstValueFrom(this.http.get(`${this.baseUrl}/logs/error`, params));
508
- }
509
- /**
510
- * 获取访问日志尾部
511
- */
512
- async getAccessLogs(tail = 100) {
513
- const params = new HttpParams().set("tail", tail.toString());
514
- return await firstValueFrom(this.http.get(`${this.baseUrl}/logs/access`, params));
515
- }
516
- /**
517
- * 获取日志文件路径信息
518
- */
519
- async getLogsInfo() {
520
- return await firstValueFrom(this.http.get(`${this.baseUrl}/logs/info`));
521
- }
522
- };
523
- NginxService = __decorate([
524
- Injectable({
525
- providedIn: "root"
526
- })
527
- ], NginxService);
528
-
529
- // src/app/utils/monaco-languages.ts
530
- function registerNginxLanguage() {
531
- const w = window;
532
- if (!w.monaco)
533
- return;
534
- const monaco = w.monaco;
535
- if (monaco.languages.getLanguages().some((l) => l.id === "nginx"))
536
- return;
537
- monaco.languages.register({ id: "nginx", extensions: [".conf"], aliases: ["Nginx"] });
538
- monaco.languages.setMonarchTokensProvider("nginx", {
539
- tokenizer: {
540
- root: [
541
- // Comments
542
- [/#.*$/, "comment"],
543
- // Section blocks
544
- [/^\s*(http|server|events|stream|upstream|location|map|geo|types|if|in|limit_except|split_clients|match)\b/, "keyword.control"],
545
- // Directive keywords (common nginx directives)
546
- [
547
- /\b(root|index|try_files|alias|return|rewrite|proxy_pass|fastcgi_pass|scgi_pass|uwsgi_pass|include|set|add_header|expires|proxy_set_header|fastcgi_param|listen|server_name|error_page|default_server|upstream|zone|least_conn|ip_hash|keepalive|keepalive_requests|keepalive_timeout|worker_processes|worker_connections|use|multi_accept|accept_mutex|sendfile|tcp_nopush|tcp_nodelay|reset_timedout|resolver|resolver_timeout|client_max_body_size|client_body_buffer|gzip|gunzip|gzip_types|gzip_comp_level|gzip_vary|open_file_cache|open_file_cache_valid|open_file_cache_min_uses|log_format|access_log|error_log|pid|user|group|daemon|master_process|worker_rlimit_nofile|worker_rlimit_core|worker_shutdown|load_module|types|default_type|server_tokens|aio|directio|sendfile_max_chunk|proxy_buffering|proxy_buffer_size|proxy_buffers|proxy_busy_buffers_size|proxy_temp_path|proxy_temp_file_write_size|fastcgi_buffer_size|fastcgi_buffers|fastcgi_busy_buffers_size|fastcgi_temp_path|fastcgi_temp_file_write_size|fastcgi_cache|fastcgi_cache_key|fastcgi_cache_path|fastcgi_cache_valid|fastcgi_cache_use_stale|fastcgi_no_cache|fastcgi_cache_bypass|fastcgi_ignore_headers|fastcgi_intercept_errors|fastcgi_next_upstream|fastcgi_next_upstream_tries|fastcgi_connect_timeout|fastcgi_send_timeout|fastcgi_read_timeout|proxy_connect_timeout|proxy_send_timeout|proxy_read_timeout|proxy_ignore_client_abort|proxy_hide_header|proxy_pass_header|proxy_redirect|proxy_cookie_domain|proxy_cookie_path|proxy_set_header|proxy_http_version|proxy_force_ranges|proxy_cache|proxy_cache_key|proxy_cache_path|proxy_cache_valid|proxy_cache_use_stale|proxy_cache_background_update|proxy_cache_lock|proxy_cache_lock_timeout|proxy_no_cache|proxy_cache_bypass|proxy_ignore_headers|proxy_intercept_errors|proxy_next_upstream|proxy_next_upstream_tries|proxy_next_upstream_timeout|proxy_store|proxy_store_access|auth_basic|auth_basic_user_file|satisfy|allow|deny|limit_req|limit_req_zone|limit_req_status|limit_conn|limit_conn_zone|limit_conn_status|return|rewrite|break|set|if|set_by_lua|access_by_lua|content_by_lua|header_filter_by_lua|body_filter_by_lua|log_by_lua|rewrite_by_lua|init_by_lua|init_worker_by_lua|ssl_certificate|ssl_certificate_key|ssl_session_cache|ssl_session_timeout|ssl_protocols|ssl_ciphers|ssl_prefer_server_ciphers|ssl_verify_client|ssl_client_certificate|ssl_trusted_certificate|ssl_crl|real_ip_header|set_real_ip_from|geo|geoip_country|geoip_city|memcached_pass|memcached_connect_timeout|memcached_send_timeout|memcached_read_timeout|grpc_pass|grpc_connect_timeout|grpc_send_timeout|grpc_read_timeout|grpc_buffer_size|websocket|websocket_connect_timeout|websocket_send_timeout|websocket_read_timeout|websocket_ping_interval|websocket_ping_timeout|concat|concat_types|secure_link|secure_link_md5|add_before_body|add_after_body|addition_types|image_filter|xslt_stylesheet|xslt_types|random_index|geo|map|perl|perl_modules|perl_require|perl_handler|slice|mp4|mp4_buffer_size|mp4_max_buffer_size|flv|ogmp|status|status_format|check|check_http_send|check_http_expect_alive|check_shm_size|check_status|upstream_conf|js_set|js_content|js_header_filter_by_lua|js_body_filter_by_lua|js_log_by_lua|js_access_by_lua|js_content_by_lua|js_rewrite_by_lua|js_preread_by_lua|influxdb|set_|add_|proxy_|fastcgi_|uwsgi_|scgi_|grpc_|limit_|auth_|secure_|websocket_|ssl_|real_|memcached_|grpc_|proxy_next_upstream_tries|proxy_next_upstream_timeout)\b/,
548
- "keyword.directive"
549
- ],
550
- // Boolean/keyword values
551
- [/\b(on|off|yes|no|true|false|default|none|any|auto|inline|upstream|down|backup|weight|max_fails|fail_timeout)\b/, "keyword.value"],
552
- // Numbers (with size units)
553
- [/\b\d+[kKmMgG]?\b/, "number"],
554
- // Strings
555
- [/"[^"]*"/, "string"],
556
- [/'[^']*'/, "string"],
557
- // Variables
558
- [/\$[a-zA-Z_][a-zA-Z0-9_]*/, "variable"],
559
- // IP addresses and ports in configs
560
- [/\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(:\d+)?\b/, "number"],
561
- // Ports
562
- [/\b\d{1,5}\b/, "number"]
563
- ]
564
- }
565
- });
566
- monaco.editor.defineTheme("nginx-theme", {
567
- base: "vs",
568
- inherit: true,
569
- rules: [
570
- { token: "comment", foreground: "6A9955", fontStyle: "italic" },
571
- { token: "keyword.control", foreground: "0000FF", fontStyle: "bold" },
572
- { token: "keyword.directive", foreground: "795E26" },
573
- { token: "keyword.value", foreground: "0000FF" },
574
- { token: "string", foreground: "A31515" },
575
- { token: "number", foreground: "098658" },
576
- { token: "variable", foreground: "001188" }
577
- ]
578
- });
579
- }
580
-
581
- // src/app/pages/nginx/components/nginx-config-editor/nginx-config-editor.component.ts
582
- var NginxConfigEditorComponent = class NginxConfigEditorComponent2 {
583
- refreshToken = 0;
584
- editorShellRef;
585
- nginxService = inject(NginxService);
586
- message = inject(NzMessageService);
587
- config = signal(null);
588
- configFiles = signal([]);
589
- selectedFilePath = signal("");
590
- originalContent = signal("");
591
- editorContent = signal("");
592
- filesLoading = signal(false);
593
- loading = signal(true);
594
- saving = signal(false);
595
- validating = signal(false);
596
- validationResult = signal(null);
597
- editorHeight = signal(520);
598
- editorOptions = {
599
- language: "nginx"
600
- // automaticLayout: true,
601
- // renderSideBySide: false,
602
- // ignoreTrimWhitespace: false,
603
- // originalEditable: false,
604
- // enableSplitViewResizing: true,
605
- // lineNumbers: 'on',
606
- // minimap: { enabled: false },
607
- // scrollBeyondLastLine: false,
608
- };
609
- mainConfigPath = "";
610
- diffEditor = null;
611
- resizeObserver = null;
612
- async ngOnInit() {
613
- await this.loadConfig(true);
614
- await this.loadConfigFiles();
615
- }
616
- ngAfterViewInit() {
617
- this.setupEditorHeightObserver();
618
- this.updateEditorHeight();
619
- }
620
- ngOnChanges(changes) {
621
- const tokenChange = changes["refreshToken"];
622
- if (tokenChange && !tokenChange.firstChange) {
623
- void this.loadConfigFiles();
624
- }
625
- }
626
- ngOnDestroy() {
627
- this.resizeObserver?.disconnect();
628
- this.resizeObserver = null;
629
- }
630
- onCodeEditorInitialized(editorInstance) {
631
- registerNginxLanguage();
632
- if (this.isDiffEditor(editorInstance)) {
633
- this.diffEditor = editorInstance;
634
- }
635
- this.scheduleLayout();
636
- }
637
- onEditorContentChange(value) {
638
- this.editorContent.set(String(value ?? ""));
639
- }
640
- async loadConfig(forceMain = false) {
641
- this.loading.set(true);
642
- try {
643
- const targetPath = this.selectedFilePath();
644
- const useMain = forceMain || !targetPath || this.isSamePath(targetPath, this.mainConfigPath || targetPath);
645
- const res = useMain ? await this.nginxService.getConfig() : await this.nginxService.getConfigFile(targetPath);
646
- if (res.success && res.config) {
647
- const content = res.config.content || "";
648
- this.config.set(res.config);
649
- this.originalContent.set(content);
650
- this.editorContent.set(content);
651
- this.validationResult.set(null);
652
- this.selectedFilePath.set(res.config.mainConfigPath || targetPath);
653
- if (useMain || !this.mainConfigPath) {
654
- this.mainConfigPath = res.config.mainConfigPath;
655
- }
656
- this.mergeConfigFiles([res.config.mainConfigPath]);
657
- this.updateEditorHeight();
658
- this.scheduleLayout();
659
- } else {
660
- this.message.error(res.error || "\u52A0\u8F7D\u914D\u7F6E\u5931\u8D25");
661
- }
662
- } catch (err) {
663
- this.message.error("\u52A0\u8F7D\u914D\u7F6E\u5931\u8D25: " + err.message);
664
- } finally {
665
- this.loading.set(false);
666
- }
667
- }
668
- async saveConfig() {
669
- const targetPath = this.selectedFilePath() || this.config()?.mainConfigPath || "";
670
- if (!targetPath) {
671
- this.message.error("\u672A\u9009\u62E9\u914D\u7F6E\u6587\u4EF6");
672
- return;
673
- }
674
- this.saving.set(true);
675
- try {
676
- const latestContent = this.getCurrentEditorContent();
677
- const useMain = this.isSamePath(targetPath, this.mainConfigPath || targetPath);
678
- const res = useMain ? await this.nginxService.updateConfig(latestContent) : await this.nginxService.updateConfigFile(targetPath, latestContent);
679
- if (res.success) {
680
- this.message.success("\u4FDD\u5B58\u6210\u529F");
681
- await this.loadConfig();
682
- await this.loadConfigFiles();
683
- } else {
684
- this.message.error(res.error || "\u4FDD\u5B58\u5931\u8D25");
685
- }
686
- } catch (err) {
687
- this.message.error("\u4FDD\u5B58\u5931\u8D25: " + err.message);
688
- } finally {
689
- this.saving.set(false);
690
- }
691
- }
692
- async validateConfig() {
693
- this.validating.set(true);
694
- try {
695
- const res = await this.nginxService.validateConfig(this.getCurrentEditorContent());
696
- this.validationResult.set(res);
697
- if (res.valid) {
698
- this.message.success("\u914D\u7F6E\u9A8C\u8BC1\u901A\u8FC7");
699
- } else {
700
- this.message.error("\u914D\u7F6E\u9A8C\u8BC1\u5931\u8D25");
701
- }
702
- } catch (err) {
703
- this.message.error("\u9A8C\u8BC1\u5931\u8D25: " + err.message);
704
- } finally {
705
- this.validating.set(false);
706
- }
707
- }
708
- async loadConfigFiles() {
709
- this.filesLoading.set(true);
710
- try {
711
- const res = await this.nginxService.getConfigFiles();
712
- if (res.success) {
713
- const incoming = res.files || [];
714
- const merged = this.buildFileList(incoming);
715
- this.configFiles.set(merged);
716
- const current = this.selectedFilePath();
717
- if (current && merged.length && !merged.some((item) => this.isSamePath(item, current))) {
718
- this.selectedFilePath.set(merged[0]);
719
- await this.loadConfig();
720
- }
721
- } else {
722
- this.message.error(res.error || "\u52A0\u8F7D\u6587\u4EF6\u5217\u8868\u5931\u8D25");
723
- }
724
- } catch (err) {
725
- this.message.error("\u52A0\u8F7D\u6587\u4EF6\u5217\u8868\u5931\u8D25: " + err.message);
726
- } finally {
727
- this.filesLoading.set(false);
728
- }
729
- }
730
- async onSelectConfigFile(filePath) {
731
- const target = String(filePath || "").trim();
732
- if (!target || this.isSamePath(target, this.selectedFilePath())) {
733
- return;
734
- }
735
- this.selectedFilePath.set(target);
736
- await this.loadConfig();
737
- }
738
- getFileLabel(filePath) {
739
- const normalized = String(filePath || "").replace(/\\/g, "/");
740
- const parts = normalized.split("/");
741
- const fileName = parts[parts.length - 1] || filePath;
742
- return fileName;
743
- }
744
- hasUnsavedChanges() {
745
- return this.getCurrentEditorContent() !== this.originalContent();
746
- }
747
- restoreFromOriginal() {
748
- const original = this.originalContent();
749
- const modified = this.diffEditor?.getModel()?.modified;
750
- if (modified) {
751
- modified.setValue(original);
752
- }
753
- this.editorContent.set(original);
754
- this.validationResult.set(null);
755
- this.scheduleLayout();
756
- this.message.success("\u5DF2\u8FD8\u539F\u5230\u5DF2\u52A0\u8F7D\u7248\u672C");
757
- }
758
- getCurrentEditorContent() {
759
- const modified = this.diffEditor?.getModel()?.modified;
760
- if (modified) {
761
- return modified.getValue();
762
- }
763
- return this.editorContent();
764
- }
765
- mergeConfigFiles(paths) {
766
- const next = this.buildFileList([...this.configFiles(), ...paths]);
767
- this.configFiles.set(next);
768
- }
769
- buildFileList(paths) {
770
- const map = /* @__PURE__ */ new Map();
771
- for (const path of paths) {
772
- const key = this.normalizePath(path);
773
- if (!key) {
774
- continue;
775
- }
776
- if (!map.has(key)) {
777
- map.set(key, path);
778
- }
779
- }
780
- const list = Array.from(map.values());
781
- list.sort((a, b) => a.localeCompare(b));
782
- if (this.mainConfigPath) {
783
- const index = list.findIndex((item) => this.isSamePath(item, this.mainConfigPath));
784
- if (index > 0) {
785
- const [main] = list.splice(index, 1);
786
- list.unshift(main);
787
- }
788
- if (index < 0) {
789
- list.unshift(this.mainConfigPath);
790
- }
791
- }
792
- return list;
793
- }
794
- isSamePath(a, b) {
795
- return this.normalizePath(a) === this.normalizePath(b);
796
- }
797
- normalizePath(input) {
798
- return String(input || "").trim().replace(/\\/g, "/").toLowerCase();
799
- }
800
- scheduleLayout() {
801
- setTimeout(() => {
802
- try {
803
- this.updateEditorHeight();
804
- this.syncDiffEditorHeight();
805
- this.diffEditor?.layout();
806
- } catch {
807
- }
808
- }, 0);
809
- }
810
- setupEditorHeightObserver() {
811
- const host = this.editorShellRef?.nativeElement;
812
- if (!host || typeof ResizeObserver === "undefined") {
813
- return;
814
- }
815
- this.resizeObserver?.disconnect();
816
- this.resizeObserver = new ResizeObserver(() => {
817
- this.updateEditorHeight();
818
- setTimeout(() => {
819
- this.syncDiffEditorHeight();
820
- this.diffEditor?.layout();
821
- }, 0);
822
- });
823
- this.resizeObserver.observe(host);
824
- }
825
- updateEditorHeight() {
826
- const host = this.editorShellRef?.nativeElement;
827
- if (!host) {
828
- this.editorHeight.set(520);
829
- return;
830
- }
831
- const measured = host.clientHeight > 0 ? host.clientHeight : 0;
832
- const next = Math.max(520, measured);
833
- if (this.editorHeight() !== next) {
834
- this.editorHeight.set(next);
835
- }
836
- }
837
- syncDiffEditorHeight() {
838
- const container = this.diffEditor?.getContainerDomNode();
839
- if (!container) {
840
- return;
841
- }
842
- const heightPx = `${this.editorHeight()}px`;
843
- container.style.height = heightPx;
844
- const diffDom = container.querySelector(".monaco-diff-editor");
845
- if (!diffDom) {
846
- return;
847
- }
848
- diffDom.style.height = heightPx;
849
- diffDom.style.minHeight = "520px";
850
- }
851
- isDiffEditor(editorInstance) {
852
- return typeof editorInstance.getOriginalEditor === "function";
853
- }
854
- static propDecorators = {
855
- refreshToken: [{ type: Input }],
856
- editorShellRef: [{ type: ViewChild, args: ["editorShell", { static: true }] }]
857
- };
858
- };
859
- NginxConfigEditorComponent = __decorate([
860
- Component({
861
- selector: "app-nginx-config-editor",
862
- standalone: true,
863
- imports: [
864
- CommonModule,
865
- FormsModule,
866
- NzButtonModule,
867
- NzCodeEditorModule,
868
- NzIconModule,
869
- NzPopconfirmModule,
870
- NzSelectModule,
871
- NzToolTipModule
872
- ],
873
- template: `
874
- <div class="config-editor">
875
- <div class="editor-toolbar">
876
- <div class="toolbar-left">
877
- <div class="file-info">
878
- <nz-icon nzType="file-text" nzTheme="outline"></nz-icon>
879
- <span class="file-path">{{ config()?.mainConfigPath || '\u672A\u52A0\u8F7D' }}</span>
880
- @if (config()?.isWritable === false) {
881
- <nz-icon
882
- nzType="lock"
883
- nzTheme="outline"
884
- nz-tooltip
885
- nzTooltipTitle="\u914D\u7F6E\u6587\u4EF6\u53EA\u8BFB\uFF0C\u53EF\u80FD\u9700\u8981\u7BA1\u7406\u5458\u6743\u9650"
886
- ></nz-icon>
887
- }
888
- </div>
889
-
890
- <nz-select
891
- class="file-selector"
892
- [ngModel]="selectedFilePath()"
893
- (ngModelChange)="onSelectConfigFile($event)"
894
- nzPlaceHolder="\u9009\u62E9\u914D\u7F6E\u6587\u4EF6"
895
- [nzDisabled]="loading() || !configFiles().length"
896
- >
897
- @for (file of configFiles(); track file) {
898
- <nz-option [nzValue]="file" [nzLabel]="getFileLabel(file)"></nz-option>
899
- }
900
- </nz-select>
901
- <button nz-button nzType="default" (click)="loadConfigFiles()" [nzLoading]="filesLoading()">
902
- <nz-icon nzType="reload" nzTheme="outline"></nz-icon>
903
- \u5237\u65B0\u6587\u4EF6\u5217\u8868
904
- </button>
905
- </div>
906
-
907
- <div class="editor-actions">
908
- <button nz-button (click)="loadConfig()" [nzLoading]="loading()">
909
- <nz-icon nzType="reload" nzTheme="outline"></nz-icon>
910
- \u5237\u65B0
911
- </button>
912
- <button
913
- nz-button
914
- [disabled]="!hasUnsavedChanges()"
915
- nzType="default"
916
- nz-popconfirm
917
- nzPopconfirmTitle="\u786E\u8BA4\u8FD8\u539F\u4E3A\u5F53\u524D\u5DF2\u52A0\u8F7D\u7248\u672C\uFF1F"
918
- nzPopconfirmPlacement="top"
919
- (nzOnConfirm)="restoreFromOriginal()"
920
- >
921
- <nz-icon nzType="rollback" nzTheme="outline"></nz-icon>
922
- \u8FD8\u539F
923
- </button>
924
- <button nz-button (click)="validateConfig()" [nzLoading]="validating()">
925
- <nz-icon nzType="check-circle" nzTheme="outline"></nz-icon>
926
- \u9A8C\u8BC1
927
- </button>
928
- <button
929
- nz-button
930
- nzType="primary"
931
- (click)="saveConfig()"
932
- [nzLoading]="saving()"
933
- [disabled]="!config()?.isWritable"
934
- >
935
- <nz-icon nzType="save" nzTheme="outline"></nz-icon>
936
- \u4FDD\u5B58
937
- </button>
938
- </div>
939
- </div>
940
-
941
- <div class="editor-container" #editorShell>
942
- <nz-code-editor
943
- class="code-editor"
944
- [style.height.px]="editorHeight()"
945
- nzEditorMode="diff"
946
- [ngModel]="editorContent()"
947
- (ngModelChange)="onEditorContentChange($event)"
948
- [nzOriginalText]="originalContent()"
949
- [nzEditorOption]="editorOptions"
950
- [nzLoading]="loading()"
951
- (nzEditorInitialized)="onCodeEditorInitialized($event)"
952
- ></nz-code-editor>
953
- </div>
954
-
955
- @if (validationResult()) {
956
- <div class="validation-result" [class.error]="!validationResult()?.valid">
957
- @if (validationResult()?.valid) {
958
- <nz-icon nzType="check-circle" nzTheme="outline" class="success-icon"></nz-icon>
959
- <span>\u914D\u7F6E\u9A8C\u8BC1\u901A\u8FC7</span>
960
- } @else {
961
- <nz-icon nzType="close-circle" nzTheme="outline" class="error-icon"></nz-icon>
962
- <span>\u914D\u7F6E\u9A8C\u8BC1\u5931\u8D25</span>
963
- }
964
-
965
- @if (validationResult()?.errors?.length) {
966
- <div class="error-list">
967
- @for (error of validationResult()?.errors; track $index) {
968
- <div class="error-item">{{ error }}</div>
969
- }
970
- </div>
971
- }
972
-
973
- @if (validationResult()?.warnings?.length) {
974
- <div class="warning-list">
975
- @for (warning of validationResult()?.warnings; track $index) {
976
- <div class="warning-item">{{ warning }}</div>
977
- }
978
- </div>
979
- }
980
- </div>
981
- }
982
- </div>
983
- `,
984
- styles: [nginx_config_editor_component_default]
985
- })
986
- ], NginxConfigEditorComponent);
987
-
988
- // angular:jit:style:inline:src\app\pages\nginx\components\nginx-tabs\nginx-secondary-logs-tab\nginx-secondary-logs-tab.component.ts;CiAgICA6aG9zdCB7CiAgICAgIGRpc3BsYXk6IGJsb2NrOwogICAgfQoKICAgIC5wYW5lbC1oZWFkZXItcm93IHsKICAgICAgZGlzcGxheTogZmxleDsKICAgICAganVzdGlmeS1jb250ZW50OiBzcGFjZS1iZXR3ZWVuOwogICAgICBhbGlnbi1pdGVtczogY2VudGVyOwogICAgICBnYXA6IDhweDsKICAgICAgbWFyZ2luLWJvdHRvbTogMTJweDsKCiAgICAgICYuY29tcGFjdCB7CiAgICAgICAgbWFyZ2luLWJvdHRvbTogMTBweDsKICAgICAgfQogICAgfQoKICAgIC5sb2ctdGFicyB7CiAgICAgIGRpc3BsYXk6IGZsZXg7CiAgICAgIGdhcDogMDsKICAgIH0KCiAgICAubG9nLXRhYiB7CiAgICAgIGJhY2tncm91bmQ6IHRyYW5zcGFyZW50OwogICAgICBjb2xvcjogdmFyKC0tdGV4dC0zKTsKICAgICAgZm9udC1zaXplOiB2YXIoLS1uZ2lueC1mb250LXNpemUtc20sIDEycHgpOwogICAgICBmb250LXdlaWdodDogNTAwOwogICAgICBwYWRkaW5nOiA2cHggMTJweDsKICAgICAgY3Vyc29yOiBwb2ludGVyOwogICAgICB0cmFuc2l0aW9uOiBhbGwgMTIwbXMgZWFzZTsKICAgICAgYm9yZGVyOiBub25lOwogICAgICBib3JkZXItYm90dG9tOiAycHggc29saWQgdHJhbnNwYXJlbnQ7CiAgICAgIG1hcmdpbi1ib3R0b206IC0xcHg7CgogICAgICAmOmhvdmVyIHsKICAgICAgICBjb2xvcjogdmFyKC0tdGV4dC0yKTsKICAgICAgfQoKICAgICAgJi5hY3RpdmUgewogICAgICAgIGNvbG9yOiB2YXIoLS1ibHVlKTsKICAgICAgICBib3JkZXItYm90dG9tLWNvbG9yOiB2YXIoLS1ibHVlKTsKICAgICAgICBmb250LXdlaWdodDogNjAwOwogICAgICB9CiAgICB9CgogICAgLmxvZy1hY3Rpb25zIHsKICAgICAgZGlzcGxheTogZmxleDsKICAgICAgZ2FwOiA0cHg7CiAgICB9CgogICAgLmxvZy1hY3Rpb24tYnRuIHsKICAgICAgbnotaWNvbiB7CiAgICAgICAgZm9udC1zaXplOiAxMnB4OwogICAgICB9CiAgICB9CiAg
989
- var nginx_secondary_logs_tab_component_default = "/* angular:styles/component:less;b3096ebdccdca035783796d3b68baf57245add19ad24976e1a6210c236808f2e;D:\\ng-manager\\webapp\\src\\app\\pages\\nginx\\components\\nginx-tabs\\nginx-secondary-logs-tab\\nginx-secondary-logs-tab.component.ts */\n:host {\n display: block;\n}\n.panel-header-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n gap: 8px;\n margin-bottom: 12px;\n}\n.panel-header-row.compact {\n margin-bottom: 10px;\n}\n.log-tabs {\n display: flex;\n gap: 0;\n}\n.log-tab {\n background: transparent;\n color: var(--text-3);\n font-size: var(--nginx-font-size-sm, 12px);\n font-weight: 500;\n padding: 6px 12px;\n cursor: pointer;\n transition: all 120ms ease;\n border: none;\n border-bottom: 2px solid transparent;\n margin-bottom: -1px;\n}\n.log-tab:hover {\n color: var(--text-2);\n}\n.log-tab.active {\n color: var(--blue);\n border-bottom-color: var(--blue);\n font-weight: 600;\n}\n.log-actions {\n display: flex;\n gap: 4px;\n}\n.log-action-btn nz-icon {\n font-size: 12px;\n}\n/*# sourceMappingURL=nginx-secondary-logs-tab.component.css.map */\n";
990
-
991
- // angular:jit:style:inline:src\app\pages\nginx\components\nginx-log-viewer\nginx-log-viewer.component.ts;CiAgICA6aG9zdCB7CiAgICAgIGRpc3BsYXk6IGJsb2NrOwogICAgfQoKICAgIC5sb2ctcGFuZWwgewogICAgICBiYWNrZ3JvdW5kOiAjMWIyMzMyOwogICAgICBib3JkZXItcmFkaXVzOiA2cHg7CiAgICAgIG92ZXJmbG93OiBoaWRkZW47CiAgICB9CgogICAgLmxvZy1oZWFkZXIgewogICAgICBkaXNwbGF5OiBmbGV4OwogICAgICBhbGlnbi1pdGVtczogY2VudGVyOwogICAgICBqdXN0aWZ5LWNvbnRlbnQ6IHNwYWNlLWJldHdlZW47CiAgICAgIHBhZGRpbmc6IDhweCAxNHB4OwogICAgICBiYWNrZ3JvdW5kOiByZ2JhKDAsIDAsIDAsIDAuMTUpOwogICAgfQoKICAgIC5sb2ctaGVhZGVyLXRpdGxlIHsKICAgICAgZm9udC1zaXplOiB2YXIoLS1uZ2lueC1mb250LXNpemUtc20sIDEycHgpOwogICAgICBmb250LXdlaWdodDogNzAwOwogICAgICB0ZXh0LXRyYW5zZm9ybTogdXBwZXJjYXNlOwogICAgICBsZXR0ZXItc3BhY2luZzogMC41cHg7CiAgICAgIGNvbG9yOiByZ2JhKDI1NSwgMjU1LCAyNTUsIDAuNCk7CiAgICB9CgogICAgLmxvZy1oZWFkZXItc3RhdHVzIHsKICAgICAgZm9udC1zaXplOiB2YXIoLS1uZ2lueC1mb250LXNpemUtc20sIDEycHgpOwogICAgICBjb2xvcjogcmdiYSgyNTUsIDI1NSwgMjU1LCAwLjUpOwogICAgICBmb250LWZhbWlseTogdmFyKC0tbmdpbngtZm9udC1mYW1pbHktbW9ubywgdWktbW9ub3NwYWNlLCBTRk1vbm8tUmVndWxhciwgTWVubG8sIE1vbmFjbywgQ29uc29sYXMsICdMaWJlcmF0aW9uIE1vbm8nLCBtb25vc3BhY2UpOwogICAgICBmb250LXdlaWdodDogNjAwOwogICAgICBkaXNwbGF5OiBmbGV4OwogICAgICBhbGlnbi1pdGVtczogY2VudGVyOwogICAgICBnYXA6IDVweDsKCiAgICAgICYucmVhbHRpbWUgewogICAgICAgIGNvbG9yOiAjM2RkNjhjOwogICAgICB9CgogICAgICAmLnN0YXR1cy1vayB7CiAgICAgICAgY29sb3I6ICMzZGQ2OGM7CiAgICAgIH0KCiAgICAgICYuc3RhdHVzLXdhcm4gewogICAgICAgIGNvbG9yOiAjZjdiYTFlOwogICAgICB9CgogICAgICAmLnN0YXR1cy1lcnJvciB7CiAgICAgICAgY29sb3I6ICNlZjUzNTA7CiAgICAgIH0KICAgIH0KCiAgICAucmVhbHRpbWUtZG90IHsKICAgICAgd2lkdGg6IDZweDsKICAgICAgaGVpZ2h0OiA2cHg7CiAgICAgIGJvcmRlci1yYWRpdXM6IDUwJTsKICAgICAgYmFja2dyb3VuZDogIzNkZDY4YzsKICAgICAgYW5pbWF0aW9uOiBwdWxzZSAycyBlYXNlLWluLW91dCBpbmZpbml0ZTsKICAgIH0KCiAgICBAa2V5ZnJhbWVzIHB1bHNlIHsKICAgICAgMCUsCiAgICAgIDEwMCUgewogICAgICAgIG9wYWNpdHk6IDE7CiAgICAgIH0KCiAgICAgIDUwJSB7CiAgICAgICAgb3BhY2l0eTogMC40OwogICAgICB9CiAgICB9CgogICAgLmxvZy1ib2R5IHsKICAgICAgcGFkZGluZzogMTBweCAxNHB4OwogICAgICBtYXgtaGVpZ2h0OiAxODBweDsKICAgICAgb3ZlcmZsb3cteTogYXV0bzsKICAgICAgZm9udC1mYW1pbHk6IHZhcigtLW5naW54LWZvbnQtZmFtaWx5LW1vbm8sIHVpLW1vbm9zcGFjZSwgU0ZNb25vLVJlZ3VsYXIsIE1lbmxvLCBNb25hY28sIENvbnNvbGFzLCAnTGliZXJhdGlvbiBNb25vJywgbW9ub3NwYWNlKTsKICAgICAgZm9udC1zaXplOiB2YXIoLS1uZ2lueC1mb250LXNpemUtc20sIDEycHgpOwogICAgICBsaW5lLWhlaWdodDogMS44OwoKICAgICAgJjo6LXdlYmtpdC1zY3JvbGxiYXIgewogICAgICAgIHdpZHRoOiA2cHg7CiAgICAgIH0KCiAgICAgICY6Oi13ZWJraXQtc2Nyb2xsYmFyLXRyYWNrIHsKICAgICAgICBiYWNrZ3JvdW5kOiB0cmFuc3BhcmVudDsKICAgICAgfQoKICAgICAgJjo6LXdlYmtpdC1zY3JvbGxiYXItdGh1bWIgewogICAgICAgIGJhY2tncm91bmQ6ICMzMzM5NDQ7CiAgICAgICAgYm9yZGVyLXJhZGl1czogM3B4OwogICAgICB9CiAgICB9CgogICAgLmxvZy1lbXB0eSB7CiAgICAgIHBhZGRpbmc6IDE2cHggMDsKICAgICAgZm9udC1zaXplOiB2YXIoLS1uZ2lueC1mb250LXNpemUtc20sIDEycHgpOwogICAgICBjb2xvcjogcmdiYSgyNTUsIDI1NSwgMjU1LCAwLjM1KTsKICAgICAgdGV4dC1hbGlnbjogY2VudGVyOwogICAgICBmb250LWZhbWlseTogdmFyKC0tbmdpbngtZm9udC1mYW1pbHktbW9ubywgdWktbW9ub3NwYWNlLCBTRk1vbm8tUmVndWxhciwgTWVubG8sIE1vbmFjbywgQ29uc29sYXMsICdMaWJlcmF0aW9uIE1vbm8nLCBtb25vc3BhY2UpOwogICAgfQoKICAgIC5sb2ctbGluZSB7CiAgICAgIGRpc3BsYXk6IGZsZXg7CiAgICAgIGFsaWduLWl0ZW1zOiBmbGV4LXN0YXJ0OwogICAgICBnYXA6IDEwcHg7CiAgICAgIGN1cnNvcjogZGVmYXVsdDsKICAgIH0KCiAgICAubG9nLXRpbWUgewogICAgICBjb2xvcjogcmdiYSgyNTUsIDI1NSwgMjU1LCAwLjUpOwogICAgICBmbGV4LXNocmluazogMDsKICAgICAgbWluLXdpZHRoOiA0NnB4OwogICAgfQoKICAgIC5sb2ctbGV2ZWwgewogICAgICBmb250LXdlaWdodDogNzAwOwogICAgICBmbGV4LXNocmluazogMDsKICAgICAgd2lkdGg6IDMycHg7CgogICAgICAmLmluZm8gewogICAgICAgIGNvbG9yOiAjMTY1ZGZmOwogICAgICB9CgogICAgICAmLndhcm4gewogICAgICAgIGNvbG9yOiAjZmY3ZDAwOwogICAgICB9CgogICAgICAmLmVycm9yIHsKICAgICAgICBjb2xvcjogI2VmNTM1MDsKICAgICAgfQoKICAgICAgJi5vayB7CiAgICAgICAgY29sb3I6ICMzZGQ2OGM7CiAgICAgIH0KICAgIH0KCiAgICAubG9nLW1zZyB7CiAgICAgIGNvbG9yOiByZ2JhKDI1NSwgMjU1LCAyNTUsIDAuNTUpOwogICAgICBmbGV4OiAxOwogICAgICB3b3JkLWJyZWFrOiBicmVhay1hbGw7CiAgICAgIHdoaXRlLXNwYWNlOiBwcmUtd3JhcDsKICAgIH0KICA=
992
- var nginx_log_viewer_component_default = '/* angular:styles/component:less;2c6128abba9250a43aed06c03b2f2b645b1342fd6ded6acf07230aa9e8a07c7a;D:\\ng-manager\\webapp\\src\\app\\pages\\nginx\\components\\nginx-log-viewer\\nginx-log-viewer.component.ts */\n:host {\n display: block;\n}\n.log-panel {\n background: #1b2332;\n border-radius: 6px;\n overflow: hidden;\n}\n.log-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 14px;\n background: rgba(0, 0, 0, 0.15);\n}\n.log-header-title {\n font-size: var(--nginx-font-size-sm, 12px);\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: rgba(255, 255, 255, 0.4);\n}\n.log-header-status {\n font-size: var(--nginx-font-size-sm, 12px);\n color: rgba(255, 255, 255, 0.5);\n font-family: var(--nginx-font-family-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace);\n font-weight: 600;\n display: flex;\n align-items: center;\n gap: 5px;\n}\n.log-header-status.realtime {\n color: #3dd68c;\n}\n.log-header-status.status-ok {\n color: #3dd68c;\n}\n.log-header-status.status-warn {\n color: #f7ba1e;\n}\n.log-header-status.status-error {\n color: #ef5350;\n}\n.realtime-dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: #3dd68c;\n animation: pulse 2s ease-in-out infinite;\n}\n@keyframes pulse {\n 0%, 100% {\n opacity: 1;\n }\n 50% {\n opacity: 0.4;\n }\n}\n.log-body {\n padding: 10px 14px;\n max-height: 180px;\n overflow-y: auto;\n font-family: var(--nginx-font-family-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace);\n font-size: var(--nginx-font-size-sm, 12px);\n line-height: 1.8;\n}\n.log-body::-webkit-scrollbar {\n width: 6px;\n}\n.log-body::-webkit-scrollbar-track {\n background: transparent;\n}\n.log-body::-webkit-scrollbar-thumb {\n background: #333944;\n border-radius: 3px;\n}\n.log-empty {\n padding: 16px 0;\n font-size: var(--nginx-font-size-sm, 12px);\n color: rgba(255, 255, 255, 0.35);\n text-align: center;\n font-family: var(--nginx-font-family-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace);\n}\n.log-line {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n cursor: default;\n}\n.log-time {\n color: rgba(255, 255, 255, 0.5);\n flex-shrink: 0;\n min-width: 46px;\n}\n.log-level {\n font-weight: 700;\n flex-shrink: 0;\n width: 32px;\n}\n.log-level.info {\n color: #165dff;\n}\n.log-level.warn {\n color: #ff7d00;\n}\n.log-level.error {\n color: #ef5350;\n}\n.log-level.ok {\n color: #3dd68c;\n}\n.log-msg {\n color: rgba(255, 255, 255, 0.55);\n flex: 1;\n word-break: break-all;\n white-space: pre-wrap;\n}\n/*# sourceMappingURL=nginx-log-viewer.component.css.map */\n';
993
-
994
- // src/app/pages/nginx/components/nginx-log-viewer/nginx-log-viewer.component.ts
995
- var NginxLogViewerComponent = class NginxLogViewerComponent2 {
996
- title = "error.log";
997
- status = "";
998
- statusTone = "default";
999
- showRealtime = false;
1000
- maxHeight = 0;
1001
- logs = [];
1002
- logBody;
1003
- scrollToBottom() {
1004
- if (this.logBody) {
1005
- this.logBody.nativeElement.scrollTop = this.logBody.nativeElement.scrollHeight;
1006
- }
1007
- }
1008
- getLevelClass(level) {
1009
- return level;
1010
- }
1011
- getLevelLabel(level) {
1012
- const map = {
1013
- info: "INFO",
1014
- warn: "WARN",
1015
- error: "ERR",
1016
- ok: "OK"
1017
- };
1018
- return map[level] ?? level.toUpperCase();
1019
- }
1020
- static propDecorators = {
1021
- title: [{ type: Input }],
1022
- status: [{ type: Input }],
1023
- statusTone: [{ type: Input }],
1024
- showRealtime: [{ type: Input }],
1025
- maxHeight: [{ type: Input }],
1026
- logs: [{ type: Input }],
1027
- logBody: [{ type: ViewChild, args: ["logBody"] }]
1028
- };
1029
- };
1030
- NginxLogViewerComponent = __decorate([
1031
- Component({
1032
- selector: "app-nginx-log-viewer",
1033
- standalone: true,
1034
- imports: [CommonModule, NzIconModule],
1035
- template: `
1036
- <div class="log-panel">
1037
- <div class="log-header">
1038
- <span class="log-header-title">{{ title }}</span>
1039
- <span
1040
- class="log-header-status"
1041
- [class.realtime]="showRealtime"
1042
- [class.status-ok]="statusTone === 'ok'"
1043
- [class.status-warn]="statusTone === 'warn'"
1044
- [class.status-error]="statusTone === 'error'"
1045
- >
1046
- @if (showRealtime) {
1047
- <span class="realtime-dot"></span>
1048
- \u5B9E\u65F6
1049
- } @else {
1050
- {{ status }}
1051
- }
1052
- </span>
1053
- </div>
1054
- <div class="log-body" [style.maxHeight.px]="maxHeight" #logBody>
1055
- @if (logs.length === 0) {
1056
- <div class="log-empty">\u6682\u65E0\u65E5\u5FD7</div>
1057
- }
1058
- @for (log of logs; track $index) {
1059
- <div class="log-line">
1060
- <span class="log-time">{{ log.time }}</span>
1061
- <span class="log-level" [class]="getLevelClass(log.level)">
1062
- {{ getLevelLabel(log.level) }}
1063
- </span>
1064
- <span class="log-msg">{{ log.msg }}</span>
1065
- </div>
1066
- }
1067
- </div>
1068
- </div>
1069
- `,
1070
- styles: [nginx_log_viewer_component_default]
1071
- })
1072
- ], NginxLogViewerComponent);
1073
-
1074
- // src/app/pages/nginx/components/nginx-tabs/nginx-secondary-logs-tab/nginx-secondary-logs-tab.component.ts
1075
- var NginxSecondaryLogsTabComponent = class NginxSecondaryLogsTabComponent2 {
1076
- nginxService = inject(NginxService);
1077
- wsClient = inject(WsClientService);
1078
- message = inject(NzMessageService);
1079
- wsSub;
1080
- logViewerComponent;
1081
- activeLogTab = signal("error");
1082
- isConnected = signal(false);
1083
- loading = signal(false);
1084
- // 分别存储 error 和 access 日志
1085
- errorLogs = signal([]);
1086
- accessLogs = signal([]);
1087
- // 当前显示的日志
1088
- currentLogs = signal([]);
1089
- // 最大日志行数限制
1090
- maxLogLines = 500;
1091
- statusText = signal("\u672A\u8FDE\u63A5");
1092
- statusTone = signal("default");
1093
- ngOnInit() {
1094
- this.setupWsSubscription();
1095
- this.loadInitialLogs();
1096
- }
1097
- ngOnDestroy() {
1098
- this.unsubscribeWs();
1099
- this.wsSub?.unsubscribe();
1100
- }
1101
- switchLogTab(tab) {
1102
- this.activeLogTab.set(tab);
1103
- this.updateCurrentLogs();
1104
- if (this.isConnected()) {
1105
- this.unsubscribeWs();
1106
- this.subscribeWs(tab);
1107
- }
1108
- }
1109
- /**
1110
- * 滚动日志视图到底部
1111
- */
1112
- scrollToBottom() {
1113
- this.logViewerComponent?.scrollToBottom();
1114
- }
1115
- async refreshLogs() {
1116
- await this.loadInitialLogs();
1117
- this.message.success("\u65E5\u5FD7\u5DF2\u5237\u65B0");
1118
- }
1119
- clearLogs() {
1120
- const tab = this.activeLogTab();
1121
- if (tab === "error") {
1122
- this.errorLogs.set([]);
1123
- } else {
1124
- this.accessLogs.set([]);
1125
- }
1126
- this.updateCurrentLogs();
1127
- }
1128
- setupWsSubscription() {
1129
- this.wsClient.stateChanges().subscribe((state) => {
1130
- const connected = state === "open";
1131
- this.isConnected.set(connected);
1132
- this.statusText.set(connected ? "\u5DF2\u8FDE\u63A5" : state === "connecting" ? "\u8FDE\u63A5\u4E2D..." : "\u672A\u8FDE\u63A5");
1133
- this.statusTone.set(connected ? "ok" : "default");
1134
- if (connected) {
1135
- this.subscribeWs(this.activeLogTab());
1136
- }
1137
- });
1138
- this.wsSub = this.wsClient.messages().pipe(filter((msg) => msg.op === "nginx.log.append" || msg.op === "nginx.log.tail")).subscribe((msg) => {
1139
- if (msg.op === "nginx.log.tail") {
1140
- this.handleLogTail(msg);
1141
- setTimeout(() => {
1142
- this.scrollToBottom();
1143
- }, 100);
1144
- } else if (msg.op === "nginx.log.append") {
1145
- this.handleLogAppend(msg);
1146
- }
1147
- });
1148
- this.wsClient.connect();
1149
- }
1150
- subscribeWs(logType) {
1151
- this.wsClient.send({
1152
- op: "sub",
1153
- topic: "nginx",
1154
- logType,
1155
- tail: 50
1156
- });
1157
- }
1158
- unsubscribeWs() {
1159
- this.wsClient.send({
1160
- op: "unsub",
1161
- topic: "nginx"
1162
- });
1163
- }
1164
- handleLogTail(msg) {
1165
- const entries = msg.lines.map((line) => this.parseLogLine(line));
1166
- if (msg.logType === "error") {
1167
- this.errorLogs.set(entries);
1168
- } else {
1169
- this.accessLogs.set(entries);
1170
- }
1171
- this.updateCurrentLogs();
1172
- }
1173
- handleLogAppend(msg) {
1174
- const entry = this.parseLogLine(msg.line);
1175
- const isCurrentTab = msg.logType === this.activeLogTab();
1176
- if (msg.logType === "error") {
1177
- this.errorLogs.update((logs) => {
1178
- const updated = [...logs, entry];
1179
- return updated.length > this.maxLogLines ? updated.slice(-this.maxLogLines) : updated;
1180
- });
1181
- } else {
1182
- this.accessLogs.update((logs) => {
1183
- const updated = [...logs, entry];
1184
- return updated.length > this.maxLogLines ? updated.slice(-this.maxLogLines) : updated;
1185
- });
1186
- }
1187
- if (isCurrentTab) {
1188
- this.updateCurrentLogs();
1189
- }
1190
- }
1191
- updateCurrentLogs() {
1192
- const tab = this.activeLogTab();
1193
- const logs = tab === "error" ? this.errorLogs() : this.accessLogs();
1194
- this.currentLogs.set(logs);
1195
- }
1196
- async loadInitialLogs() {
1197
- this.loading.set(true);
1198
- try {
1199
- const [errorRes, accessRes] = await Promise.all([
1200
- this.nginxService.getErrorLogs(100),
1201
- this.nginxService.getAccessLogs(100)
1202
- ]);
1203
- if (errorRes.success && errorRes.lines) {
1204
- this.errorLogs.set(errorRes.lines.map((line) => this.parseLogLine(line)));
1205
- }
1206
- if (accessRes.success && accessRes.lines) {
1207
- this.accessLogs.set(accessRes.lines.map((line) => this.parseLogLine(line)));
1208
- }
1209
- this.updateCurrentLogs();
1210
- } catch (err) {
1211
- this.message.error("\u52A0\u8F7D\u65E5\u5FD7\u5931\u8D25: " + err.message);
1212
- } finally {
1213
- this.loading.set(false);
1214
- }
1215
- }
1216
- parseLogLine(line) {
1217
- const timeMatch = line.match(/^(\d{4}\/\d{2}\/\d{2}\s+\d{2}:\d{2}:\d{2})/);
1218
- const levelMatch = line.match(/\[(error|warn|notice|info|crit|alert|emerg)\]/i);
1219
- let time = "";
1220
- if (timeMatch) {
1221
- time = timeMatch[1].split(/\s+/)[1] || timeMatch[1];
1222
- } else {
1223
- const altTimeMatch = line.match(/(\d{2}:\d{2}:\d{2})/);
1224
- time = altTimeMatch ? altTimeMatch[1] : (/* @__PURE__ */ new Date()).toLocaleTimeString("zh-CN", { hour12: false });
1225
- }
1226
- let level = "info";
1227
- if (levelMatch) {
1228
- const lvl = levelMatch[1].toLowerCase();
1229
- if (lvl === "error" || lvl === "crit" || lvl === "alert" || lvl === "emerg") {
1230
- level = "error";
1231
- } else if (lvl === "warn") {
1232
- level = "warn";
1233
- }
1234
- }
1235
- return { time, level, msg: line };
1236
- }
1237
- static propDecorators = {
1238
- logViewerComponent: [{ type: ViewChild, args: [NginxLogViewerComponent] }]
1239
- };
1240
- };
1241
- NginxSecondaryLogsTabComponent = __decorate([
1242
- Component({
1243
- selector: "app-nginx-secondary-logs-tab",
1244
- standalone: true,
1245
- imports: [CommonModule, NzButtonModule, NzIconModule, NginxLogViewerComponent],
1246
- template: `
1247
- <div class="panel-header-row compact">
1248
- <div class="log-tabs">
1249
- <button class="log-tab" [class.active]="activeLogTab() === 'error'" (click)="switchLogTab('error')">
1250
- error.log
1251
- </button>
1252
- <button class="log-tab" [class.active]="activeLogTab() === 'access'" (click)="switchLogTab('access')">
1253
- access.log
1254
- </button>
1255
- </div>
1256
- <div class="log-actions">
1257
- <button nz-button nzType="default" class="log-action-btn" (click)="scrollToBottom()">
1258
- <nz-icon nzType="vertical-align-bottom" nzTheme="outline" />
1259
- </button>
1260
- <button nz-button nzType="default" (click)="refreshLogs()">
1261
- <nz-icon nzType="reload" nzTheme="outline" />
1262
- </button>
1263
- <button nz-button nzType="default" class="log-action-btn" (click)="clearLogs()">
1264
- <nz-icon nzType="delete" nzTheme="outline" />
1265
- </button>
1266
- </div>
1267
- </div>
1268
- <app-nginx-log-viewer
1269
- [title]="activeLogTab() === 'error' ? 'error.log' : 'access.log'"
1270
- [showRealtime]="isConnected()"
1271
- [status]="statusText()"
1272
- [statusTone]="statusTone()"
1273
- [logs]="currentLogs()"
1274
- [maxHeight]="400"
1275
- ></app-nginx-log-viewer>
1276
- `,
1277
- styles: [nginx_secondary_logs_tab_component_default]
1278
- })
1279
- ], NginxSecondaryLogsTabComponent);
1280
-
1281
- // angular:jit:style:inline:src\app\pages\nginx\components\nginx-tabs\nginx-secondary-perf-tab\nginx-secondary-perf-tab.component.ts;CiAgICA6aG9zdCB7CiAgICAgIGRpc3BsYXk6IGJsb2NrOwogICAgfQoKICAgIC5wYW5lbC1oZWFkZXItcm93IHsKICAgICAgZGlzcGxheTogZmxleDsKICAgICAganVzdGlmeS1jb250ZW50OiBzcGFjZS1iZXR3ZWVuOwogICAgICBhbGlnbi1pdGVtczogY2VudGVyOwogICAgICBnYXA6IDhweDsKICAgICAgbWFyZ2luLWJvdHRvbTogMTJweDsKICAgIH0KCiAgICAucGFuZWwtdGlwIHsKICAgICAgZm9udC1zaXplOiB2YXIoLS1uZ2lueC1mb250LXNpemUtc20sIDEycHgpOwogICAgICBjb2xvcjogdmFyKC0tdGV4dC0zKTsKICAgIH0KCiAgICAuc2V0dGluZy1yb3cgewogICAgICBkaXNwbGF5OiBmbGV4OwogICAgICBqdXN0aWZ5LWNvbnRlbnQ6IHNwYWNlLWJldHdlZW47CiAgICAgIGFsaWduLWl0ZW1zOiBjZW50ZXI7CiAgICAgIGdhcDogMTBweDsKICAgICAgcGFkZGluZzogMTBweCAwOwogICAgICBib3JkZXItYm90dG9tOiAxcHggc29saWQgdmFyKC0tYm9yZGVyLWxpZ2h0KTsKCiAgICAgICYubm8tYm9yZGVyIHsKICAgICAgICBib3JkZXItYm90dG9tOiBub25lOwogICAgICB9CiAgICB9CgogICAgLnNldHRpbmctbGFiZWwgewogICAgICBmb250LXNpemU6IHZhcigtLW5naW54LWZvbnQtc2l6ZS1zbSwgMTJweCk7CiAgICAgIGZvbnQtd2VpZ2h0OiA2MDA7CiAgICAgIGNvbG9yOiB2YXIoLS10ZXh0LTEpOwogICAgfQoKICAgIC5zZXR0aW5nLWRlc2MgewogICAgICBmb250LXNpemU6IHZhcigtLW5naW54LWZvbnQtc2l6ZS1zbSwgMTJweCk7CiAgICAgIGNvbG9yOiB2YXIoLS10ZXh0LTMpOwogICAgICBtYXJnaW4tdG9wOiAycHg7CiAgICB9CgogICAgLnNldHRpbmctY3RybCB7CiAgICAgIGRpc3BsYXk6IGZsZXg7CiAgICAgIGFsaWduLWl0ZW1zOiBjZW50ZXI7CiAgICAgIGdhcDogOHB4OwogICAgfQoKICAgIC5zZXR0aW5nLWlucHV0IHsKICAgICAgd2lkdGg6IDExMHB4OwogICAgICBwYWRkaW5nOiA1cHggOHB4OwogICAgICBib3JkZXItcmFkaXVzOiA0cHg7CiAgICAgIGJvcmRlcjogMXB4IHNvbGlkIHZhcigtLWJvcmRlcik7CiAgICAgIGJhY2tncm91bmQ6IHZhcigtLWJnLWlucHV0KTsKICAgICAgY29sb3I6IHZhcigtLXRleHQtMik7CiAgICAgIGZvbnQtc2l6ZTogdmFyKC0tbmdpbngtZm9udC1zaXplLXNtLCAxMnB4KTsKICAgICAgdGV4dC1hbGlnbjogY2VudGVyOwogICAgICBmb250LWZhbWlseTogdmFyKC0tbmdpbngtZm9udC1mYW1pbHktbW9ubywgdWktbW9ub3NwYWNlLCBTRk1vbm8tUmVndWxhciwgTWVubG8sIE1vbmFjbywgQ29uc29sYXMsICdMaWJlcmF0aW9uIE1vbm8nLCBtb25vc3BhY2UpOwogICAgICBvdXRsaW5lOiBub25lOwogICAgfQoKICAgIC5zZXR0aW5nLWlucHV0OmZvY3VzLAogICAgLnNldHRpbmctdGV4dGFyZWE6Zm9jdXMgewogICAgICBib3JkZXItY29sb3I6IHZhcigtLWJsdWUpOwogICAgICBib3gtc2hhZG93OiAwIDAgMCAycHggdmFyKC0tYmx1ZS1ib3JkZXIpOwogICAgfQoKICAgIC5zZXR0aW5nLXRleHRhcmVhIHsKICAgICAgd2lkdGg6IDEwMCU7CiAgICAgIG1hcmdpbi10b3A6IDhweDsKICAgICAgYm9yZGVyOiAxcHggc29saWQgdmFyKC0tYm9yZGVyKTsKICAgICAgYm9yZGVyLXJhZGl1czogNnB4OwogICAgICBiYWNrZ3JvdW5kOiB2YXIoLS1iZy1pbnB1dCk7CiAgICAgIGNvbG9yOiB2YXIoLS10ZXh0LTIpOwogICAgICBmb250LXNpemU6IHZhcigtLW5naW54LWZvbnQtc2l6ZS1zbSwgMTJweCk7CiAgICAgIHBhZGRpbmc6IDhweCAxMHB4OwogICAgICByZXNpemU6IHZlcnRpY2FsOwogICAgICBvdXRsaW5lOiBub25lOwogICAgfQogICAgLm1vbm8gewogICAgICBmb250LWZhbWlseTogdmFyKC0tbmdpbngtZm9udC1mYW1pbHktbW9ubywgdWktbW9ub3NwYWNlLCBTRk1vbm8tUmVndWxhciwgTWVubG8sIE1vbmFjbywgQ29uc29sYXMsICdMaWJlcmF0aW9uIE1vbm8nLCBtb25vc3BhY2UpOwogICAgfQogIA==
1282
- var nginx_secondary_perf_tab_component_default = '/* angular:styles/component:less;62bf71d91779f62ccddaefe75011805fcb2e351af3c0f17f5fca16469cc80704;D:\\ng-manager\\webapp\\src\\app\\pages\\nginx\\components\\nginx-tabs\\nginx-secondary-perf-tab\\nginx-secondary-perf-tab.component.ts */\n:host {\n display: block;\n}\n.panel-header-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n gap: 8px;\n margin-bottom: 12px;\n}\n.panel-tip {\n font-size: var(--nginx-font-size-sm, 12px);\n color: var(--text-3);\n}\n.setting-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n gap: 10px;\n padding: 10px 0;\n border-bottom: 1px solid var(--border-light);\n}\n.setting-row.no-border {\n border-bottom: none;\n}\n.setting-label {\n font-size: var(--nginx-font-size-sm, 12px);\n font-weight: 600;\n color: var(--text-1);\n}\n.setting-desc {\n font-size: var(--nginx-font-size-sm, 12px);\n color: var(--text-3);\n margin-top: 2px;\n}\n.setting-ctrl {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n.setting-input {\n width: 110px;\n padding: 5px 8px;\n border-radius: 4px;\n border: 1px solid var(--border);\n background: var(--bg-input);\n color: var(--text-2);\n font-size: var(--nginx-font-size-sm, 12px);\n text-align: center;\n font-family: var(--nginx-font-family-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace);\n outline: none;\n}\n.setting-input:focus,\n.setting-textarea:focus {\n border-color: var(--blue);\n box-shadow: 0 0 0 2px var(--blue-border);\n}\n.setting-textarea {\n width: 100%;\n margin-top: 8px;\n border: 1px solid var(--border);\n border-radius: 6px;\n background: var(--bg-input);\n color: var(--text-2);\n font-size: var(--nginx-font-size-sm, 12px);\n padding: 8px 10px;\n resize: vertical;\n outline: none;\n}\n.mono {\n font-family: var(--nginx-font-family-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace);\n}\n/*# sourceMappingURL=nginx-secondary-perf-tab.component.css.map */\n';
1283
-
1284
- // src/app/pages/nginx/services/nginx-module.store.ts
1285
- var NginxModuleStore = class NginxModuleStore2 {
1286
- nginxService;
1287
- constructor(nginxService) {
1288
- this.nginxService = nginxService;
1289
- }
1290
- upstreams = signal([]);
1291
- sslCertificates = signal([]);
1292
- trafficConfig = signal({
1293
- rateLimitEnabled: false,
1294
- rateLimit: "",
1295
- connLimitEnabled: false,
1296
- connLimit: 0,
1297
- blacklistIps: []
1298
- });
1299
- performanceConfig = signal({
1300
- gzipEnabled: false,
1301
- gzipTypes: "",
1302
- keepaliveTimeout: "",
1303
- workerProcesses: "",
1304
- sendfile: false,
1305
- tcpNopush: false
1306
- });
1307
- moduleSettings = signal({
1308
- backupRetention: 5,
1309
- configBackupRetention: 20
1310
- });
1311
- upstreamsLoading = signal(false);
1312
- sslLoading = signal(false);
1313
- trafficLoading = signal(false);
1314
- performanceLoading = signal(false);
1315
- settingsLoading = signal(false);
1316
- async loadUpstreams() {
1317
- this.upstreamsLoading.set(true);
1318
- try {
1319
- const res = await this.nginxService.getUpstreams();
1320
- if (res.success && res.upstreams) {
1321
- this.upstreams.set(res.upstreams);
1322
- }
1323
- return res;
1324
- } finally {
1325
- this.upstreamsLoading.set(false);
1326
- }
1327
- }
1328
- async saveUpstreams(upstreams) {
1329
- const res = await this.nginxService.saveUpstreams(upstreams);
1330
- if (res.success) {
1331
- this.upstreams.set(upstreams.map((item) => __spreadValues({}, item)));
1332
- }
1333
- return res;
1334
- }
1335
- async loadSslCertificates() {
1336
- this.sslLoading.set(true);
1337
- try {
1338
- const res = await this.nginxService.getSslCertificates();
1339
- if (res.success && res.certificates) {
1340
- this.sslCertificates.set(res.certificates.map((item) => __spreadValues({}, item)));
1341
- }
1342
- return res;
1343
- } finally {
1344
- this.sslLoading.set(false);
1345
- }
1346
- }
1347
- async saveSslCertificates(certificates) {
1348
- const res = await this.nginxService.saveSslCertificates(certificates);
1349
- if (res.success) {
1350
- this.sslCertificates.set(certificates.map((item) => __spreadValues({}, item)));
1351
- }
1352
- return res;
1353
- }
1354
- async loadTrafficConfig() {
1355
- this.trafficLoading.set(true);
1356
- try {
1357
- const res = await this.nginxService.getTrafficConfig();
1358
- if (res.success && res.traffic) {
1359
- this.trafficConfig.set(__spreadProps(__spreadValues({}, res.traffic), {
1360
- connLimit: Math.max(0, Number(res.traffic.connLimit ?? 0))
1361
- }));
1362
- }
1363
- return res;
1364
- } finally {
1365
- this.trafficLoading.set(false);
1366
- }
1367
- }
1368
- async saveTrafficConfig(traffic) {
1369
- const res = await this.nginxService.saveTrafficConfig(traffic);
1370
- if (res.success) {
1371
- this.trafficConfig.set(__spreadValues({}, traffic));
1372
- }
1373
- return res;
1374
- }
1375
- async loadPerformanceConfig() {
1376
- this.performanceLoading.set(true);
1377
- try {
1378
- const res = await this.nginxService.getPerformanceConfig();
1379
- if (res.success && res.performance) {
1380
- this.performanceConfig.set(__spreadValues({}, res.performance));
1381
- }
1382
- return res;
1383
- } finally {
1384
- this.performanceLoading.set(false);
1385
- }
1386
- }
1387
- async savePerformanceConfig(performance) {
1388
- const res = await this.nginxService.savePerformanceConfig(performance);
1389
- if (res.success) {
1390
- this.performanceConfig.set(__spreadValues({}, performance));
1391
- }
1392
- return res;
1393
- }
1394
- async loadModuleSettings() {
1395
- this.settingsLoading.set(true);
1396
- try {
1397
- const res = await this.nginxService.getModuleSettings();
1398
- if (res.success && res.settings) {
1399
- this.moduleSettings.set({
1400
- backupRetention: Math.max(1, Number(res.settings.backupRetention ?? 5)),
1401
- configBackupRetention: Math.max(1, Number(res.settings.configBackupRetention ?? 20))
1402
- });
1403
- }
1404
- return res;
1405
- } finally {
1406
- this.settingsLoading.set(false);
1407
- }
1408
- }
1409
- async saveModuleSettings(settings) {
1410
- const res = await this.nginxService.saveModuleSettings(settings);
1411
- if (res.success && res.settings) {
1412
- this.moduleSettings.set({
1413
- backupRetention: Math.max(1, Number(res.settings.backupRetention ?? 5)),
1414
- configBackupRetention: Math.max(1, Number(res.settings.configBackupRetention ?? 20))
1415
- });
1416
- }
1417
- return res;
1418
- }
1419
- static ctorParameters = () => [
1420
- { type: NginxService }
1421
- ];
1422
- };
1423
- NginxModuleStore = __decorate([
1424
- Injectable({
1425
- providedIn: "root"
1426
- })
1427
- ], NginxModuleStore);
1428
-
1429
- // src/app/pages/nginx/components/nginx-tabs/nginx-secondary-perf-tab/nginx-secondary-perf-tab.component.ts
1430
- var NginxSecondaryPerfTabComponent = class NginxSecondaryPerfTabComponent2 {
1431
- moduleStore = inject(NginxModuleStore);
1432
- message = inject(NzMessageService);
1433
- saving = signal(false);
1434
- dirty = signal(false);
1435
- config = signal({
1436
- gzipEnabled: false,
1437
- gzipTypes: "",
1438
- keepaliveTimeout: "",
1439
- workerProcesses: "",
1440
- sendfile: false,
1441
- tcpNopush: false
1442
- });
1443
- async ngOnInit() {
1444
- await this.load();
1445
- }
1446
- async load() {
1447
- try {
1448
- const res = await this.moduleStore.loadPerformanceConfig();
1449
- if (res.success && res.performance) {
1450
- this.config.set(__spreadValues({}, this.moduleStore.performanceConfig()));
1451
- this.dirty.set(false);
1452
- } else {
1453
- this.message.error(res.error || "\u52A0\u8F7D\u6027\u80FD\u914D\u7F6E\u5931\u8D25");
1454
- }
1455
- } catch (err) {
1456
- this.message.error("\u52A0\u8F7D\u6027\u80FD\u914D\u7F6E\u5931\u8D25: " + err.message);
1457
- }
1458
- }
1459
- setBoolean(key, value) {
1460
- this.config.update((prev) => __spreadProps(__spreadValues({}, prev), {
1461
- [key]: value
1462
- }));
1463
- this.markDirty();
1464
- }
1465
- markDirty() {
1466
- this.dirty.set(true);
1467
- }
1468
- async save() {
1469
- const payload = __spreadProps(__spreadValues({}, this.config()), {
1470
- gzipTypes: this.config().gzipTypes.trim(),
1471
- keepaliveTimeout: this.config().keepaliveTimeout.trim(),
1472
- workerProcesses: this.config().workerProcesses.trim()
1473
- });
1474
- this.saving.set(true);
1475
- try {
1476
- const res = await this.moduleStore.savePerformanceConfig(payload);
1477
- if (res.success) {
1478
- this.message.success("\u6027\u80FD\u4F18\u5316\u914D\u7F6E\u5DF2\u4FDD\u5B58");
1479
- this.dirty.set(false);
1480
- await this.load();
1481
- } else {
1482
- this.message.error(res.error || "\u4FDD\u5B58\u6027\u80FD\u914D\u7F6E\u5931\u8D25");
1483
- }
1484
- } catch (err) {
1485
- this.message.error("\u4FDD\u5B58\u6027\u80FD\u914D\u7F6E\u5931\u8D25: " + err.message);
1486
- } finally {
1487
- this.saving.set(false);
1488
- }
1489
- }
1490
- };
1491
- NginxSecondaryPerfTabComponent = __decorate([
1492
- Component({
1493
- selector: "app-nginx-secondary-perf-tab",
1494
- standalone: true,
1495
- imports: [CommonModule, FormsModule, NzButtonModule, NzIconModule, NzSwitchModule],
1496
- template: `
1497
- <div class="panel-header-row">
1498
- <span class="panel-tip">\u6027\u80FD\u4F18\u5316\u914D\u7F6E</span>
1499
- <button nz-button nzType="primary" (click)="save()" [nzLoading]="saving()" [disabled]="!dirty()">
1500
- <nz-icon nzType="save" nzTheme="outline"></nz-icon>
1501
- \u4FDD\u5B58
1502
- </button>
1503
- </div>
1504
-
1505
- <div class="setting-row">
1506
- <div>
1507
- <div class="setting-label">Gzip \u538B\u7F29</div>
1508
- <div class="setting-desc">\u662F\u5426\u542F\u7528 gzip</div>
1509
- </div>
1510
- <div class="setting-ctrl">
1511
- <nz-switch
1512
- nzSize="small"
1513
- name="gzipEnabled"
1514
- [ngModel]="config().gzipEnabled"
1515
- (ngModelChange)="setBoolean('gzipEnabled', $event)"
1516
- ></nz-switch>
1517
- </div>
1518
- </div>
1519
-
1520
- <div class="setting-row">
1521
- <div>
1522
- <div class="setting-label">Keepalive \u8D85\u65F6</div>
1523
- <div class="setting-desc">keepalive_timeout</div>
1524
- </div>
1525
- <div class="setting-ctrl">
1526
- <input
1527
- class="setting-input"
1528
- [(ngModel)]="config().keepaliveTimeout"
1529
- (ngModelChange)="markDirty()"
1530
- placeholder="65s"
1531
- />
1532
- </div>
1533
- </div>
1534
-
1535
- <div class="setting-row">
1536
- <div>
1537
- <div class="setting-label">Worker \u8FDB\u7A0B</div>
1538
- <div class="setting-desc">worker_processes</div>
1539
- </div>
1540
- <div class="setting-ctrl">
1541
- <input
1542
- class="setting-input"
1543
- [(ngModel)]="config().workerProcesses"
1544
- (ngModelChange)="markDirty()"
1545
- placeholder="auto"
1546
- />
1547
- </div>
1548
- </div>
1549
-
1550
- <div class="setting-row">
1551
- <div>
1552
- <div class="setting-label">sendfile</div>
1553
- <div class="setting-desc">\u9759\u6001\u6587\u4EF6\u96F6\u62F7\u8D1D</div>
1554
- </div>
1555
- <div class="setting-ctrl">
1556
- <nz-switch
1557
- nzSize="small"
1558
- name="sendfile"
1559
- [ngModel]="config().sendfile"
1560
- (ngModelChange)="setBoolean('sendfile', $event)"
1561
- ></nz-switch>
1562
- </div>
1563
- </div>
1564
-
1565
- <div class="setting-row">
1566
- <div>
1567
- <div class="setting-label">tcp_nopush</div>
1568
- <div class="setting-desc">\u4F18\u5316\u5927\u6587\u4EF6\u4F20\u8F93</div>
1569
- </div>
1570
- <div class="setting-ctrl">
1571
- <nz-switch
1572
- nzSize="small"
1573
- name="tcpNopush"
1574
- [ngModel]="config().tcpNopush"
1575
- (ngModelChange)="setBoolean('tcpNopush', $event)"
1576
- ></nz-switch>
1577
- </div>
1578
- </div>
1579
-
1580
- <div class="setting-row no-border">
1581
- <div class="full-width">
1582
- <div class="setting-label">Gzip Types</div>
1583
- <div class="setting-desc">\u7A7A\u683C\u5206\u9694 MIME \u5217\u8868</div>
1584
- <textarea
1585
- class="setting-textarea mono"
1586
- [(ngModel)]="config().gzipTypes"
1587
- (ngModelChange)="markDirty()"
1588
- rows="3"
1589
- ></textarea>
1590
- </div>
1591
- </div>
1592
- `,
1593
- styles: [nginx_secondary_perf_tab_component_default]
1594
- })
1595
- ], NginxSecondaryPerfTabComponent);
1596
-
1597
- // angular:jit:style:inline:src\app\pages\nginx\components\nginx-tabs\nginx-secondary-settings-tab\nginx-secondary-settings-tab.component.ts;CiAgICAuc2V0dGluZy1yb3cgewogICAgICBkaXNwbGF5OiBmbGV4OwogICAgICBqdXN0aWZ5LWNvbnRlbnQ6IHNwYWNlLWJldHdlZW47CiAgICAgIGFsaWduLWl0ZW1zOiBjZW50ZXI7CiAgICAgIGdhcDogMTBweDsKICAgICAgcGFkZGluZzogMTBweCAwOwogICAgICBib3JkZXItYm90dG9tOiAxcHggc29saWQgdmFyKC0tYm9yZGVyLWxpZ2h0KTsKCiAgICAgICY6bGFzdC1jaGlsZCB7CiAgICAgICAgYm9yZGVyLWJvdHRvbTogbm9uZTsKICAgICAgfQoKICAgICAgJi5kYW5nZXItcm93IHsKICAgICAgICBwYWRkaW5nOiA2cHggMCAwOwogICAgICAgIGJvcmRlci1ib3R0b206IG5vbmU7CiAgICAgIH0KICAgIH0KCiAgICAuc2V0dGluZy1sYWJlbCB7CiAgICAgIGZvbnQtc2l6ZTogdmFyKC0tbmdpbngtZm9udC1zaXplLXNtLCAxMnB4KTsKICAgICAgZm9udC13ZWlnaHQ6IDYwMDsKICAgICAgY29sb3I6IHZhcigtLXRleHQtMSk7CgogICAgICAmLmRhbmdlciB7CiAgICAgICAgY29sb3I6IHZhcigtLXJlZCk7CiAgICAgICAgbWFyZ2luLWJvdHRvbTogNHB4OwogICAgICB9CiAgICB9CgogICAgLnNldHRpbmctZGVzYyB7CiAgICAgIGZvbnQtc2l6ZTogdmFyKC0tbmdpbngtZm9udC1zaXplLXNtLCAxMnB4KTsKICAgICAgY29sb3I6IHZhcigtLXRleHQtMyk7CiAgICAgIG1hcmdpbi10b3A6IDJweDsKICAgIH0KCiAgICAuc2V0dGluZy1jdHJsIHsKICAgICAgZGlzcGxheTogZmxleDsKICAgICAgYWxpZ24taXRlbXM6IGNlbnRlcjsKICAgICAgZ2FwOiA4cHg7CiAgICB9CgogICAgLnJldGVudGlvbi1pbnB1dCB7CiAgICAgIHdpZHRoOiA5MnB4OwogICAgfQoKICAgIC5tb25vIHsKICAgICAgZm9udC1mYW1pbHk6IHZhcigtLW5naW54LWZvbnQtZmFtaWx5LW1vbm8sIHVpLW1vbm9zcGFjZSwgU0ZNb25vLVJlZ3VsYXIsIE1lbmxvLCBNb25hY28sIENvbnNvbGFzLCAnTGliZXJhdGlvbiBNb25vJywgbW9ub3NwYWNlKTsKCiAgICAgICYuc3Ryb25nIHsKICAgICAgICBjb2xvcjogdmFyKC0tdGV4dC0xKTsKICAgICAgICBmb250LXdlaWdodDogNjAwOwogICAgICB9CiAgICB9CgogICAgLmRhbmdlci1ib3ggewogICAgICBtYXJnaW4tdG9wOiA4cHg7CiAgICAgIHBhZGRpbmc6IDEwcHggMTJweDsKICAgICAgYm9yZGVyOiAxcHggc29saWQgcmdiYSgyNDUsIDYzLCA2MywgMC4yKTsKICAgICAgYmFja2dyb3VuZDogdmFyKC0tcmVkLWJnKTsKICAgICAgYm9yZGVyLXJhZGl1czogNnB4OwogICAgfQoKICAgIEBtZWRpYSAobWF4LXdpZHRoOiA3NjhweCkgewogICAgICAuc2V0dGluZy1yb3cgewogICAgICAgIGZsZXgtZGlyZWN0aW9uOiBjb2x1bW47CiAgICAgICAgYWxpZ24taXRlbXM6IGZsZXgtc3RhcnQ7CiAgICAgIH0KICAgIH0KICA=
1598
- var nginx_secondary_settings_tab_component_default = '/* angular:styles/component:less;fbfac6a855202f42b3e67e336f2491eb905f2ab84fd62f243dd847b271e5448a;D:\\ng-manager\\webapp\\src\\app\\pages\\nginx\\components\\nginx-tabs\\nginx-secondary-settings-tab\\nginx-secondary-settings-tab.component.ts */\n.setting-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n gap: 10px;\n padding: 10px 0;\n border-bottom: 1px solid var(--border-light);\n}\n.setting-row:last-child {\n border-bottom: none;\n}\n.setting-row.danger-row {\n padding: 6px 0 0;\n border-bottom: none;\n}\n.setting-label {\n font-size: var(--nginx-font-size-sm, 12px);\n font-weight: 600;\n color: var(--text-1);\n}\n.setting-label.danger {\n color: var(--red);\n margin-bottom: 4px;\n}\n.setting-desc {\n font-size: var(--nginx-font-size-sm, 12px);\n color: var(--text-3);\n margin-top: 2px;\n}\n.setting-ctrl {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n.retention-input {\n width: 92px;\n}\n.mono {\n font-family: var(--nginx-font-family-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace);\n}\n.mono.strong {\n color: var(--text-1);\n font-weight: 600;\n}\n.danger-box {\n margin-top: 8px;\n padding: 10px 12px;\n border: 1px solid rgba(245, 63, 63, 0.2);\n background: var(--red-bg);\n border-radius: 6px;\n}\n@media (max-width: 768px) {\n .setting-row {\n flex-direction: column;\n align-items: flex-start;\n }\n}\n/*# sourceMappingURL=nginx-secondary-settings-tab.component.css.map */\n';
1599
-
1600
- // src/app/pages/nginx/components/nginx-tabs/nginx-secondary-settings-tab/nginx-secondary-settings-tab.component.ts
1601
- var NginxSecondarySettingsTabComponent = class NginxSecondarySettingsTabComponent2 {
1602
- moduleStore = inject(NginxModuleStore);
1603
- message = inject(NzMessageService);
1604
- instance = null;
1605
- configFileCount = 0;
1606
- unbind = new EventEmitter();
1607
- backupRetention = signal(5);
1608
- configBackupRetention = signal(20);
1609
- retentionDirty = signal(false);
1610
- saving = signal(false);
1611
- async ngOnInit() {
1612
- try {
1613
- const res = await this.moduleStore.loadModuleSettings();
1614
- if (res.success && res.settings) {
1615
- this.backupRetention.set(Math.max(1, Number(res.settings.backupRetention ?? 5)));
1616
- this.configBackupRetention.set(Math.max(1, Number(res.settings.configBackupRetention ?? 20)));
1617
- }
1618
- } catch {
1619
- }
1620
- }
1621
- setBackupRetention(value) {
1622
- const parsed = Number(value);
1623
- const normalized = Number.isFinite(parsed) ? Math.max(1, Math.trunc(parsed)) : 1;
1624
- this.backupRetention.set(normalized);
1625
- this.retentionDirty.set(true);
1626
- }
1627
- setConfigBackupRetention(value) {
1628
- const parsed = Number(value);
1629
- const normalized = Number.isFinite(parsed) ? Math.max(1, Math.trunc(parsed)) : 1;
1630
- this.configBackupRetention.set(normalized);
1631
- this.retentionDirty.set(true);
1632
- }
1633
- async saveBackupRetention() {
1634
- this.saving.set(true);
1635
- try {
1636
- const res = await this.moduleStore.saveModuleSettings({
1637
- backupRetention: this.backupRetention(),
1638
- configBackupRetention: this.configBackupRetention()
1639
- });
1640
- if (res.success && res.settings) {
1641
- this.backupRetention.set(Math.max(1, Number(res.settings.backupRetention ?? 5)));
1642
- this.configBackupRetention.set(Math.max(1, Number(res.settings.configBackupRetention ?? 20)));
1643
- this.retentionDirty.set(false);
1644
- this.message.success("\u5907\u4EFD\u4FDD\u7559\u6570\u91CF\u5DF2\u4FDD\u5B58");
1645
- } else {
1646
- this.message.error(res.error || "\u4FDD\u5B58\u5907\u4EFD\u4FDD\u7559\u6570\u91CF\u5931\u8D25");
1647
- }
1648
- } catch (err) {
1649
- this.message.error("\u4FDD\u5B58\u5907\u4EFD\u4FDD\u7559\u6570\u91CF\u5931\u8D25: " + err.message);
1650
- } finally {
1651
- this.saving.set(false);
1652
- }
1653
- }
1654
- static propDecorators = {
1655
- instance: [{ type: Input }],
1656
- configFileCount: [{ type: Input }],
1657
- unbind: [{ type: Output }]
1658
- };
1659
- };
1660
- NginxSecondarySettingsTabComponent = __decorate([
1661
- Component({
1662
- selector: "app-nginx-secondary-settings-tab",
1663
- standalone: true,
1664
- imports: [CommonModule, FormsModule, NzButtonModule, NzInputModule],
1665
- template: `
1666
- <div class="setting-row">
1667
- <div>
1668
- <div class="setting-label">Nginx \u8DEF\u5F84</div>
1669
- <div class="setting-desc">\u53EF\u6267\u884C\u6587\u4EF6\u4F4D\u7F6E</div>
1670
- </div>
1671
- <div class="setting-ctrl">
1672
- <span class="mono strong">{{ instance?.path || '-' }}</span>
1673
- </div>
1674
- </div>
1675
- <div class="setting-row">
1676
- <div>
1677
- <div class="setting-label">\u914D\u7F6E\u6587\u4EF6</div>
1678
- <div class="setting-desc">\u4E3B\u914D\u7F6E\u8DEF\u5F84</div>
1679
- </div>
1680
- <div class="setting-ctrl">
1681
- <span class="mono strong">{{ instance?.configPath || '-' }}</span>
1682
- </div>
1683
- </div>
1684
- <div class="setting-row">
1685
- <div>
1686
- <div class="setting-label">\u914D\u7F6E\u6587\u4EF6\u6570</div>
1687
- <div class="setting-desc">\u6839\u636E include \u6307\u4EE4\u89E3\u6790</div>
1688
- </div>
1689
- <div class="setting-ctrl">
1690
- <span class="mono strong">{{ configFileCount }}</span>
1691
- </div>
1692
- </div>
1693
- <div class="setting-row">
1694
- <div>
1695
- <div class="setting-label">\u72B6\u6001\u5907\u4EFD\u4FDD\u7559\u6570</div>
1696
- <div class="setting-desc">.ngm-nginx-module.json.backup-* \u6700\u591A\u4FDD\u7559\u6570\u91CF</div>
1697
- </div>
1698
- <div class="setting-ctrl">
1699
- <input
1700
- nz-input
1701
- type="number"
1702
- min="1"
1703
- class="retention-input mono"
1704
- [ngModel]="backupRetention()"
1705
- (ngModelChange)="setBackupRetention($event)"
1706
- />
1707
- <button
1708
- nz-button
1709
- nzType="default"
1710
- (click)="saveBackupRetention()"
1711
- [disabled]="!retentionDirty() || saving()"
1712
- >
1713
- \u4FDD\u5B58
1714
- </button>
1715
- </div>
1716
- </div>
1717
- <div class="setting-row">
1718
- <div>
1719
- <div class="setting-label">\u914D\u7F6E\u5907\u4EFD\u4FDD\u7559\u6570</div>
1720
- <div class="setting-desc">*.conf.backup-* \u6700\u591A\u4FDD\u7559\u6570\u91CF</div>
1721
- </div>
1722
- <div class="setting-ctrl">
1723
- <input
1724
- nz-input
1725
- type="number"
1726
- min="1"
1727
- class="retention-input mono"
1728
- [ngModel]="configBackupRetention()"
1729
- (ngModelChange)="setConfigBackupRetention($event)"
1730
- />
1731
- <button
1732
- nz-button
1733
- nzType="default"
1734
- (click)="saveBackupRetention()"
1735
- [disabled]="!retentionDirty() || saving()"
1736
- >
1737
- \u4FDD\u5B58
1738
- </button>
1739
- </div>
1740
- </div>
1741
- <div class="danger-box">
1742
- <div class="setting-label danger">\u5371\u9669\u64CD\u4F5C</div>
1743
- <div class="setting-row danger-row">
1744
- <div>
1745
- <div class="setting-label">\u89E3\u7ED1\u5B9E\u4F8B</div>
1746
- <div class="setting-desc">\u4E0D\u5F71\u54CD Nginx \u670D\u52A1\u672C\u8EAB</div>
1747
- </div>
1748
- <button nz-button nzDanger nzType="default" (click)="unbind.emit()">
1749
- \u89E3\u7ED1
1750
- </button>
1751
- </div>
1752
- </div>
1753
- `,
1754
- styles: [nginx_secondary_settings_tab_component_default]
1755
- })
1756
- ], NginxSecondarySettingsTabComponent);
1757
-
1758
- // angular:jit:style:inline:src\app\pages\nginx\components\nginx-tabs\nginx-secondary-ssl-tab\nginx-secondary-ssl-tab.component.ts;CiAgICA6aG9zdCB7CiAgICAgIGRpc3BsYXk6IGJsb2NrOwogICAgfQoKICAgIC5wYW5lbC1oZWFkZXItcm93IHsKICAgICAgZGlzcGxheTogZmxleDsKICAgICAganVzdGlmeS1jb250ZW50OiBzcGFjZS1iZXR3ZWVuOwogICAgICBhbGlnbi1pdGVtczogY2VudGVyOwogICAgICBnYXA6IDhweDsKICAgICAgbWFyZ2luLWJvdHRvbTogMTJweDsKICAgIH0KCiAgICAucGFuZWwtdGlwIHsKICAgICAgZm9udC1zaXplOiB2YXIoLS1uZ2lueC1mb250LXNpemUtc20sIDEycHgpOwogICAgICBjb2xvcjogdmFyKC0tdGV4dC0zKTsKICAgIH0KCiAgICAuaGVhZGVyLWFjdGlvbnMgewogICAgICBkaXNwbGF5OiBmbGV4OwogICAgICBnYXA6IDhweDsKICAgIH0KCiAgICAuc3NsLWNhcmRzIHsKICAgICAgZGlzcGxheTogZ3JpZDsKICAgICAgZ3JpZC10ZW1wbGF0ZS1jb2x1bW5zOiByZXBlYXQoYXV0by1maWxsLCBtaW5tYXgoMzIwcHgsIDFmcikpOwogICAgICBnYXA6IDEwcHg7CiAgICB9CgogICAgLnNzbC1jYXJkIHsKICAgICAgZGlzcGxheTogZmxleDsKICAgICAgZmxleC1kaXJlY3Rpb246IGNvbHVtbjsKICAgICAgZ2FwOiAxMHB4OwogICAgICBib3JkZXI6IDFweCBzb2xpZCB2YXIoLS1ib3JkZXItbGlnaHQpOwogICAgICBib3JkZXItcmFkaXVzOiA2cHg7CiAgICAgIHBhZGRpbmc6IDEwcHg7CiAgICAgIGJhY2tncm91bmQ6ICNmZmY7CiAgICB9CgogICAgLmNhcmQtaGVhZCB7CiAgICAgIGRpc3BsYXk6IGZsZXg7CiAgICAgIGFsaWduLWl0ZW1zOiBjZW50ZXI7CiAgICAgIGp1c3RpZnktY29udGVudDogc3BhY2UtYmV0d2VlbjsKICAgICAgZ2FwOiAxMHB4OwogICAgICBib3JkZXItYm90dG9tOiAxcHggc29saWQgdmFyKC0tYm9yZGVyLWxpZ2h0KTsKICAgICAgcGFkZGluZy1ib3R0b206IDhweDsKICAgIH0KCiAgICAuY2FyZC1kb21haW4gewogICAgICBmb250LXNpemU6IHZhcigtLW5naW54LWZvbnQtc2l6ZS1iYXNlLCAxNHB4KTsKICAgICAgY29sb3I6IHZhcigtLXRleHQtMSk7CiAgICAgIGZvbnQtd2VpZ2h0OiA2MDA7CiAgICAgIG1pbi13aWR0aDogMDsKICAgICAgb3ZlcmZsb3c6IGhpZGRlbjsKICAgICAgdGV4dC1vdmVyZmxvdzogZWxsaXBzaXM7CiAgICAgIHdoaXRlLXNwYWNlOiBub3dyYXA7CiAgICB9CgogICAgLnN0YXR1cy1iYWRnZSB7CiAgICAgIGRpc3BsYXk6IGlubGluZS1mbGV4OwogICAgICBhbGlnbi1pdGVtczogY2VudGVyOwogICAgICBoZWlnaHQ6IDI0cHg7CiAgICAgIGJvcmRlci1yYWRpdXM6IDEycHg7CiAgICAgIHBhZGRpbmc6IDAgMTBweDsKICAgICAgZm9udC1zaXplOiB2YXIoLS1uZ2lueC1mb250LXNpemUtc20sIDEycHgpOwogICAgICBsaW5lLWhlaWdodDogMjRweDsKICAgICAgYm9yZGVyOiAxcHggc29saWQgdHJhbnNwYXJlbnQ7CiAgICAgIHdoaXRlLXNwYWNlOiBub3dyYXA7CiAgICB9CgogICAgLnN0YXR1cy1iYWRnZS52YWxpZCB7CiAgICAgIGNvbG9yOiB2YXIoLS1ncmVlbik7CiAgICAgIGJvcmRlci1jb2xvcjogcmdiYSgwLCAxODAsIDQyLCAwLjIpOwogICAgICBiYWNrZ3JvdW5kOiB2YXIoLS1ncmVlbi1iZyk7CiAgICB9CgogICAgLnN0YXR1cy1iYWRnZS5leHBpcmluZyB7CiAgICAgIGNvbG9yOiB2YXIoLS1vcmFuZ2UpOwogICAgICBib3JkZXItY29sb3I6IHJnYmEoMjU1LCAxMjUsIDAsIDAuMik7CiAgICAgIGJhY2tncm91bmQ6IHZhcigtLW9yYW5nZS1iZyk7CiAgICB9CgogICAgLnN0YXR1cy1iYWRnZS5leHBpcmVkIHsKICAgICAgY29sb3I6IHZhcigtLXJlZCk7CiAgICAgIGJvcmRlci1jb2xvcjogcmdiYSgyNDUsIDYzLCA2MywgMC4yKTsKICAgICAgYmFja2dyb3VuZDogdmFyKC0tcmVkLWJnKTsKICAgIH0KCiAgICAuc3RhdHVzLWJhZGdlLnBlbmRpbmcgewogICAgICBjb2xvcjogdmFyKC0tdGV4dC0yKTsKICAgICAgYm9yZGVyLWNvbG9yOiB2YXIoLS1ib3JkZXIpOwogICAgICBiYWNrZ3JvdW5kOiB2YXIoLS1iZy1pbnB1dCk7CiAgICB9CgogICAgLm1ldGEtbGlzdCB7CiAgICAgIGRpc3BsYXk6IGZsZXg7CiAgICAgIGZsZXgtZGlyZWN0aW9uOiBjb2x1bW47CiAgICAgIGdhcDogOHB4OwogICAgICBmbGV4OiAxOwogICAgfQoKICAgIC5tZXRhLWl0ZW0gewogICAgICBkaXNwbGF5OiBncmlkOwogICAgICBncmlkLXRlbXBsYXRlLWNvbHVtbnM6IDgwcHggMWZyOwogICAgICBnYXA6IDhweDsKICAgICAgbWluLXdpZHRoOiAwOwogICAgfQoKICAgIC5tZXRhLWl0ZW0uYmxvY2sgewogICAgICBncmlkLXRlbXBsYXRlLWNvbHVtbnM6IDFmcjsKICAgIH0KCiAgICAubWV0YS1sYWJlbCB7CiAgICAgIGZvbnQtc2l6ZTogdmFyKC0tbmdpbngtZm9udC1zaXplLXNtLCAxMnB4KTsKICAgICAgY29sb3I6IHZhcigtLXRleHQtMyk7CiAgICAgIGZvbnQtd2VpZ2h0OiA2MDA7CiAgICB9CgogICAgLm1ldGEtdmFsdWUgewogICAgICBjb2xvcjogdmFyKC0tdGV4dC0xKTsKICAgICAgZm9udC1zaXplOiB2YXIoLS1uZ2lueC1mb250LXNpemUtc20sIDEycHgpOwogICAgICBtaW4td2lkdGg6IDA7CiAgICAgIHdvcmQtYnJlYWs6IGJyZWFrLWFsbDsKICAgIH0KCiAgICAubW9ubyB7CiAgICAgIGZvbnQtZmFtaWx5OiB2YXIoLS1uZ2lueC1mb250LWZhbWlseS1tb25vLCB1aS1tb25vc3BhY2UsIFNGTW9uby1SZWd1bGFyLCBNZW5sbywgTW9uYWNvLCBDb25zb2xhcywgJ0xpYmVyYXRpb24gTW9ubycsIG1vbm9zcGFjZSk7CiAgICB9CgogICAgLmNhcmQtYWN0aW9ucyB7CiAgICAgIGRpc3BsYXk6IGZsZXg7CiAgICAgIGp1c3RpZnktY29udGVudDogZmxleC1lbmQ7CiAgICAgIGdhcDogOHB4OwogICAgfQoKICAgIC5kcmF3ZXItYm9keSB7CiAgICAgIHBhZGRpbmc6IDAgMjRweCAxNnB4OwogICAgfQoKICAgIC5mb3JtLWdyaWQgewogICAgICBkaXNwbGF5OiBncmlkOwogICAgICBncmlkLXRlbXBsYXRlLWNvbHVtbnM6IDFmciAxZnI7CiAgICAgIGdhcDogMTJweDsKICAgIH0KCiAgICAuZHJhd2VyLWZvb3RlciB7CiAgICAgIHBvc2l0aW9uOiBhYnNvbHV0ZTsKICAgICAgYm90dG9tOiAwOwogICAgICBsZWZ0OiAwOwogICAgICByaWdodDogMDsKICAgICAgcGFkZGluZzogMTJweCAyNHB4OwogICAgICBib3JkZXItdG9wOiAxcHggc29saWQgcmdiYSgwLCAwLCAwLCAwLjA2KTsKICAgICAgZGlzcGxheTogZmxleDsKICAgICAganVzdGlmeS1jb250ZW50OiBmbGV4LWVuZDsKICAgICAgZ2FwOiA4cHg7CiAgICAgIGJhY2tncm91bmQ6ICNmZmY7CiAgICB9CgogICAgLmVtcHR5LXJvdyB7CiAgICAgIGdyaWQtY29sdW1uOiAxIC8gLTE7CiAgICAgIGJvcmRlcjogMXB4IGRhc2hlZCB2YXIoLS1ib3JkZXIpOwogICAgICBib3JkZXItcmFkaXVzOiA2cHg7CiAgICAgIGNvbG9yOiB2YXIoLS10ZXh0LTMpOwogICAgICBmb250LXNpemU6IHZhcigtLW5naW54LWZvbnQtc2l6ZS1zbSwgMTJweCk7CiAgICAgIHRleHQtYWxpZ246IGNlbnRlcjsKICAgICAgcGFkZGluZzogMjBweCAxMnB4OwogICAgICBiYWNrZ3JvdW5kOiAjZmZmOwogICAgfQoKICAgIEBtZWRpYSAobWF4LXdpZHRoOiA5OTJweCkgewogICAgICAucGFuZWwtaGVhZGVyLXJvdyB7CiAgICAgICAgZmxleC1kaXJlY3Rpb246IGNvbHVtbjsKICAgICAgICBhbGlnbi1pdGVtczogZmxleC1zdGFydDsKICAgICAgfQoKICAgICAgLmhlYWRlci1hY3Rpb25zIHsKICAgICAgICB3aWR0aDogMTAwJTsKICAgICAgfQoKICAgICAgLmZvcm0tZ3JpZCB7CiAgICAgICAgZ3JpZC10ZW1wbGF0ZS1jb2x1bW5zOiAxZnI7CiAgICAgIH0KICAgIH0KICA=
1759
- var nginx_secondary_ssl_tab_component_default = '/* angular:styles/component:less;e564a99d38a425a93e8df6d9ba8cdf5980494baf3ea6d1f832d04b10cd22736f;D:\\ng-manager\\webapp\\src\\app\\pages\\nginx\\components\\nginx-tabs\\nginx-secondary-ssl-tab\\nginx-secondary-ssl-tab.component.ts */\n:host {\n display: block;\n}\n.panel-header-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n gap: 8px;\n margin-bottom: 12px;\n}\n.panel-tip {\n font-size: var(--nginx-font-size-sm, 12px);\n color: var(--text-3);\n}\n.header-actions {\n display: flex;\n gap: 8px;\n}\n.ssl-cards {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));\n gap: 10px;\n}\n.ssl-card {\n display: flex;\n flex-direction: column;\n gap: 10px;\n border: 1px solid var(--border-light);\n border-radius: 6px;\n padding: 10px;\n background: #fff;\n}\n.card-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 10px;\n border-bottom: 1px solid var(--border-light);\n padding-bottom: 8px;\n}\n.card-domain {\n font-size: var(--nginx-font-size-base, 14px);\n color: var(--text-1);\n font-weight: 600;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.status-badge {\n display: inline-flex;\n align-items: center;\n height: 24px;\n border-radius: 12px;\n padding: 0 10px;\n font-size: var(--nginx-font-size-sm, 12px);\n line-height: 24px;\n border: 1px solid transparent;\n white-space: nowrap;\n}\n.status-badge.valid {\n color: var(--green);\n border-color: rgba(0, 180, 42, 0.2);\n background: var(--green-bg);\n}\n.status-badge.expiring {\n color: var(--orange);\n border-color: rgba(255, 125, 0, 0.2);\n background: var(--orange-bg);\n}\n.status-badge.expired {\n color: var(--red);\n border-color: rgba(245, 63, 63, 0.2);\n background: var(--red-bg);\n}\n.status-badge.pending {\n color: var(--text-2);\n border-color: var(--border);\n background: var(--bg-input);\n}\n.meta-list {\n display: flex;\n flex-direction: column;\n gap: 8px;\n flex: 1;\n}\n.meta-item {\n display: grid;\n grid-template-columns: 80px 1fr;\n gap: 8px;\n min-width: 0;\n}\n.meta-item.block {\n grid-template-columns: 1fr;\n}\n.meta-label {\n font-size: var(--nginx-font-size-sm, 12px);\n color: var(--text-3);\n font-weight: 600;\n}\n.meta-value {\n color: var(--text-1);\n font-size: var(--nginx-font-size-sm, 12px);\n min-width: 0;\n word-break: break-all;\n}\n.mono {\n font-family: var(--nginx-font-family-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace);\n}\n.card-actions {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n}\n.drawer-body {\n padding: 0 24px 16px;\n}\n.form-grid {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 12px;\n}\n.drawer-footer {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n padding: 12px 24px;\n border-top: 1px solid rgba(0, 0, 0, 0.06);\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n background: #fff;\n}\n.empty-row {\n grid-column: 1 / -1;\n border: 1px dashed var(--border);\n border-radius: 6px;\n color: var(--text-3);\n font-size: var(--nginx-font-size-sm, 12px);\n text-align: center;\n padding: 20px 12px;\n background: #fff;\n}\n@media (max-width: 992px) {\n .panel-header-row {\n flex-direction: column;\n align-items: flex-start;\n }\n .header-actions {\n width: 100%;\n }\n .form-grid {\n grid-template-columns: 1fr;\n }\n}\n/*# sourceMappingURL=nginx-secondary-ssl-tab.component.css.map */\n';
1760
-
1761
- // src/app/pages/nginx/components/nginx-tabs/nginx-secondary-ssl-tab/nginx-secondary-ssl-tab.component.ts
1762
- var NginxSecondarySslTabComponent = class NginxSecondarySslTabComponent2 {
1763
- moduleStore = inject(NginxModuleStore);
1764
- message = inject(NzMessageService);
1765
- loading = signal(false);
1766
- saving = signal(false);
1767
- dirty = signal(false);
1768
- rows = signal([]);
1769
- drawerVisible = signal(false);
1770
- editingId = signal(null);
1771
- drawerForm = this.createEmptyForm();
1772
- async ngOnInit() {
1773
- await this.loadData();
1774
- }
1775
- async loadData() {
1776
- this.loading.set(true);
1777
- try {
1778
- const res = await this.moduleStore.loadSslCertificates();
1779
- if (res.success && res.certificates) {
1780
- this.rows.set(this.moduleStore.sslCertificates().map((item) => __spreadValues({}, item)));
1781
- this.dirty.set(false);
1782
- } else {
1783
- this.message.error(res.error || "\u52A0\u8F7D SSL \u914D\u7F6E\u5931\u8D25");
1784
- }
1785
- } catch (err) {
1786
- this.message.error("\u52A0\u8F7D SSL \u914D\u7F6E\u5931\u8D25: " + err.message);
1787
- } finally {
1788
- this.loading.set(false);
1789
- }
1790
- }
1791
- openCreateDrawer() {
1792
- this.editingId.set(null);
1793
- this.drawerForm = this.createEmptyForm();
1794
- this.drawerVisible.set(true);
1795
- }
1796
- openEditDrawer(row) {
1797
- this.editingId.set(row.id);
1798
- this.drawerForm = {
1799
- domain: row.domain,
1800
- certPath: row.certPath,
1801
- keyPath: row.keyPath,
1802
- expireAt: row.expireAt,
1803
- status: row.status,
1804
- autoRenew: row.autoRenew
1805
- };
1806
- this.drawerVisible.set(true);
1807
- }
1808
- closeDrawer() {
1809
- this.drawerVisible.set(false);
1810
- }
1811
- submitDrawer() {
1812
- const domain = this.drawerForm.domain.trim();
1813
- if (!domain) {
1814
- this.message.warning("\u8BC1\u4E66\u57DF\u540D\u4E0D\u80FD\u4E3A\u7A7A");
1815
- return;
1816
- }
1817
- if (!this.drawerForm.certPath.trim()) {
1818
- this.message.warning("\u8BC1\u4E66\u8DEF\u5F84\u4E0D\u80FD\u4E3A\u7A7A");
1819
- return;
1820
- }
1821
- if (!this.drawerForm.keyPath.trim()) {
1822
- this.message.warning("\u79C1\u94A5\u8DEF\u5F84\u4E0D\u80FD\u4E3A\u7A7A");
1823
- return;
1824
- }
1825
- if (this.drawerForm.expireAt.trim() && !this.isValidDateString(this.drawerForm.expireAt.trim())) {
1826
- this.message.warning("\u5230\u671F\u65F6\u95F4\u683C\u5F0F\u65E0\u6548\uFF0C\u8BF7\u4F7F\u7528 YYYY-MM-DD");
1827
- return;
1828
- }
1829
- const normalized = {
1830
- id: this.editingId() || this.makeId(),
1831
- domain,
1832
- certPath: this.drawerForm.certPath.trim(),
1833
- keyPath: this.drawerForm.keyPath.trim(),
1834
- expireAt: this.drawerForm.expireAt.trim(),
1835
- status: this.normalizeStatus(this.drawerForm.status),
1836
- autoRenew: this.drawerForm.autoRenew
1837
- };
1838
- const duplicate = this.rows().find((item) => {
1839
- if (this.editingId() && item.id === this.editingId()) {
1840
- return false;
1841
- }
1842
- return this.buildSslUniqueKey(item) === this.buildSslUniqueKey(normalized);
1843
- });
1844
- if (duplicate) {
1845
- this.message.warning("\u8BC1\u4E66\u57DF\u540D + \u8BC1\u4E66\u8DEF\u5F84 + \u79C1\u94A5\u8DEF\u5F84\u91CD\u590D");
1846
- return;
1847
- }
1848
- if (this.editingId()) {
1849
- this.rows.update((rows) => rows.map((item) => item.id === normalized.id ? normalized : item));
1850
- } else {
1851
- this.rows.update((rows) => [...rows, normalized]);
1852
- }
1853
- this.markDirty();
1854
- this.closeDrawer();
1855
- }
1856
- removeRow(id) {
1857
- this.rows.update((rows) => rows.filter((row) => row.id !== id));
1858
- this.markDirty();
1859
- }
1860
- markDirty() {
1861
- this.dirty.set(true);
1862
- }
1863
- statusText(status) {
1864
- switch (status) {
1865
- case "valid":
1866
- return "\u6709\u6548";
1867
- case "expiring":
1868
- return "\u5373\u5C06\u8FC7\u671F";
1869
- case "expired":
1870
- return "\u5DF2\u8FC7\u671F";
1871
- default:
1872
- return "\u5F85\u63A5\u5165";
1873
- }
1874
- }
1875
- async saveAll() {
1876
- const payload = [];
1877
- const uniqueSet = /* @__PURE__ */ new Set();
1878
- for (const row of this.rows()) {
1879
- const domain = row.domain.trim();
1880
- if (!domain) {
1881
- this.message.warning("\u8BC1\u4E66\u57DF\u540D\u4E0D\u80FD\u4E3A\u7A7A");
1882
- return;
1883
- }
1884
- const certPath = row.certPath.trim();
1885
- const keyPath = row.keyPath.trim();
1886
- if (!certPath) {
1887
- this.message.warning(`\u57DF\u540D "${domain}" \u7684\u8BC1\u4E66\u8DEF\u5F84\u4E0D\u80FD\u4E3A\u7A7A`);
1888
- return;
1889
- }
1890
- if (!keyPath) {
1891
- this.message.warning(`\u57DF\u540D "${domain}" \u7684\u79C1\u94A5\u8DEF\u5F84\u4E0D\u80FD\u4E3A\u7A7A`);
1892
- return;
1893
- }
1894
- const expireAt = row.expireAt.trim();
1895
- if (expireAt && !this.isValidDateString(expireAt)) {
1896
- this.message.warning(`\u57DF\u540D "${domain}" \u7684\u5230\u671F\u65F6\u95F4\u683C\u5F0F\u65E0\u6548\uFF0C\u8BF7\u4F7F\u7528 YYYY-MM-DD`);
1897
- return;
1898
- }
1899
- const uniqueKey = this.buildSslUniqueKey({ domain, certPath, keyPath });
1900
- if (uniqueSet.has(uniqueKey)) {
1901
- this.message.warning(`\u68C0\u6D4B\u5230\u91CD\u590D\u8BC1\u4E66\u8BB0\u5F55: ${domain}`);
1902
- return;
1903
- }
1904
- uniqueSet.add(uniqueKey);
1905
- payload.push(__spreadProps(__spreadValues({}, row), {
1906
- id: row.id || this.makeId(),
1907
- domain,
1908
- certPath,
1909
- keyPath,
1910
- expireAt,
1911
- status: this.normalizeStatus(row.status)
1912
- }));
1913
- }
1914
- this.saving.set(true);
1915
- try {
1916
- const res = await this.moduleStore.saveSslCertificates(payload);
1917
- if (res.success) {
1918
- this.message.success("SSL \u914D\u7F6E\u5DF2\u4FDD\u5B58");
1919
- this.dirty.set(false);
1920
- await this.loadData();
1921
- } else {
1922
- this.message.error(res.error || "\u4FDD\u5B58 SSL \u914D\u7F6E\u5931\u8D25");
1923
- }
1924
- } catch (err) {
1925
- this.message.error("\u4FDD\u5B58 SSL \u914D\u7F6E\u5931\u8D25: " + err.message);
1926
- } finally {
1927
- this.saving.set(false);
1928
- }
1929
- }
1930
- normalizeStatus(status) {
1931
- if (status === "valid" || status === "expiring" || status === "expired" || status === "pending") {
1932
- return status;
1933
- }
1934
- return "pending";
1935
- }
1936
- makeId() {
1937
- return `ssl-${Date.now()}-${Math.random().toString(16).slice(2, 8)}`;
1938
- }
1939
- isValidDateString(value) {
1940
- if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) {
1941
- return false;
1942
- }
1943
- const [year, month, day] = value.split("-").map((item) => Number(item));
1944
- if (!Number.isInteger(year) || !Number.isInteger(month) || !Number.isInteger(day)) {
1945
- return false;
1946
- }
1947
- const date = new Date(Date.UTC(year, month - 1, day));
1948
- return date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day;
1949
- }
1950
- buildSslUniqueKey(input) {
1951
- return `${input.domain.trim().toLowerCase()}|${input.certPath.trim().toLowerCase()}|${input.keyPath.trim().toLowerCase()}`;
1952
- }
1953
- createEmptyForm() {
1954
- return {
1955
- domain: "",
1956
- certPath: "",
1957
- keyPath: "",
1958
- expireAt: "",
1959
- status: "pending",
1960
- autoRenew: false
1961
- };
1962
- }
1963
- };
1964
- NginxSecondarySslTabComponent = __decorate([
1965
- Component({
1966
- selector: "app-nginx-secondary-ssl-tab",
1967
- standalone: true,
1968
- imports: [
1969
- CommonModule,
1970
- FormsModule,
1971
- NzButtonModule,
1972
- NzDrawerModule,
1973
- NzFormModule,
1974
- NzIconModule,
1975
- NzInputModule,
1976
- NzSelectModule,
1977
- NzSpinModule,
1978
- NzSwitchModule
1979
- ],
1980
- template: `
1981
- <div class="panel-header-row">
1982
- <span class="panel-tip">\u7BA1\u7406 SSL \u8BC1\u4E66\u4E0E\u5230\u671F\u72B6\u6001</span>
1983
- <div class="header-actions">
1984
- <button nz-button nzType="default" (click)="openCreateDrawer()">
1985
- <nz-icon nzType="plus" nzTheme="outline"></nz-icon>
1986
- \u65B0\u589E\u8BC1\u4E66
1987
- </button>
1988
- <button nz-button nzType="primary" (click)="saveAll()" [nzLoading]="saving()" [disabled]="!dirty()">
1989
- <nz-icon nzType="save" nzTheme="outline"></nz-icon>
1990
- \u4FDD\u5B58
1991
- </button>
1992
- </div>
1993
- </div>
1994
-
1995
- <nz-spin [nzSpinning]="loading()">
1996
- <div class="ssl-cards">
1997
- @if (!rows().length) {
1998
- <div class="empty-row">\u6682\u65E0 SSL \u8BC1\u4E66\u914D\u7F6E\uFF0C\u70B9\u51FB\u201C\u65B0\u589E\u8BC1\u4E66\u201D\u5F00\u59CB\u7EF4\u62A4</div>
1999
- } @else {
2000
- @for (row of rows(); track row.id) {
2001
- <div class="ssl-card">
2002
- <div class="card-head">
2003
- <div class="card-domain mono">{{ row.domain }}</div>
2004
- <span class="status-badge" [ngClass]="row.status">{{ statusText(row.status) }}</span>
2005
- </div>
2006
-
2007
- <div class="meta-list">
2008
- <div class="meta-item">
2009
- <span class="meta-label">\u5230\u671F\u65F6\u95F4</span>
2010
- <span class="meta-value mono">{{ row.expireAt || '-' }}</span>
2011
- </div>
2012
- <div class="meta-item">
2013
- <span class="meta-label">\u81EA\u52A8\u7EED\u671F</span>
2014
- <span class="meta-value">{{ row.autoRenew ? '\u5DF2\u542F\u7528' : '\u672A\u542F\u7528' }}</span>
2015
- </div>
2016
- <div class="meta-item block">
2017
- <span class="meta-label">\u8BC1\u4E66\u8DEF\u5F84</span>
2018
- <span class="meta-value mono">{{ row.certPath || '-' }}</span>
2019
- </div>
2020
- <div class="meta-item block">
2021
- <span class="meta-label">\u79C1\u94A5\u8DEF\u5F84</span>
2022
- <span class="meta-value mono">{{ row.keyPath || '-' }}</span>
2023
- </div>
2024
- </div>
2025
-
2026
- <div class="card-actions">
2027
- <button nz-button nzType="default" nzSize="small" (click)="openEditDrawer(row)">\u7F16\u8F91</button>
2028
- <button nz-button nzType="default" nzDanger nzSize="small" (click)="removeRow(row.id)">\u5220\u9664</button>
2029
- </div>
2030
- </div>
2031
- }
2032
- }
2033
- </div>
2034
- </nz-spin>
2035
-
2036
- <nz-drawer
2037
- [nzVisible]="drawerVisible()"
2038
- [nzTitle]="editingId() ? '\u7F16\u8F91\u8BC1\u4E66' : '\u65B0\u589E\u8BC1\u4E66'"
2039
- [nzWidth]="500"
2040
- [nzPlacement]="'right'"
2041
- (nzOnClose)="closeDrawer()"
2042
- >
2043
- <ng-container *nzDrawerContent>
2044
- <div class="drawer-body">
2045
- <form nz-form nzLayout="vertical">
2046
- <nz-form-item>
2047
- <nz-form-label nzRequired>\u57DF\u540D</nz-form-label>
2048
- <nz-form-control>
2049
- <input
2050
- nz-input
2051
- [(ngModel)]="drawerForm.domain"
2052
- name="sslDomain"
2053
- placeholder="example.com"
2054
- class="mono"
2055
- />
2056
- </nz-form-control>
2057
- </nz-form-item>
2058
-
2059
- <div class="form-grid">
2060
- <nz-form-item>
2061
- <nz-form-label>\u72B6\u6001</nz-form-label>
2062
- <nz-form-control>
2063
- <nz-select [(ngModel)]="drawerForm.status" name="sslStatus">
2064
- <nz-option nzValue="valid" nzLabel="\u6709\u6548"></nz-option>
2065
- <nz-option nzValue="expiring" nzLabel="\u5373\u5C06\u8FC7\u671F"></nz-option>
2066
- <nz-option nzValue="expired" nzLabel="\u5DF2\u8FC7\u671F"></nz-option>
2067
- <nz-option nzValue="pending" nzLabel="\u5F85\u63A5\u5165"></nz-option>
2068
- </nz-select>
2069
- </nz-form-control>
2070
- </nz-form-item>
2071
-
2072
- <nz-form-item>
2073
- <nz-form-label>\u5230\u671F\u65F6\u95F4</nz-form-label>
2074
- <nz-form-control>
2075
- <input
2076
- nz-input
2077
- [(ngModel)]="drawerForm.expireAt"
2078
- name="sslExpireAt"
2079
- placeholder="YYYY-MM-DD"
2080
- class="mono"
2081
- />
2082
- </nz-form-control>
2083
- </nz-form-item>
2084
- </div>
2085
-
2086
- <nz-form-item>
2087
- <nz-form-label>\u81EA\u52A8\u7EED\u671F</nz-form-label>
2088
- <nz-form-control>
2089
- <nz-switch [(ngModel)]="drawerForm.autoRenew" name="sslAutoRenew"></nz-switch>
2090
- </nz-form-control>
2091
- </nz-form-item>
2092
-
2093
- <nz-form-item>
2094
- <nz-form-label nzRequired>\u8BC1\u4E66\u8DEF\u5F84</nz-form-label>
2095
- <nz-form-control>
2096
- <input
2097
- nz-input
2098
- [(ngModel)]="drawerForm.certPath"
2099
- name="sslCertPath"
2100
- placeholder="/etc/nginx/ssl/fullchain.pem"
2101
- class="mono"
2102
- />
2103
- </nz-form-control>
2104
- </nz-form-item>
2105
-
2106
- <nz-form-item>
2107
- <nz-form-label nzRequired>\u79C1\u94A5\u8DEF\u5F84</nz-form-label>
2108
- <nz-form-control>
2109
- <input
2110
- nz-input
2111
- [(ngModel)]="drawerForm.keyPath"
2112
- name="sslKeyPath"
2113
- placeholder="/etc/nginx/ssl/privkey.pem"
2114
- class="mono"
2115
- />
2116
- </nz-form-control>
2117
- </nz-form-item>
2118
- </form>
2119
- </div>
2120
-
2121
- <div class="drawer-footer">
2122
- <button nz-button nzType="default" (click)="closeDrawer()">\u53D6\u6D88</button>
2123
- <button nz-button nzType="primary" (click)="submitDrawer()">\u786E\u5B9A</button>
2124
- </div>
2125
- </ng-container>
2126
- </nz-drawer>
2127
- `,
2128
- styles: [nginx_secondary_ssl_tab_component_default]
2129
- })
2130
- ], NginxSecondarySslTabComponent);
2131
-
2132
- // angular:jit:style:inline:src\app\pages\nginx\components\nginx-tabs\nginx-secondary-test-tab\nginx-secondary-test-tab.component.ts;CiAgICA6aG9zdCB7CiAgICAgIGRpc3BsYXk6IGJsb2NrOwogICAgfQoKICAgIC5wYW5lbC1oZWFkZXItcm93IHsKICAgICAgZGlzcGxheTogZmxleDsKICAgICAganVzdGlmeS1jb250ZW50OiBzcGFjZS1iZXR3ZWVuOwogICAgICBhbGlnbi1pdGVtczogY2VudGVyOwogICAgICBnYXA6IDhweDsKICAgICAgbWFyZ2luLWJvdHRvbTogMTJweDsKICAgIH0KCiAgICAucGFuZWwtdGlwIHsKICAgICAgZm9udC1zaXplOiB2YXIoLS1uZ2lueC1mb250LXNpemUtYmFzZSwgMTRweCk7CiAgICAgIGNvbG9yOiB2YXIoLS10ZXh0LTIpOwogICAgfQoKICAgIC5ydW4tdGVzdC1idG4gewogICAgICBiYWNrZ3JvdW5kOiB2YXIoLS1ncmVlbik7CiAgICAgIGNvbG9yOiAjZmZmOwogICAgICBib3JkZXItY29sb3I6IHZhcigtLWdyZWVuKTsKICAgICAgZm9udC13ZWlnaHQ6IDYwMDsKICAgICAgbWluLWhlaWdodDogMjhweDsKICAgICAgcGFkZGluZy1pbmxpbmU6IDEwcHg7CgogICAgICAmOmhvdmVyLAogICAgICAmOmZvY3VzIHsKICAgICAgICBiYWNrZ3JvdW5kOiAjMDBhMTIyOwogICAgICAgIGJvcmRlci1jb2xvcjogIzAwYTEyMjsKICAgICAgICBjb2xvcjogI2ZmZjsKICAgICAgfQogICAgfQoKICAgIEBtZWRpYSAobWF4LXdpZHRoOiA5OTJweCkgewogICAgICAucGFuZWwtaGVhZGVyLXJvdyB7CiAgICAgICAgZmxleC1kaXJlY3Rpb246IGNvbHVtbjsKICAgICAgICBhbGlnbi1pdGVtczogZmxleC1zdGFydDsKICAgICAgfQogICAgfQogIA==
2133
- var nginx_secondary_test_tab_component_default = "/* angular:styles/component:less;4e4a26faa779f63e11c9d16baaa3bc4dfacf1d8fda3e06af5ec5604e4dfcf2b4;D:\\ng-manager\\webapp\\src\\app\\pages\\nginx\\components\\nginx-tabs\\nginx-secondary-test-tab\\nginx-secondary-test-tab.component.ts */\n:host {\n display: block;\n}\n.panel-header-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n gap: 8px;\n margin-bottom: 12px;\n}\n.panel-tip {\n font-size: var(--nginx-font-size-base, 14px);\n color: var(--text-2);\n}\n.run-test-btn {\n background: var(--green);\n color: #fff;\n border-color: var(--green);\n font-weight: 600;\n min-height: 28px;\n padding-inline: 10px;\n}\n.run-test-btn:hover,\n.run-test-btn:focus {\n background: #00a122;\n border-color: #00a122;\n color: #fff;\n}\n@media (max-width: 992px) {\n .panel-header-row {\n flex-direction: column;\n align-items: flex-start;\n }\n}\n/*# sourceMappingURL=nginx-secondary-test-tab.component.css.map */\n";
2134
-
2135
- // src/app/pages/nginx/components/nginx-tabs/nginx-secondary-test-tab/nginx-secondary-test-tab.component.ts
2136
- var NginxSecondaryTestTabComponent = class NginxSecondaryTestTabComponent2 {
2137
- loading = false;
2138
- logs = [];
2139
- runTest = new EventEmitter();
2140
- static propDecorators = {
2141
- loading: [{ type: Input }],
2142
- logs: [{ type: Input }],
2143
- runTest: [{ type: Output }]
2144
- };
2145
- };
2146
- NginxSecondaryTestTabComponent = __decorate([
2147
- Component({
2148
- selector: "app-nginx-secondary-test-tab",
2149
- standalone: true,
2150
- imports: [CommonModule, NzButtonModule, NzIconModule, NginxLogViewerComponent],
2151
- template: `
2152
- <div class="panel-header-row">
2153
- <span class="panel-tip">\u9A8C\u8BC1 Nginx \u914D\u7F6E\u6587\u4EF6\u8BED\u6CD5\u6B63\u786E\u6027</span>
2154
- <button
2155
- nz-button
2156
- nzType="default"
2157
- nzSize="small"
2158
- class="run-test-btn"
2159
- (click)="runTest.emit()"
2160
- [nzLoading]="loading"
2161
- >
2162
- <nz-icon nzType="check-circle" nzTheme="outline"></nz-icon>
2163
- \u6267\u884C\u68C0\u6D4B
2164
- </button>
2165
- </div>
2166
- <app-nginx-log-viewer
2167
- [title]="'\u7ED3\u679C'"
2168
- [status]="'\u2713 syntax is ok'"
2169
- statusTone="ok"
2170
- [logs]="logs"
2171
- [maxHeight]="180"
2172
- ></app-nginx-log-viewer>
2173
- `,
2174
- styles: [nginx_secondary_test_tab_component_default]
2175
- })
2176
- ], NginxSecondaryTestTabComponent);
2177
-
2178
- // angular:jit:style:inline:src\app\pages\nginx\components\nginx-tabs\nginx-secondary-traffic-tab\nginx-secondary-traffic-tab.component.ts;CiAgICA6aG9zdCB7CiAgICAgIGRpc3BsYXk6IGJsb2NrOwogICAgfQoKICAgIC5wYW5lbC1oZWFkZXItcm93IHsKICAgICAgZGlzcGxheTogZmxleDsKICAgICAganVzdGlmeS1jb250ZW50OiBzcGFjZS1iZXR3ZWVuOwogICAgICBhbGlnbi1pdGVtczogY2VudGVyOwogICAgICBnYXA6IDhweDsKICAgICAgbWFyZ2luLWJvdHRvbTogMTJweDsKICAgIH0KCiAgICAucGFuZWwtdGlwIHsKICAgICAgZm9udC1zaXplOiB2YXIoLS1uZ2lueC1mb250LXNpemUtc20sIDEycHgpOwogICAgICBjb2xvcjogdmFyKC0tdGV4dC0zKTsKICAgIH0KCiAgICAuc2V0dGluZy1yb3cgewogICAgICBkaXNwbGF5OiBmbGV4OwogICAgICBqdXN0aWZ5LWNvbnRlbnQ6IHNwYWNlLWJldHdlZW47CiAgICAgIGFsaWduLWl0ZW1zOiBjZW50ZXI7CiAgICAgIGdhcDogMTBweDsKICAgICAgcGFkZGluZzogMTBweCAwOwogICAgICBib3JkZXItYm90dG9tOiAxcHggc29saWQgdmFyKC0tYm9yZGVyLWxpZ2h0KTsKCiAgICAgICYubm8tYm9yZGVyIHsKICAgICAgICBib3JkZXItYm90dG9tOiBub25lOwogICAgICB9CiAgICB9CgogICAgLnNldHRpbmctbGFiZWwgewogICAgICBmb250LXNpemU6IHZhcigtLW5naW54LWZvbnQtc2l6ZS1zbSwgMTJweCk7CiAgICAgIGZvbnQtd2VpZ2h0OiA2MDA7CiAgICAgIGNvbG9yOiB2YXIoLS10ZXh0LTEpOwogICAgfQoKICAgIC5zZXR0aW5nLWRlc2MgewogICAgICBmb250LXNpemU6IHZhcigtLW5naW54LWZvbnQtc2l6ZS1zbSwgMTJweCk7CiAgICAgIGNvbG9yOiB2YXIoLS10ZXh0LTMpOwogICAgICBtYXJnaW4tdG9wOiAycHg7CiAgICB9CgogICAgLnNldHRpbmctY3RybCB7CiAgICAgIGRpc3BsYXk6IGZsZXg7CiAgICAgIGFsaWduLWl0ZW1zOiBjZW50ZXI7CiAgICAgIGdhcDogOHB4OwogICAgfQoKICAgIC5zZXR0aW5nLWlucHV0IHsKICAgICAgd2lkdGg6IDExMHB4OwogICAgICBwYWRkaW5nOiA1cHggOHB4OwogICAgICBib3JkZXItcmFkaXVzOiA0cHg7CiAgICAgIGJvcmRlcjogMXB4IHNvbGlkIHZhcigtLWJvcmRlcik7CiAgICAgIGJhY2tncm91bmQ6IHZhcigtLWJnLWlucHV0KTsKICAgICAgY29sb3I6IHZhcigtLXRleHQtMik7CiAgICAgIGZvbnQtc2l6ZTogdmFyKC0tbmdpbngtZm9udC1zaXplLXNtLCAxMnB4KTsKICAgICAgdGV4dC1hbGlnbjogY2VudGVyOwogICAgICBmb250LWZhbWlseTogdmFyKC0tbmdpbngtZm9udC1mYW1pbHktbW9ubywgdWktbW9ub3NwYWNlLCBTRk1vbm8tUmVndWxhciwgTWVubG8sIE1vbmFjbywgQ29uc29sYXMsICdMaWJlcmF0aW9uIE1vbm8nLCBtb25vc3BhY2UpOwogICAgICBvdXRsaW5lOiBub25lOwogICAgfQoKICAgIC5zZXR0aW5nLWlucHV0OmZvY3VzLAogICAgLnNldHRpbmctdGV4dGFyZWE6Zm9jdXMgewogICAgICBib3JkZXItY29sb3I6IHZhcigtLWJsdWUpOwogICAgICBib3gtc2hhZG93OiAwIDAgMCAycHggdmFyKC0tYmx1ZS1ib3JkZXIpOwogICAgfQoKICAgIC5zZXR0aW5nLXRleHRhcmVhIHsKICAgICAgd2lkdGg6IDEwMCU7CiAgICAgIG1hcmdpbi10b3A6IDhweDsKICAgICAgYm9yZGVyOiAxcHggc29saWQgdmFyKC0tYm9yZGVyKTsKICAgICAgYm9yZGVyLXJhZGl1czogNnB4OwogICAgICBiYWNrZ3JvdW5kOiB2YXIoLS1iZy1pbnB1dCk7CiAgICAgIGNvbG9yOiB2YXIoLS10ZXh0LTIpOwogICAgICBmb250LXNpemU6IHZhcigtLW5naW54LWZvbnQtc2l6ZS1zbSwgMTJweCk7CiAgICAgIHBhZGRpbmc6IDhweCAxMHB4OwogICAgICByZXNpemU6IHZlcnRpY2FsOwogICAgICBvdXRsaW5lOiBub25lOwogICAgfQoKICAgIC5tb25vIHsKICAgICAgZm9udC1mYW1pbHk6IHZhcigtLW5naW54LWZvbnQtZmFtaWx5LW1vbm8sIHVpLW1vbm9zcGFjZSwgU0ZNb25vLVJlZ3VsYXIsIE1lbmxvLCBNb25hY28sIENvbnNvbGFzLCAnTGliZXJhdGlvbiBNb25vJywgbW9ub3NwYWNlKTsKICAgIH0KICA=
2179
- var nginx_secondary_traffic_tab_component_default = '/* angular:styles/component:less;5a71cddcc596825b3daa12a965f8e30bb11a9306650c2cb8ca3f89edaa6ffb1a;D:\\ng-manager\\webapp\\src\\app\\pages\\nginx\\components\\nginx-tabs\\nginx-secondary-traffic-tab\\nginx-secondary-traffic-tab.component.ts */\n:host {\n display: block;\n}\n.panel-header-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n gap: 8px;\n margin-bottom: 12px;\n}\n.panel-tip {\n font-size: var(--nginx-font-size-sm, 12px);\n color: var(--text-3);\n}\n.setting-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n gap: 10px;\n padding: 10px 0;\n border-bottom: 1px solid var(--border-light);\n}\n.setting-row.no-border {\n border-bottom: none;\n}\n.setting-label {\n font-size: var(--nginx-font-size-sm, 12px);\n font-weight: 600;\n color: var(--text-1);\n}\n.setting-desc {\n font-size: var(--nginx-font-size-sm, 12px);\n color: var(--text-3);\n margin-top: 2px;\n}\n.setting-ctrl {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n.setting-input {\n width: 110px;\n padding: 5px 8px;\n border-radius: 4px;\n border: 1px solid var(--border);\n background: var(--bg-input);\n color: var(--text-2);\n font-size: var(--nginx-font-size-sm, 12px);\n text-align: center;\n font-family: var(--nginx-font-family-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace);\n outline: none;\n}\n.setting-input:focus,\n.setting-textarea:focus {\n border-color: var(--blue);\n box-shadow: 0 0 0 2px var(--blue-border);\n}\n.setting-textarea {\n width: 100%;\n margin-top: 8px;\n border: 1px solid var(--border);\n border-radius: 6px;\n background: var(--bg-input);\n color: var(--text-2);\n font-size: var(--nginx-font-size-sm, 12px);\n padding: 8px 10px;\n resize: vertical;\n outline: none;\n}\n.mono {\n font-family: var(--nginx-font-family-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace);\n}\n/*# sourceMappingURL=nginx-secondary-traffic-tab.component.css.map */\n';
2180
-
2181
- // src/app/pages/nginx/components/nginx-tabs/nginx-secondary-traffic-tab/nginx-secondary-traffic-tab.component.ts
2182
- var NginxSecondaryTrafficTabComponent = class NginxSecondaryTrafficTabComponent2 {
2183
- moduleStore = inject(NginxModuleStore);
2184
- message = inject(NzMessageService);
2185
- saving = signal(false);
2186
- dirty = signal(false);
2187
- config = signal({
2188
- rateLimitEnabled: false,
2189
- rateLimit: "",
2190
- connLimitEnabled: false,
2191
- connLimit: 0,
2192
- blacklistIps: []
2193
- });
2194
- blacklistText = signal("");
2195
- async ngOnInit() {
2196
- await this.load();
2197
- }
2198
- async load() {
2199
- try {
2200
- const res = await this.moduleStore.loadTrafficConfig();
2201
- if (res.success && res.traffic) {
2202
- const traffic = this.moduleStore.trafficConfig();
2203
- this.config.set(__spreadProps(__spreadValues({}, traffic), {
2204
- connLimit: Math.max(0, Number(traffic.connLimit ?? 0))
2205
- }));
2206
- this.blacklistText.set((traffic.blacklistIps || []).join("\n"));
2207
- this.dirty.set(false);
2208
- } else {
2209
- this.message.error(res.error || "\u52A0\u8F7D\u6D41\u91CF\u914D\u7F6E\u5931\u8D25");
2210
- }
2211
- } catch (err) {
2212
- this.message.error("\u52A0\u8F7D\u6D41\u91CF\u914D\u7F6E\u5931\u8D25: " + err.message);
2213
- }
2214
- }
2215
- setRateLimitEnabled(checked) {
2216
- this.config.update((prev) => __spreadProps(__spreadValues({}, prev), { rateLimitEnabled: checked }));
2217
- this.markDirty();
2218
- }
2219
- setConnLimitEnabled(checked) {
2220
- this.config.update((prev) => __spreadProps(__spreadValues({}, prev), { connLimitEnabled: checked }));
2221
- this.markDirty();
2222
- }
2223
- setBlacklistText(value) {
2224
- this.blacklistText.set(value || "");
2225
- this.markDirty();
2226
- }
2227
- markDirty() {
2228
- this.dirty.set(true);
2229
- }
2230
- async save() {
2231
- const payload = __spreadProps(__spreadValues({}, this.config()), {
2232
- rateLimit: this.config().rateLimit.trim(),
2233
- connLimit: this.config().connLimitEnabled ? Math.max(1, Number(this.config().connLimit || 1)) : Math.max(0, Number(this.config().connLimit || 0)),
2234
- blacklistIps: this.blacklistText().split(/[\n,]/).map((item) => item.trim()).filter(Boolean)
2235
- });
2236
- this.saving.set(true);
2237
- try {
2238
- const res = await this.moduleStore.saveTrafficConfig(payload);
2239
- if (res.success) {
2240
- this.message.success("\u6D41\u91CF\u63A7\u5236\u914D\u7F6E\u5DF2\u4FDD\u5B58");
2241
- this.dirty.set(false);
2242
- await this.load();
2243
- } else {
2244
- this.message.error(res.error || "\u4FDD\u5B58\u6D41\u91CF\u63A7\u5236\u914D\u7F6E\u5931\u8D25");
2245
- }
2246
- } catch (err) {
2247
- this.message.error("\u4FDD\u5B58\u6D41\u91CF\u63A7\u5236\u914D\u7F6E\u5931\u8D25: " + err.message);
2248
- } finally {
2249
- this.saving.set(false);
2250
- }
2251
- }
2252
- };
2253
- NginxSecondaryTrafficTabComponent = __decorate([
2254
- Component({
2255
- selector: "app-nginx-secondary-traffic-tab",
2256
- standalone: true,
2257
- imports: [CommonModule, FormsModule, NzButtonModule, NzIconModule, NzSwitchModule],
2258
- template: `
2259
- <div class="panel-header-row">
2260
- <span class="panel-tip">\u6D41\u91CF\u63A7\u5236\u7B56\u7565</span>
2261
- <button nz-button nzType="primary" (click)="save()" [nzLoading]="saving()" [disabled]="!dirty()">
2262
- <nz-icon nzType="save" nzTheme="outline"></nz-icon>
2263
- \u4FDD\u5B58
2264
- </button>
2265
- </div>
2266
-
2267
- <div class="setting-row">
2268
- <div>
2269
- <div class="setting-label">\u8BF7\u6C42\u9650\u6D41</div>
2270
- <div class="setting-desc">\u6BCF\u4E2A IP \u6BCF\u79D2\u6700\u5927\u8BF7\u6C42\u6570</div>
2271
- </div>
2272
- <div class="setting-ctrl">
2273
- <input
2274
- class="setting-input"
2275
- [(ngModel)]="config().rateLimit"
2276
- (ngModelChange)="markDirty()"
2277
- placeholder="20r/s"
2278
- />
2279
- <nz-switch
2280
- name="rateLimitEnabled"
2281
- nzSize="small"
2282
- [ngModel]="config().rateLimitEnabled"
2283
- (ngModelChange)="setRateLimitEnabled($event)"
2284
- ></nz-switch>
2285
- </div>
2286
- </div>
2287
-
2288
- <div class="setting-row">
2289
- <div>
2290
- <div class="setting-label">\u8FDE\u63A5\u9650\u5236</div>
2291
- <div class="setting-desc">\u5355 IP \u6700\u5927\u5E76\u53D1\u8FDE\u63A5\u6570</div>
2292
- </div>
2293
- <div class="setting-ctrl">
2294
- <input
2295
- class="setting-input"
2296
- type="number"
2297
- min="1"
2298
- [(ngModel)]="config().connLimit"
2299
- (ngModelChange)="markDirty()"
2300
- />
2301
- <nz-switch
2302
- name="connLimitEnabled"
2303
- nzSize="small"
2304
- [ngModel]="config().connLimitEnabled"
2305
- (ngModelChange)="setConnLimitEnabled($event)"
2306
- ></nz-switch>
2307
- </div>
2308
- </div>
2309
-
2310
- <div class="setting-row no-border">
2311
- <div class="full-width">
2312
- <div class="setting-label">\u9ED1\u540D\u5355 IP</div>
2313
- <div class="setting-desc">\u9017\u53F7\u6216\u6362\u884C\u5206\u9694</div>
2314
- <textarea
2315
- class="setting-textarea mono"
2316
- [ngModel]="blacklistText()"
2317
- (ngModelChange)="setBlacklistText($event)"
2318
- rows="4"
2319
- placeholder="192.168.1.10&#10;10.10.10.5"
2320
- ></textarea>
2321
- </div>
2322
- </div>
2323
- `,
2324
- styles: [nginx_secondary_traffic_tab_component_default]
2325
- })
2326
- ], NginxSecondaryTrafficTabComponent);
2327
-
2328
- // angular:jit:style:inline:src\app\pages\nginx\components\nginx-tabs\nginx-secondary-upstream-tab\nginx-secondary-upstream-tab.component.ts;CiAgICA6aG9zdCB7CiAgICAgIGRpc3BsYXk6IGJsb2NrOwogICAgfQoKICAgIC5saXN0LWhlYWRlciB7CiAgICAgIGRpc3BsYXk6IGZsZXg7CiAgICAgIGFsaWduLWl0ZW1zOiBjZW50ZXI7CiAgICAgIGp1c3RpZnktY29udGVudDogc3BhY2UtYmV0d2VlbjsKICAgICAgZ2FwOiAxMHB4OwogICAgICBtYXJnaW4tYm90dG9tOiAxMnB4OwogICAgfQoKICAgIC5wYW5lbC10aXAgewogICAgICBmb250LXNpemU6IHZhcigtLW5naW54LWZvbnQtc2l6ZS1zbSwgMTJweCk7CiAgICAgIGNvbG9yOiB2YXIoLS10ZXh0LTMpOwogICAgfQoKICAgIC5oZWFkZXItYWN0aW9ucyB7CiAgICAgIGRpc3BsYXk6IGZsZXg7CiAgICAgIGdhcDogOHB4OwogICAgICBmbGV4LXNocmluazogMDsKICAgIH0KCiAgICAudXBzdHJlYW0tZ3JpZC1zaGVsbCB7CiAgICAgIGJvcmRlcjogbm9uZTsKICAgICAgYm9yZGVyLXJhZGl1czogMDsKICAgICAgb3ZlcmZsb3c6IGhpZGRlbjsKICAgIH0KCiAgICAudXBzdHJlYW0tZ3JpZC1oZWFkLAogICAgLnVwc3RyZWFtLWdyaWQtcm93IHsKICAgICAgZGlzcGxheTogZ3JpZDsKICAgICAgZ3JpZC10ZW1wbGF0ZS1jb2x1bW5zOiBtaW5tYXgoMTgwcHgsIDFmcikgbWlubWF4KDE1MHB4LCAwLjlmcikgbWlubWF4KDEzMHB4LCAwLjdmcikgbWlubWF4KDIyMHB4LCAxLjZmcikgODhweCAxMTZweDsKICAgICAgYWxpZ24taXRlbXM6IGNlbnRlcjsKICAgICAgY29sdW1uLWdhcDogOHB4OwogICAgICBwYWRkaW5nOiAwIDEycHg7CiAgICB9CgogICAgLnVwc3RyZWFtLWdyaWQtaGVhZCB7CiAgICAgIG1pbi1oZWlnaHQ6IDQycHg7CiAgICAgIGJhY2tncm91bmQ6ICNmYWZhZmE7CiAgICAgIGJvcmRlci1ib3R0b206IDFweCBzb2xpZCByZ2JhKDAsIDAsIDAsIDAuMDYpOwoKICAgICAgLmNlbGwgewogICAgICAgIGZvbnQtc2l6ZTogdmFyKC0tbmdpbngtZm9udC1zaXplLXNtLCAxMnB4KTsKICAgICAgICBjb2xvcjogcmdiYSgwLCAwLCAwLCAwLjQ1KTsKICAgICAgICB0ZXh0LXRyYW5zZm9ybTogdXBwZXJjYXNlOwogICAgICAgIGxldHRlci1zcGFjaW5nOiAwLjRweDsKICAgICAgICBmb250LXdlaWdodDogNzAwOwogICAgICB9CiAgICB9CgogICAgLnVwc3RyZWFtLWdyaWQtYm9keSB7CiAgICAgIGJhY2tncm91bmQ6ICNmZmY7CiAgICB9CgogICAgLnVwc3RyZWFtLWdyaWQtcm93IHsKICAgICAgbWluLWhlaWdodDogNThweDsKICAgICAgYm9yZGVyLWJvdHRvbTogMXB4IHNvbGlkIHJnYmEoMCwgMCwgMCwgMC4wNSk7CiAgICAgIHRyYW5zaXRpb246IGJhY2tncm91bmQgMTIwbXMgZWFzZTsKCiAgICAgICY6bGFzdC1jaGlsZCB7CiAgICAgICAgYm9yZGVyLWJvdHRvbTogbm9uZTsKICAgICAgfQoKICAgICAgJjpob3ZlciB7CiAgICAgICAgYmFja2dyb3VuZDogcmdiYSgwLCAwLCAwLCAwLjAyKTsKICAgICAgfQogICAgfQoKICAgIC5yZWFkb25seS1yb3cgewogICAgICBiYWNrZ3JvdW5kOiByZ2JhKDAsIDAsIDAsIDAuMDE1KTsKICAgIH0KCiAgICAuY2VsbCB7CiAgICAgIG1pbi13aWR0aDogMDsKICAgIH0KICAgIC5yb3ctYWN0aW9uc3sKICAgICAgZGlzcGxheTogZmxleDsKICAgICAgZ2FwOiA0cHg7CiAgICAgIGp1c3RpZnktY29udGVudDogZmxleC1lbmQ7CiAgICB9CiAgICAudXBzdHJlYW0tbmFtZSB7CiAgICAgIGRpc3BsYXk6IGlubGluZS1ibG9jazsKICAgICAgZm9udC1zaXplOiB2YXIoLS1uZ2lueC1mb250LXNpemUtYmFzZSwgMTRweCk7CiAgICAgIGZvbnQtd2VpZ2h0OiA2MDA7CiAgICAgIGNvbG9yOiB2YXIoLS10ZXh0LTEpOwogICAgICB3b3JkLWJyZWFrOiBicmVhay1hbGw7CiAgICB9CgogICAgLnN0cmF0ZWd5LXBpbGwgewogICAgICBkaXNwbGF5OiBpbmxpbmUtZmxleDsKICAgICAgYWxpZ24taXRlbXM6IGNlbnRlcjsKICAgICAgaGVpZ2h0OiAyMnB4OwogICAgICBib3JkZXItcmFkaXVzOiAxMXB4OwogICAgICBwYWRkaW5nOiAwIDEwcHg7CiAgICAgIGZvbnQtc2l6ZTogdmFyKC0tbmdpbngtZm9udC1zaXplLXNtLCAxMnB4KTsKICAgICAgbGluZS1oZWlnaHQ6IDIycHg7CiAgICAgIGNvbG9yOiB2YXIoLS10ZXh0LTIpOwogICAgICBiYWNrZ3JvdW5kOiB2YXIoLS1iZy1pbnB1dCk7CiAgICAgIGJvcmRlcjogMXB4IHNvbGlkIHZhcigtLWJvcmRlci1saWdodCk7CiAgICB9CgogICAgLnNvdXJjZS1iYWRnZSB7CiAgICAgIGRpc3BsYXk6IGlubGluZS1mbGV4OwogICAgICBhbGlnbi1pdGVtczogY2VudGVyOwogICAgICBtYXgtd2lkdGg6IDEwMCU7CiAgICAgIGhlaWdodDogMjJweDsKICAgICAgcGFkZGluZzogMCA4cHg7CiAgICAgIGJvcmRlci1yYWRpdXM6IDRweDsKICAgICAgYmFja2dyb3VuZDogcmdiYSgwLCAwLCAwLCAwLjA0KTsKICAgICAgY29sb3I6IHZhcigtLXRleHQtMyk7CiAgICAgIGJvcmRlcjogMXB4IHNvbGlkIHJnYmEoMCwgMCwgMCwgMC4wOCk7CiAgICAgIGZvbnQtc2l6ZTogdmFyKC0tbmdpbngtZm9udC1zaXplLXNtLCAxMnB4KTsKICAgICAgb3ZlcmZsb3c6IGhpZGRlbjsKICAgICAgdGV4dC1vdmVyZmxvdzogZWxsaXBzaXM7CiAgICAgIHdoaXRlLXNwYWNlOiBub3dyYXA7CiAgICB9CgogICAgLnNvdXJjZS1iYWRnZS5tYW5hZ2VkIHsKICAgICAgYmFja2dyb3VuZDogcmdiYSgyMiwgOTMsIDI1NSwgMC4wOCk7CiAgICAgIGNvbG9yOiAjMTY1ZGZmOwogICAgICBib3JkZXItY29sb3I6IHJnYmEoMjIsIDkzLCAyNTUsIDAuMik7CiAgICB9CgogICAgLm5vZGVzLXdyYXAgewogICAgICBkaXNwbGF5OiBmbGV4OwogICAgICBmbGV4LXdyYXA6IHdyYXA7CiAgICAgIGdhcDogNnB4OwogICAgICBwYWRkaW5nOiA4cHggMDsKICAgIH0KCiAgICAubm9kZS1jaGlwIHsKICAgICAgZGlzcGxheTogaW5saW5lLWZsZXg7CiAgICAgIGFsaWduLWl0ZW1zOiBjZW50ZXI7CiAgICAgIGhlaWdodDogMjJweDsKICAgICAgcGFkZGluZzogMCA4cHg7CiAgICAgIGJvcmRlci1yYWRpdXM6IDRweDsKICAgICAgYm9yZGVyOiAxcHggc29saWQgcmdiYSgyMiwgOTMsIDI1NSwgMC4yKTsKICAgICAgYmFja2dyb3VuZDogcmdiYSgyMiwgOTMsIDI1NSwgMC4wOCk7CiAgICAgIGNvbG9yOiAjMTY1ZGZmOwogICAgICBmb250LXNpemU6IHZhcigtLW5naW54LWZvbnQtc2l6ZS1zbSwgMTJweCk7CiAgICAgIG1heC13aWR0aDogMTAwJTsKICAgICAgd29yZC1icmVhazogYnJlYWstYWxsOwogICAgfQoKICAgIC5ub2RlLWNvdW50IHsKICAgICAgZm9udC1zaXplOiB2YXIoLS1uZ2lueC1mb250LXNpemUtYmFzZSwgMTRweCk7CiAgICAgIGNvbG9yOiB2YXIoLS10ZXh0LTIpOwogICAgICBmb250LXdlaWdodDogNjAwOwogICAgfQoKICAgIC5hY3Rpb24tY29sIHsKICAgICAganVzdGlmeS1zZWxmOiBlbmQ7CiAgICB9CgogICAgLmVtcHR5LXN0YXRlIHsKICAgICAgdGV4dC1hbGlnbjogY2VudGVyOwogICAgICBwYWRkaW5nOiA0OHB4IDA7CgogICAgICAuZW1wdHktaWNvbiB7CiAgICAgICAgZm9udC1zaXplOiA0OHB4OwogICAgICAgIGNvbG9yOiByZ2JhKDAsIDAsIDAsIDAuMik7CiAgICAgICAgbWFyZ2luLWJvdHRvbTogMTZweDsKICAgICAgfQoKICAgICAgcCB7CiAgICAgICAgY29sb3I6IHJnYmEoMCwgMCwgMCwgMC40KTsKICAgICAgICBtYXJnaW46IDA7CiAgICAgIH0KICAgIH0KCiAgICAubW9ubyB7CiAgICAgIGZvbnQtZmFtaWx5OiB2YXIoLS1uZ2lueC1mb250LWZhbWlseS1tb25vLCB1aS1tb25vc3BhY2UsIFNGTW9uby1SZWd1bGFyLCBNZW5sbywgTW9uYWNvLCBDb25zb2xhcywgJ0xpYmVyYXRpb24gTW9ubycsIG1vbm9zcGFjZSk7CiAgICB9CgogICAgLmRyYXdlci1ib2R5IHsKICAgICAgcGFkZGluZzogMCAyNHB4IDE2cHg7CiAgICB9CgogICAgLmRyYXdlci1mb290ZXIgewogICAgICBwb3NpdGlvbjogYWJzb2x1dGU7CiAgICAgIGJvdHRvbTogMDsKICAgICAgbGVmdDogMDsKICAgICAgcmlnaHQ6IDA7CiAgICAgIHBhZGRpbmc6IDEycHggMjRweDsKICAgICAgYm9yZGVyLXRvcDogMXB4IHNvbGlkIHJnYmEoMCwgMCwgMCwgMC4wNik7CiAgICAgIGRpc3BsYXk6IGZsZXg7CiAgICAgIGp1c3RpZnktY29udGVudDogZmxleC1lbmQ7CiAgICAgIGdhcDogOHB4OwogICAgICBiYWNrZ3JvdW5kOiAjZmZmOwogICAgfQogIA==
2329
- var nginx_secondary_upstream_tab_component_default = '/* angular:styles/component:less;ec91592b326dafbcee5619c4f0f01ba9546bebad899d8280f72860951520b245;D:\\ng-manager\\webapp\\src\\app\\pages\\nginx\\components\\nginx-tabs\\nginx-secondary-upstream-tab\\nginx-secondary-upstream-tab.component.ts */\n:host {\n display: block;\n}\n.list-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 10px;\n margin-bottom: 12px;\n}\n.panel-tip {\n font-size: var(--nginx-font-size-sm, 12px);\n color: var(--text-3);\n}\n.header-actions {\n display: flex;\n gap: 8px;\n flex-shrink: 0;\n}\n.upstream-grid-shell {\n border: none;\n border-radius: 0;\n overflow: hidden;\n}\n.upstream-grid-head,\n.upstream-grid-row {\n display: grid;\n grid-template-columns: minmax(180px, 1fr) minmax(150px, 0.9fr) minmax(130px, 0.7fr) minmax(220px, 1.6fr) 88px 116px;\n align-items: center;\n column-gap: 8px;\n padding: 0 12px;\n}\n.upstream-grid-head {\n min-height: 42px;\n background: #fafafa;\n border-bottom: 1px solid rgba(0, 0, 0, 0.06);\n}\n.upstream-grid-head .cell {\n font-size: var(--nginx-font-size-sm, 12px);\n color: rgba(0, 0, 0, 0.45);\n text-transform: uppercase;\n letter-spacing: 0.4px;\n font-weight: 700;\n}\n.upstream-grid-body {\n background: #fff;\n}\n.upstream-grid-row {\n min-height: 58px;\n border-bottom: 1px solid rgba(0, 0, 0, 0.05);\n transition: background 120ms ease;\n}\n.upstream-grid-row:last-child {\n border-bottom: none;\n}\n.upstream-grid-row:hover {\n background: rgba(0, 0, 0, 0.02);\n}\n.readonly-row {\n background: rgba(0, 0, 0, 0.015);\n}\n.cell {\n min-width: 0;\n}\n.row-actions {\n display: flex;\n gap: 4px;\n justify-content: flex-end;\n}\n.upstream-name {\n display: inline-block;\n font-size: var(--nginx-font-size-base, 14px);\n font-weight: 600;\n color: var(--text-1);\n word-break: break-all;\n}\n.strategy-pill {\n display: inline-flex;\n align-items: center;\n height: 22px;\n border-radius: 11px;\n padding: 0 10px;\n font-size: var(--nginx-font-size-sm, 12px);\n line-height: 22px;\n color: var(--text-2);\n background: var(--bg-input);\n border: 1px solid var(--border-light);\n}\n.source-badge {\n display: inline-flex;\n align-items: center;\n max-width: 100%;\n height: 22px;\n padding: 0 8px;\n border-radius: 4px;\n background: rgba(0, 0, 0, 0.04);\n color: var(--text-3);\n border: 1px solid rgba(0, 0, 0, 0.08);\n font-size: var(--nginx-font-size-sm, 12px);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.source-badge.managed {\n background: rgba(22, 93, 255, 0.08);\n color: #165dff;\n border-color: rgba(22, 93, 255, 0.2);\n}\n.nodes-wrap {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n padding: 8px 0;\n}\n.node-chip {\n display: inline-flex;\n align-items: center;\n height: 22px;\n padding: 0 8px;\n border-radius: 4px;\n border: 1px solid rgba(22, 93, 255, 0.2);\n background: rgba(22, 93, 255, 0.08);\n color: #165dff;\n font-size: var(--nginx-font-size-sm, 12px);\n max-width: 100%;\n word-break: break-all;\n}\n.node-count {\n font-size: var(--nginx-font-size-base, 14px);\n color: var(--text-2);\n font-weight: 600;\n}\n.action-col {\n justify-self: end;\n}\n.empty-state {\n text-align: center;\n padding: 48px 0;\n}\n.empty-state .empty-icon {\n font-size: 48px;\n color: rgba(0, 0, 0, 0.2);\n margin-bottom: 16px;\n}\n.empty-state p {\n color: rgba(0, 0, 0, 0.4);\n margin: 0;\n}\n.mono {\n font-family: var(--nginx-font-family-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace);\n}\n.drawer-body {\n padding: 0 24px 16px;\n}\n.drawer-footer {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n padding: 12px 24px;\n border-top: 1px solid rgba(0, 0, 0, 0.06);\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n background: #fff;\n}\n/*# sourceMappingURL=nginx-secondary-upstream-tab.component.css.map */\n';
2330
-
2331
- // src/app/pages/nginx/components/nginx-tabs/nginx-secondary-upstream-tab/nginx-secondary-upstream-tab.component.ts
2332
- var NginxSecondaryUpstreamTabComponent = class NginxSecondaryUpstreamTabComponent2 {
2333
- moduleStore = inject(NginxModuleStore);
2334
- message = inject(NzMessageService);
2335
- loading = signal(false);
2336
- saving = signal(false);
2337
- dirty = signal(false);
2338
- rows = signal([]);
2339
- drawerVisible = signal(false);
2340
- editingId = signal(null);
2341
- drawerForm = this.createEmptyForm();
2342
- async ngOnInit() {
2343
- await this.loadData();
2344
- }
2345
- async loadData() {
2346
- this.loading.set(true);
2347
- try {
2348
- const res = await this.moduleStore.loadUpstreams();
2349
- if (res.success && res.upstreams) {
2350
- this.rows.set(this.moduleStore.upstreams().map((item) => ({
2351
- id: item.id,
2352
- name: item.name,
2353
- strategy: item.strategy,
2354
- nodes: (item.nodes || []).map((node) => node.trim()).filter(Boolean),
2355
- sourceFile: item.sourceFile || "",
2356
- managed: item.managed !== false,
2357
- readonly: item.readonly === true || item.managed === false
2358
- })));
2359
- this.dirty.set(false);
2360
- } else {
2361
- this.message.error(res.error || "\u52A0\u8F7D Upstream \u5931\u8D25");
2362
- }
2363
- } catch (err) {
2364
- this.message.error("\u52A0\u8F7D Upstream \u5931\u8D25: " + err.message);
2365
- } finally {
2366
- this.loading.set(false);
2367
- }
2368
- }
2369
- openCreateDrawer() {
2370
- this.editingId.set(null);
2371
- this.drawerForm = this.createEmptyForm();
2372
- this.drawerVisible.set(true);
2373
- }
2374
- openEditDrawer(row) {
2375
- if (row.readonly) {
2376
- this.message.info("\u8BE5 Upstream \u6765\u81EA\u5916\u90E8\u914D\u7F6E\u6587\u4EF6\uFF0C\u5F53\u524D\u4E3A\u53EA\u8BFB\u5C55\u793A");
2377
- return;
2378
- }
2379
- this.editingId.set(row.id);
2380
- this.drawerForm = {
2381
- name: row.name,
2382
- strategy: row.strategy,
2383
- nodesText: row.nodes.join(", ")
2384
- };
2385
- this.drawerVisible.set(true);
2386
- }
2387
- closeDrawer() {
2388
- this.drawerVisible.set(false);
2389
- }
2390
- submitDrawer() {
2391
- const name = this.drawerForm.name.trim();
2392
- if (!name) {
2393
- this.message.warning("Upstream \u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A");
2394
- return;
2395
- }
2396
- const nodes = this.parseNodes(this.drawerForm.nodesText);
2397
- if (!nodes.length) {
2398
- this.message.warning(`Upstream "${name}" \u81F3\u5C11\u9700\u8981\u4E00\u4E2A\u8282\u70B9`);
2399
- return;
2400
- }
2401
- const normalizedRow = {
2402
- id: this.editingId() || this.makeId(),
2403
- name,
2404
- strategy: this.drawerForm.strategy,
2405
- nodes,
2406
- sourceFile: "",
2407
- managed: true,
2408
- readonly: false
2409
- };
2410
- if (this.editingId()) {
2411
- this.rows.update((rows) => rows.map((item) => item.id === normalizedRow.id ? __spreadProps(__spreadValues({}, normalizedRow), {
2412
- sourceFile: item.sourceFile,
2413
- managed: item.managed,
2414
- readonly: item.readonly
2415
- }) : item));
2416
- } else {
2417
- this.rows.update((rows) => [...rows, normalizedRow]);
2418
- }
2419
- this.markDirty();
2420
- this.closeDrawer();
2421
- }
2422
- removeRow(id) {
2423
- const target = this.rows().find((row) => row.id === id);
2424
- if (target?.readonly) {
2425
- this.message.info("\u8BE5 Upstream \u6765\u81EA\u5916\u90E8\u914D\u7F6E\u6587\u4EF6\uFF0C\u5F53\u524D\u4E0D\u53EF\u5220\u9664");
2426
- return;
2427
- }
2428
- this.rows.update((rows) => rows.filter((row) => row.id !== id));
2429
- this.markDirty();
2430
- }
2431
- markDirty() {
2432
- this.dirty.set(true);
2433
- }
2434
- async saveAll() {
2435
- const payload = [];
2436
- for (const row of this.rows()) {
2437
- if (row.readonly) {
2438
- continue;
2439
- }
2440
- const name = row.name.trim();
2441
- if (!name) {
2442
- this.message.warning("Upstream \u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A");
2443
- return;
2444
- }
2445
- if (!/^[a-zA-Z0-9._-]+$/.test(name)) {
2446
- this.message.warning(`Upstream \u540D\u79F0\u4E0D\u5408\u6CD5: ${name}`);
2447
- return;
2448
- }
2449
- const nodes = (row.nodes || []).map((item) => item.trim()).filter(Boolean);
2450
- if (!nodes.length) {
2451
- this.message.warning(`Upstream "${name}" \u81F3\u5C11\u9700\u8981\u4E00\u4E2A\u8282\u70B9`);
2452
- return;
2453
- }
2454
- payload.push({
2455
- id: row.id,
2456
- name,
2457
- strategy: row.strategy,
2458
- nodes,
2459
- sourceFile: row.sourceFile,
2460
- managed: true,
2461
- readonly: false
2462
- });
2463
- }
2464
- this.saving.set(true);
2465
- try {
2466
- const res = await this.moduleStore.saveUpstreams(payload);
2467
- if (res.success) {
2468
- this.message.success("Upstream \u914D\u7F6E\u5DF2\u4FDD\u5B58");
2469
- this.dirty.set(false);
2470
- await this.loadData();
2471
- } else {
2472
- this.message.error(res.error || "\u4FDD\u5B58 Upstream \u5931\u8D25");
2473
- }
2474
- } catch (err) {
2475
- this.message.error("\u4FDD\u5B58 Upstream \u5931\u8D25: " + err.message);
2476
- } finally {
2477
- this.saving.set(false);
2478
- }
2479
- }
2480
- makeId() {
2481
- return `upstream-${Date.now()}-${Math.random().toString(16).slice(2, 8)}`;
2482
- }
2483
- parseNodes(nodesText) {
2484
- return nodesText.split(/[,;\n]/).map((item) => item.trim()).filter(Boolean);
2485
- }
2486
- createEmptyForm() {
2487
- return {
2488
- name: "",
2489
- strategy: "round-robin",
2490
- nodesText: ""
2491
- };
2492
- }
2493
- sourceLabel(row) {
2494
- const raw = row.sourceFile || "";
2495
- const normalized = raw.replace(/\\/g, "/");
2496
- const name = normalized.split("/").pop() || normalized;
2497
- return name || (row.managed ? "\u6258\u7BA1\u6587\u4EF6" : "\u5916\u90E8\u6587\u4EF6");
2498
- }
2499
- sourceHint(row) {
2500
- if (!row.sourceFile) {
2501
- return row.managed ? "\u6258\u7BA1 upstream\uFF08\u6765\u6E90\u672A\u89E3\u6790\uFF09" : "\u5916\u90E8 upstream\uFF08\u6765\u6E90\u672A\u89E3\u6790\uFF09";
2502
- }
2503
- return `${row.managed ? "\u6258\u7BA1" : "\u5916\u90E8"}: ${row.sourceFile}`;
2504
- }
2505
- };
2506
- NginxSecondaryUpstreamTabComponent = __decorate([
2507
- Component({
2508
- selector: "app-nginx-secondary-upstream-tab",
2509
- standalone: true,
2510
- imports: [
2511
- CommonModule,
2512
- FormsModule,
2513
- NzButtonModule,
2514
- NzDrawerModule,
2515
- NzFormModule,
2516
- NzIconModule,
2517
- NzInputModule,
2518
- NzSelectModule,
2519
- NzSpinModule
2520
- ],
2521
- template: `
2522
- <div class="list-header">
2523
- <span class="panel-tip">\u7BA1\u7406\u540E\u7AEF\u670D\u52A1\u96C6\u7FA4\u7684\u8D1F\u8F7D\u5747\u8861\u914D\u7F6E</span>
2524
- <div class="header-actions">
2525
- <button nz-button nzType="default" (click)="openCreateDrawer()">
2526
- <nz-icon nzType="plus" nzTheme="outline"></nz-icon>
2527
- \u65B0\u589E Upstream
2528
- </button>
2529
- <button nz-button nzType="primary" (click)="saveAll()" [nzLoading]="saving()" [disabled]="!dirty()">
2530
- <nz-icon nzType="save" nzTheme="outline"></nz-icon>
2531
- \u4FDD\u5B58
2532
- </button>
2533
- </div>
2534
- </div>
2535
-
2536
- <div class="upstream-grid-shell">
2537
- <div class="upstream-grid-head">
2538
- <div class="cell name-col">UPSTREAM \u540D\u79F0</div>
2539
- <div class="cell source-col">\u6765\u6E90</div>
2540
- <div class="cell strategy-col">\u7B56\u7565</div>
2541
- <div class="cell nodes-col">\u8282\u70B9</div>
2542
- <div class="cell count-col">\u8282\u70B9\u6570</div>
2543
- <div class="cell action-col">\u64CD\u4F5C</div>
2544
- </div>
2545
-
2546
- <nz-spin [nzSpinning]="loading()">
2547
- <div class="upstream-grid-body">
2548
- @if (!loading() && !rows().length) {
2549
- <div class="empty-state">
2550
- <nz-icon nzType="cluster" nzTheme="outline" class="empty-icon"></nz-icon>
2551
- <p>\u6682\u65E0 Upstream \u914D\u7F6E</p>
2552
- </div>
2553
- } @else {
2554
- @for (row of rows(); track row.id) {
2555
- <div class="upstream-grid-row" [class.readonly-row]="row.readonly">
2556
- <div class="cell name-col">
2557
- <span class="upstream-name mono">{{ row.name }}</span>
2558
- </div>
2559
-
2560
- <div class="cell source-col">
2561
- <span class="source-badge" [class.managed]="row.managed" [title]="sourceHint(row)">
2562
- {{ sourceLabel(row) }}
2563
- </span>
2564
- </div>
2565
-
2566
- <div class="cell strategy-col">
2567
- <span class="strategy-pill mono">{{ row.strategy }}</span>
2568
- </div>
2569
-
2570
- <div class="cell nodes-col">
2571
- <div class="nodes-wrap">
2572
- @for (node of row.nodes; track node + '-' + $index) {
2573
- <span class="node-chip mono">{{ node }}</span>
2574
- }
2575
- </div>
2576
- </div>
2577
-
2578
- <div class="cell count-col">
2579
- <span class="node-count">{{ row.nodes.length }}</span>
2580
- </div>
2581
-
2582
- <div class="cell action-col">
2583
- <div class="row-actions">
2584
- <button nz-button nzSize="small" nzType="link" (click)="openEditDrawer(row)" [disabled]="row.readonly">
2585
- <nz-icon nzType="edit" nzTheme="outline"></nz-icon>
2586
- </button>
2587
- <button nz-button nzSize="small" nzType="link" nzDanger (click)="removeRow(row.id)" [disabled]="row.readonly">
2588
- <nz-icon nzType="delete" nzTheme="outline"></nz-icon>
2589
- </button>
2590
- </div>
2591
- </div>
2592
- </div>
2593
- }
2594
- }
2595
- </div>
2596
- </nz-spin>
2597
- </div>
2598
-
2599
- <nz-drawer
2600
- [nzVisible]="drawerVisible()"
2601
- [nzTitle]="editingId() ? '\u7F16\u8F91 Upstream' : '\u65B0\u589E Upstream'"
2602
- [nzWidth]="460"
2603
- [nzPlacement]="'right'"
2604
- (nzOnClose)="closeDrawer()"
2605
- >
2606
- <ng-container *nzDrawerContent>
2607
- <div class="drawer-body">
2608
- <form nz-form nzLayout="vertical">
2609
- <nz-form-item>
2610
- <nz-form-label nzRequired>\u540D\u79F0</nz-form-label>
2611
- <nz-form-control>
2612
- <input
2613
- nz-input
2614
- [(ngModel)]="drawerForm.name"
2615
- name="upstreamName"
2616
- placeholder="\u4F8B\u5982 backend_cluster"
2617
- class="mono"
2618
- />
2619
- </nz-form-control>
2620
- </nz-form-item>
2621
-
2622
- <nz-form-item>
2623
- <nz-form-label nzRequired>\u7B56\u7565</nz-form-label>
2624
- <nz-form-control>
2625
- <nz-select [(ngModel)]="drawerForm.strategy" name="upstreamStrategy">
2626
- <nz-option nzValue="round-robin" nzLabel="round-robin"></nz-option>
2627
- <nz-option nzValue="least_conn" nzLabel="least_conn"></nz-option>
2628
- <nz-option nzValue="ip_hash" nzLabel="ip_hash"></nz-option>
2629
- <nz-option nzValue="hash" nzLabel="hash"></nz-option>
2630
- </nz-select>
2631
- </nz-form-control>
2632
- </nz-form-item>
2633
-
2634
- <nz-form-item>
2635
- <nz-form-label nzRequired>\u8282\u70B9\uFF08\u9017\u53F7\u6216\u6362\u884C\u5206\u9694\uFF09</nz-form-label>
2636
- <nz-form-control>
2637
- <textarea
2638
- nz-input
2639
- rows="5"
2640
- [(ngModel)]="drawerForm.nodesText"
2641
- name="upstreamNodes"
2642
- placeholder="127.0.0.1:3001, 127.0.0.1:3002"
2643
- class="mono"
2644
- ></textarea>
2645
- </nz-form-control>
2646
- </nz-form-item>
2647
- </form>
2648
- </div>
2649
-
2650
- <div class="drawer-footer">
2651
- <button nz-button nzType="default" (click)="closeDrawer()">\u53D6\u6D88</button>
2652
- <button nz-button nzType="primary" (click)="submitDrawer()">\u786E\u5B9A</button>
2653
- </div>
2654
- </ng-container>
2655
- </nz-drawer>
2656
- `,
2657
- styles: [nginx_secondary_upstream_tab_component_default]
2658
- })
2659
- ], NginxSecondaryUpstreamTabComponent);
2660
-
2661
- // angular:jit:style:inline:src\app\pages\nginx\components\nginx-section-card\nginx-section-card.component.ts;CiAgICA6aG9zdCB7CiAgICAgIGRpc3BsYXk6IGJsb2NrOwogICAgfQoKICAgIC5jYXJkIHsKICAgICAgYmFja2dyb3VuZDogdmFyKC0tYmctd2hpdGUpOwogICAgICBib3JkZXI6IDFweCBzb2xpZCB2YXIoLS1ib3JkZXIpOwogICAgICBib3JkZXItbGVmdC13aWR0aDogM3B4OwogICAgICBib3JkZXItcmFkaXVzOiA4cHg7CiAgICAgIGJveC1zaGFkb3c6IHZhcigtLXNoYWRvdy1jYXJkKTsKICAgICAgbWFyZ2luLWJvdHRvbTogMTRweDsKICAgICAgb3ZlcmZsb3c6IGhpZGRlbjsKICAgICAgYm9yZGVyLWxlZnQtY29sb3I6IHRyYW5zcGFyZW50OwogICAgfQoKICAgIC5jYXJkLmNhcmQtYmx1ZSB7CiAgICAgIGJvcmRlci1sZWZ0LWNvbG9yOiB2YXIoLS1ibHVlKTsKICAgIH0KCiAgICAuY2FyZC5jYXJkLWdyZWVuIHsKICAgICAgYm9yZGVyLWxlZnQtY29sb3I6IHZhcigtLWdyZWVuKTsKICAgIH0KCiAgICAuY2FyZC5jYXJkLW9yYW5nZSB7CiAgICAgIGJvcmRlci1sZWZ0LWNvbG9yOiB2YXIoLS1vcmFuZ2UpOwogICAgfQoKICAgIC5jYXJkLWhlYWRlciB7CiAgICAgIGRpc3BsYXk6IGZsZXg7CiAgICAgIGFsaWduLWl0ZW1zOiBjZW50ZXI7CiAgICAgIGp1c3RpZnktY29udGVudDogc3BhY2UtYmV0d2VlbjsKICAgICAgcGFkZGluZzogMTJweCAxNnB4IDEycHggMjBweDsKICAgICAgYm9yZGVyLWJvdHRvbTogMXB4IHNvbGlkIHZhcigtLWJvcmRlci1saWdodCk7CiAgICAgIGdhcDogMTBweDsKICAgIH0KCiAgICAuY2FyZC1oZWFkZXItbGVmdCB7CiAgICAgIGRpc3BsYXk6IGZsZXg7CiAgICAgIGFsaWduLWl0ZW1zOiBjZW50ZXI7CiAgICAgIGdhcDogOHB4OwogICAgICBtaW4td2lkdGg6IDA7CiAgICB9CgogICAgLmNhcmQtdGl0bGUgewogICAgICBmb250LXNpemU6IHZhcigtLW5naW54LWZvbnQtc2l6ZS1iYXNlLCAxNHB4KTsKICAgICAgZm9udC13ZWlnaHQ6IDcwMDsKICAgICAgY29sb3I6IHZhcigtLXRleHQtMSk7CiAgICB9CgogICAgLmNhcmQtc3VidGl0bGUgewogICAgICBmb250LXNpemU6IHZhcigtLW5naW54LWZvbnQtc2l6ZS1zbSwgMTJweCk7CiAgICAgIGNvbG9yOiB2YXIoLS10ZXh0LTMpOwogICAgfQoKICAgIC5jYXJkLWFjdGlvbnMgewogICAgICBkaXNwbGF5OiBmbGV4OwogICAgICBnYXA6IDhweDsKICAgICAgZmxleC13cmFwOiB3cmFwOwogICAgICBqdXN0aWZ5LWNvbnRlbnQ6IGZsZXgtZW5kOwogICAgfQoKICAgIC5jYXJkLWJvZHkgewogICAgICBwYWRkaW5nOiAxNHB4IDE4cHg7CgogICAgICAmLm5vLXBhZGRpbmcgewogICAgICAgIHBhZGRpbmc6IDA7CiAgICAgIH0KICAgIH0KCiAgICBAbWVkaWEgKG1heC13aWR0aDogNzY4cHgpIHsKICAgICAgLmNhcmQtaGVhZGVyIHsKICAgICAgICBmbGV4LWRpcmVjdGlvbjogY29sdW1uOwogICAgICAgIGFsaWduLWl0ZW1zOiBmbGV4LXN0YXJ0OwogICAgICB9CgogICAgICAuY2FyZC1hY3Rpb25zIHsKICAgICAgICB3aWR0aDogMTAwJTsKICAgICAgfQogICAgfQogIA==
2662
- var nginx_section_card_component_default = "/* angular:styles/component:less;be0d28def7a058758ca93c978518ebddcea4fe4be40c09bbcf97a80d5213437c;D:\\ng-manager\\webapp\\src\\app\\pages\\nginx\\components\\nginx-section-card\\nginx-section-card.component.ts */\n:host {\n display: block;\n}\n.card {\n background: var(--bg-white);\n border: 1px solid var(--border);\n border-left-width: 3px;\n border-radius: 8px;\n box-shadow: var(--shadow-card);\n margin-bottom: 14px;\n overflow: hidden;\n border-left-color: transparent;\n}\n.card.card-blue {\n border-left-color: var(--blue);\n}\n.card.card-green {\n border-left-color: var(--green);\n}\n.card.card-orange {\n border-left-color: var(--orange);\n}\n.card-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 16px 12px 20px;\n border-bottom: 1px solid var(--border-light);\n gap: 10px;\n}\n.card-header-left {\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n}\n.card-title {\n font-size: var(--nginx-font-size-base, 14px);\n font-weight: 700;\n color: var(--text-1);\n}\n.card-subtitle {\n font-size: var(--nginx-font-size-sm, 12px);\n color: var(--text-3);\n}\n.card-actions {\n display: flex;\n gap: 8px;\n flex-wrap: wrap;\n justify-content: flex-end;\n}\n.card-body {\n padding: 14px 18px;\n}\n.card-body.no-padding {\n padding: 0;\n}\n@media (max-width: 768px) {\n .card-header {\n flex-direction: column;\n align-items: flex-start;\n }\n .card-actions {\n width: 100%;\n }\n}\n/*# sourceMappingURL=nginx-section-card.component.css.map */\n";
2663
-
2664
- // src/app/pages/nginx/components/nginx-section-card/nginx-section-card.component.ts
2665
- var NginxSectionCardComponent = class NginxSectionCardComponent2 {
2666
- accent = "blue";
2667
- showHeader = false;
2668
- title = "";
2669
- subtitle = "";
2670
- noBodyPadding = false;
2671
- static propDecorators = {
2672
- accent: [{ type: Input }],
2673
- showHeader: [{ type: Input }],
2674
- title: [{ type: Input }],
2675
- subtitle: [{ type: Input }],
2676
- noBodyPadding: [{ type: Input }]
2677
- };
2678
- };
2679
- NginxSectionCardComponent = __decorate([
2680
- Component({
2681
- selector: "app-nginx-section-card",
2682
- standalone: true,
2683
- template: `
2684
- <section class="card" [class.card-blue]="accent === 'blue'" [class.card-green]="accent === 'green'" [class.card-orange]="accent === 'orange'">
2685
- @if (showHeader) {
2686
- <div class="card-header">
2687
- <div class="card-header-left">
2688
- <span class="card-title">{{ title }}</span>
2689
- @if (subtitle) {
2690
- <span class="card-subtitle">{{ subtitle }}</span>
2691
- }
2692
- </div>
2693
- <div class="card-actions">
2694
- <ng-content select="[nginxCardActions]"></ng-content>
2695
- </div>
2696
- </div>
2697
- }
2698
-
2699
- <div class="card-body" [class.no-padding]="noBodyPadding">
2700
- <ng-content></ng-content>
2701
- </div>
2702
- </section>
2703
- `,
2704
- styles: [nginx_section_card_component_default]
2705
- })
2706
- ], NginxSectionCardComponent);
2707
-
2708
- // angular:jit:template:src\app\pages\nginx\components\nginx-server-list\nginx-server-list.component.html
2709
- var nginx_server_list_component_default = `<div class="server-list">\r
2710
- <!-- \u9876\u90E8\u64CD\u4F5C\u680F -->\r
2711
- @if (showToolbar) {\r
2712
- <div class="list-header">\r
2713
- <div class="header-left">\r
2714
- <button nz-button nzType="default" (click)="importServer()">\r
2715
- <nz-icon nzType="upload" nzTheme="outline"></nz-icon>\r
2716
- \u5BFC\u5165\r
2717
- </button>\r
2718
- <button nz-button nzType="primary" (click)="openDrawer(null)">\r
2719
- <nz-icon nzType="plus" nzTheme="outline"></nz-icon>\r
2720
- \u65B0\u589E Server\r
2721
- </button>\r
2722
- </div>\r
2723
- </div>\r
2724
- }\r
2725
- \r
2726
- <div class="server-grid-shell">\r
2727
- <div class="server-grid-head">\r
2728
- <div class="cell status-col">\u72B6\u6001</div>\r
2729
- <div class="cell">Server \u540D\u79F0</div>
2730
- <div class="cell listen-col">\u76D1\u542C\u7AEF\u53E3</div>
2731
- <div class="cell domain-col">\u57DF\u540D</div>
2732
- <div class="cell access-col">\u8BBF\u95EE\u5730\u5740</div>
2733
- <div class="cell root-col">\u6839\u76EE\u5F55</div>
2734
- <div class="cell action-col">\u64CD\u4F5C</div>
2735
- </div>
2736
- \r
2737
- <nz-spin [nzSpinning]="loading()">\r
2738
- <div class="server-grid-body">\r
2739
- @if (!loading() && !servers().length) {\r
2740
- <div class="empty-state">\r
2741
- <nz-icon nzType="inbox" nzTheme="outline" class="empty-icon"></nz-icon>\r
2742
- <p>\u6682\u65E0 Server \u914D\u7F6E</p>\r
2743
- </div>\r
2744
- } @else {\r
2745
- @for (server of servers(); track server.id) {\r
2746
- <div class="server-grid-row" [class.disabled-row]="!server.enabled">\r
2747
- <div class="cell status-col">\r
2748
- <nz-switch\r
2749
- nzSize="small"\r
2750
- [ngModel]="server.enabled"\r
2751
- (ngModelChange)="toggleServer(server.id, $event)"\r
2752
- ></nz-switch>\r
2753
- </div>\r
2754
- \r
2755
- <div class="cell">\r
2756
- <div class="server-name">\r
2757
- <nz-icon nzType="file" nzTheme="outline" class="conf-icon"></nz-icon>\r
2758
- {{ server.name }}\r
2759
- </div>\r
2760
- </div>\r
2761
- \r
2762
- <div class="cell listen-col">\r
2763
- @for (port of server.listen; track $index) {\r
2764
- @if (port === '443' || server.ssl) {\r
2765
- <span class="server-listen ssl">:{{ port }} ssl</span>\r
2766
- } @else {\r
2767
- <span class="server-listen">:{{ port }}</span>\r
2768
- }\r
2769
- }\r
2770
- </div>\r
2771
- \r
2772
- <div class="cell domain-col">
2773
- <span class="server-domain">{{ (server.domains || []).join(', ') || '\u2014' }}</span>
2774
- </div>
2775
-
2776
- <div class="cell access-col">
2777
- @if (getAccessUrls(server).length) {
2778
- <div class="access-links">
2779
- @for (url of getAccessUrls(server); track url) {
2780
- <a
2781
- class="access-link"
2782
- [href]="url"
2783
- target="_blank"
2784
- rel="noopener noreferrer"
2785
- >
2786
- {{ url }}
2787
- </a>
2788
- }
2789
- </div>
2790
- } @else {
2791
- <span class="server-domain">\u2014</span>
2792
- }
2793
- </div>
2794
-
2795
- <div class="cell root-col">
2796
- <span class="server-root" [title]="server.root || ''">{{
2797
- server.root || '\u2014'
2798
- }}</span>
2799
- </div>\r
2800
- \r
2801
- <div class="cell action-col">\r
2802
- <div class="row-actions">\r
2803
- <button nz-button nzType="link" nzSize="small" (click)="openDrawer(server)">\r
2804
- <nz-icon nzType="edit" nzTheme="outline"></nz-icon>\r
2805
- </button>\r
2806
- <button nz-button nzType="link" nzSize="small" (click)="copyServer(server)">\r
2807
- <nz-icon nzType="copy" nzTheme="outline"></nz-icon>\r
2808
- </button>\r
2809
- <button\r
2810
- nz-button\r
2811
- nzDanger\r
2812
- nzType="link"\r
2813
- nzSize="small"\r
2814
- nz-popconfirm\r
2815
- [nzPopconfirmTitle]="'\u5220\u9664\u540E\u65E0\u6CD5\u6062\u590D\uFF0C\u786E\u8BA4\u5220\u9664' + server.name + '\u5417\uFF1F'"\r
2816
- [nzOkButtonProps]="{ nzDanger: true }"\r
2817
- nzPopconfirmOkText="\u5220\u9664"\r
2818
- nzPopconfirmCancelText="\u53D6\u6D88"\r
2819
- (nzOnConfirm)="deleteServer(server)"\r
2820
- >\r
2821
- <nz-icon nzType="delete" nzTheme="outline"></nz-icon>\r
2822
- </button>\r
2823
- </div>\r
2824
- </div>\r
2825
- </div>\r
2826
- }\r
2827
- }\r
2828
- </div>\r
2829
- </nz-spin>\r
2830
- </div>\r
2831
- </div>\r
2832
- \r
2833
- <!-- Server \u65B0\u589E/\u7F16\u8F91 Drawer -->\r
2834
- <app-nginx-server-drawer
2835
- [visible]="drawerVisible"
2836
- (visibleChange)="onDrawerVisibleChange($event)"
2837
- [editingServer]="editingServer()"
2838
- (saved)="onSaved()"
2839
- ></app-nginx-server-drawer>
2840
- \r
2841
- <!-- \u67E5\u770B\u914D\u7F6E\u6A21\u6001\u6846 -->\r
2842
- <nz-modal\r
2843
- [(nzVisible)]="configModalVisible"\r
2844
- nzTitle="Server \u914D\u7F6E"\r
2845
- (nzOnCancel)="configModalVisible = false"\r
2846
- [nzFooter]="null"\r
2847
- nzWidth="800px"\r
2848
- >\r
2849
- <ng-container *nzModalContent>\r
2850
- <pre class="config-preview">{{ viewingConfig() }}</pre>\r
2851
- </ng-container>\r
2852
- </nz-modal>\r
2853
- `;
2854
-
2855
- // angular:jit:style:src\app\pages\nginx\components\nginx-server-list\nginx-server-list.component.less
2856
- var nginx_server_list_component_default2 = '/* src/app/pages/nginx/components/nginx-server-list/nginx-server-list.component.less */\n.server-list .list-header {\n display: flex;\n align-items: center;\n margin-bottom: 16px;\n}\n.server-list .list-header .header-left {\n display: flex;\n gap: 8px;\n}\n.server-grid-shell {\n border: none;\n border-radius: 0;\n overflow: hidden;\n}\n.server-grid-head,\n.server-grid-row {\n display: grid;\n grid-template-columns: 72px minmax(160px, 1.1fr) minmax(130px, 0.8fr) minmax(150px, 1fr) minmax(220px, 1.2fr) minmax(180px, 1fr) 120px;\n align-items: center;\n column-gap: 8px;\n padding: 0 12px;\n}\n.server-grid-head {\n min-height: 42px;\n background: #fafafa;\n border-bottom: 1px solid rgba(0, 0, 0, 0.06);\n}\n.server-grid-head .cell {\n font-size: var(--nginx-font-size-sm, 12px);\n color: rgba(0, 0, 0, 0.45);\n text-transform: uppercase;\n letter-spacing: 0.4px;\n font-weight: 700;\n}\n.server-grid-body {\n background: #fff;\n}\n.server-grid-row {\n min-height: 56px;\n border-bottom: 1px solid rgba(0, 0, 0, 0.05);\n transition: background 120ms ease;\n}\n.server-grid-row:last-child {\n border-bottom: none;\n}\n.server-grid-row:hover {\n background: rgba(0, 0, 0, 0.02);\n}\n.cell {\n min-width: 0;\n}\n.action-col {\n justify-self: end;\n}\n.server-name {\n display: flex;\n align-items: center;\n gap: 8px;\n font-weight: 500;\n font-size: var(--nginx-font-size-base, 14px);\n}\n.conf-icon {\n color: rgba(0, 0, 0, 0.35);\n flex-shrink: 0;\n}\n.server-listen {\n font-family: var(--nginx-font-family-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace);\n font-size: var(--nginx-font-size-sm, 12px);\n color: #e5a832;\n background: rgba(229, 168, 50, 0.12);\n padding: 2px 8px;\n border-radius: 4px;\n margin-right: 4px;\n}\n.server-listen.ssl {\n color: #5ea6f7;\n background: rgba(94, 166, 247, 0.12);\n}\n.server-domain {\n font-family: var(--nginx-font-family-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace);\n font-size: var(--nginx-font-size-sm, 12px);\n color: rgba(0, 0, 0, 0.55);\n}\n.server-root {\n font-family: var(--nginx-font-family-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace);\n font-size: var(--nginx-font-size-sm, 12px);\n color: rgba(0, 0, 0, 0.4);\n max-width: 200px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n display: block;\n}\n.access-link {\n font-family: var(--nginx-font-family-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace);\n font-size: var(--nginx-font-size-sm, 12px);\n color: var(--blue);\n text-decoration: none;\n display: inline-block;\n max-width: 100%;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.access-link:hover {\n text-decoration: underline;\n}\n.access-links {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n.row-actions {\n display: flex;\n gap: 4px;\n justify-content: flex-end;\n}\n.empty-state {\n text-align: center;\n padding: 48px 0;\n}\n.empty-state .empty-icon {\n font-size: 48px;\n color: rgba(0, 0, 0, 0.2);\n margin-bottom: 16px;\n}\n.empty-state p {\n color: rgba(0, 0, 0, 0.4);\n margin: 0;\n}\n@media (max-width: 1100px) {\n .server-grid-head {\n display: none;\n }\n .server-grid-row {\n grid-template-columns: 1fr;\n gap: 8px;\n padding: 12px;\n align-items: stretch;\n }\n .status-col,\n .listen-col,\n .domain-col,\n .access-col,\n .root-col,\n .action-col {\n justify-self: start;\n }\n .action-col {\n width: 100%;\n }\n .row-actions {\n opacity: 1;\n }\n}\n.config-preview {\n background: #f6ffed;\n border: 1px solid #b7eb8f;\n border-radius: 4px;\n padding: 16px;\n margin: 0;\n font-family: var(--nginx-font-family-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace);\n font-size: var(--nginx-font-size-base, 14px);\n line-height: 1.6;\n overflow-x: auto;\n white-space: pre-wrap;\n}\n/*# sourceMappingURL=nginx-server-list.component.css.map */\n';
2857
-
2858
- // angular:jit:template:src\app\pages\nginx\components\nginx-server-drawer\nginx-server-drawer.component.html
2859
- var nginx_server_drawer_component_default = `<nz-drawer
2860
- [nzVisible]="visible"
2861
- [nzPlacement]="'right'"
2862
- [nzWidth]="520"
2863
- [nzTitle]="drawerTitle"
2864
- (nzOnClose)="onClose()"
2865
- [nzClosable]="true"
2866
- [nzMaskClosable]="true"
2867
- >
2868
- <ng-container *nzDrawerContent>
2869
- <div class="drawer-body">
2870
- <form nz-form nzLayout="vertical">
2871
- <!-- Server \u540D\u79F0 -->
2872
- <nz-form-item>
2873
- <nz-form-label nzRequired>Server \u540D\u79F0</nz-form-label>
2874
- <nz-form-control nzHasFeedback nzErrorTip="\u8BF7\u8F93\u5165 Server \u540D\u79F0">
2875
- <input
2876
- nz-input
2877
- [(ngModel)]="formData.name"
2878
- name="serverName"
2879
- placeholder="\u4F8B\u5982\uFF1Amain-site"
2880
- />
2881
- </nz-form-control>
2882
- </nz-form-item>
2883
-
2884
- <!-- \u534F\u8BAE + \u57DF\u540D + \u76D1\u542C\u7AEF\u53E3 -->
2885
- <nz-form-item>
2886
- <nz-form-label nzRequired>\u57DF\u540D</nz-form-label>
2887
- <nz-form-control>
2888
- <div nz-input-group nzCompact class="domain-row-wrapper">
2889
- <nz-select
2890
- class="domain-row-protocol"
2891
- [(ngModel)]="formData.protocol"
2892
- name="protocol"
2893
- nzPlaceHolder="HTTP"
2894
- >
2895
- <nz-option nzValue="http" nzLabel="HTTP"></nz-option>
2896
- <nz-option nzValue="https" nzLabel="HTTPS (SSL)"></nz-option>
2897
- </nz-select>
2898
-
2899
- <nz-select
2900
- class="domain-row-domain"
2901
- nzMode="tags"
2902
- [(ngModel)]="domainValues"
2903
- name="domains"
2904
- nzPlaceHolder="\u53EF\u8F93\u5165\u6216\u9009\u62E9\u591A\u4E2A\u57DF\u540D"
2905
- [nzTokenSeparators]="[',', ' ']"
2906
- (ngModelChange)="syncDomains()"
2907
- >
2908
- @for (domain of commonDomainOptions; track domain) {
2909
- <nz-option [nzValue]="domain" [nzLabel]="domain"></nz-option>
2910
- }
2911
- </nz-select>
2912
-
2913
- <nz-select
2914
- class="domain-row-listen"
2915
- nzMode="tags"
2916
- [(ngModel)]="listenValues"
2917
- name="listenPort"
2918
- nzPlaceHolder="\u7AEF\u53E3"
2919
- [nzTokenSeparators]="[',', ' ']"
2920
- >
2921
- @for (port of commonListenOptions; track port) {
2922
- <nz-option [nzValue]="port" [nzLabel]="port"></nz-option>
2923
- }
2924
- </nz-select>
2925
- </div>
2926
- </nz-form-control>
2927
- <div class="field-hint">\u57DF\u540D\u548C\u7AEF\u53E3\u5747\u652F\u6301\u591A\u4E2A\u503C\uFF0C\u4F8B\u5982\uFF1Alocalhost, 127.0.0.1 / 80, 443</div>
2928
- </nz-form-item>
2929
-
2930
- <!-- \u6839\u76EE\u5F55 + \u9ED8\u8BA4\u9996\u9875 -->
2931
- <nz-form-item>
2932
- <nz-form-label nzTooltipTitle="\u9759\u6001\u8D44\u6E90\u6839\u76EE\u5F55\uFF0C\u901A\u5E38\u7528\u4E8E\u63D0\u4F9B\u9759\u6001\u6587\u4EF6\u670D\u52A1\uFF0C\u5982index.html\u6240\u5728\u76EE\u5F55"
2933
- [nzTooltipIcon]="'question-circle'">\u6839\u76EE\u5F55</nz-form-label>
2934
- <nz-form-control>
2935
- <div nz-input-group nzCompact class="root-row-wrapper">
2936
- <input
2937
- nz-input
2938
- class="mono-input root-row-root"
2939
- [(ngModel)]="formData.root"
2940
- name="root"
2941
- placeholder="\u5982\uFF1AD:\\\\dist\\\\www"
2942
- />
2943
- <nz-select
2944
- class="root-row-index"
2945
- nzMode="tags"
2946
- [(ngModel)]="indexValues"
2947
- name="serverIndex"
2948
- nzPlaceHolder="\u9ED8\u8BA4\u9996\u9875"
2949
- [nzTokenSeparators]="[',', ' ']"
2950
- (ngModelChange)="syncIndex()"
2951
- >
2952
- @for (item of commonIndexOptions; track item) {
2953
- <nz-option [nzValue]="item" [nzLabel]="item"></nz-option>
2954
- }
2955
- </nz-select>
2956
- </div>
2957
- </nz-form-control>
2958
- <div class="field-hint">\u6839\u76EE\u5F55\u793A\u4F8B\uFF1AD:\\dist\\www\uFF1B\u9ED8\u8BA4\u9996\u9875\u793A\u4F8B\uFF1Aindex.html, index.htm</div>
2959
- </nz-form-item>
2960
-
2961
- <!-- Location \u89C4\u5219 -->
2962
- <nz-form-item>
2963
- <nz-form-label nzTooltipTitle="\u7528\u4E8E\u5B9A\u4E49\u8BF7\u6C42\u8DEF\u5F84\u7684\u5904\u7406\u89C4\u5219\uFF0C\u5982 API \u4EE3\u7406\u670D\u52A1\u6216\u9759\u6001\u8D44\u6E90\u8DEF\u5F84"
2964
- [nzTooltipIcon]="'question-circle'">Location \u89C4\u5219</nz-form-label>
2965
- <nz-form-control>
2966
- <div class="location-toolbar">
2967
- <button nz-button nzType="default" type="button" (click)="addLocation('empty')">
2968
- <span nz-icon nzType="plus"></span>
2969
- \u65B0\u589E\u89C4\u5219
2970
- </button>
2971
- <!-- <button nz-button nzType="default" type="button" (click)="addLocation('api')">
2972
- \u5FEB\u901F\u65B0\u589E API \u4EE3\u7406
2973
- </button> -->
2974
- </div>
2975
-
2976
- @for (loc of formData.locations; track $index) {
2977
- <div class="location-item">
2978
- <div class="location-item-head">
2979
- <span>Location {{ $index + 1 }}</span>
2980
- <button
2981
- nz-button
2982
- nzType="link"
2983
- nzDanger
2984
- type="button"
2985
- (click)="removeLocation($index)"
2986
- >
2987
- \u5220\u9664
2988
- </button>
2989
- </div>
2990
-
2991
- <div class="location-grid">
2992
- <input
2993
- nz-input
2994
- [(ngModel)]="loc.path"
2995
- [name]="'locationPath' + $index"
2996
- placeholder="/api/"
2997
- />
2998
- <input
2999
- nz-input
3000
- class="mono-input"
3001
- [(ngModel)]="loc.proxyPass"
3002
- [name]="'locationProxy' + $index"
3003
- placeholder="http://127.0.0.1:6808"
3004
- />
3005
- </div>
3006
-
3007
- <textarea
3008
- nz-input
3009
- class="mono-textarea location-extra-textarea"
3010
- rows="3"
3011
- [(ngModel)]="loc.rawConfig"
3012
- [name]="'locationExtra' + $index"
3013
- placeholder="\u989D\u5916 location \u6307\u4EE4\uFF0C\u5982\uFF1A&#10;proxy_http_version 1.1;&#10;proxy_set_header Upgrade $http_upgrade;&#10;proxy_set_header Connection &quot;upgrade&quot;;"
3014
- ></textarea>
3015
- </div>
3016
- }
3017
- </nz-form-control>
3018
- <div class="field-hint">\u5E38\u89C1\u573A\u666F\uFF1A\`location /api/\` + \`proxy_pass http://127.0.0.1:6808;\`</div>
3019
- </nz-form-item>
3020
-
3021
- <!-- SSL \u914D\u7F6E\uFF08\u534F\u8BAE\u4E3A HTTPS \u65F6\u663E\u793A\uFF09 -->
3022
- @if (formData.protocol === 'https') {
3023
- <nz-form-item>
3024
- <nz-form-label nzRequired>SSL \u8BC1\u4E66\u8DEF\u5F84</nz-form-label>
3025
- <nz-form-control nzHasFeedback nzErrorTip="\u8BF7\u586B\u5199 SSL \u8BC1\u4E66\u8DEF\u5F84">
3026
- <input
3027
- nz-input
3028
- [(ngModel)]="formData.sslCert"
3029
- name="sslCert"
3030
- placeholder="/etc/nginx/ssl/cert.pem"
3031
- class="mono-input"
3032
- />
3033
- </nz-form-control>
3034
- </nz-form-item>
3035
-
3036
- <nz-form-item>
3037
- <nz-form-label nzRequired>SSL \u79C1\u94A5\u8DEF\u5F84</nz-form-label>
3038
- <nz-form-control nzHasFeedback nzErrorTip="\u8BF7\u586B\u5199 SSL \u79C1\u94A5\u8DEF\u5F84">
3039
- <input
3040
- nz-input
3041
- [(ngModel)]="formData.sslKey"
3042
- name="sslKey"
3043
- placeholder="/etc/nginx/ssl/key.pem"
3044
- class="mono-input"
3045
- />
3046
- </nz-form-control>
3047
- </nz-form-item>
3048
- }
3049
-
3050
- <!-- \u81EA\u5B9A\u4E49\u914D\u7F6E -->
3051
- <nz-form-item>
3052
- <nz-form-label>\u81EA\u5B9A\u4E49\u914D\u7F6E\u7247\u6BB5</nz-form-label>
3053
- <nz-form-control>
3054
- <textarea
3055
- nz-input
3056
- [(ngModel)]="formData.extraConfig"
3057
- name="extraConfig"
3058
- class="mono-textarea"
3059
- rows="4"
3060
- placeholder="# \u5728\u6B64\u6DFB\u52A0\u81EA\u5B9A\u4E49 nginx \u914D\u7F6E&#10;location /api {&#10; proxy_pass http://backend_cluster;&#10;}"
3061
- ></textarea>
3062
- </nz-form-control>
3063
- <div class="field-hint">\u7528\u4E8E\u8865\u5145\u9AD8\u7EA7\u6307\u4EE4\uFF08\`location\` \u5E38\u89C4\u573A\u666F\u5EFA\u8BAE\u5728\u4E0A\u9762\u7684 Location \u89C4\u5219\u4E2D\u914D\u7F6E\uFF09</div>
3064
- </nz-form-item>
3065
- </form>
3066
- </div>
3067
-
3068
- <div class="drawer-footer">
3069
- <button nz-button nzType="default" (click)="onClose()">\u53D6\u6D88</button>
3070
- <!-- <button nz-button nzType="default" (click)="previewConfig()">\u9884\u89C8\u914D\u7F6E</button> -->
3071
- <button nz-button nzType="primary" (click)="save()" [nzLoading]="saving()">\u4FDD\u5B58</button>
3072
- </div>
3073
- </ng-container>
3074
- </nz-drawer>
3075
- `;
3076
-
3077
- // angular:jit:style:src\app\pages\nginx\components\nginx-server-drawer\nginx-server-drawer.component.less
3078
- var nginx_server_drawer_component_default2 = "/* src/app/pages/nginx/components/nginx-server-drawer/nginx-server-drawer.component.less */\n:host {\n display: block;\n}\n.drawer-body {\n padding: 0 24px 16px;\n}\n.mono-textarea {\n resize: none;\n}\n.domain-row-wrapper,\n.root-row-wrapper {\n display: flex;\n width: 100%;\n}\n.domain-row-protocol {\n width: 130px;\n flex: 0 0 130px;\n}\n.domain-row-domain {\n flex: 1 1 auto;\n min-width: 0;\n margin-left: -1px;\n}\n.domain-row-listen {\n width: 180px;\n flex: 0 0 180px;\n margin-left: -1px;\n}\n.root-row-root {\n flex: 1 1 auto;\n min-width: 0;\n}\n.root-row-index {\n width: 220px;\n flex: 0 0 220px;\n margin-left: -1px;\n}\n.field-hint {\n margin-top: 4px;\n font-size: 12px;\n opacity: 0.7;\n}\n.location-toolbar {\n display: flex;\n gap: 8px;\n margin-bottom: 10px;\n}\n.location-item {\n border: 1px solid rgba(0, 0, 0, 0.08);\n border-radius: 8px;\n padding: 10px;\n background: #fafafa;\n margin-bottom: 10px;\n}\n.location-item-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 8px;\n}\n.location-grid {\n display: grid;\n grid-template-columns: 160px 1fr;\n gap: 8px;\n}\n.location-extra-textarea {\n margin-top: 8px;\n}\n.drawer-footer {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n padding: 12px 24px;\n border-top: 1px solid rgba(0, 0, 0, 0.06);\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n background: #fff;\n}\n@media (max-width: 640px) {\n .domain-row-wrapper,\n .root-row-wrapper {\n flex-wrap: wrap;\n gap: 8px;\n }\n .domain-row-protocol,\n .domain-row-domain,\n .domain-row-listen,\n .root-row-root,\n .root-row-index {\n width: 100%;\n flex: 1 1 100%;\n margin-left: 0;\n }\n}\n/*# sourceMappingURL=nginx-server-drawer.component.css.map */\n";
3079
-
3080
- // src/app/pages/nginx/components/nginx-server-drawer/nginx-server-drawer.component.ts
3081
- var NginxServerDrawerComponent = class NginxServerDrawerComponent2 {
3082
- visible = false;
3083
- editingServer = null;
3084
- visibleChange = new EventEmitter();
3085
- saved = new EventEmitter();
3086
- nginxService = inject(NginxService);
3087
- message = inject(NzMessageService);
3088
- cdr = inject(ChangeDetectorRef);
3089
- resetTimer = null;
3090
- visibleEmitTimer = null;
3091
- saving = signal(false);
3092
- commonListenOptions = ["80", "443", "8080", "8443"];
3093
- commonDomainOptions = ["127.0.0.1", "localhost", "example.com"];
3094
- commonIndexOptions = ["index.html"];
3095
- listenValues = [];
3096
- domainValues = ["127.0.0.1"];
3097
- indexValues = ["index.html"];
3098
- formData = {
3099
- name: "",
3100
- listen: [],
3101
- domains: ["127.0.0.1"],
3102
- root: "",
3103
- index: ["index.html"],
3104
- locations: [],
3105
- ssl: false,
3106
- protocol: "http",
3107
- enabled: true,
3108
- sslCert: "",
3109
- sslKey: "",
3110
- extraConfig: ""
3111
- };
3112
- get drawerTitle() {
3113
- return this.editingServer ? "\u7F16\u8F91 Server " : "\u65B0\u589E Server ";
3114
- }
3115
- ngOnChanges(changes) {
3116
- const visibleChanged = "visible" in changes;
3117
- const serverChanged = "editingServer" in changes;
3118
- const shouldReset = visibleChanged && this.visible || serverChanged && this.visible && !changes["editingServer"]?.firstChange;
3119
- if (shouldReset) {
3120
- this.scheduleResetForm();
3121
- }
3122
- }
3123
- ngOnDestroy() {
3124
- if (this.resetTimer) {
3125
- clearTimeout(this.resetTimer);
3126
- this.resetTimer = null;
3127
- }
3128
- if (this.visibleEmitTimer) {
3129
- clearTimeout(this.visibleEmitTimer);
3130
- this.visibleEmitTimer = null;
3131
- }
3132
- }
3133
- scheduleResetForm() {
3134
- if (this.resetTimer) {
3135
- clearTimeout(this.resetTimer);
3136
- }
3137
- this.resetTimer = setTimeout(() => {
3138
- this.resetTimer = null;
3139
- this.resetForm();
3140
- this.cdr.detectChanges();
3141
- }, 0);
3142
- }
3143
- emitVisibleChange(nextVisible) {
3144
- if (this.visibleEmitTimer) {
3145
- clearTimeout(this.visibleEmitTimer);
3146
- }
3147
- this.visibleEmitTimer = setTimeout(() => {
3148
- this.visibleEmitTimer = null;
3149
- this.visibleChange.emit(nextVisible);
3150
- }, 0);
3151
- }
3152
- resetForm() {
3153
- if (this.editingServer) {
3154
- const parsedListen = this.parseListenValues(this.editingServer.listen);
3155
- this.formData = {
3156
- name: this.editingServer.name,
3157
- listen: parsedListen.length ? parsedListen : ["80"],
3158
- domains: [...this.editingServer.domains || []],
3159
- root: this.editingServer.root || "",
3160
- index: [...this.editingServer.index?.length ? this.editingServer.index : ["index.html"]],
3161
- locations: this.editingServer.locations.map((l) => __spreadValues({}, l)),
3162
- ssl: this.editingServer.ssl,
3163
- protocol: this.editingServer.ssl ? "https" : "http",
3164
- enabled: this.editingServer.enabled,
3165
- sslCert: this.editingServer.sslCert || "",
3166
- sslKey: this.editingServer.sslKey || "",
3167
- extraConfig: this.editingServer.extraConfig || ""
3168
- };
3169
- this.listenValues = [...this.formData.listen];
3170
- this.domainValues = [...this.formData.domains || []];
3171
- this.indexValues = [...this.formData.index || ["index.html"]];
3172
- } else {
3173
- this.formData = {
3174
- name: "",
3175
- listen: [],
3176
- domains: ["127.0.0.1"],
3177
- root: "",
3178
- index: ["index.html"],
3179
- locations: [],
3180
- //{ path: '/', proxyPass: '' }
3181
- ssl: false,
3182
- protocol: "http",
3183
- enabled: true,
3184
- sslCert: "",
3185
- sslKey: "",
3186
- extraConfig: ""
3187
- };
3188
- this.listenValues = [];
3189
- this.domainValues = ["127.0.0.1"];
3190
- this.indexValues = ["index.html"];
3191
- }
3192
- }
3193
- syncDomains() {
3194
- this.formData.domains = (this.domainValues || []).flatMap((item) => item.split(/[,\s]+/)).map((item) => item.trim()).filter((item) => item.length > 0).filter((item, index, arr) => arr.indexOf(item) === index);
3195
- }
3196
- onClose() {
3197
- this.emitVisibleChange(false);
3198
- }
3199
- syncIndex() {
3200
- const normalized = (this.indexValues || []).flatMap((item) => String(item || "").split(/[,\s]+/)).map((item) => item.trim()).filter(Boolean).filter((item, index, arr) => arr.indexOf(item) === index);
3201
- this.indexValues = normalized.length ? normalized : ["index.html"];
3202
- this.formData.index = [...this.indexValues];
3203
- }
3204
- addLocation(template = "empty") {
3205
- const list = [...this.formData.locations || []];
3206
- if (template === "api") {
3207
- list.push({
3208
- path: "/api/",
3209
- proxyPass: "http://127.0.0.1:6808"
3210
- });
3211
- } else {
3212
- list.push({
3213
- path: "",
3214
- proxyPass: ""
3215
- });
3216
- }
3217
- this.formData.locations = list;
3218
- }
3219
- removeLocation(index) {
3220
- const list = [...this.formData.locations || []];
3221
- list.splice(index, 1);
3222
- this.formData.locations = list;
3223
- }
3224
- previewConfig() {
3225
- this.syncListen();
3226
- this.syncDomains();
3227
- this.syncIndex();
3228
- this.normalizeLocations();
3229
- this.message.info("\u914D\u7F6E\u9884\u89C8\u529F\u80FD\u5F00\u53D1\u4E2D");
3230
- }
3231
- async save() {
3232
- if (!this.formData.name.trim()) {
3233
- this.message.warning("\u8BF7\u8F93\u5165 Server \u540D\u79F0");
3234
- return;
3235
- }
3236
- this.syncDomains();
3237
- if (!this.formData.domains?.length) {
3238
- this.message.warning("\u8BF7\u81F3\u5C11\u586B\u5199\u4E00\u4E2A\u57DF\u540D");
3239
- return;
3240
- }
3241
- this.syncListen();
3242
- if (!this.formData.listen.length) {
3243
- this.message.warning("\u8BF7\u81F3\u5C11\u586B\u5199\u4E00\u4E2A\u76D1\u542C\u7AEF\u53E3");
3244
- return;
3245
- }
3246
- if (this.formData.protocol === "https") {
3247
- if (!this.formData.sslCert?.trim()) {
3248
- this.message.warning("\u8BF7\u586B\u5199 SSL \u8BC1\u4E66\u8DEF\u5F84");
3249
- return;
3250
- }
3251
- if (!this.formData.sslKey?.trim()) {
3252
- this.message.warning("\u8BF7\u586B\u5199 SSL \u79C1\u94A5\u8DEF\u5F84");
3253
- return;
3254
- }
3255
- }
3256
- this.syncIndex();
3257
- this.normalizeLocations();
3258
- this.saving.set(true);
3259
- try {
3260
- let res;
3261
- if (this.editingServer) {
3262
- res = await this.nginxService.updateServer(this.editingServer.id, this.formData);
3263
- } else {
3264
- res = await this.nginxService.createServer(this.formData);
3265
- }
3266
- if (res.success) {
3267
- this.message.success(this.editingServer ? "Server \u5DF2\u66F4\u65B0" : "Server \u5DF2\u521B\u5EFA");
3268
- this.saved.emit();
3269
- this.emitVisibleChange(false);
3270
- } else {
3271
- this.message.error(res.error || "\u64CD\u4F5C\u5931\u8D25");
3272
- }
3273
- } catch (err) {
3274
- this.message.error("\u64CD\u4F5C\u5931\u8D25: " + this.extractErrorMessage(err));
3275
- } finally {
3276
- this.saving.set(false);
3277
- }
3278
- }
3279
- syncListen() {
3280
- const normalized = [];
3281
- for (const item of this.listenValues || []) {
3282
- const raw = String(item || "").trim();
3283
- if (!raw) {
3284
- continue;
3285
- }
3286
- const parsed = Number(raw);
3287
- if (!Number.isInteger(parsed) || parsed < 1 || parsed > 65535) {
3288
- this.message.warning(`\u76D1\u542C\u7AEF\u53E3 "${raw}" \u65E0\u6548\uFF0C\u8BF7\u8F93\u5165 1-65535 \u7684\u6574\u6570`);
3289
- continue;
3290
- }
3291
- const port = String(parsed);
3292
- if (!normalized.includes(port)) {
3293
- normalized.push(port);
3294
- }
3295
- }
3296
- this.listenValues = normalized;
3297
- this.formData.listen = [...normalized];
3298
- }
3299
- parseListenValues(listenValues) {
3300
- const normalized = [];
3301
- for (const item of listenValues || []) {
3302
- const match = String(item || "").match(/\d+/);
3303
- if (!match) {
3304
- continue;
3305
- }
3306
- const parsed = Number(match[0]);
3307
- if (!Number.isInteger(parsed) || parsed < 1 || parsed > 65535) {
3308
- continue;
3309
- }
3310
- const port = String(parsed);
3311
- if (!normalized.includes(port)) {
3312
- normalized.push(port);
3313
- }
3314
- }
3315
- return normalized;
3316
- }
3317
- normalizeLocations() {
3318
- const normalizeText = (value) => {
3319
- const text = String(value || "").trim();
3320
- return text ? text : void 0;
3321
- };
3322
- const parseList = (value) => {
3323
- if (Array.isArray(value)) {
3324
- const arr2 = value.map((item) => String(item || "").trim()).filter(Boolean);
3325
- return arr2.length ? arr2 : void 0;
3326
- }
3327
- const text = String(value || "").trim();
3328
- if (!text) {
3329
- return void 0;
3330
- }
3331
- const arr = text.split(/\s+/).map((item) => item.trim()).filter(Boolean);
3332
- return arr.length ? arr : void 0;
3333
- };
3334
- const normalized = (this.formData.locations || []).map((location) => {
3335
- const path = String(location.path || "").trim() || "/";
3336
- const proxyPass = normalizeText(location.proxyPass);
3337
- const root = normalizeText(location.root);
3338
- const index = parseList(location.index);
3339
- const tryFiles = parseList(location.tryFiles);
3340
- const rawConfig = normalizeText(location.rawConfig);
3341
- return {
3342
- path,
3343
- proxyPass,
3344
- root,
3345
- index,
3346
- tryFiles,
3347
- rawConfig
3348
- };
3349
- }).filter((location) => Boolean(location.proxyPass || location.root || location.index?.length || location.tryFiles?.length || location.rawConfig));
3350
- this.formData.locations = normalized;
3351
- }
3352
- extractErrorMessage(err) {
3353
- const error = err;
3354
- return error?.error?.error?.message || error?.error?.message || error?.message || "\u8BF7\u6C42\u5931\u8D25";
3355
- }
3356
- static propDecorators = {
3357
- visible: [{ type: Input }],
3358
- editingServer: [{ type: Input }],
3359
- visibleChange: [{ type: Output }],
3360
- saved: [{ type: Output }]
3361
- };
3362
- };
3363
- NginxServerDrawerComponent = __decorate([
3364
- Component({
3365
- selector: "app-nginx-server-drawer",
3366
- standalone: true,
3367
- imports: [
3368
- CommonModule,
3369
- FormsModule,
3370
- NzButtonModule,
3371
- NzDrawerModule,
3372
- NzFormModule,
3373
- NzIconModule,
3374
- NzInputModule,
3375
- NzSelectModule
3376
- ],
3377
- template: nginx_server_drawer_component_default,
3378
- styles: [nginx_server_drawer_component_default2]
3379
- })
3380
- ], NginxServerDrawerComponent);
3381
-
3382
- // src/app/pages/nginx/components/nginx-server-list/nginx-server-list.component.ts
3383
- var NginxServerListComponent = class NginxServerListComponent2 {
3384
- showToolbar = true;
3385
- openCreateToken = 0;
3386
- summaryChange = new EventEmitter();
3387
- serverListMutated = new EventEmitter();
3388
- nginxService = inject(NginxService);
3389
- message = inject(NzMessageService);
3390
- modal = inject(NzModalService);
3391
- servers = signal([]);
3392
- loading = signal(false);
3393
- drawerVisible = false;
3394
- editingServer = signal(null);
3395
- configModalVisible = false;
3396
- viewingConfig = signal("");
3397
- ngOnInit() {
3398
- this.loadServers();
3399
- }
3400
- ngOnChanges(changes) {
3401
- const openCreate = changes["openCreateToken"];
3402
- if (openCreate && !openCreate.firstChange) {
3403
- this.openDrawer(null);
3404
- }
3405
- }
3406
- async loadServers() {
3407
- this.loading.set(true);
3408
- try {
3409
- const res = await this.nginxService.getServers();
3410
- if (res.success && res.servers) {
3411
- this.servers.set(res.servers);
3412
- this.summaryChange.emit({
3413
- total: res.servers.length,
3414
- enabled: res.servers.filter((server) => server.enabled).length
3415
- });
3416
- }
3417
- } catch (err) {
3418
- this.message.error("\u52A0\u8F7D\u5931\u8D25: " + err.message);
3419
- } finally {
3420
- this.loading.set(false);
3421
- }
3422
- }
3423
- openDrawer(server) {
3424
- this.editingServer.set(server);
3425
- this.drawerVisible = true;
3426
- }
3427
- onDrawerVisibleChange(visible) {
3428
- this.drawerVisible = visible;
3429
- if (!visible) {
3430
- this.editingServer.set(null);
3431
- }
3432
- }
3433
- onSaved() {
3434
- this.loadServers();
3435
- this.serverListMutated.emit();
3436
- }
3437
- importServer() {
3438
- this.message.info("\u5BFC\u5165\u529F\u80FD\u5F00\u53D1\u4E2D");
3439
- }
3440
- async toggleServer(id, enabled) {
3441
- try {
3442
- const res = enabled ? await this.nginxService.enableServer(id) : await this.nginxService.disableServer(id);
3443
- if (res.success) {
3444
- this.message.success(enabled ? "\u5DF2\u542F\u7528" : "\u5DF2\u7981\u7528");
3445
- await this.loadServers();
3446
- this.serverListMutated.emit();
3447
- } else {
3448
- this.message.error(res.error || "\u64CD\u4F5C\u5931\u8D25");
3449
- }
3450
- } catch (err) {
3451
- this.message.error("\u64CD\u4F5C\u5931\u8D25: " + err.message);
3452
- }
3453
- }
3454
- copyServer(server) {
3455
- this.editingServer.set(server);
3456
- this.drawerVisible = true;
3457
- this.message.info("\u590D\u5236 Server - \u8BF7\u4FEE\u6539\u540D\u79F0\u540E\u4FDD\u5B58");
3458
- }
3459
- async deleteServer(server) {
3460
- try {
3461
- const res = await this.nginxService.deleteServer(server.id);
3462
- if (res.success) {
3463
- this.message.success("\u5DF2\u5220\u9664");
3464
- this.loadServers();
3465
- this.serverListMutated.emit();
3466
- } else {
3467
- this.message.error(res.error || "\u5220\u9664\u5931\u8D25");
3468
- }
3469
- } catch (err) {
3470
- this.message.error("\u5220\u9664\u5931\u8D25: " + err.message);
3471
- }
3472
- }
3473
- getAccessUrls(server) {
3474
- return this.buildAccessUrls(server);
3475
- }
3476
- buildAccessUrls(server) {
3477
- const scheme = server.ssl ? "https" : "http";
3478
- const ports = this.extractPorts(server.listen);
3479
- const hosts = this.extractHosts(server);
3480
- const selectedPort = ports[0] ?? (server.ssl ? 443 : 80);
3481
- if (!hosts.length) {
3482
- return [];
3483
- }
3484
- return hosts.map((host) => this.buildUrl(scheme, host, selectedPort)).filter(Boolean).slice(0, 6);
3485
- }
3486
- extractPorts(listen) {
3487
- const ports = /* @__PURE__ */ new Set();
3488
- for (const item of listen || []) {
3489
- const port = this.parseListenPort(item);
3490
- if (port !== null) {
3491
- ports.add(port);
3492
- }
3493
- }
3494
- return Array.from(ports.values()).sort((a, b) => a - b);
3495
- }
3496
- parseListenPort(rawListen) {
3497
- const text = String(rawListen || "").trim();
3498
- if (!text || /^unix:/i.test(text)) {
3499
- return null;
3500
- }
3501
- const token = text.split(/\s+/)[0] || "";
3502
- let portToken = token;
3503
- if (/^\[[^\]]+\]:\d+$/.test(token)) {
3504
- portToken = token.replace(/^.*\]:/, "");
3505
- } else if (token.includes(":")) {
3506
- portToken = token.slice(token.lastIndexOf(":") + 1);
3507
- }
3508
- const port = Number(portToken);
3509
- if (!Number.isInteger(port) || port < 1 || port > 65535) {
3510
- return null;
3511
- }
3512
- return port;
3513
- }
3514
- extractHosts(server) {
3515
- const hosts = /* @__PURE__ */ new Set();
3516
- for (const domain of server.domains || []) {
3517
- const item = String(domain || "").trim();
3518
- if (!item || item === "_" || item === "*") {
3519
- continue;
3520
- }
3521
- hosts.add(item);
3522
- }
3523
- if (!hosts.size) {
3524
- const listenHost = this.parseListenHost(server.listen?.[0] || "");
3525
- if (listenHost) {
3526
- hosts.add(listenHost);
3527
- }
3528
- }
3529
- if (!hosts.size) {
3530
- hosts.add("127.0.0.1");
3531
- }
3532
- return Array.from(hosts.values());
3533
- }
3534
- parseListenHost(rawListen) {
3535
- const text = String(rawListen || "").trim();
3536
- if (!text || /^unix:/i.test(text)) {
3537
- return null;
3538
- }
3539
- const token = text.split(/\s+/)[0] || "";
3540
- if (/^\[[^\]]+\]:\d+$/.test(token)) {
3541
- const host = token.slice(1, token.indexOf("]")).trim();
3542
- return this.normalizeHost(host);
3543
- }
3544
- if (token.includes(":")) {
3545
- const host = token.slice(0, token.lastIndexOf(":")).trim();
3546
- return this.normalizeHost(host);
3547
- }
3548
- return null;
3549
- }
3550
- normalizeHost(host) {
3551
- if (!host || host === "*" || host === "0.0.0.0" || host === "::" || host === "[::]") {
3552
- return null;
3553
- }
3554
- return host;
3555
- }
3556
- buildUrl(scheme, host, port) {
3557
- const normalizedHost = this.normalizeHostForUrl(host);
3558
- if (!normalizedHost) {
3559
- return "";
3560
- }
3561
- const hidePort = scheme === "http" && port === 80 || scheme === "https" && port === 443;
3562
- return `${scheme}://${normalizedHost}${hidePort ? "" : `:${port}`}`;
3563
- }
3564
- normalizeHostForUrl(host) {
3565
- const text = String(host || "").trim();
3566
- if (!text) {
3567
- return "";
3568
- }
3569
- if (text.includes(":") && !text.startsWith("[") && !text.endsWith("]")) {
3570
- return `[${text}]`;
3571
- }
3572
- return text;
3573
- }
3574
- static propDecorators = {
3575
- showToolbar: [{ type: Input }],
3576
- openCreateToken: [{ type: Input }],
3577
- summaryChange: [{ type: Output }],
3578
- serverListMutated: [{ type: Output }]
3579
- };
3580
- };
3581
- NginxServerListComponent = __decorate([
3582
- Component({
3583
- selector: "app-nginx-server-list",
3584
- standalone: true,
3585
- imports: [
3586
- CommonModule,
3587
- FormsModule,
3588
- NzButtonModule,
3589
- NzIconModule,
3590
- NzModalModule,
3591
- NzSpinModule,
3592
- NzTooltipModule,
3593
- NzSwitchModule,
3594
- NzPopconfirmModule,
3595
- NginxServerDrawerComponent
3596
- ],
3597
- template: nginx_server_list_component_default,
3598
- styles: [nginx_server_list_component_default2]
3599
- })
3600
- ], NginxServerListComponent);
3601
-
3602
- // angular:jit:style:inline:src\app\pages\nginx\components\nginx-stat-card\nginx-stat-card.component.ts;CiAgICA6aG9zdCB7CiAgICAgIGRpc3BsYXk6IGJsb2NrOwogICAgfQoKICAgIC5zdGF0LWNhcmQgewogICAgICBiYWNrZ3JvdW5kOiB2YXIoLS1iZy13aGl0ZSk7CiAgICAgIGJvcmRlcjogMXB4IHNvbGlkIHZhcigtLWJvcmRlcik7CiAgICAgIGJvcmRlci1yYWRpdXM6IDhweDsKICAgICAgcGFkZGluZzogMTRweDsKICAgICAgYm94LXNoYWRvdzogdmFyKC0tc2hhZG93LWNhcmQpOwogICAgICB0cmFuc2l0aW9uOiBhbGwgMTQwbXMgZWFzZTsKCiAgICAgICY6aG92ZXIgewogICAgICAgIGJveC1zaGFkb3c6IHZhcigtLXNoYWRvdy1ob3Zlcik7CiAgICAgICAgdHJhbnNmb3JtOiB0cmFuc2xhdGVZKC0xcHgpOwogICAgICB9CiAgICB9CgogICAgLnN0YXQtbGFiZWwgewogICAgICBmb250LXNpemU6IHZhcigtLW5naW54LWZvbnQtc2l6ZS1zbSwgMTJweCk7CiAgICAgIGNvbG9yOiB2YXIoLS10ZXh0LTMpOwogICAgICBmb250LXdlaWdodDogNjAwOwogICAgICB0ZXh0LXRyYW5zZm9ybTogdXBwZXJjYXNlOwogICAgICBsZXR0ZXItc3BhY2luZzogMC41cHg7CiAgICAgIG1hcmdpbi1ib3R0b206IDZweDsKICAgIH0KCiAgICAuc3RhdC12YWx1ZSB7CiAgICAgIGZvbnQtc2l6ZTogdmFyKC0tbmdpbngtZm9udC1zaXplLWtwaSwgMjJweCk7CiAgICAgIGxpbmUtaGVpZ2h0OiAxLjE7CiAgICAgIGZvbnQtd2VpZ2h0OiA4MDA7CiAgICAgIGNvbG9yOiB2YXIoLS10ZXh0LTEpOwogICAgICBmb250LWZhbWlseTogdmFyKC0tbmdpbngtZm9udC1mYW1pbHktbW9ubywgdWktbW9ub3NwYWNlLCBTRk1vbm8tUmVndWxhciwgTWVubG8sIE1vbmFjbywgQ29uc29sYXMsICdMaWJlcmF0aW9uIE1vbm8nLCBtb25vc3BhY2UpOwogICAgICBtYXJnaW4tYm90dG9tOiA0cHg7CgogICAgICAmLmdyZWVuIHsKICAgICAgICBjb2xvcjogdmFyKC0tZ3JlZW4pOwogICAgICB9CgogICAgICAmLmJsdWUgewogICAgICAgIGNvbG9yOiB2YXIoLS1ibHVlKTsKICAgICAgfQoKICAgICAgJi5wdXJwbGUgewogICAgICAgIGNvbG9yOiB2YXIoLS1wdXJwbGUpOwogICAgICB9CgogICAgICAmLm9yYW5nZSB7CiAgICAgICAgY29sb3I6IHZhcigtLW9yYW5nZSk7CiAgICAgIH0KCiAgICAgICYucmVkIHsKICAgICAgICBjb2xvcjogdmFyKC0tcmVkKTsKICAgICAgfQogICAgfQoKICAgIC5zdGF0LXN1YiB7CiAgICAgIGZvbnQtc2l6ZTogdmFyKC0tbmdpbngtZm9udC1zaXplLXNtLCAxMnB4KTsKICAgICAgY29sb3I6IHZhcigtLXRleHQtMyk7CiAgICAgIGZvbnQtZmFtaWx5OiB2YXIoLS1uZ2lueC1mb250LWZhbWlseS1tb25vLCB1aS1tb25vc3BhY2UsIFNGTW9uby1SZWd1bGFyLCBNZW5sbywgTW9uYWNvLCBDb25zb2xhcywgJ0xpYmVyYXRpb24gTW9ubycsIG1vbm9zcGFjZSk7CiAgICB9CiAg
3603
- var nginx_stat_card_component_default = '/* angular:styles/component:less;1d06b6323828ffaa434e23281d02e9600bef0e7eacce61bf5884196c0db708c4;D:\\ng-manager\\webapp\\src\\app\\pages\\nginx\\components\\nginx-stat-card\\nginx-stat-card.component.ts */\n:host {\n display: block;\n}\n.stat-card {\n background: var(--bg-white);\n border: 1px solid var(--border);\n border-radius: 8px;\n padding: 14px;\n box-shadow: var(--shadow-card);\n transition: all 140ms ease;\n}\n.stat-card:hover {\n box-shadow: var(--shadow-hover);\n transform: translateY(-1px);\n}\n.stat-label {\n font-size: var(--nginx-font-size-sm, 12px);\n color: var(--text-3);\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n margin-bottom: 6px;\n}\n.stat-value {\n font-size: var(--nginx-font-size-kpi, 22px);\n line-height: 1.1;\n font-weight: 800;\n color: var(--text-1);\n font-family: var(--nginx-font-family-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace);\n margin-bottom: 4px;\n}\n.stat-value.green {\n color: var(--green);\n}\n.stat-value.blue {\n color: var(--blue);\n}\n.stat-value.purple {\n color: var(--purple);\n}\n.stat-value.orange {\n color: var(--orange);\n}\n.stat-value.red {\n color: var(--red);\n}\n.stat-sub {\n font-size: var(--nginx-font-size-sm, 12px);\n color: var(--text-3);\n font-family: var(--nginx-font-family-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace);\n}\n/*# sourceMappingURL=nginx-stat-card.component.css.map */\n';
3604
-
3605
- // src/app/pages/nginx/components/nginx-stat-card/nginx-stat-card.component.ts
3606
- var NginxStatCardComponent = class NginxStatCardComponent2 {
3607
- label = "";
3608
- value = "-";
3609
- sub = "";
3610
- toneClass = "";
3611
- static propDecorators = {
3612
- label: [{ type: Input }],
3613
- value: [{ type: Input }],
3614
- sub: [{ type: Input }],
3615
- toneClass: [{ type: Input }]
3616
- };
3617
- };
3618
- NginxStatCardComponent = __decorate([
3619
- Component({
3620
- selector: "app-nginx-stat-card",
3621
- standalone: true,
3622
- imports: [CommonModule],
3623
- template: `
3624
- <div class="stat-card">
3625
- <div class="stat-label">{{ label }}</div>
3626
- <div class="stat-value" [ngClass]="toneClass">{{ value }}</div>
3627
- <div class="stat-sub">{{ sub }}</div>
3628
- </div>
3629
- `,
3630
- styles: [nginx_stat_card_component_default]
3631
- })
3632
- ], NginxStatCardComponent);
3633
-
3634
- // src/app/pages/nginx/nginx.component.ts
3635
- var NginxComponent = class NginxComponent2 {
3636
- nginxService = inject(NginxService);
3637
- message = inject(NzMessageService);
3638
- modal = inject(NzModalService);
3639
- // Core state
3640
- instance = signal(null);
3641
- status = signal(null);
3642
- loading = signal(false);
3643
- binding = signal(false);
3644
- // 操作中状态,防止重复点击
3645
- controlling = signal(false);
3646
- // UI state
3647
- bindModalVisible = false;
3648
- bindPath = "";
3649
- secondaryTab = signal("upstream");
3650
- configExpanded = signal(false);
3651
- openServerDrawerToken = signal(0);
3652
- configEditorRefreshToken = signal(0);
3653
- configLoading = signal(false);
3654
- // Data state
3655
- configFiles = signal([]);
3656
- serverSummary = signal({ total: 0, enabled: 0 });
3657
- recentLogs = signal([]);
3658
- runtimeDisplay = signal("-");
3659
- runtimeStartedAtLabel = signal("-");
3660
- runtimeBaseSeconds = null;
3661
- runtimeBaseTimestamp = 0;
3662
- runtimeRafId = null;
3663
- runtimeRenderBucket = null;
3664
- secondaryTabs = [
3665
- { id: "upstream", label: "Upstream \u7BA1\u7406" },
3666
- { id: "ssl", label: "SSL \u8BC1\u4E66" },
3667
- { id: "traffic", label: "\u6D41\u91CF\u63A7\u5236" },
3668
- { id: "perf", label: "\u6027\u80FD\u4F18\u5316" },
3669
- { id: "logs", label: "\u65E5\u5FD7" },
3670
- { id: "test", label: "\u914D\u7F6E\u68C0\u6D4B" },
3671
- { id: "settings", label: "\u8BBE\u7F6E" }
3672
- ];
3673
- async ngOnInit() {
3674
- await this.loadStatus();
3675
- await this.loadConfigFiles();
3676
- }
3677
- ngOnDestroy() {
3678
- this.stopRuntimeTicker();
3679
- }
3680
- switchSecondaryTab(tab) {
3681
- this.secondaryTab.set(tab);
3682
- }
3683
- onSecondaryTabIndexChange(index) {
3684
- const target = this.secondaryTabs[index];
3685
- if (target) {
3686
- this.secondaryTab.set(target.id);
3687
- }
3688
- }
3689
- secondaryTabIndex() {
3690
- const index = this.secondaryTabs.findIndex((tab) => tab.id === this.secondaryTab());
3691
- return index >= 0 ? index : 0;
3692
- }
3693
- toggleConfigExpanded() {
3694
- this.configExpanded.update((open) => !open);
3695
- }
3696
- requestCreateServer() {
3697
- this.openServerDrawerToken.update((token) => token + 1);
3698
- }
3699
- importServer() {
3700
- this.message.info("\u5BFC\u5165\u529F\u80FD\u5F00\u53D1\u4E2D");
3701
- }
3702
- onServerSummaryChange(summary) {
3703
- this.serverSummary.set(summary);
3704
- }
3705
- onServerListMutated() {
3706
- void this.loadConfigFiles();
3707
- if (this.configExpanded()) {
3708
- this.configEditorRefreshToken.update((token) => token + 1);
3709
- }
3710
- }
3711
- get serviceStatusText() {
3712
- return this.status()?.isRunning ? "\u8FD0\u884C\u4E2D" : "\u5DF2\u505C\u6B62";
3713
- }
3714
- get uptimeText() {
3715
- if (!this.status()?.isRunning) {
3716
- return "-";
3717
- }
3718
- return this.runtimeDisplay() || this.status()?.uptime || "-";
3719
- }
3720
- get uptimeSubText() {
3721
- if (!this.status()?.isRunning) {
3722
- return "\u670D\u52A1\u672A\u8FD0\u884C";
3723
- }
3724
- const startedAt = this.runtimeStartedAtLabel();
3725
- if (startedAt && startedAt !== "-") {
3726
- return `\u5F00\u59CB\u65F6\u95F4 ${startedAt}`;
3727
- }
3728
- return "\u5F00\u59CB\u65F6\u95F4\u672A\u77E5";
3729
- }
3730
- get pidText() {
3731
- return this.status()?.pid ? `PID ${this.status()?.pid}` : "\u672A\u68C0\u6D4B\u5230 PID";
3732
- }
3733
- get activeConnectionText() {
3734
- const activeConnections = this.status()?.activeConnections;
3735
- if (Number.isFinite(activeConnections)) {
3736
- return String(activeConnections);
3737
- }
3738
- return this.status()?.isRunning ? "N/A" : "-";
3739
- }
3740
- get activeConnectionSubText() {
3741
- const activeConnections = this.status()?.activeConnections;
3742
- if (!this.status()?.isRunning) {
3743
- return "\u670D\u52A1\u672A\u8FD0\u884C";
3744
- }
3745
- if (Number.isFinite(activeConnections)) {
3746
- return "ESTABLISHED TCP \u8FDE\u63A5\u6570";
3747
- }
3748
- return "\u5F53\u524D\u73AF\u5883\u6682\u4E0D\u53EF\u7528";
3749
- }
3750
- get enabledServerText() {
3751
- const summary = this.serverSummary();
3752
- return `${summary.enabled} \u542F\u7528 / ${Math.max(summary.total - summary.enabled, 0)} \u7981\u7528`;
3753
- }
3754
- async refreshAll() {
3755
- await Promise.all([this.loadStatus(), this.loadConfigFiles()]);
3756
- this.message.success("\u72B6\u6001\u5DF2\u5237\u65B0");
3757
- }
3758
- async loadStatus() {
3759
- this.loading.set(true);
3760
- try {
3761
- const stats = await this.nginxService.getStats();
3762
- if (stats.success && stats.status) {
3763
- this.instance.set(stats.instance || null);
3764
- this.status.set(stats.status);
3765
- this.syncRuntimeState(stats.status);
3766
- if (stats.serverSummary) {
3767
- this.serverSummary.set({
3768
- total: stats.serverSummary.total,
3769
- enabled: stats.serverSummary.enabled
3770
- });
3771
- }
3772
- return;
3773
- }
3774
- const res = await this.nginxService.getStatus();
3775
- this.instance.set(res.instance);
3776
- this.status.set(res.status);
3777
- this.syncRuntimeState(res.status);
3778
- } catch {
3779
- } finally {
3780
- this.loading.set(false);
3781
- }
3782
- }
3783
- async loadConfigFiles() {
3784
- this.configLoading.set(true);
3785
- try {
3786
- const res = await this.nginxService.getConfigFiles();
3787
- this.configFiles.set(res.success ? res.files || [] : []);
3788
- } catch {
3789
- this.configFiles.set([]);
3790
- } finally {
3791
- this.configLoading.set(false);
3792
- }
3793
- }
3794
- showBindModal() {
3795
- this.bindPath = this.getBindPathCandidates()[0] || "";
3796
- this.bindModalVisible = true;
3797
- }
3798
- showPathHint() {
3799
- this.message.info("\u5F53\u524D\u7248\u672C\u8BF7\u624B\u52A8\u8F93\u5165\u8DEF\u5F84\uFF0C\u6587\u4EF6\u6D4F\u89C8\u9009\u62E9\u80FD\u529B\u540E\u7EED\u63A5\u5165");
3800
- }
3801
- autoDetectBindPath() {
3802
- const candidates = this.getBindPathCandidates();
3803
- if (!candidates.length) {
3804
- this.message.warning("\u672A\u8BC6\u522B\u5230\u53EF\u7528\u7684\u9ED8\u8BA4\u8DEF\u5F84\uFF0C\u8BF7\u624B\u52A8\u8F93\u5165");
3805
- return;
3806
- }
3807
- this.bindPath = candidates[0];
3808
- this.message.success(`\u5DF2\u586B\u5145\u8DEF\u5F84\uFF1A${this.bindPath}`);
3809
- }
3810
- async bindNginx() {
3811
- if (!this.bindPath.trim()) {
3812
- this.message.warning("\u8BF7\u8F93\u5165 Nginx \u8DEF\u5F84");
3813
- return;
3814
- }
3815
- this.binding.set(true);
3816
- try {
3817
- const res = await this.nginxService.bind(this.bindPath.trim());
3818
- if (res.success && res.instance) {
3819
- this.instance.set(res.instance);
3820
- this.bindModalVisible = false;
3821
- this.message.success("\u7ED1\u5B9A\u6210\u529F");
3822
- await this.refreshAll();
3823
- } else {
3824
- this.message.error(res.error || "\u7ED1\u5B9A\u5931\u8D25");
3825
- }
3826
- } catch (err) {
3827
- this.message.error("\u7ED1\u5B9A\u5931\u8D25: " + err.message);
3828
- } finally {
3829
- this.binding.set(false);
3830
- }
3831
- }
3832
- unbind() {
3833
- this.modal.confirm({
3834
- nzTitle: "\u786E\u8BA4\u89E3\u7ED1",
3835
- nzContent: "\u89E3\u7ED1\u540E\u5C06\u65E0\u6CD5\u7BA1\u7406 Nginx\uFF0C\u662F\u5426\u7EE7\u7EED\uFF1F",
3836
- nzOkText: "\u89E3\u7ED1",
3837
- nzOkType: "primary",
3838
- nzOnOk: async () => {
3839
- try {
3840
- await this.nginxService.unbind();
3841
- this.instance.set(null);
3842
- this.status.set(null);
3843
- this.syncRuntimeState(null);
3844
- this.serverSummary.set({ total: 0, enabled: 0 });
3845
- this.configFiles.set([]);
3846
- this.message.success("\u89E3\u7ED1\u6210\u529F");
3847
- } catch (err) {
3848
- this.message.error("\u89E3\u7ED1\u5931\u8D25: " + err.message);
3849
- }
3850
- }
3851
- });
3852
- }
3853
- async startNginx() {
3854
- if (this.controlling())
3855
- return;
3856
- this.controlling.set(true);
3857
- this.loading.set(true);
3858
- try {
3859
- const res = await this.nginxService.start();
3860
- if (res.success) {
3861
- this.appendLog("ok", "nginx start executed");
3862
- this.message.success("\u542F\u52A8\u6210\u529F");
3863
- await this.loadStatus();
3864
- } else {
3865
- this.message.error(res.error || "\u542F\u52A8\u5931\u8D25");
3866
- }
3867
- } catch (err) {
3868
- this.message.error("\u542F\u52A8\u5931\u8D25: " + err.message);
3869
- } finally {
3870
- this.loading.set(false);
3871
- this.controlling.set(false);
3872
- }
3873
- }
3874
- async stopNginx() {
3875
- if (this.controlling())
3876
- return;
3877
- this.controlling.set(true);
3878
- this.loading.set(true);
3879
- try {
3880
- const res = await this.nginxService.stop();
3881
- if (res.success) {
3882
- this.appendLog("warn", "nginx stop executed");
3883
- this.message.success("\u505C\u6B62\u6210\u529F");
3884
- await this.loadStatus();
3885
- } else {
3886
- this.message.error(res.error || "\u505C\u6B62\u5931\u8D25");
3887
- }
3888
- } catch (err) {
3889
- this.message.error("\u505C\u6B62\u5931\u8D25: " + err.message);
3890
- } finally {
3891
- this.loading.set(false);
3892
- this.controlling.set(false);
3893
- }
3894
- }
3895
- async reloadNginx() {
3896
- if (this.controlling())
3897
- return;
3898
- this.controlling.set(true);
3899
- this.loading.set(true);
3900
- try {
3901
- const res = await this.nginxService.reload();
3902
- if (res.success) {
3903
- this.appendLog("ok", "nginx reload executed");
3904
- this.message.success("\u91CD\u8F7D\u6210\u529F");
3905
- await this.loadStatus();
3906
- } else {
3907
- this.message.error(res.error || "\u91CD\u8F7D\u5931\u8D25");
3908
- }
3909
- } catch (err) {
3910
- this.message.error("\u91CD\u8F7D\u5931\u8D25: " + err.message);
3911
- } finally {
3912
- this.loading.set(false);
3913
- this.controlling.set(false);
3914
- }
3915
- }
3916
- async restartNginx() {
3917
- if (this.controlling())
3918
- return;
3919
- this.controlling.set(true);
3920
- this.loading.set(true);
3921
- try {
3922
- const stopRes = await this.nginxService.stop();
3923
- if (!stopRes.success) {
3924
- this.message.error("\u91CD\u542F\u5931\u8D25: " + (stopRes.error || "\u505C\u6B62\u5931\u8D25"));
3925
- return;
3926
- }
3927
- const startRes = await this.nginxService.start();
3928
- if (startRes.success) {
3929
- this.appendLog("info", "nginx restart executed");
3930
- this.message.success("\u91CD\u542F\u6210\u529F");
3931
- await this.loadStatus();
3932
- } else {
3933
- this.message.error("\u91CD\u542F\u5931\u8D25: " + (startRes.error || "\u542F\u52A8\u5931\u8D25"));
3934
- }
3935
- } catch (err) {
3936
- this.message.error("\u91CD\u542F\u5931\u8D25: " + err.message);
3937
- } finally {
3938
- this.loading.set(false);
3939
- this.controlling.set(false);
3940
- }
3941
- }
3942
- async testConfig() {
3943
- this.loading.set(true);
3944
- try {
3945
- const res = await this.nginxService.test();
3946
- if (res.valid) {
3947
- this.appendLog("ok", "configuration test passed");
3948
- this.message.success("\u914D\u7F6E\u9A8C\u8BC1\u901A\u8FC7");
3949
- if (res.warnings?.length) {
3950
- res.warnings.forEach((w) => this.message.warning(w));
3951
- }
3952
- } else {
3953
- this.appendLog("error", "configuration test failed");
3954
- this.message.error("\u914D\u7F6E\u9A8C\u8BC1\u5931\u8D25");
3955
- res.errors?.forEach((e) => this.message.error(e));
3956
- }
3957
- } catch (err) {
3958
- this.message.error("\u6D4B\u8BD5\u5931\u8D25: " + err.message);
3959
- } finally {
3960
- this.loading.set(false);
3961
- }
3962
- }
3963
- appendLog(level, msg) {
3964
- const time = (/* @__PURE__ */ new Date()).toLocaleTimeString("zh-CN", {
3965
- hour12: false,
3966
- hour: "2-digit",
3967
- minute: "2-digit",
3968
- second: "2-digit"
3969
- });
3970
- this.recentLogs.update((logs) => [{ time, level, msg }, ...logs].slice(0, 120));
3971
- }
3972
- getBindPathCandidates() {
3973
- const ua = typeof navigator !== "undefined" ? navigator.userAgent.toLowerCase() : "";
3974
- if (ua.includes("windows")) {
3975
- return ["C:\\nginx\\nginx.exe", "D:\\nginx\\nginx.exe"];
3976
- }
3977
- if (ua.includes("mac")) {
3978
- return ["/opt/homebrew/bin/nginx", "/usr/local/bin/nginx"];
3979
- }
3980
- return ["/usr/sbin/nginx", "/usr/local/nginx/sbin/nginx", "/usr/local/bin/nginx"];
3981
- }
3982
- syncRuntimeState(status) {
3983
- if (!status?.isRunning) {
3984
- this.stopRuntimeTicker();
3985
- this.runtimeBaseSeconds = null;
3986
- this.runtimeBaseTimestamp = 0;
3987
- this.runtimeDisplay.set("-");
3988
- this.runtimeStartedAtLabel.set("-");
3989
- return;
3990
- }
3991
- const seconds = this.parseUptimeSeconds(status.uptime);
3992
- if (seconds === null) {
3993
- this.stopRuntimeTicker();
3994
- this.runtimeBaseSeconds = null;
3995
- this.runtimeBaseTimestamp = 0;
3996
- this.runtimeDisplay.set(status.uptime || "-");
3997
- this.runtimeStartedAtLabel.set("-");
3998
- return;
3999
- }
4000
- this.runtimeBaseSeconds = seconds;
4001
- this.runtimeBaseTimestamp = Date.now();
4002
- this.runtimeRenderBucket = null;
4003
- const startedAt = new Date(this.runtimeBaseTimestamp - seconds * 1e3);
4004
- this.runtimeStartedAtLabel.set(this.formatDateTime(startedAt));
4005
- this.updateRuntimeDisplay();
4006
- this.startRuntimeTicker();
4007
- }
4008
- startRuntimeTicker() {
4009
- if (typeof window === "undefined") {
4010
- return;
4011
- }
4012
- if (this.runtimeRafId !== null) {
4013
- return;
4014
- }
4015
- const tick = () => {
4016
- if (this.runtimeBaseSeconds === null || !this.status()?.isRunning) {
4017
- this.stopRuntimeTicker();
4018
- return;
4019
- }
4020
- this.updateRuntimeDisplay();
4021
- this.runtimeRafId = window.requestAnimationFrame(tick);
4022
- };
4023
- this.runtimeRafId = window.requestAnimationFrame(tick);
4024
- }
4025
- stopRuntimeTicker() {
4026
- if (typeof window === "undefined") {
4027
- return;
4028
- }
4029
- if (this.runtimeRafId !== null) {
4030
- window.cancelAnimationFrame(this.runtimeRafId);
4031
- this.runtimeRafId = null;
4032
- }
4033
- this.runtimeRenderBucket = null;
4034
- }
4035
- updateRuntimeDisplay() {
4036
- if (this.runtimeBaseSeconds === null) {
4037
- return;
4038
- }
4039
- const elapsed = this.runtimeBaseSeconds + Math.max(0, Math.floor((Date.now() - this.runtimeBaseTimestamp) / 1e3));
4040
- const useMinuteBucket = elapsed >= 86400;
4041
- const bucket = useMinuteBucket ? Math.floor(elapsed / 60) : elapsed;
4042
- if (this.runtimeRenderBucket === bucket) {
4043
- return;
4044
- }
4045
- this.runtimeRenderBucket = bucket;
4046
- this.runtimeDisplay.set(this.formatElapsed(elapsed));
4047
- }
4048
- parseUptimeSeconds(value) {
4049
- const text = String(value || "").trim();
4050
- if (!text) {
4051
- return null;
4052
- }
4053
- let match = text.match(/^(\d+)\s*d\s*(\d{1,2}):(\d{1,2}):(\d{1,2})$/i);
4054
- if (match) {
4055
- const days = Number(match[1]);
4056
- const hours = Number(match[2]);
4057
- const minutes = Number(match[3]);
4058
- const seconds = Number(match[4]);
4059
- return days * 86400 + hours * 3600 + minutes * 60 + seconds;
4060
- }
4061
- match = text.match(/^(\d+)-(\d{1,2}):(\d{1,2}):(\d{1,2})$/);
4062
- if (match) {
4063
- const days = Number(match[1]);
4064
- const hours = Number(match[2]);
4065
- const minutes = Number(match[3]);
4066
- const seconds = Number(match[4]);
4067
- return days * 86400 + hours * 3600 + minutes * 60 + seconds;
4068
- }
4069
- match = text.match(/^(\d{1,2}):(\d{1,2}):(\d{1,2})$/);
4070
- if (match) {
4071
- const hours = Number(match[1]);
4072
- const minutes = Number(match[2]);
4073
- const seconds = Number(match[3]);
4074
- return hours * 3600 + minutes * 60 + seconds;
4075
- }
4076
- match = text.match(/^(\d{1,2}):(\d{1,2})$/);
4077
- if (match) {
4078
- const minutes = Number(match[1]);
4079
- const seconds = Number(match[2]);
4080
- return minutes * 60 + seconds;
4081
- }
4082
- return null;
4083
- }
4084
- formatElapsed(seconds) {
4085
- const total = Math.max(0, Math.floor(seconds));
4086
- if (total >= 86400) {
4087
- const days = Math.floor(total / 86400);
4088
- const hours2 = Math.floor(total % 86400 / 3600);
4089
- const minutes2 = Math.floor(total % 3600 / 60);
4090
- return `${days}\u5929 ${hours2}\u5C0F\u65F6 ${minutes2}\u5206\u949F`;
4091
- }
4092
- const hours = Math.floor(total / 3600);
4093
- const minutes = Math.floor(total % 3600 / 60);
4094
- const remainSeconds = total % 60;
4095
- const hh = String(hours).padStart(2, "0");
4096
- const mm = String(minutes).padStart(2, "0");
4097
- const ss = String(remainSeconds).padStart(2, "0");
4098
- return `${hh}:${mm}:${ss}`;
4099
- }
4100
- formatDateTime(date) {
4101
- const year = date.getFullYear();
4102
- const month = String(date.getMonth() + 1).padStart(2, "0");
4103
- const day = String(date.getDate()).padStart(2, "0");
4104
- const hours = String(date.getHours()).padStart(2, "0");
4105
- const minutes = String(date.getMinutes()).padStart(2, "0");
4106
- const seconds = String(date.getSeconds()).padStart(2, "0");
4107
- return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
4108
- }
4109
- };
4110
- NginxComponent = __decorate([
4111
- Component({
4112
- selector: "app-nginx",
4113
- standalone: true,
4114
- imports: [
4115
- CommonModule,
4116
- FormsModule,
4117
- NzButtonModule,
4118
- NzIconModule,
4119
- NzInputModule,
4120
- NzLayoutModule,
4121
- NzModalModule,
4122
- NzSpinModule,
4123
- NzTabsModule,
4124
- NzEmptyModule,
4125
- NzTooltipModule,
4126
- PageLayoutComponent,
4127
- NginxConfigEditorComponent,
4128
- NginxSecondaryUpstreamTabComponent,
4129
- NginxSecondarySslTabComponent,
4130
- NginxSecondaryTrafficTabComponent,
4131
- NginxSecondaryPerfTabComponent,
4132
- NginxSecondaryLogsTabComponent,
4133
- NginxSecondaryTestTabComponent,
4134
- NginxSecondarySettingsTabComponent,
4135
- NginxSectionCardComponent,
4136
- NginxServerListComponent,
4137
- NginxStatCardComponent
4138
- ],
4139
- template: nginx_component_default,
4140
- styles: [nginx_component_default2]
4141
- })
4142
- ], NginxComponent);
4143
- export {
4144
- NginxComponent
4145
- };
4146
- //# sourceMappingURL=chunk-MMPO7QLO.js.map