aegis-stack 0.2.0rc2__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.
- aegis/__init__.py +5 -0
- aegis/__main__.py +51 -0
- aegis/cli/__init__.py +6 -0
- aegis/cli/callbacks.py +114 -0
- aegis/cli/interactive.py +611 -0
- aegis/cli/utils.py +70 -0
- aegis/cli/validators.py +34 -0
- aegis/commands/__init__.py +6 -0
- aegis/commands/add.py +353 -0
- aegis/commands/add_service.py +332 -0
- aegis/commands/components.py +35 -0
- aegis/commands/init.py +370 -0
- aegis/commands/remove.py +227 -0
- aegis/commands/services.py +52 -0
- aegis/commands/update.py +252 -0
- aegis/commands/version.py +12 -0
- aegis/config/__init__.py +1 -0
- aegis/config/shared_files.py +136 -0
- aegis/core/CLAUDE.md +377 -0
- aegis/core/__init__.py +6 -0
- aegis/core/component_files.py +228 -0
- aegis/core/component_utils.py +220 -0
- aegis/core/components.py +127 -0
- aegis/core/copier_manager.py +315 -0
- aegis/core/copier_updater.py +475 -0
- aegis/core/dependency_resolver.py +119 -0
- aegis/core/manual_updater.py +554 -0
- aegis/core/post_gen_tasks.py +547 -0
- aegis/core/service_resolver.py +261 -0
- aegis/core/services.py +157 -0
- aegis/core/template_generator.py +266 -0
- aegis/core/version_compatibility.py +259 -0
- aegis/templates/CLAUDE.md +591 -0
- aegis/templates/cookiecutter-aegis-project/cookiecutter.json +39 -0
- aegis/templates/cookiecutter-aegis-project/hooks/post_gen_project.py +214 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/.dockerignore +71 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/.env.example.j2 +130 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/.gitignore +131 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/Dockerfile +53 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/Makefile +236 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/README.md.j2 +196 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/alembic/alembic.ini.j2 +111 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/alembic/env.py.j2 +91 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/alembic/script.py.mako +25 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/alembic/versions/001_initial_auth.py.j2 +51 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/__init__.py +5 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/__init__.py +6 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/ai.py.j2 +700 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/ai_rendering.py +361 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/auth.py.j2 +253 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/health.py.j2 +419 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/load_test.py.j2 +656 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/main.py.j2 +65 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/marko_terminal_renderer.py +489 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/tasks.py.j2 +328 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/{% if cookiecutter.include_scheduler == /"yes/" %}tasks.py{% endif %}" +340 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/__init__.py +0 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/__init__.py +0 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/ai/__init__.py +8 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/ai/router.py +329 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/auth/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/auth/router.py +64 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/deps.py +58 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/health.py +163 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/models.py.j2 +280 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/routing.py.j2 +32 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/scheduler.py.j2 +121 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/worker.py.j2 +478 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/hooks.py +144 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/main.py +31 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/middleware/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/middleware/cors.py +20 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/shutdown/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/shutdown/cleanup.py +14 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/startup/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/startup/component_health.py.j2 +418 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/startup/database_init.py.j2 +83 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/__init__.py +5 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/controls/__init__.py +27 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/controls/table.py +78 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/controls/text.py +142 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/__init__.py.j2 +47 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/ai_card.py +287 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/auth_card.py +198 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/base_card.py +256 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/card_factory.py +227 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/card_utils.py +333 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/database_card.py +420 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/fastapi_card.py +328 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/flet_card.py +267 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/redis_card.py +322 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/scheduler_card.py +352 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/services_card.py +233 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/worker_card.py +684 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/main.py.j2 +653 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/theme.py +48 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/scheduler/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/scheduler/main.py.j2 +156 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/CLAUDE.md.j2 +213 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/__init__.py +6 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/constants.py.j2 +30 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/pools.py +97 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/queues/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/queues/load_test.py +55 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/queues/media.py +49 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/queues/system.py +44 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/registry.py +139 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/tasks/__init__.py +120 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/tasks/load_tasks.py +507 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/tasks/simple_system_tasks.py +33 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/tasks/system_tasks.py +281 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/config.py.j2 +178 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/constants.py +58 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/db.py.j2 +176 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/log.py +92 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/security.py +62 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/entrypoints/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/entrypoints/webserver.py +40 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/entrypoints/{% if cookiecutter.include_scheduler == /"yes/" %}scheduler.py{% endif %}" +21 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/integrations/__init__.py +0 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/integrations/main.py +62 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/models/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/models/user.py +44 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/py.typed +0 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/__init__.py +8 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/config.py +130 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/conversation.py +213 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/health.py +96 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/models.py +229 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/providers.py +370 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/service.py +388 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/auth/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/auth/auth_service.py +41 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/auth/health.py +164 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/auth/user_service.py +83 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/backend/middleware_inspector.py.j2 +223 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/backend/models.py.j2 +70 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/backend/route_inspector.py.j2 +155 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/load_test.py +679 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/load_test_models.py +266 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/scheduler/__init__.py.j2 +21 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/scheduler/models.py.j2 +119 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/scheduler/scheduled_task_manager.py.j2 +273 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/scheduler/task_monitor.py.j2 +189 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/shared/__init__.py +15 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/shared/models.py +26 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/__init__.py +52 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/alerts.py +94 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/backup.py.j2 +119 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/health.py.j2 +1333 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/models.py +243 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/ui.py +52 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/assets/aegis-manifesto-dark.png +0 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/assets/aegis-manifesto-square-backup.png +0 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/assets/aegis-manifesto.png +0 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/.dockerignore +71 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/.env.example.j2 +64 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/.gitignore +131 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/Dockerfile +53 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/Makefile +211 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/README.md.j2 +172 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/docker-compose.yml.j2 +78 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/mkdocs.yml.j2 +62 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/pyproject.toml.j2 +120 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/uv.lock +1673 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docker-compose.yml.j2 +200 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/api.md +191 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/components/scheduler.md +0 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/components/scheduler.md.j2 +621 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/development.md +215 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/health.md +240 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/javascripts/mermaid-config.js +62 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/stylesheets/mermaid.css +95 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/mkdocs.yml.j2 +62 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/pyproject.toml.j2 +131 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/scripts/entrypoint.sh +87 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/scripts/entrypoint.sh.j2 +93 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/scripts/gen_docs.py +16 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/test_auth_endpoints.py.j2 +307 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/test_health_endpoints.py.j2 +262 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/test_scheduler_endpoints.py.j2 +214 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/test_worker_endpoints.py.j2 +165 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/cli/test_ai_rendering.py +427 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/cli/test_conversation_memory.py +465 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/components/test_scheduler.py +43 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/conftest.py.j2 +195 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/ai/__init__.py +1 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/ai/conftest.py +78 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/ai/test_health.py +157 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/ai/test_models.py +164 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/ai/test_service.py +198 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_auth_integration.py.j2 +528 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_component_integration.py.j2 +387 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_conversation_persistence.py +342 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_health_logic.py.j2 +663 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_load_test_models.py +619 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_load_test_service.py +603 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_middleware_inspector.py.j2 +248 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_scheduled_task_manager.py.j2 +292 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_system_service.py +98 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_worker_health_registration.py.j2 +257 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/test_core.py +49 -0
- aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/uv.lock +1673 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/.copier-answers.yml.jinja +21 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/.dockerignore +71 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/.env.example.jinja +130 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/.gitignore +131 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/Dockerfile +53 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/Makefile.jinja +236 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/README.md.jinja +196 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/alembic/alembic.ini.jinja +111 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/alembic/env.py.jinja +91 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/alembic/script.py.mako +25 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/alembic/versions/001_initial_auth.py.jinja +51 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/__init__.py.jinja +5 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/__init__.py.jinja +6 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/ai.py.jinja +700 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/ai_rendering.py +360 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/auth.py.jinja +253 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/health.py.jinja +419 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/load_test.py.jinja +656 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/main.py.jinja +65 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/marko_terminal_renderer.py +489 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/tasks.py.jinja +328 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/__init__.py +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/__init__.py +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/ai/__init__.py +8 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/ai/router.py +329 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/auth/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/auth/router.py +64 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/deps.py +58 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/health.py.jinja +163 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/models.py.jinja +280 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/routing.py.jinja +32 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/scheduler.py.jinja +121 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/worker.py.jinja +478 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/hooks.py +144 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/main.py +31 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/middleware/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/middleware/cors.py +20 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/shutdown/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/shutdown/cleanup.py +14 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/startup/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/startup/component_health.py.jinja +418 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/startup/database_init.py.jinja +83 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/__init__.py +5 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/controls/__init__.py +27 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/controls/table.py +78 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/controls/text.py +142 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/__init__.py.jinja +47 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/ai_card.py +287 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/auth_card.py +198 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/base_card.py +256 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/card_factory.py +227 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/card_utils.py +333 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/database_card.py +420 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/fastapi_card.py +328 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/flet_card.py +267 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/redis_card.py +322 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/scheduler_card.py +352 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/services_card.py +233 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/worker_card.py +684 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/main.py.jinja +653 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/theme.py +48 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/scheduler/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/scheduler/main.py.jinja +156 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/CLAUDE.md.jinja +213 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/__init__.py +6 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/constants.py.jinja +30 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/pools.py +97 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/queues/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/queues/load_test.py +55 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/queues/media.py +49 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/queues/system.py +44 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/registry.py +139 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/tasks/__init__.py +120 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/tasks/load_tasks.py +507 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/tasks/simple_system_tasks.py +33 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/tasks/system_tasks.py +281 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/core/config.py.jinja +178 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/core/constants.py +58 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/core/db.py.jinja +176 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/core/log.py +92 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/core/security.py +62 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/entrypoints/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/entrypoints/scheduler.py.jinja +21 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/entrypoints/webserver.py +39 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/integrations/__init__.py +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/integrations/main.py +61 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/models/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/models/user.py +44 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/py.typed +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/__init__.py +8 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/config.py +130 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/conversation.py +213 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/health.py +96 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/models.py +229 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/providers.py.jinja +370 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/service.py +387 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/auth/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/auth/auth_service.py +40 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/auth/health.py +162 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/auth/user_service.py +82 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/backend/middleware_inspector.py.jinja +223 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/backend/models.py.jinja +70 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/backend/route_inspector.py.jinja +155 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/load_test.py +678 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/load_test_models.py +265 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/scheduler/__init__.py.jinja +21 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/scheduler/models.py.jinja +119 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/scheduler/scheduled_task_manager.py.jinja +273 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/scheduler/task_monitor.py.jinja +189 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/shared/__init__.py +15 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/shared/models.py +26 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/__init__.py +52 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/alerts.py +94 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/backup.py.jinja +119 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/health.py.jinja +1333 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/models.py +243 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/ui.py +52 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57223!aegis-manifesto.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57224!aegis-manifesto-dark.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57225!aegis-manifesto-square-backup.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57533!aegis-manifesto.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57534!aegis-manifesto-dark.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57538!aegis-manifesto-square-backup.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57897!aegis-manifesto.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57898!aegis-manifesto-dark.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57904!aegis-manifesto-square-backup.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58315!aegis-manifesto.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58316!aegis-manifesto-dark.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58324!aegis-manifesto-square-backup.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58837!aegis-manifesto.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58838!aegis-manifesto-dark.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58849!aegis-manifesto-square-backup.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/aegis-manifesto-dark.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/aegis-manifesto-square-backup.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/assets/aegis-manifesto.png +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/clean-validation/.env.example.jinja +64 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/clean-validation/README.md.jinja +172 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/clean-validation/docker-compose.yml.jinja +78 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/clean-validation/mkdocs.yml.jinja +62 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/clean-validation/pyproject.toml.jinja +120 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/docker-compose.yml.jinja +200 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/docs/api.md.jinja +191 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/docs/components/scheduler.md +0 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/docs/components/scheduler.md.jinja +621 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/docs/development.md.jinja +215 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/docs/health.md.jinja +240 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/docs/javascripts/mermaid-config.js +62 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/docs/stylesheets/mermaid.css +95 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/mkdocs.yml.jinja +62 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/pyproject.toml.jinja +131 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/scripts/entrypoint.sh +87 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/scripts/entrypoint.sh.jinja +93 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/scripts/gen_docs.py +16 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/api/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/api/test_auth_endpoints.py.jinja +307 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/api/test_health_endpoints.py.jinja +262 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/api/test_scheduler_endpoints.py.jinja +214 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/api/test_worker_endpoints.py.jinja +165 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/cli/test_ai_rendering.py +427 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/cli/test_conversation_memory.py +465 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/components/test_scheduler.py +43 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/conftest.py.jinja +195 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/ai/__init__.py +1 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/ai/conftest.py +78 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/ai/test_health.py +157 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/ai/test_models.py +164 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/ai/test_service.py +198 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_auth_integration.py.jinja +528 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_component_integration.py.jinja +387 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_conversation_persistence.py +342 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_health_logic.py.jinja +663 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_load_test_models.py +619 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_load_test_service.py +603 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_middleware_inspector.py.jinja +248 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_scheduled_task_manager.py.jinja +292 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_system_service.py +98 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_worker_health_registration.py.jinja +257 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/tests/test_core.py +49 -0
- aegis/templates/copier-aegis-project/{{ project_slug }}/uv.lock +1673 -0
- aegis_stack-0.2.0rc2.dist-info/METADATA +165 -0
- aegis_stack-0.2.0rc2.dist-info/RECORD +392 -0
- aegis_stack-0.2.0rc2.dist-info/WHEEL +4 -0
- aegis_stack-0.2.0rc2.dist-info/entry_points.txt +3 -0
- aegis_stack-0.2.0rc2.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
"""
|
|
2
|
+
FastAPI Middleware Introspection Service
|
|
3
|
+
|
|
4
|
+
Utility functions for extracting comprehensive middleware information from FastAPI
|
|
5
|
+
applications for security auditing and dashboard display.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
import fastapi
|
|
11
|
+
|
|
12
|
+
__all__ = ["MiddlewareMetadata", "FastAPIMiddlewareInspector"]
|
|
13
|
+
|
|
14
|
+
from app.core.log import logger
|
|
15
|
+
from app.services.backend.models import MiddlewareInfo, MiddlewareMetadata
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class FastAPIMiddlewareInspector:
|
|
19
|
+
"""Utility class for extracting middleware information from FastAPI apps."""
|
|
20
|
+
|
|
21
|
+
def __init__(self, app: fastapi.FastAPI) -> None:
|
|
22
|
+
"""Initialize with FastAPI application instance."""
|
|
23
|
+
self.app = app
|
|
24
|
+
|
|
25
|
+
def get_middleware_metadata(self) -> MiddlewareMetadata:
|
|
26
|
+
"""
|
|
27
|
+
Extract comprehensive middleware information from FastAPI app.
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
MiddlewareMetadata containing middleware stack, security info, and stats
|
|
31
|
+
"""
|
|
32
|
+
try:
|
|
33
|
+
middleware_stack = []
|
|
34
|
+
|
|
35
|
+
# Inspect middleware stack using FastAPI's user_middleware attribute
|
|
36
|
+
if hasattr(self.app, 'user_middleware'):
|
|
37
|
+
for idx, middleware_item in enumerate(self.app.user_middleware):
|
|
38
|
+
middleware_info = (
|
|
39
|
+
self._extract_middleware_info_from_middleware_item(
|
|
40
|
+
middleware_item, idx
|
|
41
|
+
)
|
|
42
|
+
)
|
|
43
|
+
if middleware_info:
|
|
44
|
+
middleware_stack.append(middleware_info)
|
|
45
|
+
|
|
46
|
+
# Calculate statistics
|
|
47
|
+
total_middleware = len(middleware_stack)
|
|
48
|
+
security_middleware = self._identify_security_middleware(middleware_stack)
|
|
49
|
+
security_count = len(security_middleware)
|
|
50
|
+
|
|
51
|
+
return MiddlewareMetadata(
|
|
52
|
+
middleware_stack=middleware_stack,
|
|
53
|
+
total_middleware=total_middleware,
|
|
54
|
+
security_middleware=security_middleware,
|
|
55
|
+
security_count=security_count,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
except Exception as e:
|
|
59
|
+
logger.error(f"Failed to extract middleware metadata: {e}")
|
|
60
|
+
return self._get_fallback_metadata(str(e))
|
|
61
|
+
|
|
62
|
+
def _extract_middleware_info_from_middleware_item(
|
|
63
|
+
self, middleware_item: Any, order: int
|
|
64
|
+
) -> MiddlewareInfo | None:
|
|
65
|
+
"""Extract information from a FastAPI Middleware item."""
|
|
66
|
+
try:
|
|
67
|
+
# FastAPI stores middleware as Middleware(cls, **kwargs) objects
|
|
68
|
+
middleware_cls = middleware_item.cls
|
|
69
|
+
middleware_kwargs = (
|
|
70
|
+
middleware_item.kwargs
|
|
71
|
+
if hasattr(middleware_item, 'kwargs')
|
|
72
|
+
else {}
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
middleware_type = middleware_cls.__name__
|
|
76
|
+
middleware_module = middleware_cls.__module__
|
|
77
|
+
|
|
78
|
+
# Use kwargs as configuration
|
|
79
|
+
config = dict(middleware_kwargs)
|
|
80
|
+
|
|
81
|
+
# Determine if this is security-related middleware
|
|
82
|
+
is_security = self._is_security_middleware(
|
|
83
|
+
middleware_type, middleware_module
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
return MiddlewareInfo(
|
|
87
|
+
type=middleware_type,
|
|
88
|
+
module=middleware_module,
|
|
89
|
+
order=order,
|
|
90
|
+
config=config,
|
|
91
|
+
is_security=is_security,
|
|
92
|
+
)
|
|
93
|
+
except Exception as e:
|
|
94
|
+
logger.warning(
|
|
95
|
+
f"Failed to extract middleware info for {middleware_item}: {e}"
|
|
96
|
+
)
|
|
97
|
+
return None
|
|
98
|
+
|
|
99
|
+
def _extract_middleware_info(
|
|
100
|
+
self, middleware: Any, order: int
|
|
101
|
+
) -> MiddlewareInfo | None:
|
|
102
|
+
"""Extract information from a single middleware instance (legacy method).
|
|
103
|
+
|
|
104
|
+
This method is kept for backward compatibility and is still used in tests.
|
|
105
|
+
For production use, _extract_middleware_info_from_middleware_item is preferred.
|
|
106
|
+
"""
|
|
107
|
+
try:
|
|
108
|
+
middleware_type = type(middleware).__name__
|
|
109
|
+
middleware_module = type(middleware).__module__
|
|
110
|
+
|
|
111
|
+
# Extract middleware-specific configuration
|
|
112
|
+
config = self._extract_middleware_config(middleware)
|
|
113
|
+
|
|
114
|
+
# Determine if this is security-related middleware
|
|
115
|
+
is_security = self._is_security_middleware(
|
|
116
|
+
middleware_type, middleware_module
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
return MiddlewareInfo(
|
|
120
|
+
type=middleware_type,
|
|
121
|
+
module=middleware_module,
|
|
122
|
+
order=order,
|
|
123
|
+
config=config,
|
|
124
|
+
is_security=is_security,
|
|
125
|
+
)
|
|
126
|
+
except Exception as e:
|
|
127
|
+
logger.warning(
|
|
128
|
+
f"Failed to extract middleware info for {type(middleware)}: {e}"
|
|
129
|
+
)
|
|
130
|
+
return None
|
|
131
|
+
|
|
132
|
+
def _extract_middleware_config(self, middleware: Any) -> dict[str, Any]:
|
|
133
|
+
"""Extract configuration from middleware where possible."""
|
|
134
|
+
config = {}
|
|
135
|
+
|
|
136
|
+
try:
|
|
137
|
+
# CORS Middleware configuration
|
|
138
|
+
if hasattr(middleware, 'allow_origins'):
|
|
139
|
+
config.update({
|
|
140
|
+
"allow_origins": getattr(middleware, 'allow_origins', []),
|
|
141
|
+
"allow_methods": getattr(middleware, 'allow_methods', []),
|
|
142
|
+
"allow_headers": getattr(middleware, 'allow_headers', []),
|
|
143
|
+
"allow_credentials": getattr(
|
|
144
|
+
middleware, 'allow_credentials', False
|
|
145
|
+
),
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
# JWT/Auth middleware configuration
|
|
149
|
+
elif hasattr(middleware, 'algorithm'):
|
|
150
|
+
config.update({
|
|
151
|
+
"algorithm": getattr(middleware, 'algorithm', 'Unknown'),
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
# Rate limiting middleware
|
|
155
|
+
elif hasattr(middleware, 'calls') and hasattr(middleware, 'period'):
|
|
156
|
+
config.update({
|
|
157
|
+
"calls": getattr(middleware, 'calls', 0),
|
|
158
|
+
"period": getattr(middleware, 'period', 0),
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
# Security headers middleware
|
|
162
|
+
elif hasattr(middleware, 'csp'):
|
|
163
|
+
config.update({
|
|
164
|
+
"csp": getattr(middleware, 'csp', {}),
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
except Exception as e:
|
|
168
|
+
logger.debug(f"Could not extract config from {type(middleware)}: {e}")
|
|
169
|
+
|
|
170
|
+
return config
|
|
171
|
+
|
|
172
|
+
def _is_security_middleware(
|
|
173
|
+
self, middleware_type: str, middleware_module: str
|
|
174
|
+
) -> bool:
|
|
175
|
+
"""Determine if middleware is security-related based on type and module."""
|
|
176
|
+
security_keywords = [
|
|
177
|
+
'cors', 'auth', 'jwt', 'rate', 'security', 'limit', 'csrf', 'xss'
|
|
178
|
+
]
|
|
179
|
+
security_modules = ['starlette.middleware.cors', 'fastapi.middleware.cors']
|
|
180
|
+
|
|
181
|
+
# Check type name for security keywords
|
|
182
|
+
type_lower = middleware_type.lower()
|
|
183
|
+
if any(keyword in type_lower for keyword in security_keywords):
|
|
184
|
+
return True
|
|
185
|
+
|
|
186
|
+
# Check module name for security patterns
|
|
187
|
+
module_lower = middleware_module.lower()
|
|
188
|
+
if any(keyword in module_lower for keyword in security_keywords):
|
|
189
|
+
return True
|
|
190
|
+
|
|
191
|
+
# Check for known security middleware modules
|
|
192
|
+
if middleware_module in security_modules:
|
|
193
|
+
return True
|
|
194
|
+
|
|
195
|
+
return False
|
|
196
|
+
|
|
197
|
+
def _identify_security_middleware(self, stack: list[MiddlewareInfo]) -> list[str]:
|
|
198
|
+
"""Identify security-related middleware types."""
|
|
199
|
+
return [mw.type for mw in stack if mw.is_security]
|
|
200
|
+
|
|
201
|
+
def _get_fallback_metadata(self, error: str) -> MiddlewareMetadata:
|
|
202
|
+
"""Return fallback metadata when introspection fails."""
|
|
203
|
+
return MiddlewareMetadata(
|
|
204
|
+
middleware_stack=[],
|
|
205
|
+
total_middleware=0,
|
|
206
|
+
security_middleware=[],
|
|
207
|
+
security_count=0,
|
|
208
|
+
error=error,
|
|
209
|
+
fallback=True,
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def get_fastapi_middleware_metadata(app: fastapi.FastAPI) -> MiddlewareMetadata:
|
|
214
|
+
"""
|
|
215
|
+
Convenience function to get middleware metadata from FastAPI app.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
app: FastAPI application instance
|
|
219
|
+
Returns:
|
|
220
|
+
MiddlewareMetadata containing middleware stack and security information
|
|
221
|
+
"""
|
|
222
|
+
inspector = FastAPIMiddlewareInspector(app)
|
|
223
|
+
return inspector.get_middleware_metadata()
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Backend service models using Pydantic for type safety and validation.
|
|
3
|
+
|
|
4
|
+
These models define the structure for route introspection data and middleware
|
|
5
|
+
detection data for backend service metadata.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from pydantic import BaseModel
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class RouteInfo(BaseModel):
|
|
14
|
+
"""Information about a single FastAPI route."""
|
|
15
|
+
|
|
16
|
+
path: str
|
|
17
|
+
methods: list[str]
|
|
18
|
+
name: str | None = None
|
|
19
|
+
summary: str | None = None
|
|
20
|
+
description: str | None = None
|
|
21
|
+
tags: list[str] = []
|
|
22
|
+
deprecated: bool = False
|
|
23
|
+
path_params: list[str] = []
|
|
24
|
+
dependencies: int = 0
|
|
25
|
+
response_model: str | None = None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class RouteMetadata(BaseModel):
|
|
29
|
+
"""Comprehensive route metadata for FastAPI application."""
|
|
30
|
+
|
|
31
|
+
routes: list[RouteInfo]
|
|
32
|
+
total_routes: int
|
|
33
|
+
total_endpoints: int
|
|
34
|
+
method_counts: dict[str, int] = {}
|
|
35
|
+
route_groups: dict[str, int] = {}
|
|
36
|
+
tag_groups: dict[str, int] = {}
|
|
37
|
+
has_docs: bool = False
|
|
38
|
+
has_health: bool = False
|
|
39
|
+
deprecated_count: int = 0
|
|
40
|
+
error: str | None = None
|
|
41
|
+
fallback: bool = False
|
|
42
|
+
|
|
43
|
+
def model_dump_for_metadata(self) -> dict[str, Any]:
|
|
44
|
+
"""Convert to dict format suitable for ComponentStatus metadata."""
|
|
45
|
+
return self.model_dump(mode='json')
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class MiddlewareInfo(BaseModel):
|
|
49
|
+
"""Information about a single middleware in the stack."""
|
|
50
|
+
|
|
51
|
+
type: str
|
|
52
|
+
module: str
|
|
53
|
+
order: int
|
|
54
|
+
config: dict[str, Any] = {}
|
|
55
|
+
is_security: bool = False
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class MiddlewareMetadata(BaseModel):
|
|
59
|
+
"""Comprehensive middleware metadata for FastAPI application."""
|
|
60
|
+
|
|
61
|
+
middleware_stack: list[MiddlewareInfo]
|
|
62
|
+
total_middleware: int
|
|
63
|
+
security_middleware: list[str] = []
|
|
64
|
+
security_count: int = 0
|
|
65
|
+
error: str | None = None
|
|
66
|
+
fallback: bool = False
|
|
67
|
+
|
|
68
|
+
def model_dump_for_metadata(self) -> dict[str, Any]:
|
|
69
|
+
"""Convert to dict format suitable for ComponentStatus metadata."""
|
|
70
|
+
return self.model_dump(mode='json')
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"""
|
|
2
|
+
FastAPI Route Introspection Service
|
|
3
|
+
|
|
4
|
+
Utility functions for extracting comprehensive route information from FastAPI
|
|
5
|
+
applications for health monitoring and dashboard display.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import fastapi
|
|
9
|
+
from fastapi.routing import APIRoute
|
|
10
|
+
|
|
11
|
+
__all__ = ["RouteMetadata", "FastAPIRouteInspector"]
|
|
12
|
+
|
|
13
|
+
from app.core.log import logger
|
|
14
|
+
from app.services.backend.models import RouteInfo, RouteMetadata
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class FastAPIRouteInspector:
|
|
18
|
+
"""Utility class for extracting route information from FastAPI apps."""
|
|
19
|
+
|
|
20
|
+
def __init__(self, app: fastapi.FastAPI) -> None:
|
|
21
|
+
"""Initialize with FastAPI application instance."""
|
|
22
|
+
self.app = app
|
|
23
|
+
|
|
24
|
+
def get_routes_metadata(self) -> RouteMetadata:
|
|
25
|
+
"""
|
|
26
|
+
Extract comprehensive route information from FastAPI app.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
RouteMetadata containing route data, statistics, and groupings
|
|
30
|
+
"""
|
|
31
|
+
try:
|
|
32
|
+
routes_data = []
|
|
33
|
+
|
|
34
|
+
# Extract route information
|
|
35
|
+
for route in self.app.routes:
|
|
36
|
+
if isinstance(route, APIRoute):
|
|
37
|
+
route_info = RouteInfo(
|
|
38
|
+
path=route.path,
|
|
39
|
+
methods=sorted(list(route.methods)),
|
|
40
|
+
name=route.name,
|
|
41
|
+
summary=getattr(route, "summary", "") or None,
|
|
42
|
+
description=getattr(route, "description", "") or None,
|
|
43
|
+
tags=getattr(route, "tags", []) or [],
|
|
44
|
+
deprecated=bool(getattr(route, "deprecated", False)),
|
|
45
|
+
path_params=self._extract_path_params(route.path),
|
|
46
|
+
dependencies=(
|
|
47
|
+
len(route.dependencies) if route.dependencies else 0
|
|
48
|
+
),
|
|
49
|
+
response_model=(
|
|
50
|
+
str(route.response_model) if route.response_model else None
|
|
51
|
+
),
|
|
52
|
+
)
|
|
53
|
+
routes_data.append(route_info)
|
|
54
|
+
|
|
55
|
+
# Calculate statistics
|
|
56
|
+
total_routes = len(routes_data)
|
|
57
|
+
total_endpoints = sum(len(r.methods) for r in routes_data)
|
|
58
|
+
method_counts = self._count_methods(routes_data)
|
|
59
|
+
route_groups = self._group_routes_by_prefix(routes_data)
|
|
60
|
+
tag_groups = self._group_routes_by_tags(routes_data)
|
|
61
|
+
|
|
62
|
+
return RouteMetadata(
|
|
63
|
+
routes=routes_data,
|
|
64
|
+
total_routes=total_routes,
|
|
65
|
+
total_endpoints=total_endpoints,
|
|
66
|
+
method_counts=method_counts,
|
|
67
|
+
route_groups=route_groups,
|
|
68
|
+
tag_groups=tag_groups,
|
|
69
|
+
has_docs=any(
|
|
70
|
+
"/docs" in r.path or "/redoc" in r.path for r in routes_data
|
|
71
|
+
),
|
|
72
|
+
has_health=any("/health" in r.path for r in routes_data),
|
|
73
|
+
deprecated_count=sum(1 for r in routes_data if r.deprecated),
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
except Exception as e:
|
|
77
|
+
logger.error(f"Failed to extract route metadata: {e}")
|
|
78
|
+
return self._get_fallback_metadata(str(e))
|
|
79
|
+
|
|
80
|
+
def _extract_path_params(self, path: str) -> list[str]:
|
|
81
|
+
"""Extract path parameters from route path."""
|
|
82
|
+
import re
|
|
83
|
+
return re.findall(r'\{([^}]+)\}', path)
|
|
84
|
+
|
|
85
|
+
def _count_methods(self, routes: list[RouteInfo]) -> dict[str, int]:
|
|
86
|
+
"""Count routes by HTTP method."""
|
|
87
|
+
method_counts: dict[str, int] = {}
|
|
88
|
+
for route in routes:
|
|
89
|
+
for method in route.methods:
|
|
90
|
+
if method != "HEAD": # Skip HEAD methods as they're automatic
|
|
91
|
+
method_counts[method] = method_counts.get(method, 0) + 1
|
|
92
|
+
return method_counts
|
|
93
|
+
|
|
94
|
+
def _group_routes_by_prefix(self, routes: list[RouteInfo]) -> dict[str, int]:
|
|
95
|
+
"""Group routes by path prefix for organization."""
|
|
96
|
+
groups: dict[str, int] = {}
|
|
97
|
+
for route in routes:
|
|
98
|
+
path_parts = route.path.strip("/").split("/")
|
|
99
|
+
prefix = path_parts[0] if path_parts[0] else "root"
|
|
100
|
+
|
|
101
|
+
# Special handling for common patterns
|
|
102
|
+
if prefix in ["health", "docs", "redoc", "openapi.json"]:
|
|
103
|
+
groups[prefix] = groups.get(prefix, 0) + 1
|
|
104
|
+
elif prefix == "api" and len(path_parts) > 2:
|
|
105
|
+
# Skip API version prefix, group by actual resource
|
|
106
|
+
# /api/v1/users -> group as "users"
|
|
107
|
+
resource = path_parts[2]
|
|
108
|
+
groups[resource] = groups.get(resource, 0) + 1
|
|
109
|
+
elif prefix.startswith("api"):
|
|
110
|
+
# Fallback for other API patterns
|
|
111
|
+
groups[prefix] = groups.get(prefix, 0) + 1
|
|
112
|
+
else:
|
|
113
|
+
groups[prefix] = groups.get(prefix, 0) + 1
|
|
114
|
+
|
|
115
|
+
return groups
|
|
116
|
+
|
|
117
|
+
def _group_routes_by_tags(self, routes: list[RouteInfo]) -> dict[str, int]:
|
|
118
|
+
"""Group routes by OpenAPI tags."""
|
|
119
|
+
tag_counts: dict[str, int] = {}
|
|
120
|
+
for route in routes:
|
|
121
|
+
if not route.tags:
|
|
122
|
+
tag_counts["untagged"] = tag_counts.get("untagged", 0) + 1
|
|
123
|
+
else:
|
|
124
|
+
for tag in route.tags:
|
|
125
|
+
tag_counts[tag] = tag_counts.get(tag, 0) + 1
|
|
126
|
+
return tag_counts
|
|
127
|
+
|
|
128
|
+
def _get_fallback_metadata(self, error: str) -> RouteMetadata:
|
|
129
|
+
"""Return fallback metadata when introspection fails."""
|
|
130
|
+
return RouteMetadata(
|
|
131
|
+
routes=[],
|
|
132
|
+
total_routes=0,
|
|
133
|
+
total_endpoints=0,
|
|
134
|
+
method_counts={},
|
|
135
|
+
route_groups={},
|
|
136
|
+
tag_groups={},
|
|
137
|
+
has_docs=False,
|
|
138
|
+
has_health=False,
|
|
139
|
+
deprecated_count=0,
|
|
140
|
+
error=error,
|
|
141
|
+
fallback=True,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def get_fastapi_route_metadata(app: fastapi.FastAPI) -> RouteMetadata:
|
|
146
|
+
"""
|
|
147
|
+
Convenience function to get route metadata from FastAPI app.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
app: FastAPI application instance
|
|
151
|
+
Returns:
|
|
152
|
+
RouteMetadata containing route metadata and statistics
|
|
153
|
+
"""
|
|
154
|
+
inspector = FastAPIRouteInspector(app)
|
|
155
|
+
return inspector.get_routes_metadata()
|