Skip to content
10 changes: 10 additions & 0 deletions agent/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ class HumWatchConfig:
retention_days: int = 7
db_path: str = "./humwatch.db"
data_root: Optional[str] = None
legacy_db_path: Optional[str] = None
theme_override: Optional[str] = None
auth_token_file: Optional[str] = None
tls_certfile: Optional[str] = None
Expand Down Expand Up @@ -121,6 +122,10 @@ def _resolve_runtime_path(self, value: Optional[str]) -> Optional[Path]:
path = Path(value)
return path if path.is_absolute() else self.resolved_data_root / path

@property
def resolved_legacy_db_path(self) -> Optional[Path]:
return self._resolve_runtime_path(self.legacy_db_path)

@property
def resolved_auth_token_file(self) -> Optional[Path]:
return self._resolve_runtime_path(self.auth_token_file)
Expand Down Expand Up @@ -161,6 +166,7 @@ def load_config() -> HumWatchConfig:
retention_days=data.get("retention_days", 7),
db_path=data.get("db_path", "./humwatch.db"),
data_root=data.get("data_root"),
legacy_db_path=data.get("legacy_db_path"),
theme_override=data.get("theme_override"),
auth_token_file=data.get("auth_token_file"),
tls_certfile=data.get("tls_certfile"),
Expand Down Expand Up @@ -193,6 +199,10 @@ def load_config() -> HumWatchConfig:
if env_data_root is not None:
config.data_root = env_data_root

env_legacy_db = os.environ.get("HUMWATCH_LEGACY_DB")
if env_legacy_db is not None:
config.legacy_db_path = env_legacy_db

env_auth_token_file = os.environ.get("HUMWATCH_AUTH_TOKEN_FILE")
if env_auth_token_file is not None:
config.auth_token_file = env_auth_token_file
Expand Down
24 changes: 22 additions & 2 deletions agent/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from agent import __version__
from agent.config import PROJECT_ROOT, get_config, validate_cors_origins
from agent.database import init_db, close_db
from agent.migrations import migrate_legacy_database
from agent.migrations import MigrationSafetyError, migrate_legacy_database
from agent.security.auth import require_bearer_token
from agent.security.limits import RequestLimitMiddleware
from agent.security.tls import build_uvicorn_ssl_kwargs, validate_security_config
Expand Down Expand Up @@ -96,7 +96,27 @@ async def lifespan(app: FastAPI):
logger.info("HumWatch v%s starting on port %d", __version__, config.port)

# Carry an existing database into the runtime root before opening a new one.
migrate_legacy_database(config)
# An explicit legacy source (set by the installer) that cannot be safely
# preserved raises MigrationSafetyError, which is left to propagate here
# and stop startup rather than silently adopting an empty database.
try:
migration_result = migrate_legacy_database(config)
except MigrationSafetyError as exc:
logger.critical("Refusing to start: %s", exc)
raise

if migration_result.status == "migrated":
logger.info(
"Migrated legacy database from %s into %s (manifest: %s)",
migration_result.source, migration_result.target, migration_result.archive_manifest,
)
elif migration_result.status == "target_exists":
log = logger.info if migration_result.archive_manifest else logger.warning
log("Legacy database migration skipped: %s", migration_result.detail)
elif migration_result.status == "failed":
logger.warning("Legacy database migration failed: %s", migration_result.detail)
else:
logger.debug("Legacy database migration skipped (%s): %s", migration_result.status, migration_result.detail)

# Initialize database
await init_db()
Expand Down
Loading
Loading