fix: ensure data persistence across reinstallations
- Add entrypoint.sh: creates /app/data, stamps legacy DBs, runs alembic upgrade head - Update Dockerfile to use new entrypoint and pre-create /app/data - Change default DATABASE_URL to sqlite:///./data/dmarq.db - Add _ensure_sqlite_dir() to database.py for automatic directory creation - Update docker-compose.yml with app_data named volume for /app/data - Add 6 tests for _ensure_sqlite_dir and updated default URL Agent-Logs-Url: https://github.com/christianlouis/dmarq/sessions/2bac317b-97a2-450b-84f1-3e316f7ef54c Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
This commit is contained in:
@@ -23,7 +23,9 @@ class Settings(BaseSettings):
|
||||
API_V1_STR: str = "/api/v1"
|
||||
|
||||
# Database
|
||||
DATABASE_URL: str = "sqlite:///./dmarq.db"
|
||||
# Default to a sub-directory so the SQLite file lives in a location that
|
||||
# can be persisted via a Docker volume mount (e.g. /app/data).
|
||||
DATABASE_URL: str = "sqlite:///./data/dmarq.db"
|
||||
|
||||
# JWT Authentication
|
||||
SECRET_KEY: Optional[str] = None
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import os
|
||||
from typing import Generator
|
||||
from urllib.parse import urlparse, urlunparse
|
||||
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.engine import make_url
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
@@ -30,10 +32,34 @@ def _make_sync_db_url(url: str) -> str:
|
||||
return urlunparse(parsed._replace(scheme=sync_scheme))
|
||||
|
||||
|
||||
def _ensure_sqlite_dir(url: str) -> None:
|
||||
"""Create the parent directory for a SQLite database file if needed.
|
||||
|
||||
For SQLite URLs (``sqlite:///relative/path`` or ``sqlite:////absolute/path``),
|
||||
the parent directory must exist before SQLAlchemy tries to open (or create)
|
||||
the file. This is a no-op for in-memory databases (``sqlite://``) and for
|
||||
non-SQLite URLs.
|
||||
"""
|
||||
sa_url = make_url(url)
|
||||
if not sa_url.drivername.startswith("sqlite"):
|
||||
return
|
||||
db_path = sa_url.database
|
||||
if not db_path or db_path == ":memory:":
|
||||
return # in-memory – nothing to create
|
||||
parent = os.path.dirname(db_path)
|
||||
if parent:
|
||||
os.makedirs(parent, exist_ok=True)
|
||||
|
||||
|
||||
settings = get_settings()
|
||||
|
||||
_sync_url = _make_sync_db_url(settings.DATABASE_URL)
|
||||
|
||||
# Ensure the parent directory exists before SQLAlchemy tries to open the file
|
||||
_ensure_sqlite_dir(_sync_url)
|
||||
|
||||
# Configure SQLAlchemy (normalise async driver schemes to their sync equivalents)
|
||||
engine = create_engine(_make_sync_db_url(settings.DATABASE_URL), pool_pre_ping=True)
|
||||
engine = create_engine(_sync_url, pool_pre_ping=True)
|
||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
|
||||
# Create base class for SQLAlchemy models
|
||||
|
||||
Reference in New Issue
Block a user