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,285 @@
|
|
|
1
|
+
"""认证用例编排 / Authentication application service (use case orchestration)."""
|
|
2
|
+
import http
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
from pydantic import BaseModel
|
|
7
|
+
|
|
8
|
+
from linglong_web import LinglongConfig
|
|
9
|
+
from linglong_web.utils import logger
|
|
10
|
+
|
|
11
|
+
from cancan_microstack.public.const.opsbffsrv_error import OpsbffsrvAuthErrorCode
|
|
12
|
+
from cancan_microstack.public.error import HTTPException
|
|
13
|
+
from cancan_microstack.services.opsbffsrv.domain.auth import auth_domain
|
|
14
|
+
from cancan_microstack.services.opsbffsrv.infrastructure.auth import (
|
|
15
|
+
captcha_service,
|
|
16
|
+
redis_store,
|
|
17
|
+
totp_service,
|
|
18
|
+
)
|
|
19
|
+
from cancan_microstack.services.opsbffsrv.infrastructure.db.operate.admin_user_operate import (
|
|
20
|
+
get_admin_user_by_id,
|
|
21
|
+
mark_totp_bound,
|
|
22
|
+
update_totp_secret,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# ── Request / Response schemas ───────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
class LoginRequest(BaseModel):
|
|
29
|
+
username: str
|
|
30
|
+
password: str
|
|
31
|
+
captcha_id: str
|
|
32
|
+
captcha_answer: str
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class LoginResponse(BaseModel):
|
|
36
|
+
next_step: str # "totp_bind" | "totp_verify"
|
|
37
|
+
temp_token: str
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class CaptchaResponse(BaseModel):
|
|
41
|
+
captcha_id: str
|
|
42
|
+
image_base64: str
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class TotpSetupResponse(BaseModel):
|
|
46
|
+
otpauth_uri: str
|
|
47
|
+
qr_code_base64: str
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class TempTokenRequest(BaseModel):
|
|
51
|
+
temp_token: str
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class TotpCodeRequest(BaseModel):
|
|
55
|
+
temp_token: str
|
|
56
|
+
totp_code: str
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class SessionResponse(BaseModel):
|
|
60
|
+
authenticated: bool
|
|
61
|
+
username: str
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# ── Application methods ──────────────────────────────────────────────────────
|
|
65
|
+
|
|
66
|
+
async def _enforce_totp_attempt_limit(temp_token: str) -> None:
|
|
67
|
+
"""TOTP 暴力破解防护:超过上限则失效 temp_token 并拒绝。
|
|
68
|
+
|
|
69
|
+
Guard against TOTP brute force: once the per-temp_token failure count
|
|
70
|
+
reaches the configured cap, invalidate the temp_token and reject the request.
|
|
71
|
+
Call this BEFORE verifying the TOTP code on each attempt.
|
|
72
|
+
"""
|
|
73
|
+
max_failures = int(getattr(LinglongConfig, "AUTH_TOTP_MAX_FAILURES", 5))
|
|
74
|
+
fail_count = await redis_store.get_totp_fail_count(temp_token)
|
|
75
|
+
if fail_count >= max_failures:
|
|
76
|
+
# 失效 token,强制用户重新走密码登录流程
|
|
77
|
+
await redis_store.delete_temp_token(temp_token)
|
|
78
|
+
await redis_store.reset_totp_fail(temp_token)
|
|
79
|
+
raise HTTPException(
|
|
80
|
+
status_code=http.HTTPStatus.UNAUTHORIZED.value,
|
|
81
|
+
error_code=OpsbffsrvAuthErrorCode.TEMP_TOKEN_INVALID,
|
|
82
|
+
msg="Too many invalid TOTP attempts; please log in again",
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
async def _record_totp_failure(temp_token: str) -> None:
|
|
87
|
+
"""记录一次 TOTP 失败 / Record one TOTP failure for a temp token."""
|
|
88
|
+
temp_ttl = int(getattr(LinglongConfig, "AUTH_TEMP_TOKEN_TTL", 300))
|
|
89
|
+
await redis_store.increment_totp_fail(temp_token, ttl=temp_ttl)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
async def get_captcha() -> CaptchaResponse:
|
|
93
|
+
"""生成并存储验证码 / Generate captcha and save to Redis."""
|
|
94
|
+
captcha_id, answer, image_base64 = captcha_service.generate_captcha()
|
|
95
|
+
captcha_ttl = int(LinglongConfig.get("AUTH_CAPTCHA_TTL", 60))
|
|
96
|
+
await redis_store.save_captcha(captcha_id, answer, ttl=captcha_ttl)
|
|
97
|
+
return CaptchaResponse(captcha_id=captcha_id, image_base64=image_base64)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
async def login(req: LoginRequest, client_ip: str) -> LoginResponse:
|
|
101
|
+
"""登录入口 / Login: validate captcha → authenticate → return next step."""
|
|
102
|
+
# 1. 校验验证码
|
|
103
|
+
if not await auth_domain.validate_captcha(req.captcha_id, req.captcha_answer):
|
|
104
|
+
raise HTTPException(
|
|
105
|
+
status_code=http.HTTPStatus.BAD_REQUEST.value,
|
|
106
|
+
error_code=OpsbffsrvAuthErrorCode.CAPTCHA_INVALID,
|
|
107
|
+
msg="Invalid captcha",
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# 2. 认证
|
|
111
|
+
result = await auth_domain.authenticate(req.username, req.password, client_ip)
|
|
112
|
+
if not result.success:
|
|
113
|
+
status = (
|
|
114
|
+
http.HTTPStatus.FORBIDDEN.value
|
|
115
|
+
if result.error_code == "AUTH_IP_LOCKED"
|
|
116
|
+
else http.HTTPStatus.UNAUTHORIZED.value
|
|
117
|
+
)
|
|
118
|
+
code = {
|
|
119
|
+
"AUTH_IP_LOCKED": OpsbffsrvAuthErrorCode.IP_LOCKED,
|
|
120
|
+
"AUTH_CREDENTIALS_INVALID": OpsbffsrvAuthErrorCode.CREDENTIALS_INVALID,
|
|
121
|
+
}.get(result.error_code, OpsbffsrvAuthErrorCode.INTERNAL_ERROR)
|
|
122
|
+
raise HTTPException(status_code=status, error_code=code, msg=result.error_msg)
|
|
123
|
+
|
|
124
|
+
# 3. 返回下一步
|
|
125
|
+
next_step = "totp_bind" if not result.totp_bound else "totp_verify"
|
|
126
|
+
return LoginResponse(next_step=next_step, temp_token=result.temp_token)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
async def totp_setup(temp_token: str) -> TotpSetupResponse:
|
|
130
|
+
"""获取 TOTP 绑定信息 / Get TOTP provisioning URI and QR code."""
|
|
131
|
+
user_id = await redis_store.get_temp_token(temp_token)
|
|
132
|
+
if user_id is None:
|
|
133
|
+
raise HTTPException(
|
|
134
|
+
status_code=http.HTTPStatus.UNAUTHORIZED.value,
|
|
135
|
+
error_code=OpsbffsrvAuthErrorCode.TEMP_TOKEN_INVALID,
|
|
136
|
+
msg="Temp token invalid or expired",
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
user = await get_admin_user_by_id(user_id)
|
|
140
|
+
if user is None:
|
|
141
|
+
raise HTTPException(
|
|
142
|
+
status_code=http.HTTPStatus.UNAUTHORIZED.value,
|
|
143
|
+
error_code=OpsbffsrvAuthErrorCode.TEMP_TOKEN_INVALID,
|
|
144
|
+
msg="User not found",
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
# 如果已有加密 secret 则解密复用,否则生成新的
|
|
148
|
+
if user.totp_secret_encrypted:
|
|
149
|
+
secret = totp_service.decrypt_secret(user.totp_secret_encrypted)
|
|
150
|
+
else:
|
|
151
|
+
secret = totp_service.generate_secret()
|
|
152
|
+
encrypted = totp_service.encrypt_secret(secret)
|
|
153
|
+
await update_totp_secret(user.id, encrypted)
|
|
154
|
+
|
|
155
|
+
uri = totp_service.get_provisioning_uri(secret, user.username)
|
|
156
|
+
qr_base64 = totp_service.generate_qr_base64(uri)
|
|
157
|
+
|
|
158
|
+
return TotpSetupResponse(otpauth_uri=uri, qr_code_base64=qr_base64)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
async def totp_bind(temp_token: str, totp_code: str) -> Optional[str]:
|
|
162
|
+
"""完成 TOTP 绑定 / Bind TOTP: verify code → mark bound → create session.
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
session token string on success.
|
|
166
|
+
"""
|
|
167
|
+
user_id = await redis_store.get_temp_token(temp_token)
|
|
168
|
+
if user_id is None:
|
|
169
|
+
raise HTTPException(
|
|
170
|
+
status_code=http.HTTPStatus.UNAUTHORIZED.value,
|
|
171
|
+
error_code=OpsbffsrvAuthErrorCode.TEMP_TOKEN_INVALID,
|
|
172
|
+
msg="Temp token invalid or expired",
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# 暴力破解防护:超过上限直接失效 token
|
|
176
|
+
await _enforce_totp_attempt_limit(temp_token)
|
|
177
|
+
|
|
178
|
+
user = await get_admin_user_by_id(user_id)
|
|
179
|
+
if user is None:
|
|
180
|
+
raise HTTPException(
|
|
181
|
+
status_code=http.HTTPStatus.UNAUTHORIZED.value,
|
|
182
|
+
error_code=OpsbffsrvAuthErrorCode.TEMP_TOKEN_INVALID,
|
|
183
|
+
msg="User not found",
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
if user.totp_bound:
|
|
187
|
+
raise HTTPException(
|
|
188
|
+
status_code=http.HTTPStatus.BAD_REQUEST.value,
|
|
189
|
+
error_code=OpsbffsrvAuthErrorCode.TOTP_ALREADY_BOUND,
|
|
190
|
+
msg="TOTP already bound",
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
if not user.totp_secret_encrypted:
|
|
194
|
+
raise HTTPException(
|
|
195
|
+
status_code=http.HTTPStatus.BAD_REQUEST.value,
|
|
196
|
+
error_code=OpsbffsrvAuthErrorCode.TOTP_INVALID,
|
|
197
|
+
msg="TOTP has not been set up",
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
secret = totp_service.decrypt_secret(user.totp_secret_encrypted)
|
|
201
|
+
if not totp_service.verify_totp(secret, totp_code):
|
|
202
|
+
await _record_totp_failure(temp_token)
|
|
203
|
+
raise HTTPException(
|
|
204
|
+
status_code=http.HTTPStatus.BAD_REQUEST.value,
|
|
205
|
+
error_code=OpsbffsrvAuthErrorCode.TOTP_INVALID,
|
|
206
|
+
msg="Invalid TOTP code",
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
# 绑定成功
|
|
210
|
+
await mark_totp_bound(user.id)
|
|
211
|
+
await redis_store.delete_temp_token(temp_token)
|
|
212
|
+
await redis_store.reset_totp_fail(temp_token)
|
|
213
|
+
session_token = await auth_domain.create_session_token(user.id)
|
|
214
|
+
return session_token
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
async def totp_verify(temp_token: str, totp_code: str) -> Optional[str]:
|
|
218
|
+
"""常规 TOTP 验证 / Verify TOTP for regular login.
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
session token string on success.
|
|
222
|
+
"""
|
|
223
|
+
user_id = await redis_store.get_temp_token(temp_token)
|
|
224
|
+
if user_id is None:
|
|
225
|
+
raise HTTPException(
|
|
226
|
+
status_code=http.HTTPStatus.UNAUTHORIZED.value,
|
|
227
|
+
error_code=OpsbffsrvAuthErrorCode.TEMP_TOKEN_INVALID,
|
|
228
|
+
msg="Temp token invalid or expired",
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
# 暴力破解防护:超过上限直接失效 token
|
|
232
|
+
await _enforce_totp_attempt_limit(temp_token)
|
|
233
|
+
|
|
234
|
+
user = await get_admin_user_by_id(user_id)
|
|
235
|
+
if user is None:
|
|
236
|
+
raise HTTPException(
|
|
237
|
+
status_code=http.HTTPStatus.UNAUTHORIZED.value,
|
|
238
|
+
error_code=OpsbffsrvAuthErrorCode.TEMP_TOKEN_INVALID,
|
|
239
|
+
msg="User not found",
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
if not user.totp_secret_encrypted:
|
|
243
|
+
raise HTTPException(
|
|
244
|
+
status_code=http.HTTPStatus.BAD_REQUEST.value,
|
|
245
|
+
error_code=OpsbffsrvAuthErrorCode.TOTP_INVALID,
|
|
246
|
+
msg="TOTP not configured",
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
secret = totp_service.decrypt_secret(user.totp_secret_encrypted)
|
|
250
|
+
if not totp_service.verify_totp(secret, totp_code):
|
|
251
|
+
await _record_totp_failure(temp_token)
|
|
252
|
+
raise HTTPException(
|
|
253
|
+
status_code=http.HTTPStatus.BAD_REQUEST.value,
|
|
254
|
+
error_code=OpsbffsrvAuthErrorCode.TOTP_INVALID,
|
|
255
|
+
msg="Invalid TOTP code",
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
# 验证成功
|
|
259
|
+
await redis_store.delete_temp_token(temp_token)
|
|
260
|
+
await redis_store.reset_totp_fail(temp_token)
|
|
261
|
+
session_token = await auth_domain.create_session_token(user.id)
|
|
262
|
+
return session_token
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
async def logout(token: Optional[str]) -> None:
|
|
266
|
+
"""登出 / Logout: revoke the session token in Redis (idempotent)."""
|
|
267
|
+
if token:
|
|
268
|
+
await auth_domain.revoke_session(token)
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
async def check_session(token: str) -> Optional[SessionResponse]:
|
|
272
|
+
"""检查 session 有效性 / Check if session token is valid.
|
|
273
|
+
|
|
274
|
+
Returns:
|
|
275
|
+
SessionResponse if valid, None if invalid.
|
|
276
|
+
"""
|
|
277
|
+
user_id = await auth_domain.verify_session(token)
|
|
278
|
+
if user_id is None:
|
|
279
|
+
return None
|
|
280
|
+
|
|
281
|
+
user = await get_admin_user_by_id(user_id)
|
|
282
|
+
if user is None:
|
|
283
|
+
return None
|
|
284
|
+
|
|
285
|
+
return SessionResponse(authenticated=True, username=user.username)
|
|
File without changes
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Caddy 访问日志应用服务
|
|
3
|
+
协调访问日志接收、查询和分析的业务流程
|
|
4
|
+
"""
|
|
5
|
+
from typing import (
|
|
6
|
+
Any,
|
|
7
|
+
Dict,
|
|
8
|
+
List,
|
|
9
|
+
Optional,
|
|
10
|
+
)
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
from linglong_web.utils import logger
|
|
13
|
+
from cancan_microstack.public.schemas.caddy import CaddyAccessLog, AccessLogQuery
|
|
14
|
+
from cancan_microstack.services.opsbffsrv.domain.caddy.access_log_analysis import AccessLogAnalysisDomain
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class AccessLogAnalysisApp:
|
|
18
|
+
"""访问日志应用服务"""
|
|
19
|
+
|
|
20
|
+
def __init__(self):
|
|
21
|
+
self.domain = AccessLogAnalysisDomain()
|
|
22
|
+
|
|
23
|
+
async def ingest_single_log(self, log_data: Dict[str, Any]) -> Dict[str, Any]:
|
|
24
|
+
"""
|
|
25
|
+
接收单条访问日志
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
log_data: 日志数据字典
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
结果字典
|
|
32
|
+
"""
|
|
33
|
+
logger.debug("Ingesting single access log")
|
|
34
|
+
|
|
35
|
+
try:
|
|
36
|
+
log_entry = await self.domain.ingest_log_entry(log_data)
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
"status": "success",
|
|
40
|
+
"log": log_entry
|
|
41
|
+
}
|
|
42
|
+
except ValueError as e:
|
|
43
|
+
logger.warning(f"Log ingestion failed: {e}")
|
|
44
|
+
return {
|
|
45
|
+
"status": "error",
|
|
46
|
+
"error": str(e)
|
|
47
|
+
}
|
|
48
|
+
except Exception as e:
|
|
49
|
+
logger.error(f"Error ingesting log: {e}", exc_info=True)
|
|
50
|
+
return {
|
|
51
|
+
"status": "error",
|
|
52
|
+
"error": "Internal server error"
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async def ingest_batch_logs(self, log_lines: List[str]) -> Dict[str, Any]:
|
|
56
|
+
"""
|
|
57
|
+
批量接收访问日志
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
log_lines: JSON 格式的日志行列表
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
结果字典
|
|
64
|
+
"""
|
|
65
|
+
logger.info(f"Ingesting batch of {len(log_lines)} logs")
|
|
66
|
+
|
|
67
|
+
try:
|
|
68
|
+
ingested_count = await self.domain.ingest_log_batch(log_lines)
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
"status": "success",
|
|
72
|
+
"message": f"Successfully ingested {ingested_count} logs",
|
|
73
|
+
"count": ingested_count
|
|
74
|
+
}
|
|
75
|
+
except Exception as e:
|
|
76
|
+
logger.error(f"Error ingesting batch logs: {e}", exc_info=True)
|
|
77
|
+
return {
|
|
78
|
+
"status": "error",
|
|
79
|
+
"error": str(e)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async def search_logs(self, query: AccessLogQuery) -> List[CaddyAccessLog]:
|
|
83
|
+
"""
|
|
84
|
+
搜索访问日志
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
query: 查询参数
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
访问日志列表
|
|
91
|
+
"""
|
|
92
|
+
logger.info("Searching access logs")
|
|
93
|
+
|
|
94
|
+
try:
|
|
95
|
+
return await self.domain.search_logs(query)
|
|
96
|
+
except ValueError as e:
|
|
97
|
+
logger.warning(f"Invalid query parameters: {e}")
|
|
98
|
+
return []
|
|
99
|
+
except Exception as e:
|
|
100
|
+
logger.error(f"Error searching logs: {e}", exc_info=True)
|
|
101
|
+
return []
|
|
102
|
+
|
|
103
|
+
async def get_log_details(self, log_id: int) -> Optional[CaddyAccessLog]:
|
|
104
|
+
"""
|
|
105
|
+
获取访问日志详情
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
log_id: 日志 ID
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
访问日志对象或 None
|
|
112
|
+
"""
|
|
113
|
+
return await self.domain.get_log_details(log_id)
|
|
114
|
+
|
|
115
|
+
async def get_log_by_request_id(self, request_id: str) -> Optional[CaddyAccessLog]:
|
|
116
|
+
"""
|
|
117
|
+
根据请求 ID 获取访问日志
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
request_id: 请求 ID
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
访问日志对象或 None
|
|
124
|
+
"""
|
|
125
|
+
return await self.domain.get_log_by_request_id(request_id)
|
|
126
|
+
|
|
127
|
+
async def get_logs_for_ip(self, client_ip: str, limit: int = 100) -> List[CaddyAccessLog]:
|
|
128
|
+
"""
|
|
129
|
+
获取指定 IP 的访问日志
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
client_ip: 客户端 IP
|
|
133
|
+
limit: 返回数量限制
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
访问日志列表
|
|
137
|
+
"""
|
|
138
|
+
return await self.domain.get_logs_for_ip(client_ip, limit)
|
|
139
|
+
|
|
140
|
+
async def get_logs_for_service(self, service: str, limit: int = 100) -> List[CaddyAccessLog]:
|
|
141
|
+
"""
|
|
142
|
+
获取指定服务的访问日志
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
service: 服务名称
|
|
146
|
+
limit: 返回数量限制
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
访问日志列表
|
|
150
|
+
"""
|
|
151
|
+
return await self.domain.get_logs_for_service(service, limit)
|
|
152
|
+
|
|
153
|
+
async def get_security_events(self, event_type: str = 'waf_blocked', limit: int = 100) -> Dict[str, Any]:
|
|
154
|
+
"""
|
|
155
|
+
获取安全事件日志
|
|
156
|
+
|
|
157
|
+
Args:
|
|
158
|
+
event_type: 事件类型(waf_blocked/rate_limited)
|
|
159
|
+
limit: 返回数量限制
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
安全事件数据
|
|
163
|
+
"""
|
|
164
|
+
logger.info(f"Fetching security events: {event_type}")
|
|
165
|
+
|
|
166
|
+
try:
|
|
167
|
+
events = await self.domain.get_security_events(event_type, limit)
|
|
168
|
+
|
|
169
|
+
return {
|
|
170
|
+
"status": "success",
|
|
171
|
+
"event_type": event_type,
|
|
172
|
+
"count": len(events),
|
|
173
|
+
"data": events
|
|
174
|
+
}
|
|
175
|
+
except ValueError as e:
|
|
176
|
+
return {
|
|
177
|
+
"status": "error",
|
|
178
|
+
"error": str(e)
|
|
179
|
+
}
|
|
180
|
+
except Exception as e:
|
|
181
|
+
logger.error(f"Error fetching security events: {e}", exc_info=True)
|
|
182
|
+
return {
|
|
183
|
+
"status": "error",
|
|
184
|
+
"error": str(e)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async def analyze_geographic_distribution(
|
|
188
|
+
self,
|
|
189
|
+
start_time: Optional[datetime] = None,
|
|
190
|
+
end_time: Optional[datetime] = None
|
|
191
|
+
) -> Dict[str, Any]:
|
|
192
|
+
"""
|
|
193
|
+
分析访问的地理分布
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
start_time: 开始时间
|
|
197
|
+
end_time: 结束时间
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
地理分布数据
|
|
201
|
+
"""
|
|
202
|
+
logger.info("Analyzing geographic distribution")
|
|
203
|
+
|
|
204
|
+
try:
|
|
205
|
+
distribution = await self.domain.analyze_geographic_distribution(start_time, end_time)
|
|
206
|
+
|
|
207
|
+
return {
|
|
208
|
+
"status": "success",
|
|
209
|
+
"data": distribution
|
|
210
|
+
}
|
|
211
|
+
except Exception as e:
|
|
212
|
+
logger.error(f"Error analyzing geographic distribution: {e}", exc_info=True)
|
|
213
|
+
return {
|
|
214
|
+
"status": "error",
|
|
215
|
+
"error": str(e)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
async def analyze_status_code_distribution(
|
|
219
|
+
self,
|
|
220
|
+
start_time: Optional[datetime] = None,
|
|
221
|
+
end_time: Optional[datetime] = None
|
|
222
|
+
) -> Dict[str, Any]:
|
|
223
|
+
"""
|
|
224
|
+
分析 HTTP 状态码分布
|
|
225
|
+
|
|
226
|
+
Args:
|
|
227
|
+
start_time: 开始时间
|
|
228
|
+
end_time: 结束时间
|
|
229
|
+
|
|
230
|
+
Returns:
|
|
231
|
+
状态码分布数据
|
|
232
|
+
"""
|
|
233
|
+
logger.info("Analyzing status code distribution")
|
|
234
|
+
|
|
235
|
+
try:
|
|
236
|
+
distribution = await self.domain.analyze_status_code_distribution(start_time, end_time)
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
"status": "success",
|
|
240
|
+
"data": distribution
|
|
241
|
+
}
|
|
242
|
+
except Exception as e:
|
|
243
|
+
logger.error(f"Error analyzing status code distribution: {e}", exc_info=True)
|
|
244
|
+
return {
|
|
245
|
+
"status": "error",
|
|
246
|
+
"error": str(e)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
async def detect_suspicious_ips(
|
|
250
|
+
self,
|
|
251
|
+
time_window: int = 60,
|
|
252
|
+
request_threshold: int = 1000
|
|
253
|
+
) -> Dict[str, Any]:
|
|
254
|
+
"""
|
|
255
|
+
检测可疑 IP(高频访问)
|
|
256
|
+
|
|
257
|
+
Args:
|
|
258
|
+
time_window: 时间窗口(分钟)
|
|
259
|
+
request_threshold: 请求数阈值
|
|
260
|
+
|
|
261
|
+
Returns:
|
|
262
|
+
可疑 IP 数据
|
|
263
|
+
"""
|
|
264
|
+
logger.info(f"Detecting suspicious IPs: window={time_window}min, threshold={request_threshold}")
|
|
265
|
+
|
|
266
|
+
try:
|
|
267
|
+
suspicious_ips = await self.domain.detect_suspicious_ips(time_window, request_threshold)
|
|
268
|
+
|
|
269
|
+
return {
|
|
270
|
+
"status": "success",
|
|
271
|
+
"count": len(suspicious_ips),
|
|
272
|
+
"data": suspicious_ips
|
|
273
|
+
}
|
|
274
|
+
except Exception as e:
|
|
275
|
+
logger.error(f"Error detecting suspicious IPs: {e}", exc_info=True)
|
|
276
|
+
return {
|
|
277
|
+
"status": "error",
|
|
278
|
+
"error": str(e)
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
async def analyze_error_patterns(self, hours: int = 24) -> Dict[str, Any]:
|
|
282
|
+
"""
|
|
283
|
+
分析错误模式
|
|
284
|
+
|
|
285
|
+
Args:
|
|
286
|
+
hours: 过去多少小时
|
|
287
|
+
|
|
288
|
+
Returns:
|
|
289
|
+
错误分析结果
|
|
290
|
+
"""
|
|
291
|
+
logger.info(f"Analyzing error patterns for the past {hours} hours")
|
|
292
|
+
|
|
293
|
+
try:
|
|
294
|
+
error_analysis = await self.domain.analyze_error_patterns(hours)
|
|
295
|
+
|
|
296
|
+
# domain 返回 ErrorPatternAnalysis (pydantic),需先 model_dump 才能展开
|
|
297
|
+
return {
|
|
298
|
+
"status": "success",
|
|
299
|
+
**error_analysis.model_dump()
|
|
300
|
+
}
|
|
301
|
+
except Exception as e:
|
|
302
|
+
logger.error(f"Error analyzing error patterns: {e}", exc_info=True)
|
|
303
|
+
return {
|
|
304
|
+
"status": "error",
|
|
305
|
+
"error": str(e)
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
async def cleanup_old_logs(self, days: int = 30) -> Dict[str, Any]:
|
|
309
|
+
"""
|
|
310
|
+
清理旧的访问日志
|
|
311
|
+
|
|
312
|
+
Args:
|
|
313
|
+
days: 保留天数
|
|
314
|
+
|
|
315
|
+
Returns:
|
|
316
|
+
清理结果
|
|
317
|
+
"""
|
|
318
|
+
logger.info(f"Cleaning up logs older than {days} days")
|
|
319
|
+
|
|
320
|
+
try:
|
|
321
|
+
deleted_count = await self.domain.cleanup_old_logs(days)
|
|
322
|
+
|
|
323
|
+
return {
|
|
324
|
+
"status": "success",
|
|
325
|
+
"message": f"Deleted {deleted_count} old access log records"
|
|
326
|
+
}
|
|
327
|
+
except Exception as e:
|
|
328
|
+
logger.error(f"Error cleaning up logs: {e}", exc_info=True)
|
|
329
|
+
return {
|
|
330
|
+
"status": "error",
|
|
331
|
+
"error": str(e)
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
async def get_log_count(self, filters: Optional[Dict[str, Any]] = None) -> int:
|
|
335
|
+
"""
|
|
336
|
+
获取日志数量
|
|
337
|
+
|
|
338
|
+
Args:
|
|
339
|
+
filters: 过滤条件
|
|
340
|
+
|
|
341
|
+
Returns:
|
|
342
|
+
日志数量
|
|
343
|
+
"""
|
|
344
|
+
return await self.domain.get_log_count(filters)
|