Spaces:
Sleeping
Sleeping
Commit
·
deb575f
1
Parent(s):
d51358c
Updated docker for /data issue
Browse files- .dockerignore +0 -1
- Dockerfile +4 -0
- backend/app/config/settings.py +41 -12
.dockerignore
CHANGED
|
@@ -6,4 +6,3 @@ __pycache__/
|
|
| 6 |
node_modules/
|
| 7 |
web/node_modules/
|
| 8 |
web/.vite/
|
| 9 |
-
data/
|
|
|
|
| 6 |
node_modules/
|
| 7 |
web/node_modules/
|
| 8 |
web/.vite/
|
|
|
Dockerfile
CHANGED
|
@@ -14,6 +14,10 @@ ENV PYTHONUNBUFFERED=1 \
|
|
| 14 |
DATA_DIR=/data
|
| 15 |
WORKDIR /app
|
| 16 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
# (optional) if you hit build issues with some libs
|
| 18 |
RUN apt-get update && apt-get install -y --no-install-recommends build-essential && rm -rf /var/lib/apt/lists/*
|
| 19 |
|
|
|
|
| 14 |
DATA_DIR=/data
|
| 15 |
WORKDIR /app
|
| 16 |
|
| 17 |
+
ENV PORT=7860 DATA_DIR=/data
|
| 18 |
+
|
| 19 |
+
RUN mkdir -p /data/uploads
|
| 20 |
+
|
| 21 |
# (optional) if you hit build issues with some libs
|
| 22 |
RUN apt-get update && apt-get install -y --no-install-recommends build-essential && rm -rf /var/lib/apt/lists/*
|
| 23 |
|
backend/app/config/settings.py
CHANGED
|
@@ -1,15 +1,34 @@
|
|
| 1 |
from pathlib import Path
|
| 2 |
from pydantic import Field
|
| 3 |
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
|
|
| 4 |
|
| 5 |
-
# Resolve repo root
|
| 6 |
REPO_ROOT = Path(__file__).resolve().parents[3]
|
| 7 |
|
| 8 |
-
def
|
| 9 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
|
| 11 |
-
def
|
| 12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
|
| 14 |
def _default_frontend_dist() -> Path:
|
| 15 |
return (REPO_ROOT / "web" / "dist").resolve()
|
|
@@ -22,16 +41,16 @@ class Settings(BaseSettings):
|
|
| 22 |
populate_by_name=True,
|
| 23 |
)
|
| 24 |
|
| 25 |
-
#
|
| 26 |
OPENAI_API_KEY: str | None = None
|
| 27 |
OPENAI_MODEL_AGENT: str = "gpt-4o"
|
| 28 |
OPENAI_MODEL_CLASSIFIER: str = "gpt-4o-mini"
|
| 29 |
|
| 30 |
-
# Paths
|
| 31 |
DATA_DIR: Path = Field(default_factory=_default_data_dir)
|
| 32 |
-
REPORTS_DB: Path
|
| 33 |
-
SESSIONS_DB: Path
|
| 34 |
-
UPLOADS_DIR: Path =
|
| 35 |
FRONTEND_DIST: Path = Field(default_factory=_default_frontend_dist)
|
| 36 |
|
| 37 |
# Defaults
|
|
@@ -45,10 +64,20 @@ class Settings(BaseSettings):
|
|
| 45 |
nvidia_api_key: str | None = None
|
| 46 |
|
| 47 |
def ensure_dirs(self) -> None:
|
| 48 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
self.DATA_DIR = self.DATA_DIR.resolve()
|
|
|
|
|
|
|
| 50 |
self.UPLOADS_DIR = self.UPLOADS_DIR.resolve()
|
| 51 |
-
|
| 52 |
self.DATA_DIR.mkdir(parents=True, exist_ok=True)
|
| 53 |
self.UPLOADS_DIR.mkdir(parents=True, exist_ok=True)
|
| 54 |
|
|
|
|
| 1 |
from pathlib import Path
|
| 2 |
from pydantic import Field
|
| 3 |
from pydantic_settings import BaseSettings, SettingsConfigDict
|
| 4 |
+
import os, tempfile
|
| 5 |
|
| 6 |
+
# Resolve the repo root when running outside Docker
|
| 7 |
REPO_ROOT = Path(__file__).resolve().parents[3]
|
| 8 |
|
| 9 |
+
def _writable_dir(candidates: list[Path]) -> Path:
|
| 10 |
+
for p in candidates:
|
| 11 |
+
try:
|
| 12 |
+
p.mkdir(parents=True, exist_ok=True)
|
| 13 |
+
t = p / ".write_test"
|
| 14 |
+
t.write_text("ok", encoding="utf-8")
|
| 15 |
+
t.unlink(missing_ok=True)
|
| 16 |
+
return p
|
| 17 |
+
except Exception:
|
| 18 |
+
continue
|
| 19 |
+
raise RuntimeError(f"No writable data dir from: {candidates!r}")
|
| 20 |
|
| 21 |
+
def _default_data_dir() -> Path:
|
| 22 |
+
# 1) Respect env if provided
|
| 23 |
+
if os.getenv("DATA_DIR"):
|
| 24 |
+
return Path(os.getenv("DATA_DIR")).resolve()
|
| 25 |
+
# 2) Prefer /data inside containers (Docker/HF Spaces)
|
| 26 |
+
candidates = [Path("/data")]
|
| 27 |
+
# 3) Repo local ./data for local dev
|
| 28 |
+
candidates.append((REPO_ROOT / "data").resolve())
|
| 29 |
+
# 4) Last resort: /tmp
|
| 30 |
+
candidates.append(Path(tempfile.gettempdir()) / "pulsemaps" / "data")
|
| 31 |
+
return _writable_dir(candidates)
|
| 32 |
|
| 33 |
def _default_frontend_dist() -> Path:
|
| 34 |
return (REPO_ROOT / "web" / "dist").resolve()
|
|
|
|
| 41 |
populate_by_name=True,
|
| 42 |
)
|
| 43 |
|
| 44 |
+
# API / Models
|
| 45 |
OPENAI_API_KEY: str | None = None
|
| 46 |
OPENAI_MODEL_AGENT: str = "gpt-4o"
|
| 47 |
OPENAI_MODEL_CLASSIFIER: str = "gpt-4o-mini"
|
| 48 |
|
| 49 |
+
# Paths
|
| 50 |
DATA_DIR: Path = Field(default_factory=_default_data_dir)
|
| 51 |
+
REPORTS_DB: Path | None = None
|
| 52 |
+
SESSIONS_DB: Path | None = None
|
| 53 |
+
UPLOADS_DIR: Path | None = None
|
| 54 |
FRONTEND_DIST: Path = Field(default_factory=_default_frontend_dist)
|
| 55 |
|
| 56 |
# Defaults
|
|
|
|
| 64 |
nvidia_api_key: str | None = None
|
| 65 |
|
| 66 |
def ensure_dirs(self) -> None:
|
| 67 |
+
# Fill path fields if not set, then ensure they exist
|
| 68 |
+
if self.REPORTS_DB is None:
|
| 69 |
+
self.REPORTS_DB = (self.DATA_DIR / "pulsemaps_reports.db")
|
| 70 |
+
if self.SESSIONS_DB is None:
|
| 71 |
+
self.SESSIONS_DB = (self.DATA_DIR / "pulsemap_sessions.db")
|
| 72 |
+
if self.UPLOADS_DIR is None:
|
| 73 |
+
self.UPLOADS_DIR = (self.DATA_DIR / "uploads")
|
| 74 |
+
|
| 75 |
+
# Absolute, existing
|
| 76 |
self.DATA_DIR = self.DATA_DIR.resolve()
|
| 77 |
+
self.REPORTS_DB = self.REPORTS_DB.resolve()
|
| 78 |
+
self.SESSIONS_DB = self.SESSIONS_DB.resolve()
|
| 79 |
self.UPLOADS_DIR = self.UPLOADS_DIR.resolve()
|
| 80 |
+
|
| 81 |
self.DATA_DIR.mkdir(parents=True, exist_ok=True)
|
| 82 |
self.UPLOADS_DIR.mkdir(parents=True, exist_ok=True)
|
| 83 |
|