Skip to content

Commit cbb00ea

Browse files
authored
Merge pull request #26 from lbedner/db-health-check-integration
DB Health Check Integration
2 parents 05672b9 + 854c88f commit cbb00ea

6 files changed

Lines changed: 319 additions & 238 deletions

File tree

aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/.env.example renamed to aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/.env.example.j2

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,27 +31,28 @@ AEGIS_STACK_VERSION=dev
3131
# AEGIS_STACK_ENV_FILE=.env
3232

3333
# =============================================================================
34-
# FUTURE COMPONENT SETTINGS
34+
# COMPONENT SETTINGS
3535
# =============================================================================
36-
# These will be uncommented when components are added via `aegis add <component>`
3736

38-
# Database settings (when database component is added)
39-
# DATABASE_URL=postgresql://user:password@localhost:5432/aegis_stack
40-
# DATABASE_HOST=localhost
41-
# DATABASE_PORT=5432
42-
# DATABASE_NAME=aegis_stack
43-
# DATABASE_USER=aegis_user
44-
# DATABASE_PASSWORD=your_secure_password
37+
{% if cookiecutter.include_database == "yes" %}
38+
# Database settings
39+
# DATABASE_URL=sqlite:///./data/app.db # SQLite database (default)
40+
# DATABASE_ENGINE_ECHO=false # Set to true to log all SQL queries (debugging)
41+
{% endif %}
4542

46-
# Redis settings (when cache component is added)
43+
{% if cookiecutter.include_redis == "yes" or cookiecutter.include_cache == "yes" %}
44+
# Redis settings
4745
# REDIS_URL=redis://localhost:6379
4846
# REDIS_HOST=localhost
4947
# REDIS_PORT=6379
5048
# REDIS_PASSWORD=
49+
{% endif %}
5150

52-
# Scheduler settings (when scheduler component is added)
51+
{% if cookiecutter.include_scheduler == "yes" %}
52+
# Scheduler settings
5353
# SCHEDULER_TIMEZONE=UTC
5454
# SCHEDULER_MAX_WORKERS=10
55+
{% endif %}
5556

5657
# API Keys and Secrets (add as needed)
5758
# SECRET_KEY=your-super-secret-key-here

aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/startup/component_health.py

Lines changed: 0 additions & 226 deletions
This file was deleted.

aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/startup/component_health.py.j2

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,4 +180,11 @@ async def startup_hook() -> None:
180180
logger.info("Cache component health check registered")
181181
{%- endif %}
182182

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+
183190
logger.info("✅ Component health detection complete")

aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/health.py.j2

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,103 @@ async def check_cache_health() -> ComponentStatus:
562562
{% endif %}
563563

564564

565+
{% if cookiecutter.include_database == "yes" %}
566+
async def check_database_health() -> ComponentStatus:
567+
"""
568+
Check database connectivity and basic functionality.
569+
570+
Returns:
571+
ComponentStatus indicating database health
572+
"""
573+
try:
574+
# Import db_session from generated project
575+
from app.core.db import db_session
576+
from pathlib import Path
577+
578+
# Check if database file exists for SQLite
579+
db_url = settings.DATABASE_URL
580+
if db_url.startswith("sqlite:///"):
581+
# Extract path from SQLite URL
582+
db_path = db_url.replace("sqlite:///", "").lstrip("./")
583+
584+
# Check if database file exists
585+
if not Path(db_path).exists():
586+
return ComponentStatus(
587+
name="database",
588+
status=ComponentStatusType.WARNING,
589+
message="Database not initialized - file does not exist",
590+
response_time_ms=None,
591+
metadata={
592+
"implementation": "sqlite",
593+
"database_exists": False,
594+
"expected_path": db_path,
595+
"url": settings.DATABASE_URL,
596+
"recommendation": "Run database migrations or create database file",
597+
},
598+
)
599+
600+
# Test database connection with simple query
601+
with db_session(autocommit=False) as session:
602+
# Execute a simple query to test connectivity
603+
from sqlalchemy import text
604+
session.execute(text("SELECT 1"))
605+
# No need to commit since we're just testing connectivity
606+
607+
return ComponentStatus(
608+
name="database",
609+
status=ComponentStatusType.HEALTHY,
610+
message="Database connection successful",
611+
response_time_ms=None, # Will be set by caller
612+
metadata={
613+
"implementation": "sqlite",
614+
"url": settings.DATABASE_URL,
615+
"database_exists": True,
616+
"engine_echo": settings.DATABASE_ENGINE_ECHO,
617+
},
618+
)
619+
620+
except ImportError:
621+
return ComponentStatus(
622+
name="database",
623+
status=ComponentStatusType.UNHEALTHY,
624+
message="Database module not available",
625+
response_time_ms=None,
626+
metadata={
627+
"implementation": "sqlite",
628+
"error": "Database module not imported or configured",
629+
},
630+
)
631+
except Exception as e:
632+
# Check if it's a file not found error
633+
error_str = str(e).lower()
634+
if "unable to open database file" in error_str or "no such file" in error_str:
635+
return ComponentStatus(
636+
name="database",
637+
status=ComponentStatusType.WARNING,
638+
message="Database file not accessible",
639+
response_time_ms=None,
640+
metadata={
641+
"implementation": "sqlite",
642+
"url": settings.DATABASE_URL,
643+
"error": str(e),
644+
"recommendation": "Check database file path and permissions",
645+
},
646+
)
647+
648+
return ComponentStatus(
649+
name="database",
650+
status=ComponentStatusType.UNHEALTHY,
651+
message=f"Database connection failed: {str(e)}",
652+
response_time_ms=None,
653+
metadata={
654+
"implementation": "sqlite",
655+
"url": settings.DATABASE_URL,
656+
"error": str(e),
657+
},
658+
)
659+
{% endif %}
660+
661+
565662
{% if cookiecutter.include_worker == "yes" %}
566663
async def check_worker_health() -> ComponentStatus:
567664
"""

0 commit comments

Comments
 (0)