Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 24 additions & 8 deletions backend/app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand All @@ -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()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep writable fallback when deriving DEFAULT_DATABASE_URL

Assigning DEFAULT_DATA_DIR = get_data_dir() removes the previous writability fallback at config load, so when DATA_DIR points to a read-only or non-creatable path (e.g. serverless filesystems outside /tmp), settings.database_url is built with that unwritable location and startup now fails in init_db() when creating the SQLite parent directory. This is a regression from the prior behavior that transparently fell back to /tmp/gitplant-data; the default DB path should still be derived from a writable directory.

Useful? React with 👍 / 👎.

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"
Expand Down Expand Up @@ -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)
4 changes: 2 additions & 2 deletions backend/app/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)


Expand Down
8 changes: 4 additions & 4 deletions backend/app/routers/documents.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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)


Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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())

Expand Down
6 changes: 5 additions & 1 deletion backend/app/routers/projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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())
Expand Down