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,730 @@
1
+ """
2
+ 工作流运维应用层(opsbffsrv)
3
+ Workflow Operations Application Layer (opsbffsrv)
4
+
5
+ 职责 / Responsibilities:
6
+ 1. 调用 infrasrv API / Call infrasrv APIs
7
+ 2. 数据转换和格式化 / Data transformation and formatting
8
+ 3. 错误处理和日志记录 / Error handling and logging
9
+ """
10
+ from typing import (
11
+ Optional,
12
+ List,
13
+ Dict,
14
+ Any,
15
+ )
16
+
17
+ from cancan_microstack.public.error import HTTPException
18
+ from cancan_microstack.public.schemas.infra import workflow as wt
19
+ from cancan_microstack.public.api.infrasrv_client import InfraSrvApiClient
20
+ from cancan_microstack.public.const.workflow_consts import (
21
+ IMMUTABLE_END_NODE_IDS,
22
+ IMMUTABLE_START_NODE_IDS,
23
+ )
24
+ from linglong_web import LinglongConfig
25
+ from linglong_web.utils import logger
26
+
27
+
28
+ class WorkflowOpsApp:
29
+ """工作流运维应用服务 / Workflow operations application service"""
30
+
31
+ def __init__(self):
32
+ self.infra_client = InfraSrvApiClient(LinglongConfig.INFRASRV_HOST)
33
+
34
+ async def list_workflows(self, query: wt.WorkflowListQuery) -> wt.WorkflowListResponse:
35
+ """
36
+ 列出工作流定义(支持分页和过滤)
37
+ List workflow definitions (with pagination and filtering)
38
+
39
+ Args:
40
+ query: 查询参数 / Query parameters
41
+
42
+ Returns:
43
+ 工作流列表响应 / Workflow list response
44
+ """
45
+ try:
46
+ # 调用 infrasrv 接口 / Call infrasrv API
47
+ response = await self.infra_client.list_workflow_definitions(
48
+ page=query.page,
49
+ page_size=query.page_size,
50
+ keyword=query.keyword,
51
+ status=query.status,
52
+ )
53
+
54
+ if not response.success:
55
+ logger.error(f"Failed to list workflows from infrasrv: {response.error}")
56
+ raise HTTPException(status_code=500,
57
+ msg=f"Upstream error: {response.error.msg if response.error else 'Unknown error'}")
58
+
59
+ raw_payload = response.data
60
+ if isinstance(raw_payload, wt.WorkflowListResponse):
61
+ payload = raw_payload
62
+ else:
63
+ payload_dict = raw_payload or {}
64
+ payload = wt.WorkflowListResponse(**payload_dict) if payload_dict else wt.WorkflowListResponse(
65
+ list=[],
66
+ total=0,
67
+ page=query.page,
68
+ page_size=query.page_size,
69
+ )
70
+ workflows = payload.list
71
+ total = payload.total
72
+ filtered_workflows = workflows
73
+ filters_applied = False
74
+
75
+ # 过滤逻辑(infrasrv 暂不支持,opsbffsrv 层处理)
76
+ # Filtering logic (infrasrv doesn't support yet, handle in opsbffsrv)
77
+ if query.keyword:
78
+ filters_applied = True
79
+ keyword_lower = query.keyword.lower()
80
+ filtered_workflows = [
81
+ workflow for workflow in filtered_workflows
82
+ if self._workflow_matches_keyword(workflow, keyword_lower)
83
+ ]
84
+
85
+ if query.status:
86
+ filters_applied = True
87
+ is_active = query.status == "ACTIVE"
88
+ filtered_workflows = [
89
+ workflow for workflow in filtered_workflows
90
+ if workflow.is_active == is_active
91
+ ]
92
+
93
+ final_total = len(filtered_workflows) if filters_applied else total
94
+
95
+ return wt.WorkflowListResponse(
96
+ list=filtered_workflows,
97
+ total=final_total,
98
+ page=query.page,
99
+ page_size=query.page_size
100
+ )
101
+
102
+ except HTTPException:
103
+ raise
104
+ except Exception as e:
105
+ logger.error(f"Error listing workflows: {e}", exc_info=True)
106
+ raise HTTPException(status_code=500, msg="Internal server error")
107
+
108
+ async def get_workflow(self, workflow_id: str) -> wt.WorkflowDefinition:
109
+ """
110
+ 获取工作流详情
111
+ Get workflow details
112
+
113
+ Args:
114
+ workflow_id: 工作流唯一标识符 / Workflow unique identifier
115
+
116
+ Returns:
117
+ 工作流详情 / Workflow details
118
+ """
119
+ try:
120
+ response = await self.infra_client.get_workflow_definition(workflow_id)
121
+
122
+ if not response.success:
123
+ logger.error(f"Failed to get workflow {workflow_id} from infrasrv: {response.error}")
124
+ upstream_code = str(response.error.code) if response.error and response.error.code else ""
125
+ if upstream_code == "404":
126
+ raise HTTPException(status_code=404, msg="Workflow not found")
127
+ raise HTTPException(
128
+ status_code=500,
129
+ msg=f"Upstream error: {response.error.msg if response.error else 'Unknown error'}",
130
+ )
131
+
132
+ return self._parse_workflow_definition(response.data)
133
+
134
+ except HTTPException:
135
+ raise
136
+ except Exception as e:
137
+ logger.error(f"Error getting workflow {workflow_id}: {e}", exc_info=True)
138
+ raise HTTPException(status_code=500, msg="Internal server error")
139
+
140
+ async def list_workflow_versions(self, workflow_id: str, limit: int = 50) -> wt.WorkflowVersionListResponse:
141
+ """列出工作流版本历史
142
+ List workflow definition versions for UI consumption"""
143
+
144
+ try:
145
+ response = await self.infra_client.list_workflow_versions(workflow_id, limit)
146
+
147
+ if not response.success:
148
+ logger.error(f"Failed to list workflow versions for {workflow_id}: {response.error}")
149
+ raise HTTPException(status_code=500, msg="Failed to fetch workflow versions")
150
+
151
+ payload = response.data or {}
152
+ if isinstance(payload, wt.WorkflowVersionListResponse):
153
+ return payload
154
+ return wt.WorkflowVersionListResponse(**payload)
155
+
156
+ except HTTPException:
157
+ raise
158
+ except Exception as exc:
159
+ logger.error(f"Error listing workflow versions for {workflow_id}: {exc}", exc_info=True)
160
+ raise HTTPException(status_code=500, msg="Internal server error")
161
+
162
+ async def create_workflow(self, payload: wt.WorkflowDefinitionCreate) -> wt.WorkflowDefinition:
163
+ """
164
+ 创建工作流
165
+ Create workflow
166
+
167
+ Args:
168
+ payload: 工作流定义数据 / Workflow definition data
169
+
170
+ Returns:
171
+ 已创建的工作流 / Created workflow
172
+ """
173
+ try:
174
+ response = await self.infra_client.create_workflow_definition(payload.model_dump())
175
+
176
+ if not response.success:
177
+ logger.error(f"Failed to create workflow: {response.error}")
178
+ raise HTTPException(status_code=500,
179
+ msg=f"Upstream error: {response.error.msg if response.error else 'Unknown error'}")
180
+
181
+ return self._parse_workflow_definition(response.data)
182
+
183
+ except HTTPException:
184
+ raise
185
+ except Exception as e:
186
+ logger.error(f"Error creating workflow: {e}", exc_info=True)
187
+ raise HTTPException(status_code=500, msg="Internal server error")
188
+
189
+ async def update_workflow(self, workflow_id: str, payload: wt.WorkflowDefinitionUpdate) -> wt.WorkflowDefinition:
190
+ """
191
+ 更新工作流
192
+ Update workflow
193
+
194
+ Args:
195
+ workflow_id: 工作流唯一标识符 / Workflow unique identifier
196
+ payload: 更新数据 / Update data
197
+
198
+ Returns:
199
+ 更新后的工作流 / Updated workflow
200
+ """
201
+ try:
202
+ response = await self.infra_client.update_workflow_definition(workflow_id,
203
+ payload.model_dump(exclude_unset=True))
204
+
205
+ if not response.success:
206
+ logger.error(f"Failed to update workflow {workflow_id}: {response.error}")
207
+ raise HTTPException(status_code=500,
208
+ msg=f"Upstream error: {response.error.msg if response.error else 'Unknown error'}")
209
+
210
+ return self._parse_workflow_definition(response.data)
211
+
212
+ except HTTPException:
213
+ raise
214
+ except Exception as e:
215
+ logger.error(f"Error updating workflow {workflow_id}: {e}", exc_info=True)
216
+ raise HTTPException(status_code=500, msg="Internal server error")
217
+
218
+ async def delete_workflow(self, workflow_id: str) -> None:
219
+ """
220
+ 删除工作流
221
+ Delete workflow
222
+
223
+ Args:
224
+ workflow_id: 工作流唯一标识符 / Workflow unique identifier
225
+ """
226
+ try:
227
+ response = await self.infra_client.delete_workflow_definition(workflow_id)
228
+
229
+ if not response.success:
230
+ logger.error(f"Failed to delete workflow {workflow_id}: {response.error}")
231
+ raise HTTPException(status_code=500,
232
+ msg=f"Upstream error: {response.error.msg if response.error else 'Unknown error'}")
233
+
234
+ except HTTPException:
235
+ raise
236
+ except Exception as e:
237
+ logger.error(f"Error deleting workflow {workflow_id}: {e}", exc_info=True)
238
+ raise HTTPException(status_code=500, msg="Internal server error")
239
+
240
+ async def toggle_workflow(self, workflow_id: str) -> wt.WorkflowDefinition:
241
+ """
242
+ 切换工作流启用状态
243
+ Toggle workflow active status
244
+
245
+ Args:
246
+ workflow_id: 工作流唯一标识符 / Workflow unique identifier
247
+
248
+ Returns:
249
+ 更新后的工作流 / Updated workflow
250
+ """
251
+ try:
252
+ # 先获取当前状态 / Get current status first
253
+ workflow = await self.get_workflow(workflow_id)
254
+
255
+ # 更新状态 / Update status
256
+ payload = wt.WorkflowDefinitionUpdate(is_active=not workflow.is_active)
257
+ return await self.update_workflow(workflow_id, payload)
258
+
259
+ except HTTPException:
260
+ raise
261
+ except Exception as e:
262
+ logger.error(f"Error toggling workflow {workflow_id}: {e}", exc_info=True)
263
+ raise HTTPException(status_code=500, msg="Internal server error")
264
+
265
+ async def duplicate_workflow(self, workflow_id: str, payload: wt.DuplicateWorkflowPayload) -> wt.WorkflowDefinition:
266
+ """
267
+ 复制工作流
268
+ Duplicate workflow
269
+
270
+ Args:
271
+ workflow_id: 源工作流唯一标识符 / Source workflow unique identifier
272
+ payload: 复制配置 / Duplication config
273
+
274
+ Returns:
275
+ 复制后的工作流 / Duplicated workflow
276
+ """
277
+ try:
278
+ # 获取源工作流 / Get source workflow
279
+ source = await self.get_workflow(workflow_id)
280
+
281
+ # 创建副本 / Create copy
282
+ create_payload = wt.WorkflowDefinitionCreate(
283
+ name=payload.name,
284
+ description=f"Copy of {source.name}",
285
+ schedule=source.schedule,
286
+ graph_data=source.graph_data,
287
+ nodes_config=source.nodes_config,
288
+ global_context=source.global_context,
289
+ is_active=False # 副本默认禁用 / Copy is inactive by default
290
+ )
291
+
292
+ return await self.create_workflow(create_payload)
293
+
294
+ except HTTPException:
295
+ raise
296
+ except Exception as e:
297
+ logger.error(f"Error duplicating workflow {workflow_id}: {e}", exc_info=True)
298
+ raise HTTPException(status_code=500, msg="Internal server error")
299
+
300
+ async def publish_workflow(self, workflow_id: str) -> wt.WorkflowDefinition:
301
+ """
302
+ 发布工作流(标记为生产就绪)
303
+ Publish workflow (mark as production-ready)
304
+
305
+ Args:
306
+ workflow_id: 工作流唯一标识符 / Workflow unique identifier
307
+
308
+ Returns:
309
+ 发布后的工作流 / Published workflow
310
+ """
311
+ try:
312
+ # 发布即启用 / Publish means activate
313
+ payload = wt.WorkflowDefinitionUpdate(is_active=True)
314
+ return await self.update_workflow(workflow_id, payload)
315
+
316
+ except HTTPException:
317
+ raise
318
+ except Exception as e:
319
+ logger.error(f"Error publishing workflow {workflow_id}: {e}", exc_info=True)
320
+ raise HTTPException(status_code=500, msg="Internal server error")
321
+
322
+ async def rollback_workflow(self, workflow_id: str, payload: wt.WorkflowRollbackRequest) -> wt.WorkflowDefinition:
323
+ """回滚工作流定义
324
+ Roll back workflow definition to a target version"""
325
+
326
+ try:
327
+ response = await self.infra_client.rollback_workflow_definition(
328
+ workflow_id,
329
+ payload.model_dump(exclude_none=True),
330
+ )
331
+
332
+ if not response.success:
333
+ logger.error(f"Failed to rollback workflow {workflow_id}: {response.error}")
334
+ raise HTTPException(status_code=500,
335
+ msg=f"Upstream error: {response.error.msg if response.error else 'Unknown error'}")
336
+
337
+ return self._parse_workflow_definition(response.data)
338
+
339
+ except HTTPException:
340
+ raise
341
+ except Exception as exc:
342
+ logger.error(f"Error rolling back workflow {workflow_id}: {exc}", exc_info=True)
343
+ raise HTTPException(status_code=500, msg="Internal server error")
344
+
345
+ @staticmethod
346
+ def _workflow_matches_keyword(workflow: wt.WorkflowDefinition, keyword_lower: str) -> bool:
347
+ """匹配关键字 / Check if workflow matches keyword"""
348
+
349
+ searchable_fields = [
350
+ (workflow.name or "").lower(),
351
+ (workflow.description or "").lower(),
352
+ ]
353
+
354
+ global_ctx = workflow.global_context or {}
355
+ tags_value = global_ctx.get("tags")
356
+ if isinstance(tags_value, list):
357
+ searchable_fields.extend(str(tag).lower() for tag in tags_value)
358
+
359
+ return any(keyword_lower in field for field in searchable_fields if field)
360
+
361
+ @staticmethod
362
+ def _parse_workflow_definition(data: Any) -> wt.WorkflowDefinition:
363
+ """
364
+ 解析工作流定义响应,兼容字典与模型
365
+ Normalize workflow definition payload returned from infrasrv
366
+ """
367
+
368
+ if isinstance(data, wt.WorkflowDefinition):
369
+ return data
370
+ if isinstance(data, dict):
371
+ return wt.WorkflowDefinition(**data)
372
+ raise HTTPException(status_code=500, msg="Invalid workflow payload from infrasrv")
373
+
374
+ @staticmethod
375
+ def _normalize_nodes_config(nodes_config: Optional[Dict[str, Any]]) -> Dict[str, Dict[str, Any]]:
376
+ """统一节点配置格式,兼容 Pydantic 模型与原始字典
377
+ Normalize node configuration values (dict or Pydantic models) into plain dicts"""
378
+
379
+ normalized: Dict[str, Dict[str, Any]] = {}
380
+ if not nodes_config:
381
+ return normalized
382
+
383
+ for node_id, node in nodes_config.items():
384
+ normalized[node_id] = WorkflowOpsApp._normalize_single_node(node)
385
+ return normalized
386
+
387
+ @staticmethod
388
+ def _normalize_single_node(node: Any) -> Dict[str, Any]:
389
+ """转换单个节点配置为标准 dict 格式,便于统一校验
390
+ Convert an individual node config into a dict for downstream validation"""
391
+
392
+ if isinstance(node, wt.NodeConfig):
393
+ payload = node.model_dump()
394
+ elif hasattr(node, "model_dump"):
395
+ payload = node.model_dump()
396
+ elif isinstance(node, dict):
397
+ payload = dict(node)
398
+ else:
399
+ raise HTTPException(status_code=500, msg="Invalid node configuration payload")
400
+
401
+ node_type = payload.get("type")
402
+ payload["type"] = str(node_type).upper() if node_type is not None else None
403
+
404
+ raw_next_ids = payload.get("next_node_ids")
405
+ next_ids: List[str] = []
406
+ if isinstance(raw_next_ids, list):
407
+ next_ids = [str(item) for item in raw_next_ids if item is not None]
408
+
409
+ single_next = payload.get("next_node_id")
410
+ if single_next:
411
+ candidate = str(single_next)
412
+ if candidate not in next_ids:
413
+ next_ids.append(candidate)
414
+
415
+ payload["next_node_ids"] = next_ids
416
+ return payload
417
+
418
+ async def validate_workflow(self, workflow_id: str) -> Dict[str, Any]:
419
+ """
420
+ 验证工作流定义
421
+ Validate workflow definition
422
+
423
+ Args:
424
+ workflow_id: 工作流唯一标识符 / Workflow unique identifier
425
+
426
+ Returns:
427
+ 验证结果 / Validation result
428
+ """
429
+ try:
430
+ workflow = await self.get_workflow(workflow_id)
431
+ nodes_config = self._normalize_nodes_config(workflow.nodes_config)
432
+
433
+ issues = []
434
+
435
+ # 基础验证 / Basic validation
436
+ if not workflow.name or not workflow.name.strip():
437
+ issues.append("Workflow name cannot be empty")
438
+
439
+ if not nodes_config:
440
+ issues.append("Workflow must have at least one node")
441
+
442
+ # 检查是否有 START 节点 / Check for START node
443
+ start_nodes = [node for node in nodes_config.values() if node.get("type") == "START"]
444
+ if not start_nodes:
445
+ issues.append("Workflow must have a START node")
446
+ elif len(start_nodes) > 1:
447
+ issues.append("Workflow can only contain one START node")
448
+ else:
449
+ start_id = (start_nodes[0].get("id") or start_nodes[0].get("node_id") or "").lower()
450
+ if start_id not in IMMUTABLE_START_NODE_IDS:
451
+ issues.append("START node id must remain immutable (start/start_node)")
452
+ if not start_nodes[0].get("next_node_ids"):
453
+ issues.append("START node must define downstream nodes")
454
+
455
+ # 检查是否有 END 节点 / Check for END node
456
+ end_nodes = [node for node in nodes_config.values() if node.get("type") == "END"]
457
+ if not end_nodes:
458
+ issues.append("Workflow must have at least one END node")
459
+ elif len(end_nodes) > 1:
460
+ issues.append("Workflow can only contain one END node")
461
+ else:
462
+ end_id = (end_nodes[0].get("id") or end_nodes[0].get("node_id") or "").lower()
463
+ if end_id not in IMMUTABLE_END_NODE_IDS:
464
+ issues.append("END node id must remain immutable (end/end_node)")
465
+
466
+ # 检查节点连接 / Check node connections
467
+ for node_id, node in nodes_config.items():
468
+ next_ids = node.get("next_node_ids", [])
469
+ for next_id in next_ids:
470
+ if next_id not in nodes_config:
471
+ issues.append(f"Node {node_id} references non-existent node {next_id}")
472
+
473
+ return {
474
+ "valid": len(issues) == 0,
475
+ "issues": issues
476
+ }
477
+
478
+ except HTTPException:
479
+ raise
480
+ except Exception as e:
481
+ logger.error(f"Error validating workflow {workflow_id}: {e}", exc_info=True)
482
+ raise HTTPException(status_code=500, msg="Internal server error")
483
+
484
+ async def get_workflow_stats(self) -> wt.WorkflowStats:
485
+ """
486
+ 获取工作流统计
487
+ Get workflow statistics
488
+
489
+ Returns:
490
+ 统计信息 / Statistics
491
+ """
492
+ try:
493
+ response = await self.infra_client.get_workflow_stats()
494
+
495
+ if not response.success:
496
+ logger.error(f"Failed to get workflow stats from infrasrv: {response.error}")
497
+ # 降级:返回空统计 / Fallback: return empty stats
498
+ return wt.WorkflowStats()
499
+
500
+ payload = response.data
501
+ if isinstance(payload, wt.WorkflowStats):
502
+ return payload
503
+ if isinstance(payload, dict):
504
+ return wt.WorkflowStats(**payload)
505
+ logger.warning("Unexpected workflow stats payload type: %s", type(payload))
506
+ return wt.WorkflowStats()
507
+
508
+ except Exception as e:
509
+ logger.error(f"Error getting workflow stats: {e}", exc_info=True)
510
+ # 降级:返回空统计 / Fallback: return empty stats
511
+ return wt.WorkflowStats()
512
+
513
+ async def trigger_workflow(
514
+ self,
515
+ workflow_id: str,
516
+ payload: wt.WorkflowTriggerRequest,
517
+ ) -> wt.WorkflowTriggerResponse:
518
+ """
519
+ 触发工作流运行
520
+ Trigger workflow run
521
+
522
+ Args:
523
+ workflow_id: 工作流唯一标识符 / Workflow unique identifier
524
+ payload: 触发上下文数据 / Trigger context data
525
+
526
+ Returns:
527
+ 运行实例信息 / Run instance info
528
+ """
529
+ try:
530
+ response = await self.infra_client.trigger_workflow_run(workflow_id, payload.model_dump())
531
+
532
+ if not response.success:
533
+ logger.error(f"Failed to trigger workflow {workflow_id}: {response.error}")
534
+ raise HTTPException(status_code=500,
535
+ msg=f"Upstream error: {response.error.msg if response.error else 'Unknown error'}")
536
+
537
+ payload = response.data
538
+ if isinstance(payload, wt.WorkflowTriggerResponse):
539
+ return payload
540
+ if isinstance(payload, dict):
541
+ return wt.WorkflowTriggerResponse(**payload)
542
+ raise HTTPException(status_code=500, msg="Invalid trigger response payload")
543
+
544
+ except HTTPException:
545
+ raise
546
+ except Exception as e:
547
+ logger.error(f"Error triggering workflow {workflow_id}: {e}", exc_info=True)
548
+ raise HTTPException(status_code=500, msg="Internal server error")
549
+
550
+ async def list_runs(
551
+ self,
552
+ workflow_id: Optional[str],
553
+ reqid: Optional[str],
554
+ page: int,
555
+ page_size: int,
556
+ status: Optional[str] = None,
557
+ date_from: Optional[str] = None,
558
+ date_to: Optional[str] = None,
559
+ ) -> wt.WorkflowRunListResponse:
560
+ """
561
+ 列出工作流运行实例
562
+ List workflow runs
563
+
564
+ Args:
565
+ workflow_id: 按工作流ID过滤 / Filter by workflow ID
566
+ reqid: 按请求追踪 ID 过滤 / Filter by request trace ID
567
+ page: 页码 / Page number
568
+ page_size: 每页大小 / Page size
569
+
570
+ Returns:
571
+ 运行实例列表 / Run instance list
572
+ """
573
+ try:
574
+ response = await self.infra_client.list_workflow_runs(
575
+ workflow_id=workflow_id,
576
+ reqid=reqid,
577
+ page=page,
578
+ page_size=page_size,
579
+ status=status,
580
+ date_from=date_from,
581
+ date_to=date_to,
582
+ )
583
+
584
+ if not response.success:
585
+ logger.error(f"Failed to list workflow runs from infrasrv: {response.error}")
586
+ raise HTTPException(status_code=500,
587
+ msg=f"Upstream error: {response.error.msg if response.error else 'Unknown error'}")
588
+
589
+ payload_dict = response.data or {}
590
+ if not payload_dict:
591
+ return wt.WorkflowRunListResponse(list=[], total=0, page=page, page_size=page_size)
592
+ return wt.WorkflowRunListResponse(**payload_dict)
593
+
594
+ except HTTPException:
595
+ raise
596
+ except Exception as e:
597
+ logger.error(f"Error listing workflow runs: {e}", exc_info=True)
598
+ raise HTTPException(status_code=500, msg="Internal server error")
599
+
600
+ async def get_run_graph_status(self, run_id: str) -> wt.RunGraphResponse:
601
+ """
602
+ 获取运行图状态
603
+ Get run graph status
604
+
605
+ Args:
606
+ run_id: 运行实例唯一标识符 / Run instance unique identifier
607
+
608
+ Returns:
609
+ 图状态数据 / Graph status data
610
+ """
611
+ try:
612
+ response = await self.infra_client.get_run_graph_status(run_id)
613
+
614
+ if not response.success:
615
+ logger.error(f"Failed to get run graph status for {run_id}: {response.error}")
616
+ error_code = str(response.error.code) if response.error else "500"
617
+ error_msg = response.error.msg if response.error else "Failed to fetch run graph"
618
+ status_code = 404 if error_code == "404" else 500
619
+ raise HTTPException(status_code=status_code, msg=error_msg or "Failed to fetch run graph")
620
+
621
+ return wt.RunGraphResponse(**(response.data or {}))
622
+
623
+ except HTTPException:
624
+ raise
625
+ except Exception as e:
626
+ logger.error(f"Error getting run graph status for {run_id}: {e}", exc_info=True)
627
+ raise HTTPException(status_code=500, msg="Internal server error")
628
+
629
+ async def get_node_history(self, run_id: str, node_id: str) -> wt.NodeExecutionHistoryResponse:
630
+ """
631
+ 获取节点执行历史
632
+ Get node execution history
633
+
634
+ Args:
635
+ run_id: 运行实例唯一标识符 / Run instance unique identifier
636
+ node_id: 节点ID / Node ID
637
+
638
+ Returns:
639
+ 节点执行历史 / Node execution history
640
+ """
641
+ try:
642
+ response = await self.infra_client.get_node_history(run_id, node_id)
643
+
644
+ if not response.success:
645
+ logger.error(f"Failed to get node history for {run_id}/{node_id}: {response.error}")
646
+ raise HTTPException(status_code=404, msg="Node history not found")
647
+
648
+ payload = response.data or {}
649
+ if not payload:
650
+ return wt.NodeExecutionHistoryResponse(histories=[])
651
+ return wt.NodeExecutionHistoryResponse(**payload)
652
+
653
+ except HTTPException:
654
+ raise
655
+ except Exception as e:
656
+ logger.error(f"Error getting node history for {run_id}/{node_id}: {e}", exc_info=True)
657
+ raise HTTPException(status_code=500, msg="Internal server error")
658
+
659
+ async def list_engine_alerts(self, query: wt.WorkflowEngineAlertQuery) -> wt.WorkflowEngineAlertListResponse:
660
+ """列出工作流引擎告警 / List workflow engine alerts"""
661
+
662
+ try:
663
+ response = await self.infra_client.list_workflow_engine_alerts(
664
+ status=query.status.value if query.status else None,
665
+ severity=query.severity.value if query.severity else None,
666
+ run_id=query.run_id,
667
+ page=query.page,
668
+ page_size=query.page_size,
669
+ )
670
+ if not response.success:
671
+ logger.error(f"Failed to list workflow alerts: {response.error}")
672
+ raise HTTPException(status_code=500, msg="Failed to fetch workflow alerts")
673
+
674
+ payload = response.data or {}
675
+ if isinstance(payload, wt.WorkflowEngineAlertListResponse):
676
+ return payload
677
+ if isinstance(payload, dict):
678
+ return wt.WorkflowEngineAlertListResponse(**payload)
679
+ return wt.WorkflowEngineAlertListResponse()
680
+ except HTTPException:
681
+ raise
682
+ except Exception as exc:
683
+ logger.error(f"Error listing workflow alerts: {exc}", exc_info=True)
684
+ raise HTTPException(status_code=500, msg="Internal server error")
685
+
686
+ async def acknowledge_engine_alert(
687
+ self,
688
+ alert_id: str,
689
+ payload: wt.WorkflowEngineAlertAckRequest,
690
+ ) -> wt.WorkflowEngineAlert:
691
+ """标记告警为已知晓"""
692
+
693
+ try:
694
+ response = await self.infra_client.acknowledge_workflow_engine_alert(
695
+ alert_id,
696
+ payload.model_dump(exclude_none=True),
697
+ )
698
+ if not response.success:
699
+ logger.error(f"Failed to acknowledge workflow alert {alert_id}: {response.error}")
700
+ raise HTTPException(status_code=500, msg="Failed to acknowledge workflow alert")
701
+ data = response.data or {}
702
+ return wt.WorkflowEngineAlert(**data)
703
+ except HTTPException:
704
+ raise
705
+ except Exception as exc:
706
+ logger.error(f"Error acknowledging workflow alert {alert_id}: {exc}", exc_info=True)
707
+ raise HTTPException(status_code=500, msg="Internal server error")
708
+
709
+ async def resolve_engine_alert(
710
+ self,
711
+ alert_id: str,
712
+ payload: wt.WorkflowEngineAlertAckRequest,
713
+ ) -> wt.WorkflowEngineAlert:
714
+ """标记告警为已解决"""
715
+
716
+ try:
717
+ response = await self.infra_client.resolve_workflow_engine_alert(
718
+ alert_id,
719
+ payload.model_dump(exclude_none=True),
720
+ )
721
+ if not response.success:
722
+ logger.error(f"Failed to resolve workflow alert {alert_id}: {response.error}")
723
+ raise HTTPException(status_code=500, msg="Failed to resolve workflow alert")
724
+ data = response.data or {}
725
+ return wt.WorkflowEngineAlert(**data)
726
+ except HTTPException:
727
+ raise
728
+ except Exception as exc:
729
+ logger.error(f"Error resolving workflow alert {alert_id}: {exc}", exc_info=True)
730
+ raise HTTPException(status_code=500, msg="Internal server error")