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,138 @@
|
|
|
1
|
+
"""opsbffsrv service registry data access."""
|
|
2
|
+
from typing import (
|
|
3
|
+
List,
|
|
4
|
+
Optional,
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
from sqlalchemy import select, and_
|
|
8
|
+
|
|
9
|
+
from linglong_web import Rmanager
|
|
10
|
+
from cancan_microstack.public.schemas.infra.enums import ServiceType
|
|
11
|
+
from cancan_microstack.public.schemas.infra.service_info import ServiceInfo
|
|
12
|
+
from cancan_microstack.public.schemas.infra.service_registry import ServiceMetadata
|
|
13
|
+
from cancan_microstack.public.schemas.infra.service_instance import ServiceInstance
|
|
14
|
+
from cancan_microstack.services.opsbffsrv.infrastructure.db.model.service_info_tbl import ServiceInfoTbl
|
|
15
|
+
from cancan_microstack.services.opsbffsrv.infrastructure.db.model.service_instance_tbl import ServiceInstanceTbl
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _service_info_to_metadata(row: ServiceInfoTbl) -> ServiceMetadata:
|
|
19
|
+
"""将 ServiceInfo ORM 记录转换为 ServiceMetadata / Orchestrate metadata view."""
|
|
20
|
+
|
|
21
|
+
data = ServiceInfo.model_validate(row, from_attributes=True)
|
|
22
|
+
metadata_bag = dict(data.service_metadata or {})
|
|
23
|
+
owner = metadata_bag.get("owner")
|
|
24
|
+
deploy_region = metadata_bag.get("deploy_region")
|
|
25
|
+
config_version = metadata_bag.get("config_version")
|
|
26
|
+
|
|
27
|
+
service_type = data.service_type
|
|
28
|
+
if not isinstance(service_type, ServiceType):
|
|
29
|
+
service_type = ServiceType(service_type)
|
|
30
|
+
|
|
31
|
+
return ServiceMetadata(
|
|
32
|
+
service_name=data.service_name,
|
|
33
|
+
description=data.description,
|
|
34
|
+
service_type=service_type,
|
|
35
|
+
owner=owner,
|
|
36
|
+
health_check_path=data.health_check_path,
|
|
37
|
+
deploy_region=deploy_region,
|
|
38
|
+
config_version=config_version,
|
|
39
|
+
service_metadata=metadata_bag,
|
|
40
|
+
expected_status=data.expected_status,
|
|
41
|
+
desired_replicas=data.desired_replicas,
|
|
42
|
+
actual_replicas=data.actual_replicas,
|
|
43
|
+
last_scale_at=data.last_scale_at,
|
|
44
|
+
scale_policy=data.scale_policy,
|
|
45
|
+
registered_time=data.registered_time,
|
|
46
|
+
last_registered_time=data.last_registered_time,
|
|
47
|
+
created_time=data.created_time,
|
|
48
|
+
update_time=data.update_time,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
async def get_service_metadata(service_name: str) -> Optional[ServiceMetadata]:
|
|
53
|
+
"""获取指定服务的元数据 / Fetch metadata for a single service."""
|
|
54
|
+
|
|
55
|
+
async with Rmanager.pg_session("infra") as session:
|
|
56
|
+
async with session.begin():
|
|
57
|
+
stmt = select(ServiceInfoTbl).where(
|
|
58
|
+
and_(
|
|
59
|
+
ServiceInfoTbl.service_name == service_name,
|
|
60
|
+
ServiceInfoTbl.flag == 0,
|
|
61
|
+
)
|
|
62
|
+
)
|
|
63
|
+
row = (await session.execute(stmt)).scalar_one_or_none()
|
|
64
|
+
return _service_info_to_metadata(row) if row else None
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
async def get_service_instances(
|
|
68
|
+
service_name: str,
|
|
69
|
+
status: Optional[str] = None,
|
|
70
|
+
health_status: Optional[str] = None,
|
|
71
|
+
) -> List[ServiceInstance]:
|
|
72
|
+
"""获取指定服务的实例列表 / Fetch instances for a single service."""
|
|
73
|
+
|
|
74
|
+
async with Rmanager.pg_session("infra") as session:
|
|
75
|
+
async with session.begin():
|
|
76
|
+
stmt = select(ServiceInstanceTbl).where(
|
|
77
|
+
and_(
|
|
78
|
+
ServiceInstanceTbl.service_name == service_name,
|
|
79
|
+
ServiceInstanceTbl.flag == 0,
|
|
80
|
+
)
|
|
81
|
+
)
|
|
82
|
+
if status:
|
|
83
|
+
stmt = stmt.where(ServiceInstanceTbl.status == status)
|
|
84
|
+
if health_status:
|
|
85
|
+
stmt = stmt.where(ServiceInstanceTbl.health_status == health_status)
|
|
86
|
+
|
|
87
|
+
rows = list((await session.execute(stmt)).scalars().all())
|
|
88
|
+
return [ServiceInstance.model_validate(r, from_attributes=True) for r in rows]
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
async def get_service_by_instance(
|
|
92
|
+
service_name: str,
|
|
93
|
+
instance_id: str,
|
|
94
|
+
) -> Optional[ServiceInstance]:
|
|
95
|
+
"""根据服务名 + 实例ID 查询单条实例 / Fetch a concrete instance row."""
|
|
96
|
+
|
|
97
|
+
async with Rmanager.pg_session("infra") as session:
|
|
98
|
+
async with session.begin():
|
|
99
|
+
stmt = select(ServiceInstanceTbl).where(
|
|
100
|
+
and_(
|
|
101
|
+
ServiceInstanceTbl.service_name == service_name,
|
|
102
|
+
ServiceInstanceTbl.instance_id == instance_id,
|
|
103
|
+
ServiceInstanceTbl.flag == 0,
|
|
104
|
+
)
|
|
105
|
+
)
|
|
106
|
+
row = (await session.execute(stmt)).scalar_one_or_none()
|
|
107
|
+
return ServiceInstance.model_validate(row, from_attributes=True) if row else None
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
async def get_all_services() -> List[ServiceInstance]:
|
|
111
|
+
"""列出全部服务实例 / List every active service instance."""
|
|
112
|
+
|
|
113
|
+
async with Rmanager.pg_session("infra") as session:
|
|
114
|
+
async with session.begin():
|
|
115
|
+
stmt = select(ServiceInstanceTbl).where(ServiceInstanceTbl.flag == 0)
|
|
116
|
+
rows = list((await session.execute(stmt)).scalars().all())
|
|
117
|
+
return [ServiceInstance.model_validate(r, from_attributes=True) for r in rows]
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
async def get_all_service_names() -> List[str]:
|
|
121
|
+
"""获取所有服务名称 / Fetch distinct service names."""
|
|
122
|
+
|
|
123
|
+
async with Rmanager.pg_session("infra") as session:
|
|
124
|
+
async with session.begin():
|
|
125
|
+
stmt = select(ServiceInfoTbl.service_name).where(ServiceInfoTbl.flag == 0).order_by(
|
|
126
|
+
ServiceInfoTbl.service_name.asc())
|
|
127
|
+
rows = (await session.execute(stmt)).scalars().all()
|
|
128
|
+
return list(rows) if rows else []
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
async def list_service_metadata() -> List[ServiceMetadata]:
|
|
132
|
+
"""列出服务级元数据 / Return service metadata rows."""
|
|
133
|
+
|
|
134
|
+
async with Rmanager.pg_session("infra") as session:
|
|
135
|
+
async with session.begin():
|
|
136
|
+
stmt = select(ServiceInfoTbl).where(ServiceInfoTbl.flag == 0)
|
|
137
|
+
rows = list((await session.execute(stmt)).scalars().all())
|
|
138
|
+
return [_service_info_to_metadata(r) for r in rows]
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""OpsBffSrv DDL 管理入口
|
|
2
|
+
OpsBffSrv entry point wiring the shared AutoDDLManager with service config.
|
|
3
|
+
"""
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from linglong_web import (
|
|
7
|
+
AutoDDLManager,
|
|
8
|
+
DDLManagerConfig,
|
|
9
|
+
LinglongConfig,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
from cancan_microstack.runtime.resources import resolve_workspace_or_asset
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _build_manager() -> AutoDDLManager:
|
|
16
|
+
"""构建通用 DDL 管理器实例 / Build service-specific AutoDDLManager"""
|
|
17
|
+
|
|
18
|
+
trigger_file = resolve_workspace_or_asset("ddl/trigger.sql", "ddl/trigger.sql")
|
|
19
|
+
|
|
20
|
+
config = DDLManagerConfig(
|
|
21
|
+
script_path=Path(LinglongConfig.DDL_SCRIPT_PATH),
|
|
22
|
+
enable_auto_init=LinglongConfig.ENABLE_DDL_AUTO_INIT,
|
|
23
|
+
trigger_sql_paths=(trigger_file,),
|
|
24
|
+
abort_on_trigger_failure=True,
|
|
25
|
+
)
|
|
26
|
+
return AutoDDLManager(config)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
async def init_ddl() -> bool:
|
|
30
|
+
"""初始化 DDL(供 AppServer 调用)/ Trigger schema auto-initialization"""
|
|
31
|
+
return await _build_manager().check_and_init_tables()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""opsbffsrv Mongo infrastructure package."""
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""MongoDB 日志查询仓库 / Mongo-backed log query repository."""
|
|
2
|
+
import asyncio
|
|
3
|
+
from typing import (
|
|
4
|
+
Any,
|
|
5
|
+
Dict,
|
|
6
|
+
List,
|
|
7
|
+
Tuple,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
from pymongo.asynchronous.collection import AsyncCollection
|
|
11
|
+
from pymongo import DESCENDING
|
|
12
|
+
|
|
13
|
+
from cancan_microstack.public.schemas.logging.log_event import LogEventDocument
|
|
14
|
+
from linglong_web import LinglongConfig
|
|
15
|
+
from linglong_web import Rmanager
|
|
16
|
+
from linglong_web.utils import logger
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class LogQueryRepository:
|
|
20
|
+
"""封装日志查询操作 / Encapsulate log query operations."""
|
|
21
|
+
|
|
22
|
+
def __init__(self) -> None:
|
|
23
|
+
"""
|
|
24
|
+
Initializes the repository using the global MongoDB client.
|
|
25
|
+
"""
|
|
26
|
+
# 注意:不能在 __init__ 时缓存 Rmanager.MongoClient。
|
|
27
|
+
# Note: Do NOT cache Rmanager.MongoClient at __init__ time.
|
|
28
|
+
#
|
|
29
|
+
# 说明 / Rationale:
|
|
30
|
+
# - 该 Repository 可能在模块 import 阶段被创建(全局单例)。
|
|
31
|
+
# It may be instantiated at import time (module-level singleton).
|
|
32
|
+
# - 资源初始化(init_resources)发生在服务启动生命周期更靠后的位置。
|
|
33
|
+
# Resource initialization happens later in the server lifecycle.
|
|
34
|
+
# - 如果这里缓存了 None,会导致整个进程里一直拿不到 Mongo client。
|
|
35
|
+
# Caching None here makes Mongo client unavailable forever.
|
|
36
|
+
self._collection: AsyncCollection | None = None
|
|
37
|
+
self._init_lock = asyncio.Lock()
|
|
38
|
+
|
|
39
|
+
async def _get_collection(self) -> AsyncCollection:
|
|
40
|
+
"""Lazily initializes and returns the MongoDB collection object."""
|
|
41
|
+
if self._collection is not None:
|
|
42
|
+
return self._collection
|
|
43
|
+
|
|
44
|
+
async with self._init_lock:
|
|
45
|
+
if self._collection is not None:
|
|
46
|
+
return self._collection
|
|
47
|
+
|
|
48
|
+
client = Rmanager.MongoClient
|
|
49
|
+
if not client:
|
|
50
|
+
raise RuntimeError("MongoDB client is not available in Rmanager.")
|
|
51
|
+
|
|
52
|
+
db_name = LinglongConfig.MONGODB_DB
|
|
53
|
+
collection_name = LinglongConfig.MONGODB_COLLECTION
|
|
54
|
+
if not db_name or not collection_name:
|
|
55
|
+
raise RuntimeError(
|
|
56
|
+
"MongoDB config is not available: MONGODB_DB/MONGODB_COLLECTION is empty."
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
collection = client[db_name][collection_name]
|
|
60
|
+
self._collection = collection
|
|
61
|
+
logger.info(
|
|
62
|
+
"LogQueryRepository collection '%s/%s' initialized.", db_name, collection_name
|
|
63
|
+
)
|
|
64
|
+
return self._collection
|
|
65
|
+
|
|
66
|
+
async def query_logs(
|
|
67
|
+
self,
|
|
68
|
+
filters: Dict[str, Any],
|
|
69
|
+
skip: int,
|
|
70
|
+
limit: int,
|
|
71
|
+
) -> Tuple[List[LogEventDocument], int]:
|
|
72
|
+
collection = await self._get_collection()
|
|
73
|
+
cursor = (
|
|
74
|
+
collection.find(filters)
|
|
75
|
+
.sort("timestamp", DESCENDING)
|
|
76
|
+
.skip(skip)
|
|
77
|
+
.limit(limit)
|
|
78
|
+
)
|
|
79
|
+
raw_docs = await cursor.to_list(length=limit)
|
|
80
|
+
events = [self._convert_document(doc) for doc in raw_docs]
|
|
81
|
+
total = await collection.count_documents(filters)
|
|
82
|
+
return events, int(total)
|
|
83
|
+
|
|
84
|
+
def _convert_document(self, doc: Dict[str, Any]) -> LogEventDocument:
|
|
85
|
+
doc = dict(doc)
|
|
86
|
+
doc.pop("_id", None)
|
|
87
|
+
return LogEventDocument.model_validate(doc)
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""
|
|
2
|
+
异步操作管理 API
|
|
3
|
+
|
|
4
|
+
为前端提供异步操作管理接口:
|
|
5
|
+
1. 异步启动/停止/重启/扩缩容/重建服务(立即返回 operation_id)
|
|
6
|
+
2. 查询操作状态
|
|
7
|
+
3. 列出操作历史
|
|
8
|
+
|
|
9
|
+
架构说明:
|
|
10
|
+
- opsbffsrv: 生成 operation_id,管理操作记录(通过 infrasrv),协调异步执行
|
|
11
|
+
- controllersrv: 纯粹的 Docker 命令执行器,同步返回结果
|
|
12
|
+
- infrasrv: 存储操作记录,提供查询接口
|
|
13
|
+
"""
|
|
14
|
+
from typing import (
|
|
15
|
+
Optional,
|
|
16
|
+
)
|
|
17
|
+
import http
|
|
18
|
+
|
|
19
|
+
from pydantic import (
|
|
20
|
+
BaseModel,
|
|
21
|
+
Field,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
from cancan_microstack.public.const.error import ErrorCode
|
|
25
|
+
from linglong_web import LinglongHTTPException
|
|
26
|
+
from cancan_microstack.public.const.operation_consts import (
|
|
27
|
+
OperationType,
|
|
28
|
+
InitiatedBy,
|
|
29
|
+
InitiatedFrom,
|
|
30
|
+
)
|
|
31
|
+
from cancan_microstack.public.schemas.common import (
|
|
32
|
+
APIResponse,
|
|
33
|
+
)
|
|
34
|
+
from cancan_microstack.public.schemas.infra.operation import (
|
|
35
|
+
OperationResponse,
|
|
36
|
+
OperationListResponse,
|
|
37
|
+
)
|
|
38
|
+
from cancan_microstack.public.schemas.opsbffsrv.async_ops import AsyncOperationResponse
|
|
39
|
+
from linglong_web import (
|
|
40
|
+
build_success_response,
|
|
41
|
+
)
|
|
42
|
+
from cancan_microstack.services.opsbffsrv.application.async_operation_app import AsyncOperationApp
|
|
43
|
+
from cancan_microstack.services.opsbffsrv.infrastructure.api.infrasrv_api import InfraSrvApi
|
|
44
|
+
from linglong_web.utils import logger
|
|
45
|
+
|
|
46
|
+
# 应用层和基础设施层实例
|
|
47
|
+
_async_op_app = AsyncOperationApp()
|
|
48
|
+
_infrasrv_api = InfraSrvApi()
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class AsyncOperationRequest(BaseModel):
|
|
52
|
+
"""异步操作请求模型"""
|
|
53
|
+
service_name: str = Field(..., description="服务名称(不带 .service 后缀)")
|
|
54
|
+
operation_params: dict = Field(default_factory=dict, description="操作参数")
|
|
55
|
+
initiated_by: InitiatedBy = Field(default=InitiatedBy.OPSBFFSRV, description="操作发起者")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
async def async_start_service_handler(
|
|
59
|
+
request: AsyncOperationRequest,
|
|
60
|
+
) -> APIResponse[AsyncOperationResponse]:
|
|
61
|
+
"""异步启动服务"""
|
|
62
|
+
logger.info(f"Async start service: {request.service_name}")
|
|
63
|
+
result = await _async_op_app.submit_operation(
|
|
64
|
+
operation_type=OperationType.START,
|
|
65
|
+
service_name=request.service_name,
|
|
66
|
+
operation_params=request.operation_params,
|
|
67
|
+
initiated_by=request.initiated_by,
|
|
68
|
+
initiated_from=InitiatedFrom.FRONTEND
|
|
69
|
+
)
|
|
70
|
+
return build_success_response(data=result)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
async def async_stop_service_handler(
|
|
74
|
+
request: AsyncOperationRequest,
|
|
75
|
+
) -> APIResponse[AsyncOperationResponse]:
|
|
76
|
+
"""异步停止服务"""
|
|
77
|
+
logger.info(f"Async stop service: {request.service_name}")
|
|
78
|
+
result = await _async_op_app.submit_operation(
|
|
79
|
+
operation_type=OperationType.STOP,
|
|
80
|
+
service_name=request.service_name,
|
|
81
|
+
operation_params=request.operation_params,
|
|
82
|
+
initiated_by=request.initiated_by,
|
|
83
|
+
initiated_from=InitiatedFrom.FRONTEND
|
|
84
|
+
)
|
|
85
|
+
return build_success_response(data=result)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
async def async_restart_service_handler(
|
|
89
|
+
request: AsyncOperationRequest,
|
|
90
|
+
) -> APIResponse[AsyncOperationResponse]:
|
|
91
|
+
"""异步重启服务"""
|
|
92
|
+
logger.info(f"Async restart service: {request.service_name}")
|
|
93
|
+
result = await _async_op_app.submit_operation(
|
|
94
|
+
operation_type=OperationType.RESTART,
|
|
95
|
+
service_name=request.service_name,
|
|
96
|
+
operation_params=request.operation_params,
|
|
97
|
+
initiated_by=request.initiated_by,
|
|
98
|
+
initiated_from=InitiatedFrom.FRONTEND
|
|
99
|
+
)
|
|
100
|
+
return build_success_response(data=result)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
async def get_operation_status_handler(
|
|
105
|
+
operation_id: str,
|
|
106
|
+
) -> APIResponse[OperationResponse]:
|
|
107
|
+
"""查询操作状态"""
|
|
108
|
+
logger.debug(f"Query operation status: {operation_id}")
|
|
109
|
+
# 使用新的 get_operation 方法(基类中定义)
|
|
110
|
+
result = await _infrasrv_api.get_operation(operation_id)
|
|
111
|
+
if result.success and result.data is not None:
|
|
112
|
+
return build_success_response(data=result.data)
|
|
113
|
+
|
|
114
|
+
message = result.error.msg or "Failed to query operation status"
|
|
115
|
+
raise LinglongHTTPException(
|
|
116
|
+
status_code=http.HTTPStatus.INTERNAL_SERVER_ERROR.value,
|
|
117
|
+
error_code=result.error.code or ErrorCode.SYSTEM_ERROR,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
async def list_operations_handler(
|
|
122
|
+
service_name: Optional[str] = None,
|
|
123
|
+
status: Optional[str] = None,
|
|
124
|
+
limit: int = 50,
|
|
125
|
+
offset: int = 0
|
|
126
|
+
) -> APIResponse[OperationListResponse]:
|
|
127
|
+
"""列出操作历史"""
|
|
128
|
+
logger.debug(f"List operations: service={service_name}, status={status}")
|
|
129
|
+
result = await _infrasrv_api.list_operations(service_name, status, limit, offset)
|
|
130
|
+
if result.success and result.data is not None:
|
|
131
|
+
return build_success_response(data=result.data)
|
|
132
|
+
|
|
133
|
+
message = result.error.msg or "Failed to list operations"
|
|
134
|
+
raise LinglongHTTPException(
|
|
135
|
+
status_code=http.HTTPStatus.INTERNAL_SERVER_ERROR.value,
|
|
136
|
+
error_code=result.error.code or ErrorCode.SYSTEM_ERROR,
|
|
137
|
+
)
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""认证 HTTP handler / Authentication API handlers."""
|
|
2
|
+
import http
|
|
3
|
+
|
|
4
|
+
from fastapi import Request, Response
|
|
5
|
+
|
|
6
|
+
from linglong_web import build_success_response, LinglongConfig
|
|
7
|
+
|
|
8
|
+
from cancan_microstack.public.const.opsbffsrv_error import OpsbffsrvAuthErrorCode
|
|
9
|
+
from cancan_microstack.public.error import HTTPException
|
|
10
|
+
from cancan_microstack.services.opsbffsrv.application import auth_app
|
|
11
|
+
from cancan_microstack.services.opsbffsrv.application.auth_app import (
|
|
12
|
+
LoginRequest,
|
|
13
|
+
TempTokenRequest,
|
|
14
|
+
TotpCodeRequest,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_client_ip(request: Request) -> str:
|
|
19
|
+
"""从 X-Forwarded-For → X-Real-IP → direct 依次读取真实客户端 IP。"""
|
|
20
|
+
forwarded = request.headers.get("X-Forwarded-For")
|
|
21
|
+
if forwarded:
|
|
22
|
+
return forwarded.split(",")[0].strip()
|
|
23
|
+
real_ip = request.headers.get("X-Real-IP")
|
|
24
|
+
if real_ip:
|
|
25
|
+
return real_ip.strip()
|
|
26
|
+
return request.client.host if request.client else "unknown"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _set_session_cookie(response: Response, token: str) -> None:
|
|
30
|
+
"""设置 session Cookie / Set ops_session cookie on response."""
|
|
31
|
+
session_ttl = int(LinglongConfig.get("AUTH_SESSION_TTL", 86400))
|
|
32
|
+
cookie_secure = str(LinglongConfig.get("AUTH_COOKIE_SECURE", "true")).lower() == "true"
|
|
33
|
+
response.set_cookie(
|
|
34
|
+
key="ops_session",
|
|
35
|
+
value=token,
|
|
36
|
+
httponly=True,
|
|
37
|
+
secure=cookie_secure,
|
|
38
|
+
path="/",
|
|
39
|
+
max_age=session_ttl,
|
|
40
|
+
samesite="lax",
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
async def get_captcha_handler():
|
|
45
|
+
"""GET /v1/opsbffsrv/auth/captcha — 返回验证码图片"""
|
|
46
|
+
result = await auth_app.get_captcha()
|
|
47
|
+
return build_success_response(data=result)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
async def login_handler(request: Request, payload: LoginRequest):
|
|
51
|
+
"""POST /v1/opsbffsrv/auth/login — 登录(用户名+密码+验证码)"""
|
|
52
|
+
client_ip = get_client_ip(request)
|
|
53
|
+
result = await auth_app.login(payload, client_ip)
|
|
54
|
+
return build_success_response(data=result)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
async def totp_setup_handler(payload: TempTokenRequest):
|
|
58
|
+
"""POST /v1/opsbffsrv/auth/totp/setup — 获取 TOTP 绑定二维码"""
|
|
59
|
+
result = await auth_app.totp_setup(payload.temp_token)
|
|
60
|
+
return build_success_response(data=result)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
async def totp_bind_handler(response: Response, payload: TotpCodeRequest):
|
|
64
|
+
"""POST /v1/opsbffsrv/auth/totp/bind — 完成 TOTP 绑定"""
|
|
65
|
+
session_token = await auth_app.totp_bind(payload.temp_token, payload.totp_code)
|
|
66
|
+
_set_session_cookie(response, session_token)
|
|
67
|
+
return build_success_response(data={"message": "TOTP bound successfully"})
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
async def totp_verify_handler(response: Response, payload: TotpCodeRequest):
|
|
71
|
+
"""POST /v1/opsbffsrv/auth/totp/verify — 常规 TOTP 验证"""
|
|
72
|
+
session_token = await auth_app.totp_verify(payload.temp_token, payload.totp_code)
|
|
73
|
+
_set_session_cookie(response, session_token)
|
|
74
|
+
return build_success_response(data={"message": "TOTP verified successfully"})
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
async def logout_handler(request: Request, response: Response):
|
|
78
|
+
"""POST /v1/opsbffsrv/auth/logout — 登出(撤销 Redis 会话 + 清除 cookie)"""
|
|
79
|
+
token = request.cookies.get("ops_session")
|
|
80
|
+
await auth_app.logout(token)
|
|
81
|
+
# 清除 cookie:复用 Secure 配置,max_age=0 让浏览器立即删除。
|
|
82
|
+
cookie_secure = str(getattr(LinglongConfig, "AUTH_COOKIE_SECURE", "true")).lower() == "true"
|
|
83
|
+
response.set_cookie(
|
|
84
|
+
key="ops_session",
|
|
85
|
+
value="",
|
|
86
|
+
httponly=True,
|
|
87
|
+
secure=cookie_secure,
|
|
88
|
+
path="/",
|
|
89
|
+
max_age=0,
|
|
90
|
+
samesite="lax",
|
|
91
|
+
)
|
|
92
|
+
return build_success_response(data={"message": "Logged out"})
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
async def check_session_handler(request: Request):
|
|
96
|
+
"""GET /v1/opsbffsrv/auth/session — 检查当前 session"""
|
|
97
|
+
token = request.cookies.get("ops_session")
|
|
98
|
+
if not token:
|
|
99
|
+
raise HTTPException(
|
|
100
|
+
status_code=http.HTTPStatus.UNAUTHORIZED.value,
|
|
101
|
+
error_code=OpsbffsrvAuthErrorCode.SESSION_INVALID,
|
|
102
|
+
msg="Not authenticated",
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
result = await auth_app.check_session(token)
|
|
106
|
+
if result is None:
|
|
107
|
+
raise HTTPException(
|
|
108
|
+
status_code=http.HTTPStatus.UNAUTHORIZED.value,
|
|
109
|
+
error_code=OpsbffsrvAuthErrorCode.SESSION_INVALID,
|
|
110
|
+
msg="Session expired",
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
return build_success_response(data=result)
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Caddy 访问日志 API 接口
|
|
3
|
+
|
|
4
|
+
提供日志查询、安全事件监控、异常分析等功能
|
|
5
|
+
"""
|
|
6
|
+
from typing import (
|
|
7
|
+
Optional,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
from linglong_web.utils import logger
|
|
11
|
+
from cancan_microstack.public.schemas.common import APIResponse
|
|
12
|
+
from linglong_web import build_success_response
|
|
13
|
+
from linglong_web import limiter
|
|
14
|
+
from cancan_microstack.public.schemas.caddy import AccessLogQuery
|
|
15
|
+
from cancan_microstack.services.opsbffsrv.application.caddy.access_log_analysis_app import AccessLogAnalysisApp
|
|
16
|
+
|
|
17
|
+
# 应用层实例
|
|
18
|
+
_log_app = AccessLogAnalysisApp()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@limiter("30/second")
|
|
22
|
+
async def search_logs_handler(query: AccessLogQuery) -> APIResponse[dict]:
|
|
23
|
+
"""
|
|
24
|
+
搜索访问日志
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
query: 查询条件
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
统一响应格式
|
|
31
|
+
"""
|
|
32
|
+
logger.info(f"搜索访问日志: service={query.service_name}, route_id={query.route_id}")
|
|
33
|
+
logs = await _log_app.search_logs(query)
|
|
34
|
+
|
|
35
|
+
return build_success_response(data={
|
|
36
|
+
"logs": [log.model_dump() for log in logs],
|
|
37
|
+
"total": len(logs),
|
|
38
|
+
"query": query.model_dump(),
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@limiter("20/second")
|
|
43
|
+
async def get_security_events_handler(
|
|
44
|
+
event_type: str = "waf_blocked",
|
|
45
|
+
limit: int = 100,
|
|
46
|
+
) -> APIResponse[dict]:
|
|
47
|
+
"""
|
|
48
|
+
获取安全事件(WAF 拦截、限流触发)
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
event_type: 事件类型(waf_blocked / rate_limited)
|
|
52
|
+
limit: 返回数量
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
统一响应格式
|
|
56
|
+
"""
|
|
57
|
+
logger.info(f"获取安全事件: type={event_type}, limit={limit}")
|
|
58
|
+
# 应用层返回 dict 信封:{status, event_type, count, data:[CaddyAccessLog...]}
|
|
59
|
+
# 直接透传该信封,APIResponse 会自动序列化其中的 pydantic 模型。
|
|
60
|
+
result = await _log_app.get_security_events(event_type=event_type, limit=limit)
|
|
61
|
+
|
|
62
|
+
return build_success_response(data=result)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@limiter("20/second")
|
|
66
|
+
async def analyze_geographic_distribution_handler(
|
|
67
|
+
start_time: Optional[str] = None,
|
|
68
|
+
end_time: Optional[str] = None,
|
|
69
|
+
) -> APIResponse[dict]:
|
|
70
|
+
"""
|
|
71
|
+
分析地理分布
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
start_time: 开始时间(ISO 8601格式)
|
|
75
|
+
end_time: 结束时间(ISO 8601格式)
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
统一响应格式
|
|
79
|
+
"""
|
|
80
|
+
logger.info("分析地理分布")
|
|
81
|
+
# 应用层返回 dict 信封:{status, data:[...]},直接透传。
|
|
82
|
+
result = await _log_app.analyze_geographic_distribution(
|
|
83
|
+
start_time=start_time,
|
|
84
|
+
end_time=end_time
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
return build_success_response(data=result)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@limiter("20/second")
|
|
91
|
+
async def analyze_status_code_distribution_handler(
|
|
92
|
+
start_time: Optional[str] = None,
|
|
93
|
+
end_time: Optional[str] = None,
|
|
94
|
+
) -> APIResponse[dict]:
|
|
95
|
+
"""
|
|
96
|
+
分析状态码分布
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
start_time: 开始时间(ISO 8601格式)
|
|
100
|
+
end_time: 结束时间(ISO 8601格式)
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
统一响应格式
|
|
104
|
+
"""
|
|
105
|
+
logger.info("分析状态码分布")
|
|
106
|
+
# 应用层返回 dict 信封:{status, data:[...]},直接透传。
|
|
107
|
+
result = await _log_app.analyze_status_code_distribution(
|
|
108
|
+
start_time=start_time,
|
|
109
|
+
end_time=end_time
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
return build_success_response(data=result)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@limiter("10/second")
|
|
116
|
+
async def detect_suspicious_ips_handler(
|
|
117
|
+
threshold: int = 1000,
|
|
118
|
+
time_window_minutes: int = 10,
|
|
119
|
+
) -> APIResponse[dict]:
|
|
120
|
+
"""
|
|
121
|
+
检测可疑 IP(高频访问)
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
threshold: 阈值(次数)
|
|
125
|
+
time_window_minutes: 时间窗口(分钟)
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
统一响应格式
|
|
129
|
+
"""
|
|
130
|
+
logger.info(f"检测可疑IP: threshold={threshold}, window={time_window_minutes}min")
|
|
131
|
+
# 对齐应用层签名:detect_suspicious_ips(time_window, request_threshold)
|
|
132
|
+
# 应用层返回 dict 信封:{status, count, data:[SuspiciousIP...]},直接透传。
|
|
133
|
+
result = await _log_app.detect_suspicious_ips(
|
|
134
|
+
time_window=time_window_minutes,
|
|
135
|
+
request_threshold=threshold,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
return build_success_response(data=result)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@limiter("20/second")
|
|
142
|
+
async def analyze_error_patterns_handler(hours: int = 24) -> APIResponse[dict]:
|
|
143
|
+
"""
|
|
144
|
+
分析错误模式(4xx/5xx)
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
hours: 分析过去多少小时的错误(默认 24 小时)
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
统一响应格式
|
|
151
|
+
"""
|
|
152
|
+
logger.info(f"分析错误模式: hours={hours}")
|
|
153
|
+
# 对齐应用层签名:analyze_error_patterns(hours)
|
|
154
|
+
# 应用层返回 dict 信封:{status, total_errors, error_rate, ...},直接透传。
|
|
155
|
+
result = await _log_app.analyze_error_patterns(hours=hours)
|
|
156
|
+
|
|
157
|
+
return build_success_response(data=result)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
@limiter("5/second")
|
|
161
|
+
async def cleanup_old_logs_handler(days: int = 30) -> APIResponse[dict | None]:
|
|
162
|
+
"""
|
|
163
|
+
清理旧日志
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
days: 保留天数(默认30天)
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
统一响应格式
|
|
170
|
+
"""
|
|
171
|
+
logger.info(f"清理旧日志: days={days}")
|
|
172
|
+
result = await _log_app.cleanup_old_logs(days)
|
|
173
|
+
|
|
174
|
+
return build_success_response(data=result)
|