aegis-stack 0.1.0__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.

Potentially problematic release.


This version of aegis-stack might be problematic. Click here for more details.

Files changed (103) hide show
  1. aegis/__init__.py +5 -0
  2. aegis/__main__.py +374 -0
  3. aegis/core/CLAUDE.md +365 -0
  4. aegis/core/__init__.py +6 -0
  5. aegis/core/components.py +115 -0
  6. aegis/core/dependency_resolver.py +119 -0
  7. aegis/core/template_generator.py +163 -0
  8. aegis/templates/CLAUDE.md +306 -0
  9. aegis/templates/cookiecutter-aegis-project/cookiecutter.json +27 -0
  10. aegis/templates/cookiecutter-aegis-project/hooks/post_gen_project.py +172 -0
  11. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/.dockerignore +71 -0
  12. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/.env.example.j2 +70 -0
  13. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/.gitignore +127 -0
  14. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/Dockerfile +53 -0
  15. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/Makefile +211 -0
  16. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/README.md.j2 +196 -0
  17. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/__init__.py +5 -0
  18. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/__init__.py +6 -0
  19. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/health.py +321 -0
  20. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/load_test.py +638 -0
  21. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/main.py +41 -0
  22. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/__init__.py +0 -0
  23. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/__init__.py +0 -0
  24. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/health.py +134 -0
  25. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/models.py.j2 +247 -0
  26. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/routing.py.j2 +14 -0
  27. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/tasks.py.j2 +596 -0
  28. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/hooks.py +133 -0
  29. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/main.py +16 -0
  30. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/middleware/__init__.py +1 -0
  31. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/middleware/cors.py +20 -0
  32. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/shutdown/__init__.py +1 -0
  33. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/shutdown/cleanup.py +14 -0
  34. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/startup/__init__.py +1 -0
  35. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/startup/component_health.py.j2 +190 -0
  36. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/__init__.py +0 -0
  37. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/core/__init__.py +1 -0
  38. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/core/theme.py +46 -0
  39. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/main.py +687 -0
  40. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/scheduler/__init__.py +1 -0
  41. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/scheduler/main.py +138 -0
  42. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/CLAUDE.md +213 -0
  43. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/__init__.py +6 -0
  44. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/constants.py.j2 +30 -0
  45. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/pools.py +78 -0
  46. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/queues/__init__.py +1 -0
  47. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/queues/load_test.py +48 -0
  48. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/queues/media.py +41 -0
  49. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/queues/system.py +36 -0
  50. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/registry.py +139 -0
  51. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/tasks/__init__.py +119 -0
  52. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/tasks/load_tasks.py +526 -0
  53. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/tasks/simple_system_tasks.py +32 -0
  54. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/tasks/system_tasks.py +279 -0
  55. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/config.py.j2 +119 -0
  56. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/constants.py +60 -0
  57. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/db.py +67 -0
  58. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/log.py +85 -0
  59. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/entrypoints/__init__.py +1 -0
  60. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/entrypoints/webserver.py +40 -0
  61. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/entrypoints/{% if cookiecutter.include_scheduler == /"yes/" %}scheduler.py{% endif %}" +21 -0
  62. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/integrations/__init__.py +0 -0
  63. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/integrations/main.py +61 -0
  64. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/py.typed +0 -0
  65. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/__init__.py +1 -0
  66. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/load_test.py +661 -0
  67. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/load_test_models.py +269 -0
  68. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/shared/__init__.py +15 -0
  69. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/shared/models.py +26 -0
  70. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/__init__.py +52 -0
  71. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/alerts.py +94 -0
  72. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/health.py.j2 +1105 -0
  73. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/models.py +169 -0
  74. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/ui.py +52 -0
  75. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docker-compose.yml.j2 +195 -0
  76. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/api.md +191 -0
  77. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/components/scheduler.md +414 -0
  78. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/development.md +215 -0
  79. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/health.md +240 -0
  80. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/javascripts/mermaid-config.js +62 -0
  81. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/stylesheets/mermaid.css +95 -0
  82. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/mkdocs.yml.j2 +62 -0
  83. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/pyproject.toml.j2 +156 -0
  84. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/scripts/entrypoint.sh +87 -0
  85. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/scripts/entrypoint.sh.j2 +104 -0
  86. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/scripts/gen_docs.py +16 -0
  87. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/__init__.py +1 -0
  88. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/test_health_endpoints.py.j2 +239 -0
  89. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/components/test_scheduler.py +76 -0
  90. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/conftest.py.j2 +81 -0
  91. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/__init__.py +1 -0
  92. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_component_integration.py.j2 +376 -0
  93. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_health_logic.py.j2 +633 -0
  94. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_load_test_models.py +665 -0
  95. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_load_test_service.py +602 -0
  96. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_system_service.py +96 -0
  97. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_worker_health_registration.py.j2 +224 -0
  98. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/test_core.py +50 -0
  99. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/uv.lock +1673 -0
  100. aegis_stack-0.1.0.dist-info/METADATA +114 -0
  101. aegis_stack-0.1.0.dist-info/RECORD +103 -0
  102. aegis_stack-0.1.0.dist-info/WHEEL +4 -0
  103. aegis_stack-0.1.0.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,133 @@
1
+ # app/components/backend/hooks.py
2
+ """
3
+ Backend-specific hook management system for drop-in extensibility.
4
+
5
+ This system automatically discovers and registers:
6
+ - Middleware from app/components/backend/middleware/
7
+ - Startup hooks from app/components/backend/startup/
8
+ - Shutdown hooks from app/components/backend/shutdown/
9
+
10
+ Just drop files in the appropriate folders - no central registration required.
11
+ """
12
+
13
+ from collections.abc import Callable
14
+ import importlib
15
+ import inspect
16
+ from pathlib import Path
17
+ from typing import Any
18
+
19
+ from fastapi import FastAPI
20
+
21
+ from app.core.log import logger
22
+
23
+
24
+ class BackendHooks:
25
+ """Backend-specific hook management system."""
26
+
27
+ def __init__(self) -> None:
28
+ self.startup_hooks: list[Callable[[], Any]] = []
29
+ self.shutdown_hooks: list[Callable[[], Any]] = []
30
+
31
+ def discover_and_register_middleware(self, app: FastAPI) -> None:
32
+ """
33
+ Auto-discover and register middleware. This must be called
34
+ before the application starts.
35
+ """
36
+ middleware_dir = Path(__file__).parent / "middleware"
37
+ if not middleware_dir.exists():
38
+ return
39
+
40
+ for middleware_file in middleware_dir.glob("*.py"):
41
+ if middleware_file.name.startswith("_"):
42
+ continue
43
+
44
+ module_name = f"app.components.backend.middleware.{middleware_file.stem}"
45
+ try:
46
+ module = importlib.import_module(module_name)
47
+ if hasattr(module, "register_middleware"):
48
+ logger.info(f"Registering middleware from {module_name}")
49
+ # Middleware registration is synchronous
50
+ module.register_middleware(app)
51
+ except Exception as e:
52
+ logger.error(f"Failed to load middleware {module_name}: {e}")
53
+
54
+ async def discover_lifespan_hooks(self) -> None:
55
+ """Discover startup and shutdown hooks for the lifespan event."""
56
+ await self._discover_startup_hooks()
57
+ await self._discover_shutdown_hooks()
58
+
59
+ async def _discover_startup_hooks(self) -> None:
60
+ """Auto-discover startup hooks from app/components/backend/startup/."""
61
+ startup_dir = Path(__file__).parent / "startup"
62
+ if not startup_dir.exists():
63
+ logger.info("No backend startup directory found")
64
+ return
65
+
66
+ for startup_file in startup_dir.glob("*.py"):
67
+ if startup_file.name.startswith("_"):
68
+ continue
69
+
70
+ module_name = f"app.components.backend.startup.{startup_file.stem}"
71
+ try:
72
+ module = importlib.import_module(module_name)
73
+
74
+ # Look for startup_hook function
75
+ if hasattr(module, "startup_hook"):
76
+ logger.info(f"Registered startup hook from {module_name}")
77
+ self.startup_hooks.append(module.startup_hook)
78
+
79
+ except Exception as e:
80
+ logger.error(f"Failed to load startup hook {module_name}: {e}")
81
+
82
+ async def _discover_shutdown_hooks(self) -> None:
83
+ """Auto-discover shutdown hooks from app/components/backend/shutdown/."""
84
+ shutdown_dir = Path(__file__).parent / "shutdown"
85
+ if not shutdown_dir.exists():
86
+ logger.info("No backend shutdown directory found")
87
+ return
88
+
89
+ for shutdown_file in shutdown_dir.glob("*.py"):
90
+ if shutdown_file.name.startswith("_"):
91
+ continue
92
+
93
+ module_name = f"app.components.backend.shutdown.{shutdown_file.stem}"
94
+ try:
95
+ module = importlib.import_module(module_name)
96
+
97
+ # Look for shutdown_hook function
98
+ if hasattr(module, "shutdown_hook"):
99
+ logger.info(f"Registered shutdown hook from {module_name}")
100
+ self.shutdown_hooks.append(module.shutdown_hook)
101
+
102
+ except Exception as e:
103
+ logger.error(f"Failed to load shutdown hook {module_name}: {e}")
104
+
105
+ async def execute_startup_hooks(self) -> None:
106
+ """Execute all discovered startup hooks."""
107
+ logger.info(f"Executing {len(self.startup_hooks)} backend startup hooks")
108
+ for hook in self.startup_hooks:
109
+ try:
110
+ if inspect.iscoroutinefunction(hook):
111
+ await hook()
112
+ else:
113
+ hook()
114
+ except Exception as e:
115
+ logger.error(f"Startup hook failed: {e}")
116
+ raise
117
+
118
+ async def execute_shutdown_hooks(self) -> None:
119
+ """Execute all discovered shutdown hooks in reverse order."""
120
+ logger.info(f"Executing {len(self.shutdown_hooks)} backend shutdown hooks")
121
+ for hook in reversed(self.shutdown_hooks):
122
+ try:
123
+ if inspect.iscoroutinefunction(hook):
124
+ await hook()
125
+ else:
126
+ hook()
127
+ except Exception as e:
128
+ logger.error(f"Shutdown hook failed: {e}")
129
+ # Continue with other shutdown hooks even if one fails
130
+
131
+
132
+ # Global backend hooks instance
133
+ backend_hooks = BackendHooks()
@@ -0,0 +1,16 @@
1
+ from fastapi import FastAPI
2
+
3
+ from app.components.backend.api.routing import include_routers
4
+ from app.components.backend.hooks import backend_hooks
5
+
6
+
7
+ def create_backend_app(app: FastAPI) -> FastAPI:
8
+ """Configure FastAPI app with all backend concerns"""
9
+
10
+ # Auto-discover and register middleware
11
+ backend_hooks.discover_and_register_middleware(app)
12
+
13
+ # Include all routes
14
+ include_routers(app)
15
+
16
+ return app
@@ -0,0 +1,20 @@
1
+ # app/components/backend/middleware/cors.py
2
+ """
3
+ Auto-discovered CORS middleware for development.
4
+
5
+ This middleware is automatically registered with FastAPI when the backend starts.
6
+ """
7
+
8
+ from fastapi import FastAPI
9
+ from fastapi.middleware.cors import CORSMiddleware
10
+
11
+
12
+ def register_middleware(app: FastAPI) -> None:
13
+ """Auto-discovered middleware registration."""
14
+ app.add_middleware(
15
+ CORSMiddleware,
16
+ allow_origins=["http://localhost:3000", "http://localhost:8080"],
17
+ allow_credentials=True,
18
+ allow_methods=["*"],
19
+ allow_headers=["*"],
20
+ )
@@ -0,0 +1,14 @@
1
+ # app/components/backend/shutdown/cleanup.py
2
+ """
3
+ Auto-discovered cleanup shutdown hook.
4
+
5
+ This hook performs cleanup when the backend shuts down.
6
+ """
7
+
8
+ from app.core.log import logger
9
+
10
+
11
+ async def shutdown_hook() -> None:
12
+ """Auto-discovered shutdown hook for cleanup."""
13
+ logger.info("🧹 Running backend cleanup...")
14
+ logger.info("✅ Backend shutdown cleanup complete")
@@ -0,0 +1,190 @@
1
+ """
2
+ Component health registration startup hook.
3
+
4
+ Automatically detects available components and registers their health checks
5
+ with the system health service using Python's import system.
6
+ """
7
+
8
+ from app.core.log import logger
9
+ from app.services.system.health import register_health_check
10
+ from app.services.system.models import ComponentStatus, ComponentStatusType
11
+
12
+
13
+ async def _backend_component_health() -> ComponentStatus:
14
+ """
15
+ FastAPI backend health check.
16
+
17
+ In test environment, reports as healthy since the app is loaded.
18
+ In production, uses internal check to avoid circular dependency.
19
+ """
20
+ import os
21
+
22
+ # Check if we're in test environment
23
+ if os.getenv("PYTEST_CURRENT_TEST") or "pytest" in os.getenv("_", ""):
24
+ return ComponentStatus(
25
+ name="backend",
26
+ status=ComponentStatusType.HEALTHY,
27
+ message="FastAPI backend available (test mode)",
28
+ response_time_ms=None,
29
+ metadata={
30
+ "type": "component_check",
31
+ "environment": "test",
32
+ "note": "Backend component loaded successfully",
33
+ },
34
+ )
35
+
36
+ # In production, assume backend is healthy if we can execute this code
37
+ # (since this code IS the backend). Avoid self-HTTP-check circular dependency.
38
+ try:
39
+ # Simple internal health check - if we can import and run this, backend is up
40
+ from app.integrations.main import create_integrated_app
41
+
42
+ return ComponentStatus(
43
+ name="backend",
44
+ status=ComponentStatusType.HEALTHY,
45
+ message="FastAPI backend active (internal check)",
46
+ response_time_ms=None,
47
+ metadata={
48
+ "type": "internal_component_check",
49
+ "note": "Backend is running since this health check executed",
50
+ "check_method": "internal_execution",
51
+ },
52
+ )
53
+ except Exception as e:
54
+ return ComponentStatus(
55
+ name="backend",
56
+ status=ComponentStatusType.UNHEALTHY,
57
+ message=f"Backend component check failed: {str(e)}",
58
+ response_time_ms=None,
59
+ metadata={
60
+ "type": "internal_component_check",
61
+ "error": "import_or_execution_error",
62
+ "error_details": str(e),
63
+ },
64
+ )
65
+
66
+
67
+ async def _frontend_component_health() -> ComponentStatus:
68
+ """
69
+ Flet frontend health check.
70
+
71
+ Since the frontend runs in the same process as the backend,
72
+ we check if the frontend component is properly initialized.
73
+ """
74
+ try:
75
+ # Check if frontend component is available
76
+ from app.components.frontend.main import create_frontend_app
77
+
78
+ # Verify the frontend app factory function works
79
+ create_frontend_app()
80
+
81
+ return ComponentStatus(
82
+ name="frontend",
83
+ status=ComponentStatusType.HEALTHY,
84
+ message="Flet frontend component available",
85
+ response_time_ms=None,
86
+ metadata={
87
+ "type": "component_check",
88
+ "framework": "flet",
89
+ "note": "Frontend integrated with FastAPI",
90
+ },
91
+ )
92
+
93
+ except ImportError as e:
94
+ return ComponentStatus(
95
+ name="frontend",
96
+ status=ComponentStatusType.UNHEALTHY,
97
+ message="Frontend component not found",
98
+ response_time_ms=None,
99
+ metadata={
100
+ "type": "component_check",
101
+ "error": "import_error",
102
+ "error_details": str(e),
103
+ },
104
+ )
105
+ except Exception as e:
106
+ return ComponentStatus(
107
+ name="frontend",
108
+ status=ComponentStatusType.UNHEALTHY,
109
+ message=f"Frontend component error: {str(e)}",
110
+ response_time_ms=None,
111
+ metadata={
112
+ "type": "component_check",
113
+ "error": "unexpected_error",
114
+ "error_details": str(e),
115
+ },
116
+ )
117
+
118
+ {%- if cookiecutter.include_scheduler == "yes" %}
119
+
120
+
121
+ async def _scheduler_enabled_status() -> ComponentStatus:
122
+ """
123
+ Shows scheduler as an enabled/activated component.
124
+
125
+ This indicates the scheduler component is configured in the project
126
+ and running in its own container, but we can't health check it
127
+ without cross-container communication.
128
+ """
129
+ return ComponentStatus(
130
+ name="scheduler",
131
+ status=ComponentStatusType.HEALTHY,
132
+ message="Scheduler component activated",
133
+ response_time_ms=None,
134
+ metadata={
135
+ "type": "component_status",
136
+ "deployment": "separate_container",
137
+ "status": "activated",
138
+ "note": (
139
+ "Runs independently - health monitoring requires database "
140
+ "component"
141
+ ),
142
+ },
143
+ )
144
+ {%- endif %}
145
+
146
+
147
+ async def startup_hook() -> None:
148
+ """
149
+ Auto-detect available components and register their health checks.
150
+
151
+ Always registers core components (backend, frontend) and detects
152
+ optional components using Python's import system.
153
+ """
154
+ logger.info("Registering component health checks...")
155
+
156
+ # Always register core components
157
+ register_health_check("backend", _backend_component_health)
158
+ logger.info("Backend component health check registered")
159
+
160
+ register_health_check("frontend", _frontend_component_health)
161
+ logger.info("️Frontend component health check registered")
162
+
163
+ {%- if cookiecutter.include_scheduler == "yes" %}
164
+ # Register scheduler as enabled component (shows user it's activated)
165
+ register_health_check("scheduler", _scheduler_enabled_status)
166
+ logger.info("Scheduler component enabled (shows as activated)")
167
+ {%- endif %}
168
+
169
+ {%- if cookiecutter.include_worker == "yes" %}
170
+ # Register worker health check (shows queue status and job metrics)
171
+ from app.services.system.health import check_worker_health
172
+ register_health_check("worker", check_worker_health)
173
+ logger.info("Worker component health check registered")
174
+ {%- endif %}
175
+
176
+ {%- if cookiecutter.include_redis == "yes" %}
177
+ # Register cache health check (Redis connectivity and operations)
178
+ from app.services.system.health import check_cache_health
179
+ register_health_check("cache", check_cache_health)
180
+ logger.info("Cache component health check registered")
181
+ {%- endif %}
182
+
183
+ {%- if cookiecutter.include_database == "yes" %}
184
+ # Register database health check (SQLite connectivity and basic operations)
185
+ from app.services.system.health import check_database_health
186
+ register_health_check("database", check_database_health)
187
+ logger.info("Database component health check registered")
188
+ {%- endif %}
189
+
190
+ logger.info("✅ Component health detection complete")
@@ -0,0 +1,46 @@
1
+ """Theme management for Flet frontend."""
2
+
3
+ import flet as ft
4
+
5
+
6
+ class ThemeManager:
7
+ """Manages theme configuration for the Flet application."""
8
+
9
+ def __init__(self, page: ft.Page) -> None:
10
+ """Initialize theme manager with page reference."""
11
+ self.page = page
12
+ self.is_dark_mode = False
13
+
14
+ async def initialize_themes(self) -> None:
15
+ """Initialize the theme system."""
16
+ # Set initial light theme
17
+ self.page.theme = self.get_theme()
18
+ self.page.theme_mode = ft.ThemeMode.LIGHT
19
+ self.is_dark_mode = False
20
+
21
+ async def toggle_theme(self) -> None:
22
+ """Toggle between light and dark themes."""
23
+ if self.is_dark_mode:
24
+ self.page.theme = self.get_theme()
25
+ self.page.theme_mode = ft.ThemeMode.LIGHT
26
+ self.is_dark_mode = False
27
+ else:
28
+ self.page.theme = self.get_dark_theme()
29
+ self.page.theme_mode = ft.ThemeMode.DARK
30
+ self.is_dark_mode = True
31
+
32
+ self.page.update()
33
+
34
+ @staticmethod
35
+ def get_theme() -> ft.Theme:
36
+ """Get the default light theme configuration."""
37
+ return ft.Theme(
38
+ color_scheme_seed=ft.Colors.BLUE,
39
+ )
40
+
41
+ @staticmethod
42
+ def get_dark_theme() -> ft.Theme:
43
+ """Get the dark theme configuration."""
44
+ return ft.Theme(
45
+ color_scheme_seed=ft.Colors.BLUE,
46
+ )