58 lines
1.4 KiB
Python
58 lines
1.4 KiB
Python
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
from sqlalchemy.engine.url import make_url
|
|
|
|
|
|
def get_sqlite_db_dir(database_url: str) -> Path:
|
|
"""
|
|
Returns the directory containing the SQLite database file.
|
|
|
|
For non-SQLite or in-memory DBs, falls back to ./data relative to CWD.
|
|
"""
|
|
|
|
fallback = Path("./data").resolve()
|
|
|
|
try:
|
|
url = make_url(database_url)
|
|
except Exception:
|
|
return fallback
|
|
|
|
backend = (url.get_backend_name() or "").lower()
|
|
if backend != "sqlite":
|
|
return fallback
|
|
|
|
db = str(url.database or "").strip()
|
|
if not db or db == ":memory:":
|
|
return fallback
|
|
|
|
p = Path(db)
|
|
if not p.is_absolute():
|
|
p = Path.cwd() / p
|
|
return p.resolve().parent
|
|
|
|
|
|
def make_file_ref(path: Path, *, base_dir: Path) -> str:
|
|
base = base_dir.resolve()
|
|
target = path.resolve()
|
|
return target.relative_to(base).as_posix()
|
|
|
|
|
|
def resolve_file_ref(file_ref: str, *, base_dir: Path) -> Path:
|
|
raw = (file_ref or "").strip()
|
|
if not raw:
|
|
raise ValueError("file_ref is required")
|
|
|
|
rel = Path(raw)
|
|
if rel.is_absolute():
|
|
raise ValueError("file_ref must be relative")
|
|
if any(part == ".." for part in rel.parts):
|
|
raise ValueError("file_ref must not contain '..'")
|
|
|
|
base = base_dir.resolve()
|
|
target = (base / rel).resolve()
|
|
target.relative_to(base) # raises if escaped
|
|
return target
|
|
|