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,100 @@
1
+ """infrasrv startup entrypoint.
2
+
3
+ 用于容器内启动 infrasrv 服务(被 cmd/infrasrv/run.py 包装器调用)。
4
+ Start infrasrv inside container (called by workspace cmd wrapper).
5
+ """
6
+
7
+ import asyncio
8
+
9
+ from linglong_web import ServerRouter
10
+ from linglong_web.utils import logger
11
+
12
+ from cancan_microstack.public.web.server import AppServer
13
+ from cancan_microstack.services.infrasrv.application.logging.log_ingestion_service import get_log_ingestion_service
14
+ from cancan_microstack.services.infrasrv.application.workflow.workflow_tasks import register_workflow_tasks
15
+ from cancan_microstack.services.infrasrv.application.workflow.workflow_worker_runtime import (
16
+ start_inline_worker,
17
+ stop_inline_worker,
18
+ )
19
+ from cancan_microstack.services.infrasrv.conf.config import service_conf_dict
20
+ from cancan_microstack.services.infrasrv.domain.hooks import get_hook_manager
21
+ from cancan_microstack.services.infrasrv.domain.hooks.builtin_hooks import register_default_hooks
22
+ from cancan_microstack.services.infrasrv.infrastructure.ddl_manager import init_ddl
23
+ from cancan_microstack.services.infrasrv.interface.schedule.scheduler import scheduler_group
24
+ from cancan_microstack.services.infrasrv.router import router_list
25
+
26
+
27
+ async def initialize_infrasrv() -> None:
28
+ """infrasrv 的启动回调函数 / Startup callback for infrasrv."""
29
+
30
+ # DDL 自管理:检查并创建 infra 表
31
+ # DDL auto-init: check and create infra tables
32
+ logger.info("Checking and initializing infra tables...")
33
+ ddl_success = await init_ddl()
34
+ if not ddl_success:
35
+ logger.warning("DDL initialization failed, but continuing to start service...")
36
+
37
+ # 初始化预注册钩子
38
+ # Initialize pre-registration hooks
39
+ logger.info("Initializing pre-registration hooks...")
40
+ hook_manager = get_hook_manager()
41
+ register_default_hooks(hook_manager)
42
+ logger.info(f"Registered {len(hook_manager.get_hooks('pre_register'))} pre-registration hooks")
43
+
44
+
45
+ async def register_workflow_tasks_on_startup() -> None:
46
+ """在服务启动阶段注册所有工作流任务 / Register workflow tasks during startup."""
47
+
48
+ register_workflow_tasks()
49
+ start_inline_worker()
50
+
51
+
52
+ async def start_log_ingestion() -> None:
53
+ """启动日志消费服务 / Start log ingestion service."""
54
+
55
+ log_ingestion_service = get_log_ingestion_service()
56
+ await log_ingestion_service.start()
57
+
58
+
59
+ async def stop_inline_worker_on_shutdown() -> None:
60
+ """停止内嵌 worker,确保 infrasrv 退出前清理资源 / Stop inline worker on shutdown."""
61
+
62
+ stop_inline_worker()
63
+
64
+
65
+ async def stop_log_ingestion() -> None:
66
+ """停止日志消费服务 / Stop log ingestion service."""
67
+
68
+ log_ingestion_service = get_log_ingestion_service()
69
+ await log_ingestion_service.shutdown()
70
+
71
+
72
+ async def main(host: str = "0.0.0.0", port: int = 8080) -> None:
73
+ """启动 infrasrv / Start infrasrv."""
74
+
75
+ router_factory = ServerRouter()
76
+ router_factory.initialize(router_list)
77
+
78
+ app = AppServer()
79
+
80
+ await app.initialize(
81
+ service_name="infrasrv",
82
+ router=router_factory.get_router(),
83
+ config_dict=service_conf_dict,
84
+ scheduler_group=scheduler_group,
85
+ on_startup=[
86
+ initialize_infrasrv,
87
+ start_log_ingestion,
88
+ register_workflow_tasks_on_startup,
89
+ ],
90
+ on_shutdown=[
91
+ stop_log_ingestion,
92
+ stop_inline_worker_on_shutdown,
93
+ ],
94
+ )
95
+
96
+ await app.start(host=host, port=port)
97
+
98
+
99
+ if __name__ == "__main__":
100
+ asyncio.run(main())
@@ -0,0 +1,5 @@
1
+ """opsbffsrv entrypoints.
2
+
3
+ 该模块用于容器内启动 opsbffsrv 服务。
4
+ Entry points for starting opsbffsrv inside containers.
5
+ """
@@ -0,0 +1,96 @@
1
+ """opsbffsrv startup entrypoint.
2
+
3
+ 用于容器内启动 opsbffsrv 服务(被 cmd/opsbffsrv/run.py 包装器调用)。
4
+ Start opsbffsrv inside container (called by workspace cmd wrapper).
5
+ """
6
+
7
+ import asyncio
8
+
9
+ from linglong_web import ServerRouter
10
+ from linglong_web.utils import logger
11
+
12
+ from cancan_microstack.public.web.server import AppServer
13
+ from cancan_microstack.services.opsbffsrv.application.caddy.access_log_ingestion_service import (
14
+ get_caddy_access_log_ingestion_service,
15
+ )
16
+ from cancan_microstack.services.opsbffsrv.application.caddy.route_management_app import RouteManagementApp
17
+ from cancan_microstack.services.opsbffsrv.conf.config import service_conf_dict, validate_auth_config
18
+ from cancan_microstack.services.opsbffsrv.domain.auth.admin_init import ensure_admin_user
19
+ from cancan_microstack.services.opsbffsrv.domain.caddy.default_routes import init_default_routes
20
+ from cancan_microstack.services.opsbffsrv.infrastructure.ddl_manager import init_ddl
21
+ from cancan_microstack.services.opsbffsrv.interface.middleware.auth_middleware import auth_middleware
22
+ from cancan_microstack.services.opsbffsrv.router import router_list
23
+
24
+
25
+ async def initialize_opsbffsrv() -> None:
26
+ """opsbffsrv 的启动回调函数 / Startup callback for opsbffsrv."""
27
+
28
+ # 认证关键配置 fail-fast 校验(生产缺 Fernet key 直接拒绝启动)。
29
+ # Fail-fast validation of auth-critical config (refuse to start in prod without Fernet key).
30
+ validate_auth_config()
31
+
32
+ # DDL 自管理:检查并创建 ops 表
33
+ # DDL auto-init: check and create ops tables
34
+ logger.info("Checking and initializing ops tables...")
35
+ ddl_success = await init_ddl()
36
+ if not ddl_success:
37
+ logger.warning("DDL initialization failed, but continuing to start service...")
38
+
39
+ # 初始化 admin 用户
40
+ # Initialize admin user
41
+ logger.info("Checking and initializing admin user...")
42
+ await ensure_admin_user()
43
+
44
+ # 初始化默认路由
45
+ # Initialize default routes
46
+ await init_default_routes()
47
+
48
+ # 同步路由到 Caddy
49
+ # Sync routes to Caddy on startup
50
+ logger.info("Syncing routes to Caddy on startup...")
51
+ route_app = RouteManagementApp()
52
+ sync_result = await route_app.sync_all_routes()
53
+ if sync_result.get("status") != "success":
54
+ # Caddy may be intentionally not started in some dev flows.
55
+ # Some environments start opsbffsrv before caddy, so treat this as non-fatal.
56
+ logger.warning("Sync routes to Caddy on startup skipped/failed: %s", sync_result)
57
+ else:
58
+ logger.info("Routes synced to Caddy successfully.")
59
+
60
+ # 启动 Caddy 访问日志采集服务
61
+ # Start Caddy access log ingestion service
62
+ ingestion_service = get_caddy_access_log_ingestion_service()
63
+ await ingestion_service.start()
64
+
65
+
66
+ async def shutdown_opsbffsrv() -> None:
67
+ """opsbffsrv 的关闭回调函数 / Shutdown callback for opsbffsrv."""
68
+ ingestion_service = get_caddy_access_log_ingestion_service()
69
+ await ingestion_service.shutdown()
70
+
71
+
72
+ async def main(host: str = "0.0.0.0", port: int = 8080) -> None:
73
+ """启动 opsbffsrv / Start opsbffsrv."""
74
+
75
+ router_factory = ServerRouter()
76
+ router_factory.initialize(router_list)
77
+
78
+ app = AppServer()
79
+
80
+ await app.initialize(
81
+ service_name="opsbffsrv",
82
+ router=router_factory.get_router(),
83
+ config_dict=service_conf_dict,
84
+ on_startup=[initialize_opsbffsrv],
85
+ on_shutdown=[shutdown_opsbffsrv],
86
+ )
87
+
88
+ # 注册认证中间件
89
+ # Register authentication middleware
90
+ app.app.middleware("http")(auth_middleware)
91
+
92
+ await app.start(host=host, port=port)
93
+
94
+
95
+ if __name__ == "__main__":
96
+ asyncio.run(main())
@@ -0,0 +1,5 @@
1
+ """Internal helpers for Cancan Microstack."""
2
+ from .assets import AssetManager, AssetRecord
3
+ from .compose_builder import ComposeBuilder
4
+ from .microstack import CancanMicrostack
5
+ from .runner import ServiceRunner
@@ -0,0 +1,123 @@
1
+ """Asset loading helpers for Cancan Microstack.
2
+
3
+ 资产加载与导出工具,确保包内资源可以安全复制到工作区后再被 Docker 使用。
4
+ Asset loading/export helpers that materialize packaged resources into the caller's workspace
5
+ so Docker (which only sees mounted paths) can read them.
6
+ """
7
+ import shutil
8
+ from dataclasses import dataclass
9
+ from importlib import resources
10
+ from pathlib import Path
11
+ from typing import (
12
+ Iterable,
13
+ List,
14
+ )
15
+
16
+
17
+ @dataclass(frozen=True)
18
+ class AssetRecord:
19
+ """描述存储在包中的静态资产 / Describe packaged static asset."""
20
+
21
+ logical_name: str
22
+ path: Path
23
+ is_dir: bool
24
+
25
+
26
+ class AssetManager:
27
+ """资产管理:枚举、导出、解析包内静态文件。
28
+
29
+ Asset management facade to list, export, and resolve packaged static files.
30
+ """
31
+
32
+ def __init__(self, package: str = "cancan_microstack.assets") -> None:
33
+ self._package = package
34
+
35
+ def list_assets(self, subdir: str | None = None) -> List[AssetRecord]:
36
+ """列出可用资产(可选限定子目录)/ List available assets (optional subdir filter)."""
37
+
38
+ # 重要:不要用 resources.as_file(...) 的路径来反推 logical_name。
39
+ # 因为当资源来自 zip/轮子时,不同节点可能被物化到不同的临时目录,
40
+ # 这会导致 relative_to 失败,从而丢失前缀(例如返回 "infra" 而不是 "ddl/infra")。
41
+ # Important: do NOT derive logical_name from as_file(...) paths.
42
+ # When packaged (zip/wheel), each node may be materialized into a different temp dir,
43
+ # making relative_to() fail and losing prefixes.
44
+ traversable = resources.files(self._package)
45
+ prefix = ""
46
+ if subdir:
47
+ prefix = "/".join(self._split(subdir))
48
+ traversable = traversable.joinpath(*self._split(subdir))
49
+ records: List[AssetRecord] = []
50
+
51
+ def _walk(node, current_prefix: str) -> None:
52
+ for child in node.iterdir():
53
+ if child.name.startswith("__pycache__") or child.name == ".DS_Store":
54
+ continue
55
+ logical_name = f"{current_prefix}/{child.name}" if current_prefix else child.name
56
+ with resources.as_file(child) as located:
57
+ path = Path(located)
58
+ records.append(
59
+ AssetRecord(
60
+ logical_name=logical_name,
61
+ path=path,
62
+ is_dir=path.is_dir(),
63
+ )
64
+ )
65
+ if child.is_dir():
66
+ _walk(child, logical_name)
67
+
68
+ _walk(traversable, prefix)
69
+ return records
70
+
71
+ def export_asset(self, logical_name: str, destination: Path, overwrite: bool = False) -> Path:
72
+ """导出资产到工作区路径;目录用 copytree,文件用 copy2。
73
+
74
+ Export asset into a workspace path so Docker can see it via bind/volume mounts.
75
+ Directories use ``shutil.copytree``; files use ``shutil.copy2``.
76
+ """
77
+
78
+ traversable = resources.files(self._package).joinpath(*self._split(logical_name))
79
+ if not traversable.exists(): # pragma: no cover - defensive guard
80
+ raise FileNotFoundError(f"Asset '{logical_name}' not found")
81
+
82
+ with resources.as_file(traversable) as asset_path:
83
+ resolved = Path(asset_path)
84
+ if resolved.is_dir():
85
+ if destination.exists():
86
+ if not overwrite:
87
+ raise FileExistsError(destination)
88
+ shutil.rmtree(destination)
89
+ shutil.copytree(resolved, destination)
90
+ else:
91
+ destination.parent.mkdir(parents=True, exist_ok=True)
92
+ if destination.exists() and not overwrite:
93
+ raise FileExistsError(destination)
94
+ shutil.copy2(resolved, destination)
95
+ return destination
96
+
97
+ def read_text(self, logical_name: str) -> str:
98
+ """读取资产文本 / Read text content from asset."""
99
+
100
+ traversable = resources.files(self._package).joinpath(*self._split(logical_name))
101
+ return traversable.read_text(encoding="utf-8")
102
+
103
+ def resolve_path(self, logical_name: str) -> Path:
104
+ """将资产实体化为可访问的文件路径 / Materialize and return a filesystem path."""
105
+
106
+ traversable = resources.files(self._package).joinpath(*self._split(logical_name))
107
+ if not traversable.exists():
108
+ raise FileNotFoundError(logical_name)
109
+ with resources.as_file(traversable) as asset_path:
110
+ return Path(asset_path)
111
+
112
+ def _split(self, logical_name: str) -> Iterable[str]:
113
+ return [segment for segment in logical_name.split('/') if segment]
114
+
115
+ def _logical_name(self, path: Path) -> str:
116
+ base = resources.files(self._package)
117
+ with resources.as_file(base) as root_path:
118
+ root = Path(root_path)
119
+ try:
120
+ rel = path.relative_to(root)
121
+ except ValueError: # pragma: no cover - fallback for zipped resources
122
+ return path.name
123
+ return str(rel)
@@ -0,0 +1,102 @@
1
+ """Compose stack builder merging infra assets with service overlays.
2
+
3
+ 把内置 infra compose 与业务/覆盖文件进行深度合并,生成最终可运行的栈。
4
+ Deep-merge infra compose with service/override files to produce a runnable stack.
5
+ """
6
+ import copy
7
+ from pathlib import Path
8
+ from typing import (
9
+ Any,
10
+ Dict,
11
+ Iterable,
12
+ Sequence,
13
+ )
14
+
15
+ import yaml
16
+
17
+ from .assets import AssetManager
18
+
19
+
20
+ class ComposeBuilder:
21
+ """合并内置 infra compose 与工作区覆盖层 / Merge infra compose with workspace overlays."""
22
+
23
+ def __init__(self, asset_manager: AssetManager | None = None,
24
+ base_asset: str = "docker/docker-compose.infra.yml") -> None:
25
+ self.asset_manager = asset_manager or AssetManager()
26
+ self.base_asset = base_asset
27
+
28
+ def build(
29
+ self,
30
+ *,
31
+ workspace: Path,
32
+ service_file: Path | None = None,
33
+ overrides: Sequence[Path] | None = None,
34
+ output_file: Path | None = None,
35
+ ) -> Path:
36
+ """生成合并后的 docker compose 文件 / Generate merged docker compose file."""
37
+
38
+ workspace = workspace.expanduser().resolve()
39
+ output_path = (output_file or workspace / "compose.cancan.yml").resolve()
40
+ overrides = overrides or []
41
+
42
+ base_model = self._load_asset_yaml(self.base_asset)
43
+ merged = base_model
44
+ if service_file and service_file.exists():
45
+ merged = self._deep_merge(merged, self._load_yaml(service_file))
46
+
47
+ for override in overrides:
48
+ if override.exists():
49
+ merged = self._deep_merge(merged, self._load_yaml(override))
50
+
51
+ # Docker Compose V2 ignores the deprecated `version` field and warns.
52
+ # Docker Compose V2 会忽略已弃用的 `version` 字段并产生警告,因此统一移除。
53
+ merged.pop("version", None)
54
+
55
+ # Avoid network name collisions with pre-existing networks that were not created by Compose.
56
+ # If a network is not marked as external, letting Compose generate a project-scoped name is safer.
57
+ # 避免与历史遗留网络(非 compose 创建,缺少 label)发生冲突。
58
+ # 若网络不是 external,则移除固定 name,让 compose 自动生成项目级网络名。
59
+ networks = merged.get("networks")
60
+ if isinstance(networks, dict):
61
+ for _, net_cfg in networks.items():
62
+ if isinstance(net_cfg, dict) and not net_cfg.get("external"):
63
+ net_cfg.pop("name", None)
64
+
65
+ # When a service has a build definition, prefer local build and do not pull the image.
66
+ # 对于带 build 的服务,默认使用本地构建,避免先去 pull 同名镜像。
67
+ services = merged.get("services")
68
+ if isinstance(services, dict):
69
+ for _, svc_cfg in services.items():
70
+ if isinstance(svc_cfg, dict) and "build" in svc_cfg:
71
+ svc_cfg.setdefault("pull_policy", "never")
72
+
73
+ output_path.parent.mkdir(parents=True, exist_ok=True)
74
+ with output_path.open("w", encoding="utf-8") as fp:
75
+ yaml.safe_dump(merged, fp, sort_keys=False)
76
+ return output_path
77
+
78
+ def _load_yaml(self, path: Path) -> Dict[str, Any]:
79
+ with path.open("r", encoding="utf-8") as fp:
80
+ data = yaml.safe_load(fp) or {}
81
+ if not isinstance(data, dict): # pragma: no cover - defensive
82
+ raise ValueError(f"Compose file must be a mapping: {path}")
83
+ return data
84
+
85
+ def _load_asset_yaml(self, logical_name: str) -> Dict[str, Any]:
86
+ asset_path = self.asset_manager.resolve_path(logical_name)
87
+ return self._load_yaml(asset_path)
88
+
89
+ def _deep_merge(self, base: Dict[str, Any], override: Dict[str, Any]) -> Dict[str, Any]:
90
+ result = copy.deepcopy(base)
91
+ for key, value in override.items():
92
+ if key not in result:
93
+ result[key] = copy.deepcopy(value)
94
+ continue
95
+ if isinstance(result[key], dict) and isinstance(value, dict):
96
+ result[key] = self._deep_merge(result[key], value)
97
+ else:
98
+ result[key] = copy.deepcopy(value)
99
+ return result
100
+
101
+ def collect_override_paths(self, raw_values: Iterable[str]) -> Sequence[Path]:
102
+ return [Path(item).expanduser().resolve() for item in raw_values]
@@ -0,0 +1,152 @@
1
+ """`cancan doctor` — pre-flight environment & configuration checks.
2
+
3
+ 在 `cancan stack up` 之前一次性诊断常见的"起不来"原因:容器引擎、端口占用、
4
+ 工作区识别、运行时资产是否就绪、生产弱默认值。
5
+
6
+ Diagnose the common "stack won't come up" causes before `cancan stack up`:
7
+ container engine, port conflicts, workspace detection, bootstrapped runtime
8
+ assets, and weak production defaults. Stdlib-only.
9
+ """
10
+ from __future__ import annotations
11
+
12
+ import socket
13
+ from dataclasses import dataclass
14
+ from pathlib import Path
15
+
16
+ from ..runtime.compose_cmd import detect_compose_command
17
+ from ..runtime.workspace import detect_workspace_root
18
+
19
+ OK = "ok"
20
+ WARN = "warn"
21
+ FAIL = "fail"
22
+
23
+ _GLYPH = {OK: "✓", WARN: "!", FAIL: "✗"}
24
+
25
+ # Host-published ports declared in the bundled infra compose.
26
+ _HOST_PORTS = {
27
+ 8080: "caddy (local dev gateway)",
28
+ 22100: "controllersrv (host daemon)",
29
+ 25432: "postgres",
30
+ 26379: "redis",
31
+ 27017: "mongo",
32
+ 35672: "rabbitmq amqp",
33
+ 35673: "rabbitmq management",
34
+ }
35
+
36
+ # Weak local-dev defaults that must not survive into production.
37
+ _WEAK_DEFAULTS = {
38
+ "POSTGRES_PASSWORD": "postgres123",
39
+ "RABBITMQ_PASSWORD": "admin123",
40
+ "MONGO_INITDB_ROOT_PASSWORD": "admin123",
41
+ "MONGO_EXPRESS_PASSWORD": "admin123",
42
+ "RABBITMQ_MGMT_PASSWORD": "admin123",
43
+ }
44
+
45
+
46
+ @dataclass
47
+ class Check:
48
+ status: str
49
+ title: str
50
+ detail: str = ""
51
+
52
+
53
+ def _port_in_use(port: int, host: str = "127.0.0.1") -> bool:
54
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
55
+ sock.settimeout(0.3)
56
+ return sock.connect_ex((host, port)) == 0
57
+
58
+
59
+ def _parse_env_file(path: Path) -> dict[str, str]:
60
+ values: dict[str, str] = {}
61
+ try:
62
+ for raw in path.read_text(encoding="utf-8").splitlines():
63
+ line = raw.strip()
64
+ if not line or line.startswith("#") or "=" not in line:
65
+ continue
66
+ k, _, v = line.partition("=")
67
+ values[k.strip()] = v.strip()
68
+ except OSError:
69
+ pass
70
+ return values
71
+
72
+
73
+ def collect_checks(workspace: Path) -> list[Check]:
74
+ checks: list[Check] = []
75
+
76
+ # 1) container engine + daemon
77
+ try:
78
+ cmd = detect_compose_command()
79
+ checks.append(Check(OK, "Container engine", " ".join(cmd.argv_prefix)))
80
+ except Exception as exc:
81
+ checks.append(Check(FAIL, "Container engine", f"no usable docker/podman compose: {exc}"))
82
+
83
+ # 2) host port availability
84
+ busy = [f"{p} ({_HOST_PORTS[p]})" for p in sorted(_HOST_PORTS) if _port_in_use(p)]
85
+ if busy:
86
+ checks.append(Check(WARN, "Host ports", "in use (a previous stack? other apps?): " + ", ".join(busy)))
87
+ else:
88
+ checks.append(Check(OK, "Host ports", "all stack ports free"))
89
+
90
+ # 3) workspace detection
91
+ try:
92
+ root = detect_workspace_root(start=workspace)
93
+ checks.append(Check(OK, "Workspace root", str(root)))
94
+ except Exception:
95
+ checks.append(Check(WARN, "Workspace root", f"no workspace marker found under {workspace}; run `cancan init`"))
96
+
97
+ # 4) bootstrapped runtime assets
98
+ expected = {
99
+ ".env": workspace / ".env",
100
+ "ddl/create_db.sql": workspace / "ddl" / "create_db.sql",
101
+ "builds/caddy": workspace / "builds" / "caddy",
102
+ "builds/service/Dockerfile": workspace / "builds" / "service" / "Dockerfile",
103
+ }
104
+ missing = [name for name, p in expected.items() if not p.exists()]
105
+ if missing:
106
+ checks.append(Check(WARN, "Runtime assets", "missing (run `cancan stack up --bootstrap` or `cancan init`): " + ", ".join(missing)))
107
+ else:
108
+ checks.append(Check(OK, "Runtime assets", "bootstrapped"))
109
+
110
+ # 5) weak defaults / required secrets in .env
111
+ env_path = workspace / ".env"
112
+ if env_path.exists():
113
+ env = _parse_env_file(env_path)
114
+ weak = [k for k, default in _WEAK_DEFAULTS.items() if env.get(k, default) == default]
115
+ if not env.get("AUTH_TOTP_FERNET_KEY"):
116
+ checks.append(Check(FAIL, "AUTH_TOTP_FERNET_KEY", "empty in .env — opsbffsrv will refuse to start in prod mode"))
117
+ else:
118
+ checks.append(Check(OK, "AUTH_TOTP_FERNET_KEY", "set"))
119
+ if weak:
120
+ checks.append(Check(WARN, "Weak default secrets", "still default (CHANGE FOR PRODUCTION): " + ", ".join(weak)))
121
+ else:
122
+ checks.append(Check(OK, "Secrets", "no known weak defaults in .env"))
123
+ else:
124
+ checks.append(Check(WARN, "Config (.env)", "not found; `cancan init` / `stack up --bootstrap` will create it"))
125
+
126
+ return checks
127
+
128
+
129
+ def run_doctor(workspace: Path) -> int:
130
+ """运行诊断并打印结果,返回退出码(有 FAIL=2,仅 WARN=0)。
131
+ Run checks, print a report, return an exit code (FAIL → 2, otherwise 0).
132
+ """
133
+
134
+ checks = collect_checks(workspace)
135
+ print("cancan doctor — environment check\n")
136
+ for c in checks:
137
+ line = f" [{_GLYPH[c.status]}] {c.title}"
138
+ if c.detail:
139
+ line += f": {c.detail}"
140
+ print(line)
141
+
142
+ fails = sum(1 for c in checks if c.status == FAIL)
143
+ warns = sum(1 for c in checks if c.status == WARN)
144
+ print()
145
+ if fails:
146
+ print(f"✗ {fails} blocking issue(s), {warns} warning(s). Fix the ✗ items before `cancan stack up`.")
147
+ return 2
148
+ if warns:
149
+ print(f"! {warns} warning(s). The stack can start, but review the ! items (especially before production).")
150
+ return 0
151
+ print("✓ All checks passed.")
152
+ return 0
@@ -0,0 +1,71 @@
1
+ """High level orchestration helpers."""
2
+ from pathlib import Path
3
+ from typing import Sequence
4
+
5
+ from .assets import AssetManager
6
+ from .compose_builder import ComposeBuilder
7
+ from .runner import ServiceRunner
8
+
9
+
10
+ class CancanMicrostack:
11
+ """Convenience facade bundling assets, compose builder, and service runner."""
12
+
13
+ def __init__(self) -> None:
14
+ self.assets = AssetManager()
15
+ self.compose_builder = ComposeBuilder(self.assets)
16
+ self.runner = ServiceRunner()
17
+
18
+ def build_compose(
19
+ self,
20
+ *,
21
+ workspace: Path,
22
+ service_file: Path | None = None,
23
+ overrides: Sequence[Path] | None = None,
24
+ output: Path | None = None,
25
+ ) -> Path:
26
+ self._log(
27
+ "Building Cancan compose file",
28
+ workspace=workspace,
29
+ service_file=service_file,
30
+ overrides=overrides,
31
+ output=output,
32
+ )
33
+ return self.compose_builder.build(
34
+ workspace=workspace,
35
+ service_file=service_file,
36
+ overrides=overrides,
37
+ output_file=output,
38
+ )
39
+
40
+ def export_asset(self, logical_name: str, destination: Path, overwrite: bool = False) -> Path:
41
+ self._log("Exporting asset", logical_name=logical_name, destination=destination)
42
+ return self.assets.export_asset(logical_name, destination, overwrite)
43
+
44
+ def run_service(
45
+ self,
46
+ service_name: str,
47
+ host: str = "0.0.0.0",
48
+ port: int = 8080,
49
+ workspace: Path | None = None,
50
+ ) -> None:
51
+ self._log(
52
+ "Starting managed service",
53
+ service=service_name,
54
+ host=host,
55
+ port=port,
56
+ workspace=workspace,
57
+ )
58
+ self.runner.run(service_name, host=host, port=port, workspace=workspace)
59
+
60
+ async def run_service_async(
61
+ self,
62
+ service_name: str,
63
+ host: str = "0.0.0.0",
64
+ port: int = 8080,
65
+ workspace: Path | None = None,
66
+ ) -> None:
67
+ await self.runner.run_async(service_name, host=host, port=port, workspace=workspace)
68
+
69
+ def _log(self, message: str, **fields) -> None:
70
+ extras = " ".join(f"{k}={v}" for k, v in fields.items() if v is not None)
71
+ print(f"[cancan] {message} {extras}".strip())