cancan-microstack 0.0.1__py3-none-any.whl

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 (440) hide show
  1. cancan_microstack/__init__.py +14 -0
  2. cancan_microstack/__version__.py +10 -0
  3. cancan_microstack/assets/__init__.py +6 -0
  4. cancan_microstack/assets/builds/caddy/Caddyfile +187 -0
  5. cancan_microstack/assets/builds/caddy/DEPLOYMENT.md +303 -0
  6. cancan_microstack/assets/builds/caddy/Dockerfile +46 -0
  7. cancan_microstack/assets/builds/caddy/README.md +343 -0
  8. cancan_microstack/assets/builds/caddy/geoip/README.md +5 -0
  9. cancan_microstack/assets/builds/caddy/start.sh +78 -0
  10. cancan_microstack/assets/builds/caddy/waf/coraza.conf +179 -0
  11. cancan_microstack/assets/builds/service/Dockerfile +59 -0
  12. cancan_microstack/assets/builds/service/README.md +13 -0
  13. cancan_microstack/assets/ddl/create_db.sql +22 -0
  14. cancan_microstack/assets/ddl/infra/execution_log_tbl.sql +46 -0
  15. cancan_microstack/assets/ddl/infra/node_instance_tbl.sql +56 -0
  16. cancan_microstack/assets/ddl/infra/service_action_log_tbl.sql +36 -0
  17. cancan_microstack/assets/ddl/infra/service_config_tbl.sql +26 -0
  18. cancan_microstack/assets/ddl/infra/service_info_tbl.sql +45 -0
  19. cancan_microstack/assets/ddl/infra/service_instance_tbl.sql +54 -0
  20. cancan_microstack/assets/ddl/infra/service_operation_tbl.sql +47 -0
  21. cancan_microstack/assets/ddl/infra/workflow_definition_tbl.sql +60 -0
  22. cancan_microstack/assets/ddl/infra/workflow_definition_version_tbl.sql +35 -0
  23. cancan_microstack/assets/ddl/infra/workflow_engine_alert_tbl.sql +34 -0
  24. cancan_microstack/assets/ddl/infra/workflow_run_tbl.sql +52 -0
  25. cancan_microstack/assets/ddl/ops/admin_user_tbl.sql +34 -0
  26. cancan_microstack/assets/ddl/ops/caddy_access_log_tbl.sql +91 -0
  27. cancan_microstack/assets/ddl/ops/caddy_certificate_tbl.sql +59 -0
  28. cancan_microstack/assets/ddl/ops/caddy_rate_limit_tbl.sql +64 -0
  29. cancan_microstack/assets/ddl/ops/caddy_route_tbl.sql +63 -0
  30. cancan_microstack/assets/ddl/ops/caddy_stats_tbl.sql +77 -0
  31. cancan_microstack/assets/ddl/trigger.sql +21 -0
  32. cancan_microstack/assets/docker/docker-compose.infra.yml +401 -0
  33. cancan_microstack/assets/scripts/README.md +195 -0
  34. cancan_microstack/assets/scripts/docker/build_images.sh +44 -0
  35. cancan_microstack/assets/scripts/docker/force_rebuild_images.sh +38 -0
  36. cancan_microstack/assets/scripts/docker/rebuild_all.sh +34 -0
  37. cancan_microstack/assets/scripts/docker/rebuild_compose.sh +61 -0
  38. cancan_microstack/assets/scripts/docker/restart.sh +35 -0
  39. cancan_microstack/assets/scripts/docker/restart_compose.sh +35 -0
  40. cancan_microstack/assets/scripts/docker/start.sh +78 -0
  41. cancan_microstack/assets/scripts/docker/start_all.sh +46 -0
  42. cancan_microstack/assets/scripts/docker/start_compose.sh +66 -0
  43. cancan_microstack/assets/scripts/docker/stop.sh +67 -0
  44. cancan_microstack/assets/scripts/docker/stop_all.sh +38 -0
  45. cancan_microstack/assets/scripts/docker/stop_compose.sh +38 -0
  46. cancan_microstack/assets/scripts/podman/build_images_podman.sh +59 -0
  47. cancan_microstack/assets/scripts/podman/cleanup_podman.sh +25 -0
  48. cancan_microstack/assets/scripts/podman/force_rebuild_images_podman.sh +56 -0
  49. cancan_microstack/assets/scripts/podman/rebuild_all_podman.sh +37 -0
  50. cancan_microstack/assets/scripts/podman/rebuild_compose_podman.sh +60 -0
  51. cancan_microstack/assets/scripts/podman/restart_compose_podman.sh +73 -0
  52. cancan_microstack/assets/scripts/podman/start_all_podman.sh +66 -0
  53. cancan_microstack/assets/scripts/podman/start_compose_podman.sh +80 -0
  54. cancan_microstack/assets/scripts/podman/start_podman.sh +91 -0
  55. cancan_microstack/assets/scripts/podman/stop.sh +73 -0
  56. cancan_microstack/assets/scripts/podman/stop_all_podman.sh +34 -0
  57. cancan_microstack/assets/scripts/podman/stop_compose_podman.sh +58 -0
  58. cancan_microstack/assets/scripts/start_controllersrv.sh +9 -0
  59. cancan_microstack/assets/scripts/utils/check_all_db_tables.sh +104 -0
  60. cancan_microstack/assets/scripts/utils/check_env.sh +177 -0
  61. cancan_microstack/assets/scripts/utils/check_service_management_deployment.sh +225 -0
  62. cancan_microstack/assets/scripts/utils/deploy_service_management.sh +176 -0
  63. cancan_microstack/assets/scripts/utils/force_reload_infrasrv.sh +52 -0
  64. cancan_microstack/assets/scripts/utils/monitor_service_management.sh +187 -0
  65. cancan_microstack/assets/scripts/utils/reset_postgres_volume.sh +68 -0
  66. cancan_microstack/assets/scripts/utils/test_async_operations.sh +141 -0
  67. cancan_microstack/assets/scripts/utils/verify_real_operations.sh +76 -0
  68. cancan_microstack/assets/service/Dockerfile +65 -0
  69. cancan_microstack/assets/www/adminops/assets/AppEmpty.vue_vue_type_script_setup_true_lang-BOKUurnM.js +1 -0
  70. cancan_microstack/assets/www/adminops/assets/ConfigManage-DKV5YOUz.js +1 -0
  71. cancan_microstack/assets/www/adminops/assets/ConfigManage-Y5bhy7wG.css +1 -0
  72. cancan_microstack/assets/www/adminops/assets/ConsoleManage-8ljYvCW2.js +1 -0
  73. cancan_microstack/assets/www/adminops/assets/ConsoleManage-BWpyqbuQ.css +1 -0
  74. cancan_microstack/assets/www/adminops/assets/DashboardNew-B9Nf1OPl.js +1 -0
  75. cancan_microstack/assets/www/adminops/assets/DashboardNew-DYWZKQ1V.css +1 -0
  76. cancan_microstack/assets/www/adminops/assets/LogSearch-CA0Jhe78.js +1 -0
  77. cancan_microstack/assets/www/adminops/assets/LogSearch-CCZfTNPF.css +1 -0
  78. cancan_microstack/assets/www/adminops/assets/LoginView-BId3kP3M.css +1 -0
  79. cancan_microstack/assets/www/adminops/assets/LoginView-BQZTV_Qy.js +1 -0
  80. cancan_microstack/assets/www/adminops/assets/OperationProgressDialog-BdEYwqFq.js +1 -0
  81. cancan_microstack/assets/www/adminops/assets/OperationProgressDialog-D-pASR8G.css +1 -0
  82. cancan_microstack/assets/www/adminops/assets/PageContainer-Byss-yUC.js +1 -0
  83. cancan_microstack/assets/www/adminops/assets/PageContainer-C3nSZwM7.css +1 -0
  84. cancan_microstack/assets/www/adminops/assets/RateLimitManage-BDI8jLpC.css +1 -0
  85. cancan_microstack/assets/www/adminops/assets/RateLimitManage-DJY4NiF-.js +1 -0
  86. cancan_microstack/assets/www/adminops/assets/RouteManage-DaUQ4QLw.css +1 -0
  87. cancan_microstack/assets/www/adminops/assets/RouteManage-w9XCU0UA.js +1 -0
  88. cancan_microstack/assets/www/adminops/assets/ServiceCard-BFzHe6Tw.css +1 -0
  89. cancan_microstack/assets/www/adminops/assets/ServiceCard-BJUhWnA-.js +1 -0
  90. cancan_microstack/assets/www/adminops/assets/ServiceDetail-Cw24WuKp.js +1 -0
  91. cancan_microstack/assets/www/adminops/assets/ServiceDetail-Yum47zdB.css +1 -0
  92. cancan_microstack/assets/www/adminops/assets/ServiceList-C7ryvbhE.js +1 -0
  93. cancan_microstack/assets/www/adminops/assets/ServiceList-Cgd01fUx.css +1 -0
  94. cancan_microstack/assets/www/adminops/assets/ServiceLogs-COpG9H0h.js +1 -0
  95. cancan_microstack/assets/www/adminops/assets/ServiceLogs-H_Alq0cf.css +1 -0
  96. cancan_microstack/assets/www/adminops/assets/StatsOverview-D0TwMQkA.js +39 -0
  97. cancan_microstack/assets/www/adminops/assets/StatsOverview-lqAN6pqM.css +1 -0
  98. cancan_microstack/assets/www/adminops/assets/TotpBindView-CWlAmzFt.js +1 -0
  99. cancan_microstack/assets/www/adminops/assets/TotpBindView-HoQC1lhx.css +1 -0
  100. cancan_microstack/assets/www/adminops/assets/TotpVerifyView-BHN1VtX1.css +1 -0
  101. cancan_microstack/assets/www/adminops/assets/TotpVerifyView-D3w_lZk8.js +1 -0
  102. cancan_microstack/assets/www/adminops/assets/WorkflowCenter-DU_mpIA0.css +1 -0
  103. cancan_microstack/assets/www/adminops/assets/WorkflowCenter-i50rZyxN.js +1 -0
  104. cancan_microstack/assets/www/adminops/assets/WorkflowDesigner-CnHokPL9.js +1 -0
  105. cancan_microstack/assets/www/adminops/assets/WorkflowDesigner-DaZaZpLd.css +1 -0
  106. cancan_microstack/assets/www/adminops/assets/WorkflowRuns-B09hK48c.js +1 -0
  107. cancan_microstack/assets/www/adminops/assets/WorkflowRuns-wGutKIIU.css +1 -0
  108. cancan_microstack/assets/www/adminops/assets/caddy-nnCKf8fG.js +1 -0
  109. cancan_microstack/assets/www/adminops/assets/format-Cuzxgna9.js +1 -0
  110. cancan_microstack/assets/www/adminops/assets/index-CiFlm8oc.js +64 -0
  111. cancan_microstack/assets/www/adminops/assets/index-UW0T1Dkc.css +1 -0
  112. cancan_microstack/assets/www/adminops/assets/service-BYlgGPs_.js +1 -0
  113. cancan_microstack/assets/www/adminops/assets/service-operation-6GzLw2Z1.js +1 -0
  114. cancan_microstack/assets/www/adminops/assets/style-CcIXnQ5y.css +1 -0
  115. cancan_microstack/assets/www/adminops/assets/style-lRnStdGu.js +39 -0
  116. cancan_microstack/assets/www/adminops/assets/useDebounce-BRlqfXqf.js +1 -0
  117. cancan_microstack/assets/www/adminops/assets/workflow-CUXs39Ac.js +1 -0
  118. cancan_microstack/assets/www/adminops/index.html +16 -0
  119. cancan_microstack/assets/www/adminops/vite.svg +1 -0
  120. cancan_microstack/cli/__init__.py +14 -0
  121. cancan_microstack/cli/__main__.py +9 -0
  122. cancan_microstack/cli/main.py +552 -0
  123. cancan_microstack/cmd/__init__.py +54 -0
  124. cancan_microstack/cmd/cancan/__init__.py +12 -0
  125. cancan_microstack/cmd/cancan/run.py +395 -0
  126. cancan_microstack/cmd/controllersrv/__init__.py +0 -0
  127. cancan_microstack/cmd/controllersrv/run.py +131 -0
  128. cancan_microstack/cmd/infrasrv/__init__.py +5 -0
  129. cancan_microstack/cmd/infrasrv/run.py +100 -0
  130. cancan_microstack/cmd/opsbffsrv/__init__.py +5 -0
  131. cancan_microstack/cmd/opsbffsrv/run.py +96 -0
  132. cancan_microstack/core/__init__.py +5 -0
  133. cancan_microstack/core/assets.py +123 -0
  134. cancan_microstack/core/compose_builder.py +102 -0
  135. cancan_microstack/core/doctor.py +152 -0
  136. cancan_microstack/core/microstack.py +71 -0
  137. cancan_microstack/core/runner.py +56 -0
  138. cancan_microstack/core/stack_manager.py +186 -0
  139. cancan_microstack/public/__init__.py +7 -0
  140. cancan_microstack/public/api/__init__.py +1 -0
  141. cancan_microstack/public/api/controllersrv_client.py +277 -0
  142. cancan_microstack/public/api/infrasrv_client.py +404 -0
  143. cancan_microstack/public/const/__init__.py +1 -0
  144. cancan_microstack/public/const/action_consts.py +18 -0
  145. cancan_microstack/public/const/app_consts.py +42 -0
  146. cancan_microstack/public/const/caddy_consts.py +22 -0
  147. cancan_microstack/public/const/controllersrv_consts.py +163 -0
  148. cancan_microstack/public/const/docker_consts.py +15 -0
  149. cancan_microstack/public/const/error.py +56 -0
  150. cancan_microstack/public/const/health_consts.py +52 -0
  151. cancan_microstack/public/const/hook_enums.py +56 -0
  152. cancan_microstack/public/const/logging_enums.py +13 -0
  153. cancan_microstack/public/const/metrics_enums.py +36 -0
  154. cancan_microstack/public/const/monitor_enums.py +26 -0
  155. cancan_microstack/public/const/operation_consts.py +53 -0
  156. cancan_microstack/public/const/opsbffsrv_error.py +92 -0
  157. cancan_microstack/public/const/overrides_consts.py +13 -0
  158. cancan_microstack/public/const/redis.py +17 -0
  159. cancan_microstack/public/const/service_consts.py +15 -0
  160. cancan_microstack/public/const/workflow_consts.py +65 -0
  161. cancan_microstack/public/error.py +41 -0
  162. cancan_microstack/public/logging/__init__.py +0 -0
  163. cancan_microstack/public/logging/initializer.py +109 -0
  164. cancan_microstack/public/logging/mq_handler.py +279 -0
  165. cancan_microstack/public/schemas/__init__.py +1 -0
  166. cancan_microstack/public/schemas/caddy/__init__.py +381 -0
  167. cancan_microstack/public/schemas/caddy/analysis.py +90 -0
  168. cancan_microstack/public/schemas/caddy/route.py +18 -0
  169. cancan_microstack/public/schemas/common.py +79 -0
  170. cancan_microstack/public/schemas/controllersrv/__init__.py +3 -0
  171. cancan_microstack/public/schemas/controllersrv/async_requests.py +30 -0
  172. cancan_microstack/public/schemas/controllersrv/compose_models.py +47 -0
  173. cancan_microstack/public/schemas/controllersrv/const.py +24 -0
  174. cancan_microstack/public/schemas/controllersrv/docker_models.py +45 -0
  175. cancan_microstack/public/schemas/controllersrv/docker_responses.py +104 -0
  176. cancan_microstack/public/schemas/controllersrv/requests.py +54 -0
  177. cancan_microstack/public/schemas/controllersrv/responses.py +124 -0
  178. cancan_microstack/public/schemas/controllersrv/task_models.py +102 -0
  179. cancan_microstack/public/schemas/controllersrv/validation.py +23 -0
  180. cancan_microstack/public/schemas/hook_metrics.py +124 -0
  181. cancan_microstack/public/schemas/hooks.py +39 -0
  182. cancan_microstack/public/schemas/infra/__init__.py +0 -0
  183. cancan_microstack/public/schemas/infra/cleanup.py +25 -0
  184. cancan_microstack/public/schemas/infra/container.py +74 -0
  185. cancan_microstack/public/schemas/infra/enums.py +135 -0
  186. cancan_microstack/public/schemas/infra/health_check.py +42 -0
  187. cancan_microstack/public/schemas/infra/hook_log.py +42 -0
  188. cancan_microstack/public/schemas/infra/operation.py +90 -0
  189. cancan_microstack/public/schemas/infra/overview.py +25 -0
  190. cancan_microstack/public/schemas/infra/push.py +33 -0
  191. cancan_microstack/public/schemas/infra/service_action_log.py +47 -0
  192. cancan_microstack/public/schemas/infra/service_config.py +10 -0
  193. cancan_microstack/public/schemas/infra/service_info.py +69 -0
  194. cancan_microstack/public/schemas/infra/service_instance.py +93 -0
  195. cancan_microstack/public/schemas/infra/service_management.py +152 -0
  196. cancan_microstack/public/schemas/infra/service_operation.py +79 -0
  197. cancan_microstack/public/schemas/infra/service_registry.py +158 -0
  198. cancan_microstack/public/schemas/infra/status_types.py +19 -0
  199. cancan_microstack/public/schemas/infra/workflow.py +566 -0
  200. cancan_microstack/public/schemas/logging/__init__.py +1 -0
  201. cancan_microstack/public/schemas/logging/log_event.py +121 -0
  202. cancan_microstack/public/schemas/opsbffsrv/__init__.py +1 -0
  203. cancan_microstack/public/schemas/opsbffsrv/async_ops.py +17 -0
  204. cancan_microstack/public/schemas/opsbffsrv/db_admin.py +147 -0
  205. cancan_microstack/public/schemas/opsbffsrv/db_init.py +48 -0
  206. cancan_microstack/public/schemas/opsbffsrv/service_config.py +89 -0
  207. cancan_microstack/public/schemas/opsbffsrv/service_logs.py +54 -0
  208. cancan_microstack/public/schemas/service_operation.py +24 -0
  209. cancan_microstack/public/schemas/service_registry.py +40 -0
  210. cancan_microstack/public/types/__init__.py +7 -0
  211. cancan_microstack/public/web/__init__.py +0 -0
  212. cancan_microstack/public/web/config_value.py +105 -0
  213. cancan_microstack/public/web/server.py +385 -0
  214. cancan_microstack/py.typed +0 -0
  215. cancan_microstack/runtime/__init__.py +0 -0
  216. cancan_microstack/runtime/compose_cmd.py +228 -0
  217. cancan_microstack/runtime/host_daemon.py +318 -0
  218. cancan_microstack/runtime/overrides.py +103 -0
  219. cancan_microstack/runtime/resources.py +25 -0
  220. cancan_microstack/runtime/workspace.py +94 -0
  221. cancan_microstack/services/__init__.py +0 -0
  222. cancan_microstack/services/controllersrv/__init__.py +8 -0
  223. cancan_microstack/services/controllersrv/application/__init__.py +0 -0
  224. cancan_microstack/services/controllersrv/application/docker_compose_app.py +427 -0
  225. cancan_microstack/services/controllersrv/conf/__init__.py +0 -0
  226. cancan_microstack/services/controllersrv/conf/config.py +76 -0
  227. cancan_microstack/services/controllersrv/conf/settings.py +54 -0
  228. cancan_microstack/services/controllersrv/domain/__init__.py +0 -0
  229. cancan_microstack/services/controllersrv/domain/docker_compose/__init__.py +0 -0
  230. cancan_microstack/services/controllersrv/domain/docker_compose/docker_compose_domain.py +278 -0
  231. cancan_microstack/services/controllersrv/domain/service_validator.py +327 -0
  232. cancan_microstack/services/controllersrv/domain/task/__init__.py +17 -0
  233. cancan_microstack/services/controllersrv/domain/task/task_queue.py +286 -0
  234. cancan_microstack/services/controllersrv/domain/task/task_worker.py +495 -0
  235. cancan_microstack/services/controllersrv/infrastructure/__init__.py +0 -0
  236. cancan_microstack/services/controllersrv/interface/__init__.py +0 -0
  237. cancan_microstack/services/controllersrv/interface/api/__init__.py +0 -0
  238. cancan_microstack/services/controllersrv/interface/api/docker_control_api.py +470 -0
  239. cancan_microstack/services/controllersrv/router.py +132 -0
  240. cancan_microstack/services/infrasrv/__init__.py +4 -0
  241. cancan_microstack/services/infrasrv/application/__init__.py +0 -0
  242. cancan_microstack/services/infrasrv/application/health_check_app.py +24 -0
  243. cancan_microstack/services/infrasrv/application/logging/__init__.py +1 -0
  244. cancan_microstack/services/infrasrv/application/logging/log_ingestion_service.py +183 -0
  245. cancan_microstack/services/infrasrv/application/service_config.py +22 -0
  246. cancan_microstack/services/infrasrv/application/service_logs_app.py +53 -0
  247. cancan_microstack/services/infrasrv/application/service_management_app.py +689 -0
  248. cancan_microstack/services/infrasrv/application/service_operation_tracker.py +251 -0
  249. cancan_microstack/services/infrasrv/application/service_registry.py +53 -0
  250. cancan_microstack/services/infrasrv/application/workflow/__init__.py +0 -0
  251. cancan_microstack/services/infrasrv/application/workflow/workflow_app.py +991 -0
  252. cancan_microstack/services/infrasrv/application/workflow/workflow_queue.py +302 -0
  253. cancan_microstack/services/infrasrv/application/workflow/workflow_tasks.py +46 -0
  254. cancan_microstack/services/infrasrv/application/workflow/workflow_worker_runtime.py +122 -0
  255. cancan_microstack/services/infrasrv/conf/__init__.py +0 -0
  256. cancan_microstack/services/infrasrv/conf/config.py +98 -0
  257. cancan_microstack/services/infrasrv/domain/__init__.py +0 -0
  258. cancan_microstack/services/infrasrv/domain/health_check/__init__.py +3 -0
  259. cancan_microstack/services/infrasrv/domain/health_check/health_check_domain.py +576 -0
  260. cancan_microstack/services/infrasrv/domain/hooks/__init__.py +19 -0
  261. cancan_microstack/services/infrasrv/domain/hooks/builtin_hooks.py +308 -0
  262. cancan_microstack/services/infrasrv/domain/hooks/hook_registry.py +43 -0
  263. cancan_microstack/services/infrasrv/domain/hooks/hooks_log_utils.py +275 -0
  264. cancan_microstack/services/infrasrv/domain/hooks/init.py +17 -0
  265. cancan_microstack/services/infrasrv/domain/hooks/metrics.py +205 -0
  266. cancan_microstack/services/infrasrv/domain/hooks/pre_registration_hooks.py +490 -0
  267. cancan_microstack/services/infrasrv/domain/registry/__init__.py +0 -0
  268. cancan_microstack/services/infrasrv/domain/registry/service_registry.py +509 -0
  269. cancan_microstack/services/infrasrv/domain/service_config/__init__.py +0 -0
  270. cancan_microstack/services/infrasrv/domain/service_config/service_config.py +50 -0
  271. cancan_microstack/services/infrasrv/domain/service_logs/__init__.py +0 -0
  272. cancan_microstack/services/infrasrv/domain/service_logs/service_logs_domain.py +51 -0
  273. cancan_microstack/services/infrasrv/domain/workflow/__init__.py +4 -0
  274. cancan_microstack/services/infrasrv/domain/workflow/engine.py +159 -0
  275. cancan_microstack/services/infrasrv/domain/workflow/node_handlers.py +509 -0
  276. cancan_microstack/services/infrasrv/domain/workflow/workflow_domain.py +164 -0
  277. cancan_microstack/services/infrasrv/infrastructure/__init__.py +0 -0
  278. cancan_microstack/services/infrasrv/infrastructure/api/__init__.py +0 -0
  279. cancan_microstack/services/infrasrv/infrastructure/api/controllersrv_api.py +165 -0
  280. cancan_microstack/services/infrasrv/infrastructure/cache/__init__.py +0 -0
  281. cancan_microstack/services/infrasrv/infrastructure/cache/service_registry_cache.py +174 -0
  282. cancan_microstack/services/infrasrv/infrastructure/db/__init__.py +0 -0
  283. cancan_microstack/services/infrasrv/infrastructure/db/model/__init__.py +0 -0
  284. cancan_microstack/services/infrasrv/infrastructure/db/model/execution_log_tbl.py +53 -0
  285. cancan_microstack/services/infrasrv/infrastructure/db/model/node_instance_tbl.py +55 -0
  286. cancan_microstack/services/infrasrv/infrastructure/db/model/service_action_log_tbl.py +44 -0
  287. cancan_microstack/services/infrasrv/infrastructure/db/model/service_config_tbl.py +30 -0
  288. cancan_microstack/services/infrasrv/infrastructure/db/model/service_info_tbl.py +59 -0
  289. cancan_microstack/services/infrasrv/infrastructure/db/model/service_instance_tbl.py +88 -0
  290. cancan_microstack/services/infrasrv/infrastructure/db/model/service_operation_tbl.py +73 -0
  291. cancan_microstack/services/infrasrv/infrastructure/db/model/workflow_definition_tbl.py +55 -0
  292. cancan_microstack/services/infrasrv/infrastructure/db/model/workflow_definition_version_tbl.py +43 -0
  293. cancan_microstack/services/infrasrv/infrastructure/db/model/workflow_engine_alert_tbl.py +57 -0
  294. cancan_microstack/services/infrasrv/infrastructure/db/model/workflow_run_tbl.py +56 -0
  295. cancan_microstack/services/infrasrv/infrastructure/db/operate/__init__.py +0 -0
  296. cancan_microstack/services/infrasrv/infrastructure/db/operate/service_action_log_op.py +239 -0
  297. cancan_microstack/services/infrasrv/infrastructure/db/operate/service_config.py +80 -0
  298. cancan_microstack/services/infrasrv/infrastructure/db/operate/service_config_manager.py +198 -0
  299. cancan_microstack/services/infrasrv/infrastructure/db/operate/service_info_op.py +297 -0
  300. cancan_microstack/services/infrasrv/infrastructure/db/operate/service_instance_op.py +688 -0
  301. cancan_microstack/services/infrasrv/infrastructure/db/operate/service_operation_op.py +387 -0
  302. cancan_microstack/services/infrasrv/infrastructure/db/operate/service_registry.py +124 -0
  303. cancan_microstack/services/infrasrv/infrastructure/db/operate/workflow_op.py +804 -0
  304. cancan_microstack/services/infrasrv/infrastructure/ddl_manager.py +31 -0
  305. cancan_microstack/services/infrasrv/infrastructure/mongo/__init__.py +1 -0
  306. cancan_microstack/services/infrasrv/infrastructure/mongo/log_repository.py +129 -0
  307. cancan_microstack/services/infrasrv/interface/__init__.py +0 -0
  308. cancan_microstack/services/infrasrv/interface/api/__init__.py +0 -0
  309. cancan_microstack/services/infrasrv/interface/api/health_check_api.py +29 -0
  310. cancan_microstack/services/infrasrv/interface/api/hooks.py +284 -0
  311. cancan_microstack/services/infrasrv/interface/api/internal.py +49 -0
  312. cancan_microstack/services/infrasrv/interface/api/internal_instance_api.py +265 -0
  313. cancan_microstack/services/infrasrv/interface/api/internal_operation_api.py +206 -0
  314. cancan_microstack/services/infrasrv/interface/api/service_config.py +50 -0
  315. cancan_microstack/services/infrasrv/interface/api/service_logs_api.py +49 -0
  316. cancan_microstack/services/infrasrv/interface/api/service_management_api.py +113 -0
  317. cancan_microstack/services/infrasrv/interface/api/service_registry.py +117 -0
  318. cancan_microstack/services/infrasrv/interface/api/workflow_api.py +303 -0
  319. cancan_microstack/services/infrasrv/interface/schedule/__init__.py +0 -0
  320. cancan_microstack/services/infrasrv/interface/schedule/cleanup.py +13 -0
  321. cancan_microstack/services/infrasrv/interface/schedule/health_check.py +27 -0
  322. cancan_microstack/services/infrasrv/interface/schedule/log_cleanup.py +26 -0
  323. cancan_microstack/services/infrasrv/interface/schedule/operation_tracker.py +25 -0
  324. cancan_microstack/services/infrasrv/interface/schedule/scheduler.py +39 -0
  325. cancan_microstack/services/infrasrv/interface/schedule/workflow_scheduler.py +115 -0
  326. cancan_microstack/services/infrasrv/router.py +341 -0
  327. cancan_microstack/services/opsbffsrv/__init__.py +4 -0
  328. cancan_microstack/services/opsbffsrv/application/__init__.py +0 -0
  329. cancan_microstack/services/opsbffsrv/application/async_operation_app.py +150 -0
  330. cancan_microstack/services/opsbffsrv/application/auth_app.py +285 -0
  331. cancan_microstack/services/opsbffsrv/application/caddy/__init__.py +0 -0
  332. cancan_microstack/services/opsbffsrv/application/caddy/access_log_analysis_app.py +344 -0
  333. cancan_microstack/services/opsbffsrv/application/caddy/access_log_ingestion_service.py +169 -0
  334. cancan_microstack/services/opsbffsrv/application/caddy/certificate_management_app.py +355 -0
  335. cancan_microstack/services/opsbffsrv/application/caddy/rate_limit_management_app.py +496 -0
  336. cancan_microstack/services/opsbffsrv/application/caddy/route_management_app.py +401 -0
  337. cancan_microstack/services/opsbffsrv/application/caddy/stats_aggregation_app.py +364 -0
  338. cancan_microstack/services/opsbffsrv/application/db_admin_app.py +103 -0
  339. cancan_microstack/services/opsbffsrv/application/db_init_app.py +283 -0
  340. cancan_microstack/services/opsbffsrv/application/logging/__init__.py +1 -0
  341. cancan_microstack/services/opsbffsrv/application/logging/log_query_app.py +28 -0
  342. cancan_microstack/services/opsbffsrv/application/service_config.py +158 -0
  343. cancan_microstack/services/opsbffsrv/application/service_logs_app.py +74 -0
  344. cancan_microstack/services/opsbffsrv/application/service_registry.py +36 -0
  345. cancan_microstack/services/opsbffsrv/application/workflow_ops_app.py +730 -0
  346. cancan_microstack/services/opsbffsrv/conf/__init__.py +0 -0
  347. cancan_microstack/services/opsbffsrv/conf/config.py +224 -0
  348. cancan_microstack/services/opsbffsrv/domain/__init__.py +0 -0
  349. cancan_microstack/services/opsbffsrv/domain/auth/__init__.py +0 -0
  350. cancan_microstack/services/opsbffsrv/domain/auth/admin_init.py +38 -0
  351. cancan_microstack/services/opsbffsrv/domain/auth/auth_domain.py +108 -0
  352. cancan_microstack/services/opsbffsrv/domain/caddy/__init__.py +0 -0
  353. cancan_microstack/services/opsbffsrv/domain/caddy/access_log_analysis.py +358 -0
  354. cancan_microstack/services/opsbffsrv/domain/caddy/certificate_management.py +325 -0
  355. cancan_microstack/services/opsbffsrv/domain/caddy/default_routes.py +53 -0
  356. cancan_microstack/services/opsbffsrv/domain/caddy/rate_limit_management.py +308 -0
  357. cancan_microstack/services/opsbffsrv/domain/caddy/route_management.py +279 -0
  358. cancan_microstack/services/opsbffsrv/domain/caddy/stats_aggregation.py +654 -0
  359. cancan_microstack/services/opsbffsrv/domain/db_admin/__init__.py +0 -0
  360. cancan_microstack/services/opsbffsrv/domain/db_admin/db_admin_domain.py +118 -0
  361. cancan_microstack/services/opsbffsrv/domain/db_init/__init__.py +3 -0
  362. cancan_microstack/services/opsbffsrv/domain/db_init/db_init_domain.py +358 -0
  363. cancan_microstack/services/opsbffsrv/domain/logging/__init__.py +1 -0
  364. cancan_microstack/services/opsbffsrv/domain/logging/log_query_domain.py +99 -0
  365. cancan_microstack/services/opsbffsrv/domain/service_config/__init__.py +0 -0
  366. cancan_microstack/services/opsbffsrv/domain/service_config/service_config.py +81 -0
  367. cancan_microstack/services/opsbffsrv/domain/service_registry/__init__.py +0 -0
  368. cancan_microstack/services/opsbffsrv/domain/service_registry/service_registry.py +292 -0
  369. cancan_microstack/services/opsbffsrv/infrastructure/__init__.py +0 -0
  370. cancan_microstack/services/opsbffsrv/infrastructure/api/__init__.py +0 -0
  371. cancan_microstack/services/opsbffsrv/infrastructure/api/infrasrv_api.py +242 -0
  372. cancan_microstack/services/opsbffsrv/infrastructure/auth/__init__.py +0 -0
  373. cancan_microstack/services/opsbffsrv/infrastructure/auth/captcha_service.py +67 -0
  374. cancan_microstack/services/opsbffsrv/infrastructure/auth/password_service.py +12 -0
  375. cancan_microstack/services/opsbffsrv/infrastructure/auth/redis_store.py +131 -0
  376. cancan_microstack/services/opsbffsrv/infrastructure/auth/totp_service.py +59 -0
  377. cancan_microstack/services/opsbffsrv/infrastructure/caddy/__init__.py +0 -0
  378. cancan_microstack/services/opsbffsrv/infrastructure/caddy/access_log_parser.py +307 -0
  379. cancan_microstack/services/opsbffsrv/infrastructure/caddy/admin_api_client.py +678 -0
  380. cancan_microstack/services/opsbffsrv/infrastructure/caddy/ip_geo_locator.py +176 -0
  381. cancan_microstack/services/opsbffsrv/infrastructure/db/__init__.py +0 -0
  382. cancan_microstack/services/opsbffsrv/infrastructure/db/model/__init__.py +0 -0
  383. cancan_microstack/services/opsbffsrv/infrastructure/db/model/admin_user_tbl.py +33 -0
  384. cancan_microstack/services/opsbffsrv/infrastructure/db/model/caddy_access_log_tbl.py +90 -0
  385. cancan_microstack/services/opsbffsrv/infrastructure/db/model/caddy_certificate_tbl.py +65 -0
  386. cancan_microstack/services/opsbffsrv/infrastructure/db/model/caddy_rate_limit_tbl.py +69 -0
  387. cancan_microstack/services/opsbffsrv/infrastructure/db/model/caddy_route_tbl.py +66 -0
  388. cancan_microstack/services/opsbffsrv/infrastructure/db/model/caddy_stats_tbl.py +78 -0
  389. cancan_microstack/services/opsbffsrv/infrastructure/db/model/service_action_log_tbl.py +44 -0
  390. cancan_microstack/services/opsbffsrv/infrastructure/db/model/service_config_tbl.py +30 -0
  391. cancan_microstack/services/opsbffsrv/infrastructure/db/model/service_info_tbl.py +51 -0
  392. cancan_microstack/services/opsbffsrv/infrastructure/db/model/service_instance_tbl.py +68 -0
  393. cancan_microstack/services/opsbffsrv/infrastructure/db/operate/__init__.py +0 -0
  394. cancan_microstack/services/opsbffsrv/infrastructure/db/operate/admin_user_operate.py +59 -0
  395. cancan_microstack/services/opsbffsrv/infrastructure/db/operate/caddy_access_log.py +531 -0
  396. cancan_microstack/services/opsbffsrv/infrastructure/db/operate/caddy_certificate.py +451 -0
  397. cancan_microstack/services/opsbffsrv/infrastructure/db/operate/caddy_rate_limit.py +360 -0
  398. cancan_microstack/services/opsbffsrv/infrastructure/db/operate/caddy_route.py +271 -0
  399. cancan_microstack/services/opsbffsrv/infrastructure/db/operate/caddy_stats.py +343 -0
  400. cancan_microstack/services/opsbffsrv/infrastructure/db/operate/service_action_log_op.py +57 -0
  401. cancan_microstack/services/opsbffsrv/infrastructure/db/operate/service_config.py +86 -0
  402. cancan_microstack/services/opsbffsrv/infrastructure/db/operate/service_info_op.py +79 -0
  403. cancan_microstack/services/opsbffsrv/infrastructure/db/operate/service_instance.py +58 -0
  404. cancan_microstack/services/opsbffsrv/infrastructure/db/operate/service_registry.py +138 -0
  405. cancan_microstack/services/opsbffsrv/infrastructure/ddl_manager.py +31 -0
  406. cancan_microstack/services/opsbffsrv/infrastructure/mongo/__init__.py +1 -0
  407. cancan_microstack/services/opsbffsrv/infrastructure/mongo/log_query_repository.py +87 -0
  408. cancan_microstack/services/opsbffsrv/interface/__init__.py +0 -0
  409. cancan_microstack/services/opsbffsrv/interface/api/__init__.py +0 -0
  410. cancan_microstack/services/opsbffsrv/interface/api/async_operation_api.py +137 -0
  411. cancan_microstack/services/opsbffsrv/interface/api/auth_api.py +113 -0
  412. cancan_microstack/services/opsbffsrv/interface/api/caddy/__init__.py +3 -0
  413. cancan_microstack/services/opsbffsrv/interface/api/caddy/access_log_api.py +174 -0
  414. cancan_microstack/services/opsbffsrv/interface/api/caddy/certificate_api.py +235 -0
  415. cancan_microstack/services/opsbffsrv/interface/api/caddy/rate_limit_api.py +302 -0
  416. cancan_microstack/services/opsbffsrv/interface/api/caddy/route_api.py +250 -0
  417. cancan_microstack/services/opsbffsrv/interface/api/caddy/stats_api.py +243 -0
  418. cancan_microstack/services/opsbffsrv/interface/api/db_admin_api.py +62 -0
  419. cancan_microstack/services/opsbffsrv/interface/api/db_init_api.py +109 -0
  420. cancan_microstack/services/opsbffsrv/interface/api/instance_management_api.py +165 -0
  421. cancan_microstack/services/opsbffsrv/interface/api/log_query_api.py +41 -0
  422. cancan_microstack/services/opsbffsrv/interface/api/mongo_express_proxy_api.py +181 -0
  423. cancan_microstack/services/opsbffsrv/interface/api/pgweb_proxy_api.py +154 -0
  424. cancan_microstack/services/opsbffsrv/interface/api/rabbitmq_mgmt_proxy_api.py +518 -0
  425. cancan_microstack/services/opsbffsrv/interface/api/redis_commander_proxy_api.py +133 -0
  426. cancan_microstack/services/opsbffsrv/interface/api/service_config.py +146 -0
  427. cancan_microstack/services/opsbffsrv/interface/api/service_logs_api.py +81 -0
  428. cancan_microstack/services/opsbffsrv/interface/api/service_registry.py +66 -0
  429. cancan_microstack/services/opsbffsrv/interface/api/workflow_ops_api.py +413 -0
  430. cancan_microstack/services/opsbffsrv/interface/middleware/__init__.py +0 -0
  431. cancan_microstack/services/opsbffsrv/interface/middleware/auth_middleware.py +52 -0
  432. cancan_microstack/services/opsbffsrv/router.py +901 -0
  433. cancan_microstack/utils/__init__.py +1 -0
  434. cancan_microstack/utils/container_env.py +218 -0
  435. cancan_microstack-0.0.1.dist-info/METADATA +155 -0
  436. cancan_microstack-0.0.1.dist-info/RECORD +440 -0
  437. cancan_microstack-0.0.1.dist-info/WHEEL +5 -0
  438. cancan_microstack-0.0.1.dist-info/entry_points.txt +2 -0
  439. cancan_microstack-0.0.1.dist-info/licenses/LICENSE +21 -0
  440. cancan_microstack-0.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,678 @@
1
+ """Caddy Admin API client.
2
+
3
+ 提供与 Caddy Admin API 交互的功能。
4
+ Provides helpers to interact with Caddy Admin API.
5
+
6
+ Important:
7
+ - In some dev flows, `caddy.internal` may not exist (caddy not started yet).
8
+ Connection/DNS failures are treated as non-fatal warnings.
9
+ """
10
+
11
+ import asyncio
12
+ import copy
13
+ from typing import (
14
+ Any,
15
+ Dict,
16
+ List,
17
+ Optional,
18
+ Tuple,
19
+ )
20
+
21
+ from aiohttp import (
22
+ ClientConnectorDNSError,
23
+ ClientConnectorError,
24
+ ClientOSError,
25
+ ClientResponseError,
26
+ )
27
+
28
+ from linglong_web import (
29
+ HTTPClientConfig,
30
+ http_client,
31
+ )
32
+ from linglong_web.utils import logger
33
+
34
+ from cancan_microstack.public.const.caddy_consts import InternalRequestPath
35
+ from cancan_microstack.public.error import HTTPException
36
+
37
+
38
+ class CaddyAdminClient:
39
+ """Caddy Admin API 客户端"""
40
+
41
+ _MANAGED_ROUTE_GROUP = "cancan_managed_routes"
42
+ _MANAGED_RATE_LIMIT_GROUP = "cancan_managed_rate_limits"
43
+
44
+ def __init__(self, admin_url: str = "http://caddy.internal:2019"):
45
+ """
46
+ 初始化 Caddy Admin API 客户端
47
+
48
+ Args:
49
+ admin_url: Caddy Admin API 地址
50
+ """
51
+ self.admin_url = admin_url.rstrip('/')
52
+ self._last_load_error: Optional[str] = None
53
+
54
+ async def get_config(self) -> Optional[Dict[str, Any]]:
55
+ """
56
+ 获取当前 Caddy 配置
57
+
58
+ Returns:
59
+ 配置字典或 None
60
+ """
61
+ try:
62
+ url = f"{self.admin_url}/config/"
63
+ resp = await http_client.get(
64
+ url,
65
+ timeout=HTTPClientConfig.INTERNAL_SERVICE_TIMEOUT
66
+ )
67
+ if resp and resp.status == 200:
68
+ return await resp.json()
69
+ logger.warning(f"Failed to get Caddy config: {resp.status if resp else 'No response'}")
70
+ return None
71
+ except HTTPException as e:
72
+ logger.warning(f"HTTP error getting Caddy config: {e}")
73
+ return None
74
+ except (ClientConnectorDNSError, ClientConnectorError, ClientOSError, ClientResponseError, asyncio.TimeoutError) as e:
75
+ logger.warning("Caddy Admin API unreachable (%s): %s", self.admin_url, e)
76
+ return None
77
+ except Exception as e:
78
+ logger.error("Error getting Caddy config: %s", e, exc_info=True)
79
+ return None
80
+
81
+ async def load_config(self, config: Dict[str, Any]) -> bool:
82
+ """
83
+ 加载完整的 Caddy 配置
84
+
85
+ Args:
86
+ config: 完整配置字典
87
+
88
+ Returns:
89
+ 是否成功
90
+ """
91
+ try:
92
+ url = f"{self.admin_url}/load"
93
+ resp = await http_client.post(
94
+ url,
95
+ json=config,
96
+ timeout=HTTPClientConfig.INTERNAL_SERVICE_TIMEOUT
97
+ )
98
+ if resp and resp.status == 200:
99
+ self._last_load_error = None
100
+ logger.info("Caddy config loaded successfully")
101
+ return True
102
+ response_status = resp.status if resp else 'No response'
103
+ response_text = None
104
+ if resp:
105
+ try:
106
+ response_text = await resp.text()
107
+ except Exception as e:
108
+ logger.warning("Failed to read Caddy load error body: %s", e)
109
+ self._last_load_error = response_text or str(response_status)
110
+ logger.warning(
111
+ "Failed to load Caddy config: %s, body=%s",
112
+ response_status,
113
+ response_text,
114
+ )
115
+ return False
116
+ except HTTPException as e:
117
+ self._last_load_error = str(e)
118
+ logger.warning(f"HTTP error loading Caddy config: {e}")
119
+ return False
120
+ except (ClientConnectorDNSError, ClientConnectorError, ClientOSError, ClientResponseError, asyncio.TimeoutError) as e:
121
+ self._last_load_error = str(e)
122
+ logger.warning("Caddy Admin API unreachable (%s): %s", self.admin_url, e)
123
+ return False
124
+ except Exception as e:
125
+ self._last_load_error = str(e)
126
+ logger.error("Error loading Caddy config: %s", e, exc_info=True)
127
+ return False
128
+
129
+ async def update_route(self, route_id: str, route_config: Dict[str, Any]) -> bool:
130
+ """
131
+ 更新单个路由配置
132
+
133
+ Args:
134
+ route_id: 路由标识符
135
+ route_config: 路由配置
136
+
137
+ Returns:
138
+ 是否成功
139
+ """
140
+ try:
141
+ url = f"{self.admin_url}/config/apps/http/servers/srv0/routes/{route_id}"
142
+ resp = await http_client.patch(
143
+ url,
144
+ json=route_config,
145
+ timeout=HTTPClientConfig.INTERNAL_SERVICE_TIMEOUT
146
+ )
147
+ if resp and resp.status == 200:
148
+ logger.info(f"Route {route_id} updated successfully")
149
+ return True
150
+ logger.warning(f"Failed to update route {route_id}: {resp.status if resp else 'No response'}")
151
+ return False
152
+ except HTTPException as e:
153
+ logger.warning(f"HTTP error updating route {route_id}: {e}")
154
+ return False
155
+ except (ClientConnectorDNSError, ClientConnectorError, ClientOSError, ClientResponseError, asyncio.TimeoutError) as e:
156
+ logger.warning("Caddy Admin API unreachable (%s): %s", self.admin_url, e)
157
+ return False
158
+ except Exception as e:
159
+ logger.error("Error updating route %s: %s", route_id, e, exc_info=True)
160
+ return False
161
+
162
+ async def add_route(self, route_config: Dict[str, Any]) -> bool:
163
+ """
164
+ 添加新路由
165
+
166
+ Args:
167
+ route_config: 路由配置
168
+
169
+ Returns:
170
+ 是否成功
171
+ """
172
+ try:
173
+ url = f"{self.admin_url}/config/apps/http/servers/srv0/routes"
174
+ resp = await http_client.post(
175
+ url,
176
+ json=route_config,
177
+ timeout=HTTPClientConfig.INTERNAL_SERVICE_TIMEOUT
178
+ )
179
+ if resp and resp.status == 200:
180
+ logger.info("Route added successfully")
181
+ return True
182
+ logger.warning(f"Failed to add route: {resp.status if resp else 'No response'}")
183
+ return False
184
+ except HTTPException as e:
185
+ logger.warning(f"HTTP error adding route: {e}")
186
+ return False
187
+ except Exception as e:
188
+ logger.error(f"Error adding route: {e}", exc_info=True)
189
+ return False
190
+
191
+ async def delete_route(self, route_id: str) -> bool:
192
+ """
193
+ 删除路由
194
+
195
+ Args:
196
+ route_id: 路由标识符
197
+
198
+ Returns:
199
+ 是否成功
200
+ """
201
+ try:
202
+ url = f"{self.admin_url}/config/apps/http/servers/srv0/routes/{route_id}"
203
+ resp = await http_client.delete(
204
+ url,
205
+ timeout=HTTPClientConfig.INTERNAL_SERVICE_TIMEOUT
206
+ )
207
+ if resp and resp.status == 200:
208
+ logger.info(f"Route {route_id} deleted successfully")
209
+ return True
210
+ logger.warning(f"Failed to delete route {route_id}: {resp.status if resp else 'No response'}")
211
+ return False
212
+ except HTTPException as e:
213
+ logger.warning(f"HTTP error deleting route {route_id}: {e}")
214
+ return False
215
+ except Exception as e:
216
+ logger.error(f"Error deleting route {route_id}: {e}", exc_info=True)
217
+ return False
218
+
219
+ async def reload_config(self) -> bool:
220
+ """
221
+ 重新加载配置(优雅重启)
222
+
223
+ Returns:
224
+ 是否成功
225
+ """
226
+ try:
227
+ # Caddy Admin API 会自动应用配置,无需显式重载
228
+ # 但我们可以验证配置是否有效
229
+ config = await self.get_config()
230
+ return config is not None
231
+ except Exception as e:
232
+ logger.error(f"Error reloading Caddy config: {e}", exc_info=True)
233
+ return False
234
+
235
+ async def get_metrics(self) -> Optional[str]:
236
+ """
237
+ 获取 Prometheus 格式的监控指标
238
+
239
+ Returns:
240
+ 指标文本或 None
241
+ """
242
+ try:
243
+ # Caddy metrics endpoint (需要在 Caddyfile 中配置)
244
+ url = f"{self.admin_url.replace(':2019', ':2020')}/metrics"
245
+ resp = await http_client.get(
246
+ url,
247
+ timeout=HTTPClientConfig.INTERNAL_SERVICE_TIMEOUT
248
+ )
249
+ if resp and resp.status == 200:
250
+ return await resp.text()
251
+ logger.warning(f"Failed to get Caddy metrics: {resp.status if resp else 'No response'}")
252
+ return None
253
+ except HTTPException as e:
254
+ logger.warning(f"HTTP error getting Caddy metrics: {e}")
255
+ return None
256
+ except Exception as e:
257
+ logger.error(f"Error getting Caddy metrics: {e}", exc_info=True)
258
+ return None
259
+
260
+ async def build_route_config(
261
+ self,
262
+ domain: str,
263
+ path_pattern: str,
264
+ upstream_host: str,
265
+ upstream_port: int,
266
+ enable_https: bool = True,
267
+ force_https: bool = True,
268
+ enable_waf: bool = True,
269
+ waf_rule_set: str = "default",
270
+ strip_path_prefix: Optional[str] = None,
271
+ add_path_prefix: Optional[str] = None,
272
+ ) -> Dict[str, Any]:
273
+ """
274
+ 构建路由配置对象
275
+
276
+ Args:
277
+ domain: 域名
278
+ path_pattern: 路径模式
279
+ upstream_host: 上游主机
280
+ upstream_port: 上游端口
281
+ enable_https: 启用 HTTPS
282
+ force_https: 强制 HTTPS
283
+ enable_waf: 启用 WAF
284
+ waf_rule_set: WAF 规则集
285
+ strip_path_prefix: 去除路径前缀
286
+ add_path_prefix: 添加路径前缀
287
+
288
+ Returns:
289
+ 路由配置字典
290
+ """
291
+ # 构建匹配器
292
+ matchers = []
293
+ if domain and domain != "*":
294
+ matchers.append({"host": [domain]})
295
+ if path_pattern != "/*":
296
+ matchers.append({"path": [path_pattern]})
297
+
298
+ # 构建处理器
299
+ handlers = []
300
+
301
+ # WAF 处理器(如果启用)
302
+ if enable_waf:
303
+ directives = [
304
+ "Include /etc/caddy/waf/coraza.conf",
305
+ ]
306
+
307
+ # 当前 **仅支持 default WAF 规则集**:所有路由统一加载 /etc/caddy/waf/coraza.conf。
308
+ # 尚未实现"按路由切换 WAF 规则集"(即不会根据 waf_rule_set 映射到不同规则文件)。
309
+ # 因此传入任何非 default 的 waf_rule_set 都会被忽略并回退到 default,
310
+ # 这里显式告警以免给调用方造成"已按规则集生效"的错觉。
311
+ # Only the "default" WAF rule set is supported for now; non-default values fall back to default.
312
+ if waf_rule_set and waf_rule_set != "default":
313
+ logger.warning(
314
+ "WAF rule set %r is not supported (only 'default' is implemented); "
315
+ "falling back to the default rule set",
316
+ waf_rule_set,
317
+ )
318
+
319
+ handlers.append({
320
+ "handler": "coraza_waf",
321
+ "directives": directives
322
+ })
323
+
324
+ # 路径重写处理器
325
+ if strip_path_prefix or add_path_prefix:
326
+ rewrite = {}
327
+ if strip_path_prefix:
328
+ rewrite["strip_path_prefix"] = strip_path_prefix
329
+ if add_path_prefix:
330
+ rewrite["uri"] = f"{add_path_prefix}{{http.request.uri}}"
331
+ handlers.append({
332
+ "handler": "rewrite",
333
+ **rewrite
334
+ })
335
+
336
+ # 反向代理处理器
337
+ handlers.append({
338
+ "handler": "reverse_proxy",
339
+ "upstreams": [{
340
+ "dial": f"{upstream_host}:{upstream_port}"
341
+ }],
342
+ "health_checks": {
343
+ "active": {
344
+ "path": InternalRequestPath.HEALTH_CHECK.value,
345
+ "interval": "30s",
346
+ "timeout": "5s"
347
+ }
348
+ }
349
+ })
350
+
351
+ route_config = {
352
+ "group": self._MANAGED_ROUTE_GROUP,
353
+ "match": matchers,
354
+ "handle": handlers
355
+ }
356
+
357
+ # HTTPS 重定向(如果强制 HTTPS)
358
+ if enable_https and force_https:
359
+ route_config["terminal"] = True
360
+
361
+ return route_config
362
+
363
+ async def apply_routes_batch(self, routes: List[Dict[str, Any]]) -> bool:
364
+ """
365
+ 批量应用路由配置
366
+
367
+ Args:
368
+ routes: 路由配置列表
369
+
370
+ Returns:
371
+ 是否成功
372
+ """
373
+ try:
374
+ # 获取当前配置
375
+ config = await self.get_config()
376
+ if not config:
377
+ logger.error("Failed to get current Caddy config")
378
+ return False
379
+
380
+ # 更新路由配置
381
+ if "apps" not in config:
382
+ config["apps"] = {}
383
+ if "http" not in config["apps"]:
384
+ config["apps"]["http"] = {"servers": {}}
385
+ if "servers" not in config["apps"]["http"]:
386
+ config["apps"]["http"]["servers"] = {}
387
+
388
+ servers = config["apps"]["http"]["servers"]
389
+ target_server_key = self._resolve_target_server_key(servers)
390
+ if target_server_key not in servers:
391
+ servers[target_server_key] = {"routes": []}
392
+
393
+ server = servers[target_server_key]
394
+ existing_routes = server.get("routes", [])
395
+
396
+ # 保留非托管路由,替换托管路由
397
+ # Keep non-managed routes and replace managed routes only
398
+ preserved_routes = [
399
+ route for route in existing_routes
400
+ if route.get("group") != self._MANAGED_ROUTE_GROUP
401
+ ]
402
+
403
+ fallback_routes = []
404
+ normal_routes = []
405
+ for route in preserved_routes:
406
+ if self._is_not_found_fallback_route(route):
407
+ fallback_routes.append(route)
408
+ else:
409
+ normal_routes.append(route)
410
+
411
+ server["routes"] = normal_routes + routes + fallback_routes
412
+ logger.info(
413
+ "Applying %s managed routes to Caddy server %s",
414
+ len(routes),
415
+ target_server_key,
416
+ )
417
+
418
+ # 加载新配置
419
+ load_ok = await self.load_config(config)
420
+ if load_ok:
421
+ return True
422
+
423
+ # 模块缺失时自动移除 WAF 处理器并重试
424
+ # Auto-disable WAF handlers when module is missing and retry
425
+ if self._is_waf_module_missing(self._last_load_error):
426
+ logger.warning("Caddy WAF module missing, retrying without WAF handlers")
427
+ server["routes"] = normal_routes + self._strip_waf_handlers(routes) + fallback_routes
428
+ return await self.load_config(config)
429
+
430
+ return False
431
+ except HTTPException as e:
432
+ logger.warning(f"HTTP error applying routes batch: {e}")
433
+ return False
434
+ except Exception as e:
435
+ logger.error(f"Error applying routes batch: {e}", exc_info=True)
436
+ return False
437
+
438
+ async def build_rate_limit_config(self, rule: Dict[str, Any]) -> List[Dict[str, Any]]:
439
+ """构建限流规则对应的 Caddy 路由配置
440
+ Build Caddy route configs for a rate-limit rule
441
+ """
442
+ rule_name = str(rule.get("rule_name") or f"rule_{rule.get('id', 'unknown')}")
443
+ limit_value = int(rule.get("limit_value") or 0)
444
+ limit_window = int(rule.get("limit_window") or 60)
445
+ limit_key = str(rule.get("limit_key") or "ip")
446
+ block_status_code = int(rule.get("block_status_code") or 429)
447
+ block_message = str(rule.get("block_message") or "Too Many Requests")
448
+
449
+ if limit_value <= 0:
450
+ return []
451
+
452
+ matcher = self._build_rate_limit_matcher(rule)
453
+
454
+ route_list: List[Dict[str, Any]] = []
455
+
456
+ blacklist_ips = [ip for ip in (rule.get("blacklist_ips") or []) if ip]
457
+ if blacklist_ips:
458
+ blacklist_match = copy.deepcopy(matcher) if matcher else {}
459
+ blacklist_match["remote_ip"] = {"ranges": blacklist_ips}
460
+ route_list.append({
461
+ "group": self._MANAGED_RATE_LIMIT_GROUP,
462
+ "match": [blacklist_match] if blacklist_match else [],
463
+ "handle": [
464
+ {
465
+ "handler": "static_response",
466
+ "status_code": block_status_code,
467
+ "body": block_message,
468
+ }
469
+ ],
470
+ "terminal": True,
471
+ })
472
+
473
+ key_template = self._build_rate_limit_key_template(limit_key, rule)
474
+ zone_config: Dict[str, Any] = {
475
+ "key": key_template,
476
+ "window": f"{limit_window}s",
477
+ "max_events": limit_value,
478
+ }
479
+ if matcher:
480
+ zone_config["match"] = [matcher]
481
+
482
+ rate_limit_route = {
483
+ "group": self._MANAGED_RATE_LIMIT_GROUP,
484
+ "handle": [
485
+ {
486
+ "handler": "rate_limit",
487
+ "rate_limits": {
488
+ rule_name: zone_config,
489
+ },
490
+ }
491
+ ],
492
+ }
493
+ route_list.append(rate_limit_route)
494
+ return route_list
495
+
496
+ async def apply_rate_limits_batch(self, rate_limit_routes: List[Dict[str, Any]]) -> bool:
497
+ """批量应用限流规则到 Caddy
498
+ Apply managed rate-limit routes to Caddy in batch
499
+ """
500
+ try:
501
+ config = await self.get_config()
502
+ if not config:
503
+ logger.error("Failed to get current Caddy config")
504
+ return False
505
+
506
+ if "apps" not in config:
507
+ config["apps"] = {}
508
+ if "http" not in config["apps"]:
509
+ config["apps"]["http"] = {"servers": {}}
510
+ if "servers" not in config["apps"]["http"]:
511
+ config["apps"]["http"]["servers"] = {}
512
+
513
+ servers = config["apps"]["http"]["servers"]
514
+ target_server_key = self._resolve_target_server_key(servers)
515
+ if target_server_key not in servers:
516
+ servers[target_server_key] = {"routes": []}
517
+
518
+ server = servers[target_server_key]
519
+ existing_routes = server.get("routes", [])
520
+
521
+ preserved_routes = [
522
+ route for route in existing_routes
523
+ if route.get("group") != self._MANAGED_RATE_LIMIT_GROUP
524
+ ]
525
+
526
+ fallback_routes: List[Dict[str, Any]] = []
527
+ managed_proxy_routes: List[Dict[str, Any]] = []
528
+ normal_routes: List[Dict[str, Any]] = []
529
+ for route in preserved_routes:
530
+ if self._is_not_found_fallback_route(route):
531
+ fallback_routes.append(route)
532
+ elif route.get("group") == self._MANAGED_ROUTE_GROUP:
533
+ managed_proxy_routes.append(route)
534
+ else:
535
+ normal_routes.append(route)
536
+
537
+ server["routes"] = rate_limit_routes + normal_routes + managed_proxy_routes + fallback_routes
538
+ logger.info(
539
+ "Applying %s managed rate-limit routes to Caddy server %s",
540
+ len(rate_limit_routes),
541
+ target_server_key,
542
+ )
543
+
544
+ load_ok = await self.load_config(config)
545
+ if load_ok:
546
+ return True
547
+
548
+ if self._is_rate_limit_module_missing(self._last_load_error):
549
+ logger.warning("Caddy rate_limit module missing, cannot apply rate limits")
550
+ return False
551
+ except Exception as e:
552
+ logger.error("Error applying rate limits batch: %s", e, exc_info=True)
553
+ return False
554
+
555
+ def _build_rate_limit_matcher(self, rule: Dict[str, Any]) -> Optional[Dict[str, Any]]:
556
+ matcher: Dict[str, Any] = {}
557
+ match_type = str(rule.get("match_type") or "all")
558
+ match_pattern = rule.get("match_pattern")
559
+ match_domain = rule.get("match_domain")
560
+
561
+ if match_domain:
562
+ matcher["host"] = [str(match_domain)]
563
+
564
+ if match_type == "path" and match_pattern:
565
+ matcher["path"] = [str(match_pattern)]
566
+ elif match_type == "domain" and match_pattern and "host" not in matcher:
567
+ matcher["host"] = [str(match_pattern)]
568
+ elif match_type == "ip" and match_pattern:
569
+ matcher["remote_ip"] = {"ranges": [str(match_pattern)]}
570
+ elif match_type == "header" and match_pattern:
571
+ header_name, header_value = self._parse_header_pattern(str(match_pattern))
572
+ if header_name:
573
+ matcher["header"] = {header_name: [header_value]} if header_value else {header_name: []}
574
+
575
+ whitelist_ips = [ip for ip in (rule.get("whitelist_ips") or []) if ip]
576
+ if whitelist_ips:
577
+ matcher.setdefault("not", []).append({"remote_ip": {"ranges": whitelist_ips}})
578
+
579
+ return matcher or None
580
+
581
+ def _build_rate_limit_key_template(self, limit_key: str, rule: Dict[str, Any]) -> str:
582
+ metadata = rule.get("rule_metadata") or {}
583
+ if limit_key == "ip":
584
+ return "{http.request.remote.host}"
585
+ if limit_key == "header":
586
+ header_name = metadata.get("key_header") or metadata.get("header_name")
587
+ if header_name:
588
+ return f"{{http.request.header.{header_name}}}"
589
+ return "{http.request.remote.host}"
590
+ if limit_key == "cookie":
591
+ cookie_name = metadata.get("cookie_name") or "session"
592
+ return f"{{http.request.cookie.{cookie_name}}}"
593
+ if limit_key == "path":
594
+ return "{http.request.uri.path}"
595
+ return "{http.request.remote.host}"
596
+
597
+ def _parse_header_pattern(self, pattern: str) -> Tuple[Optional[str], Optional[str]]:
598
+ if ":" not in pattern:
599
+ return pattern.strip(), None
600
+ key, value = pattern.split(":", 1)
601
+ return key.strip(), value.strip()
602
+
603
+ def _is_rate_limit_module_missing(self, error_text: Optional[str]) -> bool:
604
+ if not error_text:
605
+ return False
606
+ return "unknown module: http.handlers.rate_limit" in error_text
607
+
608
+ def _strip_waf_handlers(self, routes: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
609
+ """
610
+ 移除 WAF 处理器,避免加载不存在的模块
611
+ Remove WAF handlers to avoid loading missing modules
612
+ """
613
+ sanitized = copy.deepcopy(routes)
614
+ for route in sanitized:
615
+ handlers = route.get("handle", [])
616
+ route["handle"] = [
617
+ handler for handler in handlers
618
+ if handler.get("handler") not in {"coraza_waf", "coraza"}
619
+ ]
620
+ return sanitized
621
+
622
+ def _is_waf_module_missing(self, error_text: Optional[str]) -> bool:
623
+ if not error_text:
624
+ return False
625
+ return (
626
+ "unknown module: http.handlers.coraza_waf" in error_text
627
+ or "unknown module: http.handlers.coraza" in error_text
628
+ )
629
+
630
+ def _resolve_target_server_key(self, servers: Dict[str, Any]) -> str:
631
+ """
632
+ 选择应写入路由的 Caddy HTTP server
633
+ Select the Caddy HTTP server key for managed routes
634
+ """
635
+ if not servers:
636
+ return "srv0"
637
+
638
+ preferred_ports = [":8080", ":80", ":443"]
639
+ for preferred_port in preferred_ports:
640
+ for server_key, server_config in servers.items():
641
+ listen = server_config.get("listen", [])
642
+ if any(preferred_port in str(item) for item in listen):
643
+ return server_key
644
+
645
+ return next(iter(servers.keys()))
646
+
647
+ def _is_not_found_fallback_route(self, route: Dict[str, Any]) -> bool:
648
+ """
649
+ 判断是否为 Caddy 默认 404 回退路由
650
+ Determine whether route is a catch-all 404 fallback route
651
+ """
652
+ if route.get("match"):
653
+ return False
654
+
655
+ handlers = route.get("handle", [])
656
+ for handler in handlers:
657
+ if (
658
+ handler.get("handler") == "subroute"
659
+ and isinstance(handler.get("routes"), list)
660
+ ):
661
+ for subroute in handler.get("routes", []):
662
+ for subhandler in subroute.get("handle", []):
663
+ if (
664
+ subhandler.get("handler") == "static_response"
665
+ and subhandler.get("status_code") == 404
666
+ ):
667
+ return True
668
+ if (
669
+ handler.get("handler") == "static_response"
670
+ and handler.get("status_code") == 404
671
+ ):
672
+ return True
673
+
674
+ return False
675
+
676
+
677
+ # 全局实例
678
+ caddy_admin_client = CaddyAdminClient()