diff --git a/backend/app/config.py b/backend/app/config.py index bdde0ca..3ecbcfb 100644 --- a/backend/app/config.py +++ b/backend/app/config.py @@ -9,7 +9,7 @@ REPO_ROOT = BACKEND_ROOT.parent -def _default_data_dir() -> Path: +def get_data_dir() -> Path: configured_data_dir = os.getenv("DATA_DIR") if configured_data_dir: candidate = Path(configured_data_dir) @@ -26,7 +26,8 @@ def _default_data_dir() -> Path: return Path("/tmp/gitplant-data") -def _ensure_writable_data_dir(path: Path) -> Path: +def ensure_data_dir() -> Path: + path = get_data_dir() try: path.mkdir(parents=True, exist_ok=True) return path @@ -37,7 +38,7 @@ def _ensure_writable_data_dir(path: Path) -> Path: # Vercel serverless functions run on a read-only filesystem except for /tmp. -DEFAULT_DATA_DIR = _ensure_writable_data_dir(_default_data_dir()) +DEFAULT_DATA_DIR = get_data_dir() DEFAULT_DB_PATH = DEFAULT_DATA_DIR / "edms.db" DEFAULT_DATABASE_URL = f"sqlite:///{quote(str(DEFAULT_DB_PATH), safe='/')}" DEFAULT_STORAGE_DIR = DEFAULT_DATA_DIR / "documents" @@ -103,17 +104,32 @@ def sanitize_database_url(database_url: str) -> str: return parsed._replace(netloc=netloc).geturl() -def _ensure_path(path: str) -> Path: +def _resolve_path(path: str) -> Path: configured = Path(path) if not configured.is_absolute(): configured = (BACKEND_ROOT / configured).resolve() + return configured + + +def _ensure_path(path: str) -> Path: + configured = _resolve_path(path) configured.mkdir(parents=True, exist_ok=True) return configured -def get_document_storage_path() -> Path: - return _ensure_path(settings.document_storage_dir) +def get_document_storage_path(*, ensure_exists: bool = True) -> Path: + if settings.document_storage_dir == str(DEFAULT_STORAGE_DIR): + base_path = ensure_data_dir() / "documents" if ensure_exists else get_data_dir() / "documents" + if ensure_exists: + base_path.mkdir(parents=True, exist_ok=True) + return base_path + return _ensure_path(settings.document_storage_dir) if ensure_exists else _resolve_path(settings.document_storage_dir) -def get_plant_storage_path() -> Path: - return _ensure_path(settings.plant_storage_dir) +def get_plant_storage_path(*, ensure_exists: bool = True) -> Path: + if settings.plant_storage_dir == str(DEFAULT_PLANT_STORAGE_DIR): + base_path = ensure_data_dir() / "plant" if ensure_exists else get_data_dir() / "plant" + if ensure_exists: + base_path.mkdir(parents=True, exist_ok=True) + return base_path + return _ensure_path(settings.plant_storage_dir) if ensure_exists else _resolve_path(settings.plant_storage_dir) diff --git a/backend/app/db.py b/backend/app/db.py index a3ff2f6..583c7d9 100644 --- a/backend/app/db.py +++ b/backend/app/db.py @@ -3,13 +3,13 @@ from app.config import resolve_sqlite_path, settings sqlite_path = resolve_sqlite_path(settings.database_url) -if sqlite_path is not None: - sqlite_path.parent.mkdir(parents=True, exist_ok=True) engine = create_engine(settings.database_url, echo=False) def init_db() -> None: + if sqlite_path is not None: + sqlite_path.parent.mkdir(parents=True, exist_ok=True) SQLModel.metadata.create_all(engine) diff --git a/backend/app/routers/documents.py b/backend/app/routers/documents.py index 200fa9a..7b7f176 100644 --- a/backend/app/routers/documents.py +++ b/backend/app/routers/documents.py @@ -54,7 +54,7 @@ router = APIRouter(prefix="/documents", tags=["documents"]) -DOCUMENT_STORAGE_DIR = get_document_storage_path() +DOCUMENT_STORAGE_DIR = get_document_storage_path(ensure_exists=False) def record_audit_event( @@ -91,7 +91,7 @@ def _clear_documents_and_storage(session: Session) -> None: session.exec(delete(Document)) session.commit() - for path in DOCUMENT_STORAGE_DIR.glob("*.pdf"): + for path in get_document_storage_path(ensure_exists=False).glob("*.pdf"): path.unlink(missing_ok=True) @@ -352,7 +352,7 @@ def create_documents_from_pdf_upload( session.refresh(document) file.file.seek(0) - destination = DOCUMENT_STORAGE_DIR / f"{document.id}.pdf" + destination = get_document_storage_path() / f"{document.id}.pdf" with destination.open("wb") as output_stream: output_stream.write(file.file.read()) document.file_path = str(destination) @@ -395,7 +395,7 @@ def upload_plant_revision( raise HTTPException(status_code=400, detail="Only PDF files are supported") next_revision = _next_revision(document.current_revision) - destination = DOCUMENT_STORAGE_DIR / f"{document_id}.pdf" + destination = get_document_storage_path() / f"{document_id}.pdf" file.file.seek(0) destination.write_bytes(file.file.read()) diff --git a/backend/app/routers/projects.py b/backend/app/routers/projects.py index 75ec9e8..258b875 100644 --- a/backend/app/routers/projects.py +++ b/backend/app/routers/projects.py @@ -30,7 +30,10 @@ ACTIVE_WORKING_STATUSES = {"WORKING", "READY"} WORKING_STORAGE_DIR = (BACKEND_ROOT / "storage" / "projects").resolve() -WORKING_STORAGE_DIR.mkdir(parents=True, exist_ok=True) + + +def ensure_working_storage_dir() -> None: + WORKING_STORAGE_DIR.mkdir(parents=True, exist_ok=True) def _to_working_response( @@ -290,6 +293,7 @@ def upload_working_revision_file( if not filename.endswith(".pdf"): raise HTTPException(status_code=400, detail="Only PDF files are supported") + ensure_working_storage_dir() destination = WORKING_STORAGE_DIR / f"{project.id}-{working.id}.pdf" file.file.seek(0) destination.write_bytes(file.file.read())