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,240 @@
1
+ # Health Monitoring
2
+
3
+ {{ cookiecutter.project_name }} includes comprehensive health monitoring capabilities through both API endpoints and CLI commands.
4
+
5
+ ## Health Check Endpoints
6
+
7
+ ### Basic Health Check
8
+ - **URL**: `GET /health/`
9
+ - **Purpose**: Quick health status check
10
+ - **Response Time**: < 100ms
11
+
12
+ ### Detailed Health Check
13
+ - **URL**: `GET /health/detailed`
14
+ - **Purpose**: Comprehensive system health with metrics
15
+ - **Response Time**: < 500ms
16
+
17
+ ## CLI Health Commands
18
+
19
+ {{ cookiecutter.project_name }} provides a built-in CLI for health monitoring:
20
+
21
+ ### Basic Health Check
22
+ ```bash
23
+ {{ cookiecutter.project_slug }} health status
24
+ ```
25
+
26
+ **Output:**
27
+ ```
28
+ ✅ {{ cookiecutter.project_name }} - System Status: HEALTHY
29
+
30
+ 🖥️ System Health:
31
+ • CPU Usage: 15.2%
32
+ • Memory Usage: 45.8%
33
+ • Disk Usage: 32.1%
34
+ • Response Time: 2.1ms{% if cookiecutter.include_scheduler == "yes" %}
35
+
36
+ ⏰ Scheduler Health:
37
+ • Status: HEALTHY
38
+ • Active Jobs: 2
39
+ • Next Run: 2024-01-01 02:00:00
40
+ • Response Time: 1.5ms{% endif %}
41
+
42
+ 🔍 Overall Health: 98.5%
43
+ ```
44
+
45
+ ### Detailed Health Check
46
+ ```bash
47
+ {{ cookiecutter.project_slug }} health status --detailed
48
+ ```
49
+
50
+ Shows comprehensive system information including:
51
+ - Component-level health status
52
+ - System resource utilization
53
+ - Response time metrics
54
+ - Uptime information
55
+
56
+ ### JSON Output
57
+ ```bash
58
+ {{ cookiecutter.project_slug }} health status --json
59
+ ```
60
+
61
+ Returns structured JSON for integration with monitoring systems:
62
+
63
+ ```json
64
+ {
65
+ "healthy": true,
66
+ "status": "healthy",
67
+ "components": {
68
+ "system": {
69
+ "status": "healthy",
70
+ "cpu_percent": 15.2,
71
+ "memory_percent": 45.8,
72
+ "disk_percent": 32.1,
73
+ "response_time_ms": 2.1
74
+ }{% if cookiecutter.include_scheduler == "yes" %},
75
+ "scheduler": {
76
+ "status": "healthy",
77
+ "active_jobs": 2,
78
+ "next_run": "2024-01-01T02:00:00Z",
79
+ "response_time_ms": 1.5
80
+ }{% endif %}
81
+ },
82
+ "timestamp": "2024-01-01T00:00:00Z",
83
+ "uptime_seconds": 3600,
84
+ "health_percentage": 98.5
85
+ }
86
+ ```
87
+
88
+ ### Health Probe
89
+ ```bash
90
+ {{ cookiecutter.project_slug }} health probe
91
+ ```
92
+
93
+ Returns simple healthy/unhealthy status for use in scripts and monitoring.
94
+
95
+ ## Using Health Checks in Development
96
+
97
+ ### Make Commands
98
+ Convenient make targets are available:
99
+
100
+ ```bash
101
+ make health # Basic health check
102
+ make health-detailed # Detailed health information
103
+ make health-json # JSON health output
104
+ make health-probe # Health probe for monitoring
105
+ ```
106
+
107
+ ## Health Check Components
108
+
109
+ ### System Health
110
+ Monitors core system resources:
111
+
112
+ - **CPU Usage**: Current CPU utilization percentage
113
+ - **Memory Usage**: RAM utilization percentage
114
+ - **Disk Usage**: Disk space utilization percentage
115
+ - **Response Time**: Health check response time{% if cookiecutter.include_scheduler == "yes" %}
116
+
117
+ ### Scheduler Health
118
+ Monitors the background task scheduler:
119
+
120
+ - **Scheduler Status**: Running/stopped status
121
+ - **Active Jobs**: Number of currently scheduled jobs
122
+ - **Next Run**: When the next job will execute
123
+ - **Job History**: Recent job execution status{% endif %}
124
+
125
+ ## Integration with Monitoring Systems
126
+
127
+ ### Prometheus Metrics
128
+ To add Prometheus metrics:
129
+
130
+ ```python
131
+ # app/components/backend/middleware/prometheus.py
132
+ from prometheus_client import Counter, Histogram, generate_latest
133
+ from fastapi import Request, Response
134
+
135
+ REQUEST_COUNT = Counter('http_requests_total', 'Total HTTP requests', ['method', 'endpoint'])
136
+ REQUEST_DURATION = Histogram('http_request_duration_seconds', 'HTTP request duration')
137
+
138
+ @app.middleware("http")
139
+ async def prometheus_middleware(request: Request, call_next):
140
+ start_time = time.time()
141
+ response = await call_next(request)
142
+
143
+ REQUEST_COUNT.labels(method=request.method, endpoint=request.url.path).inc()
144
+ REQUEST_DURATION.observe(time.time() - start_time)
145
+
146
+ return response
147
+
148
+ @app.get("/metrics")
149
+ async def metrics():
150
+ return Response(generate_latest(), media_type="text/plain")
151
+ ```
152
+
153
+ ### External Health Checks
154
+ The health endpoints can be monitored by external systems:
155
+
156
+ - **Uptime monitoring**: Ping `/health/` endpoint
157
+ - **Performance monitoring**: Track `/health/detailed` response times
158
+ - **Alerting**: Monitor health percentage thresholds
159
+
160
+ ### Docker Health Checks
161
+ The Docker container includes built-in health checks:
162
+
163
+ ```dockerfile
164
+ HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \
165
+ CMD curl -f http://localhost:8000/health || exit 1
166
+ ```
167
+
168
+ ## Troubleshooting Health Issues
169
+
170
+ ### Common Issues
171
+
172
+ **Health check timeouts:**
173
+ - Check if application is running on expected port
174
+ - Verify no firewall blocking connections
175
+ - Check application logs for startup errors
176
+
177
+ **High resource usage:**
178
+ - Monitor CPU/memory usage over time
179
+ - Check for memory leaks in long-running processes
180
+ - Review recent code changes{% if cookiecutter.include_scheduler == "yes" %}
181
+
182
+ **Scheduler issues:**
183
+ - Check scheduler logs for job failures
184
+ - Verify scheduled job configurations
185
+ - Monitor job execution times{% endif %}
186
+
187
+ ### Debug Commands
188
+ ```bash
189
+ # Check if service is running
190
+ curl http://localhost:8000/health/
191
+
192
+ # Check application logs
193
+ docker compose logs -f
194
+
195
+ # Monitor resource usage
196
+ docker stats
197
+
198
+ # Test CLI connectivity
199
+ {{ cookiecutter.project_slug }} health check --debug
200
+ ```
201
+
202
+ ## Health Check Best Practices
203
+
204
+ 1. **Regular Monitoring**: Set up automated health checks every 30-60 seconds
205
+ 2. **Alerting Thresholds**: Alert when health percentage drops below 95%
206
+ 3. **Response Time Monitoring**: Alert on health check response times > 1 second
207
+ 4. **Component-Level Monitoring**: Monitor individual components, not just overall health
208
+ 5. **Historical Tracking**: Store health metrics for trend analysis
209
+
210
+ ## Customizing Health Checks
211
+
212
+ To add custom health checks, extend the system service:
213
+
214
+ ```python
215
+ # app/services/system_service.py
216
+ async def check_database_health() -> ComponentHealth:
217
+ """Check database connectivity."""
218
+ try:
219
+ # Perform database ping
220
+ start_time = datetime.utcnow()
221
+ # ... database check logic ...
222
+ end_time = datetime.utcnow()
223
+
224
+ return ComponentHealth(
225
+ status="healthy",
226
+ details={
227
+ "connection_pool": "active",
228
+ "query_response_ms": (end_time - start_time).total_seconds() * 1000
229
+ },
230
+ response_time_ms=(end_time - start_time).total_seconds() * 1000
231
+ )
232
+ except Exception as e:
233
+ return ComponentHealth(
234
+ status="unhealthy",
235
+ details={"error": str(e)},
236
+ response_time_ms=0
237
+ )
238
+ ```
239
+
240
+ Then register it in the health check system to have it included in all health reports.
@@ -0,0 +1,62 @@
1
+ // Mermaid configuration for dark/light mode support
2
+ document.addEventListener("DOMContentLoaded", function() {
3
+ // Function to get current theme
4
+ function getCurrentTheme() {
5
+ return document.querySelector('[data-md-color-scheme]').getAttribute('data-md-color-scheme');
6
+ }
7
+
8
+ // Function to configure Mermaid theme
9
+ function configureMermaid() {
10
+ const theme = getCurrentTheme();
11
+ const mermaidTheme = theme === 'slate' ? 'dark' : 'default';
12
+
13
+ mermaid.initialize({
14
+ startOnLoad: true,
15
+ theme: mermaidTheme,
16
+ themeVariables: {
17
+ // Light mode colors
18
+ primaryColor: theme === 'default' ? '#1976d2' : '#64b5f6',
19
+ primaryTextColor: theme === 'default' ? '#000000' : '#ffffff',
20
+ primaryBorderColor: theme === 'default' ? '#1976d2' : '#64b5f6',
21
+ lineColor: theme === 'default' ? '#333333' : '#ffffff',
22
+ secondaryColor: theme === 'default' ? '#e3f2fd' : '#1e1e1e',
23
+ tertiaryColor: theme === 'default' ? '#f5f5f5' : '#2d2d2d',
24
+ background: theme === 'default' ? '#ffffff' : '#1e1e1e',
25
+ mainBkg: theme === 'default' ? '#ffffff' : '#1e1e1e',
26
+ secondBkg: theme === 'default' ? '#f8f9fa' : '#2d2d2d',
27
+ tertiaryBkg: theme === 'default' ? '#e9ecef' : '#404040'
28
+ }
29
+ });
30
+
31
+ // Re-render all mermaid diagrams
32
+ if (typeof mermaid !== 'undefined') {
33
+ const mermaidElements = document.querySelectorAll('.mermaid');
34
+ mermaidElements.forEach((element, index) => {
35
+ element.removeAttribute('data-processed');
36
+ element.innerHTML = element.getAttribute('data-original') || element.innerHTML;
37
+ if (!element.getAttribute('data-original')) {
38
+ element.setAttribute('data-original', element.innerHTML);
39
+ }
40
+ });
41
+ mermaid.init();
42
+ }
43
+ }
44
+
45
+ // Initial configuration
46
+ configureMermaid();
47
+
48
+ // Listen for theme changes
49
+ const observer = new MutationObserver(function(mutations) {
50
+ mutations.forEach(function(mutation) {
51
+ if (mutation.type === 'attributes' && mutation.attributeName === 'data-md-color-scheme') {
52
+ setTimeout(configureMermaid, 100); // Small delay to ensure theme is applied
53
+ }
54
+ });
55
+ });
56
+
57
+ // Start observing theme changes
58
+ const target = document.querySelector('[data-md-color-scheme]');
59
+ if (target) {
60
+ observer.observe(target, { attributes: true });
61
+ }
62
+ });
@@ -0,0 +1,95 @@
1
+ /* Mermaid diagram styling for light and dark modes */
2
+
3
+ /* Base mermaid diagram styling */
4
+ .mermaid {
5
+ text-align: center;
6
+ margin: 1em 0;
7
+ }
8
+
9
+ /* Light mode styling */
10
+ [data-md-color-scheme="default"] .mermaid {
11
+ --mermaid-primary-color: #1976d2;
12
+ --mermaid-primary-text-color: #000000;
13
+ --mermaid-primary-border-color: #1976d2;
14
+ --mermaid-line-color: #333333;
15
+ --mermaid-secondary-color: #e3f2fd;
16
+ --mermaid-tertiary-color: #f5f5f5;
17
+ --mermaid-background: #ffffff;
18
+ --mermaid-main-bkg: #ffffff;
19
+ --mermaid-second-bkg: #f8f9fa;
20
+ --mermaid-tertiary-bkg: #e9ecef;
21
+ }
22
+
23
+ /* Dark mode styling */
24
+ [data-md-color-scheme="slate"] .mermaid {
25
+ --mermaid-primary-color: #64b5f6;
26
+ --mermaid-primary-text-color: #ffffff;
27
+ --mermaid-primary-border-color: #64b5f6;
28
+ --mermaid-line-color: #ffffff;
29
+ --mermaid-secondary-color: #1e1e1e;
30
+ --mermaid-tertiary-color: #2d2d2d;
31
+ --mermaid-background: #1e1e1e;
32
+ --mermaid-main-bkg: #1e1e1e;
33
+ --mermaid-second-bkg: #2d2d2d;
34
+ --mermaid-tertiary-bkg: #404040;
35
+ }
36
+
37
+ /* Ensure mermaid diagrams are visible in dark mode */
38
+ [data-md-color-scheme="slate"] .mermaid svg {
39
+ background: transparent !important;
40
+ }
41
+
42
+ [data-md-color-scheme="slate"] .mermaid .node rect,
43
+ [data-md-color-scheme="slate"] .mermaid .node circle,
44
+ [data-md-color-scheme="slate"] .mermaid .node ellipse,
45
+ [data-md-color-scheme="slate"] .mermaid .node polygon {
46
+ fill: var(--mermaid-main-bkg) !important;
47
+ stroke: var(--mermaid-primary-border-color) !important;
48
+ }
49
+
50
+ [data-md-color-scheme="slate"] .mermaid .node .label {
51
+ color: var(--mermaid-primary-text-color) !important;
52
+ }
53
+
54
+ [data-md-color-scheme="slate"] .mermaid .edgePath .path {
55
+ stroke: var(--mermaid-line-color) !important;
56
+ }
57
+
58
+ [data-md-color-scheme="slate"] .mermaid .edgeLabel {
59
+ background-color: var(--mermaid-background) !important;
60
+ color: var(--mermaid-primary-text-color) !important;
61
+ }
62
+
63
+ /* Flowchart specific styling */
64
+ [data-md-color-scheme="slate"] .mermaid .cluster rect {
65
+ fill: var(--mermaid-second-bkg) !important;
66
+ stroke: var(--mermaid-primary-border-color) !important;
67
+ }
68
+
69
+ [data-md-color-scheme="slate"] .mermaid .cluster .label {
70
+ color: var(--mermaid-primary-text-color) !important;
71
+ }
72
+
73
+ /* Sequence diagram specific styling */
74
+ [data-md-color-scheme="slate"] .mermaid .actor {
75
+ fill: var(--mermaid-main-bkg) !important;
76
+ stroke: var(--mermaid-primary-border-color) !important;
77
+ }
78
+
79
+ [data-md-color-scheme="slate"] .mermaid .actor-line {
80
+ stroke: var(--mermaid-line-color) !important;
81
+ }
82
+
83
+ [data-md-color-scheme="slate"] .mermaid .messageLine0,
84
+ [data-md-color-scheme="slate"] .mermaid .messageLine1 {
85
+ stroke: var(--mermaid-line-color) !important;
86
+ }
87
+
88
+ [data-md-color-scheme="slate"] .mermaid .messageText {
89
+ fill: var(--mermaid-primary-text-color) !important;
90
+ }
91
+
92
+ [data-md-color-scheme="slate"] .mermaid .note {
93
+ fill: var(--mermaid-tertiary-bkg) !important;
94
+ stroke: var(--mermaid-primary-border-color) !important;
95
+ }
@@ -0,0 +1,62 @@
1
+ # mkdocs.yml
2
+ site_name: {{ cookiecutter.project_name }}
3
+ site_description: {{ cookiecutter.project_description }}
4
+ site_author: {{ cookiecutter.author_name }}
5
+ site_url: https://{{ cookiecutter.github_username }}.github.io/{{ cookiecutter.project_slug }}/
6
+
7
+ theme:
8
+ name: material
9
+ palette:
10
+ # Palette toggle for light vs dark mode
11
+ - scheme: default
12
+ toggle:
13
+ icon: material/brightness-7
14
+ name: Switch to dark mode
15
+ - scheme: slate
16
+ toggle:
17
+ icon: material/brightness-4
18
+ name: Switch to light mode
19
+ features:
20
+ - navigation.tabs
21
+ - navigation.top
22
+ - search.highlight
23
+ - search.suggest
24
+ - content.code.copy
25
+
26
+ # Navigation structure
27
+ nav:
28
+ - Home: index.md
29
+ - Development: development.md
30
+ - API Reference: api.md
31
+ - Health Monitoring: health.md{% if cookiecutter.include_scheduler == "yes" %}
32
+ - Components:
33
+ - Scheduler: components/scheduler.md{% endif %}
34
+
35
+ # Plugins
36
+ plugins:
37
+ - gen-files:
38
+ scripts:
39
+ - scripts/gen_docs.py
40
+ - search
41
+ - mkdocstrings:
42
+ handlers:
43
+ python:
44
+ options:
45
+ show_root_heading: true
46
+ show_source: true
47
+
48
+ # Markdown extensions
49
+ markdown_extensions:
50
+ - pymdownx.superfences:
51
+ custom_fences:
52
+ - name: mermaid
53
+ class: mermaid
54
+ format: !!python/name:pymdownx.superfences.fence_code_format
55
+
56
+ # Additional configuration for Mermaid dark mode support
57
+ extra_javascript:
58
+ - https://unpkg.com/mermaid@10.6.0/dist/mermaid.min.js
59
+ - javascripts/mermaid-config.js
60
+
61
+ extra_css:
62
+ - stylesheets/mermaid.css
@@ -0,0 +1,156 @@
1
+ [project]
2
+ name = "{{ cookiecutter.project_slug }}"
3
+ version = "{{ cookiecutter.version }}"
4
+ description = "{{ cookiecutter.project_description }}"
5
+ readme = "README.md"
6
+ requires-python = ">={{ cookiecutter.python_version }}"
7
+ authors = [
8
+ {name = "{{ cookiecutter.author_name }}", email = "{{ cookiecutter.author_email }}"}
9
+ ]
10
+
11
+ dependencies = [
12
+ "fastapi==0.116.1",
13
+ "flet[all]==0.28.3",
14
+ "uvicorn==0.35.0",
15
+ "structlog==25.4.0",
16
+ "pydantic-settings==2.10.1",
17
+ "typer==0.16.0",
18
+ # Pin click to avoid yanked versions (typer dependency)
19
+ "click==8.2.1",
20
+ # Rich CLI formatting
21
+ "rich==14.1.0",
22
+ # HTTP client for CLI API calls
23
+ "httpx==0.28.1",
24
+ # System monitoring
25
+ "psutil==7.0.0",
26
+ {%- if cookiecutter.include_scheduler == "yes" %}
27
+ "apscheduler==3.10.4",
28
+ {%- endif %}
29
+ {%- if cookiecutter.include_redis == "yes" %}
30
+ "redis==5.0.8",
31
+ {%- endif %}
32
+ {%- if cookiecutter.include_worker == "yes" %}
33
+ "arq==0.25.0",
34
+ {%- endif %}
35
+ {%- if cookiecutter.include_database == "yes" %}
36
+ "sqlmodel>=0.0.14",
37
+ "sqlalchemy>=2.0.0",
38
+ "aiosqlite>=0.19.0",
39
+ {%- endif %}
40
+ {%- if cookiecutter.include_cache == "yes" %}
41
+ "redis[hiredis]==5.0.8",
42
+ {%- endif %}
43
+ ]
44
+
45
+ [project.scripts]
46
+ {{cookiecutter.project_slug}} = "app.cli.main:main"
47
+
48
+ [project.optional-dependencies]
49
+ dev = [
50
+ "pytest==8.4.1",
51
+ "pytest-asyncio==1.1.0",
52
+ "ruff==0.12.7",
53
+ "mypy==1.17.1",
54
+ "types-psutil==7.0.0.20250801",
55
+ "pre-commit==4.2.0",
56
+ "pip-audit==2.9.0",
57
+ {%- if cookiecutter.include_worker == "yes" %}
58
+ # Development auto-reload for workers
59
+ "watchdog==4.0.2",
60
+ {%- endif %}
61
+ ]
62
+ docs = [
63
+ "mkdocs==1.6.1",
64
+ "mkdocs-material==9.6.16",
65
+ "mkdocstrings[python]==0.30.0",
66
+ "mkdocs-gen-files==0.5.0",
67
+ "pymdown-extensions==10.16.1",
68
+ ]
69
+
70
+ [build-system]
71
+ requires = ["hatchling"]
72
+ build-backend = "hatchling.build"
73
+
74
+ [tool.hatch.build.targets.wheel]
75
+ packages = ["app"]
76
+
77
+ [tool.ruff]
78
+ line-length = 88
79
+ target-version = "py{{ cookiecutter.python_version.replace('.', '') }}"
80
+
81
+ [tool.ruff.lint]
82
+ select = ["E", "F", "I", "N", "W", "UP"] # Added UP for pyupgrade (modern Python syntax)
83
+ ignore = []
84
+ fixable = ["ALL"]
85
+ unfixable = []
86
+
87
+ [tool.ruff.format]
88
+ quote-style = "double"
89
+ indent-style = "space"
90
+ skip-magic-trailing-comma = false
91
+ line-ending = "auto"
92
+
93
+ [tool.ruff.lint.isort]
94
+ force-single-line = false
95
+ force-sort-within-sections = true
96
+ known-first-party = ["app"]
97
+
98
+ [tool.mypy]
99
+ python_version = "{{ cookiecutter.python_version }}"
100
+ strict = true
101
+ plugins = ["pydantic.mypy"]
102
+
103
+ [[tool.mypy.overrides]]
104
+ module = "flet.*"
105
+ ignore_missing_imports = true
106
+
107
+ {%- if cookiecutter.include_scheduler == "yes" %}
108
+
109
+ [[tool.mypy.overrides]]
110
+ module = "apscheduler.*"
111
+ ignore_missing_imports = true
112
+ {%- endif %}
113
+
114
+ {%- if cookiecutter.include_redis == "yes" %}
115
+
116
+ [[tool.mypy.overrides]]
117
+ module = "redis.*"
118
+ ignore_missing_imports = true
119
+ {%- endif %}
120
+
121
+ {%- if cookiecutter.include_worker == "yes" %}
122
+
123
+ [[tool.mypy.overrides]]
124
+ module = "arq.*"
125
+ ignore_missing_imports = true
126
+ {%- endif %}
127
+
128
+ {%- if cookiecutter.include_database == "yes" %}
129
+
130
+ [[tool.mypy.overrides]]
131
+ module = "sqlmodel.*"
132
+ ignore_missing_imports = true
133
+ {%- endif %}
134
+
135
+ # Ignore imports for future components that may not exist yet
136
+ [[tool.mypy.overrides]]
137
+ module = "app.components.database.main"
138
+ ignore_missing_imports = true
139
+
140
+ [[tool.mypy.overrides]]
141
+ module = "app.components.cache.main"
142
+ ignore_missing_imports = true
143
+
144
+ [[tool.mypy.overrides]]
145
+ module = "app.components.worker.*"
146
+ ignore_missing_imports = true
147
+
148
+
149
+ [tool.pytest.ini_options]
150
+ asyncio_mode = "auto"
151
+ testpaths = ["tests"]
152
+
153
+ [dependency-groups]
154
+ dev = [
155
+ "httpx>=0.28.1",
156
+ ]
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -e
4
+
5
+ # More comprehensive venv cleanup to prevent Docker container conflicts
6
+ if [ -d ".venv" ]; then
7
+ echo "🧹 Found existing .venv directory, checking compatibility..."
8
+
9
+ # Check if .venv has issues (broken symlinks, wrong Python version, etc.)
10
+ if [ -L ".venv/bin/python3" ] && [ ! -e ".venv/bin/python3" ]; then
11
+ echo "🧹 Cleaning up broken venv symlinks..."
12
+ rm -rf .venv
13
+ elif [ -f ".venv/bin/python3" ]; then
14
+ # Check if the Python executable is compatible and accessible
15
+ if ! .venv/bin/python3 --version > /dev/null 2>&1; then
16
+ echo "🧹 Cleaning up incompatible venv..."
17
+ rm -rf .venv
18
+ fi
19
+ elif [ ! -w ".venv" ] || [ ! -x ".venv" ]; then
20
+ # Check for permission issues in Docker containers
21
+ echo "🧹 Cleaning up venv with permission issues..."
22
+ rm -rf .venv
23
+ else
24
+ # If directory exists but has no python executable, clean it up
25
+ if [ ! -f ".venv/bin/python3" ] && [ ! -f ".venv/bin/python" ]; then
26
+ echo "🧹 Cleaning up incomplete venv..."
27
+ rm -rf .venv
28
+ fi
29
+ fi
30
+ fi
31
+
32
+ # Configure UV environment based on execution context
33
+ if [ -n "$DOCKER_CONTAINER" ] || [ "$USER" = "root" ]; then
34
+ echo "🐳 Running in Docker container, configuring UV for containerized environment..."
35
+
36
+ # Set Docker-specific UV configuration
37
+ export UV_PROJECT_ENVIRONMENT=/code/.venv
38
+ export UV_LINK_MODE=copy
39
+ export VIRTUAL_ENV=/code/.venv
40
+
41
+ # Ensure .venv path is in PATH for CLI commands
42
+ export PATH="/code/.venv/bin:$PATH"
43
+
44
+ echo "✅ UV configured for Docker: UV_PROJECT_ENVIRONMENT=/code/.venv"
45
+ else
46
+ echo "🖥️ Running in local environment, UV will use project defaults"
47
+
48
+ # Ensure we don't inherit Docker environment variables
49
+ unset UV_PROJECT_ENVIRONMENT
50
+ unset UV_SYSTEM_PYTHON
51
+
52
+ # Let UV auto-detect local .venv
53
+ echo "✅ UV configured for local development"
54
+ fi
55
+
56
+ # Pop run_command from arguments
57
+ run_command="$1"
58
+ shift
59
+
60
+ if [ "$run_command" = "webserver" ]; then
61
+ # Web server (FastAPI + Flet)
62
+ uv run python -m app.entrypoints.webserver
63
+ elif [ "$run_command" = "scheduler" ]; then
64
+ # Scheduler component
65
+ uv run python -m app.entrypoints.scheduler
66
+ elif [ "$run_command" = "lint" ]; then
67
+ uv run ruff check .
68
+ elif [ "$run_command" = "typecheck" ]; then
69
+ uv run mypy .
70
+ elif [ "$run_command" = "test" ]; then
71
+ uv run pytest "$@"
72
+ elif [ "$run_command" = "health" ]; then
73
+ uv run python -m app.cli.health check "$@"
74
+ elif [ "$run_command" = "help" ]; then
75
+ echo "Available commands:"
76
+ echo " webserver - Run FastAPI + Flet web server"
77
+ echo " scheduler - Run scheduler component"
78
+ echo " health - Check system health status"
79
+ echo " lint - Run ruff linting"
80
+ echo " typecheck - Run mypy type checking"
81
+ echo " test - Run pytest test suite"
82
+ echo " help - Show this help message"
83
+ else
84
+ echo "Unknown command: $run_command"
85
+ echo "Available commands: webserver, scheduler, health, lint, typecheck, test, help"
86
+ exit 1
87
+ fi