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