From 08258f81d97645192eb43571d950633772df3eff Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Mon, 20 Jul 2026 20:07:15 -0400 Subject: [PATCH] config: fix media path at /var/thoughtsync (no env knob) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The media/data location is no longer configurable — DATA_DIR is a fixed constant (/var/thoughtsync) and THOUGHTSYNC_MEDIA_ROOT is removed, so a mutable path can't drift from where the volume is mounted. media_root() = DATA_DIR/media. Both compose files drop THOUGHTSYNC_DATA_DIR and mount the data volume at /var/thoughtsync (was the contradictory /data). conftest drops the stale MEDIA_ROOT monkeypatch (create_app never reads DATA_DIR). Net env surface: THOUGHTSYNC_DATABASE_URL (required) + THOUGHTSYNC_SECRET_KEY (optional break-glass). Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01FRgehjoz7Yv8LkUfADxACm --- docker-compose.dev.yml | 3 +-- docker-compose.yml | 3 +-- src/thoughtsync/config.py | 16 +++++++++------- tests/conftest.py | 9 ++++----- 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 60e7ed8..f2673b9 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -31,14 +31,13 @@ services: condition: service_healthy environment: THOUGHTSYNC_DATABASE_URL: postgresql+asyncpg://thoughtsync:thoughtsync@db:5432/thoughtsync - THOUGHTSYNC_DATA_DIR: /data PYTHONPATH: /app/src volumes: - ./pyproject.toml:/app/pyproject.toml - ./alembic.ini:/app/alembic.ini - ./alembic:/app/alembic - ./src:/app/src - - thoughtsync-dev-data:/data + - thoughtsync-dev-data:/var/thoughtsync ports: - "5000:5000" # Install deps, run migrations, then serve with live reload on the mounted src. diff --git a/docker-compose.yml b/docker-compose.yml index 9f4c7a0..28f14dc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -24,9 +24,8 @@ services: condition: service_healthy environment: THOUGHTSYNC_DATABASE_URL: postgresql+asyncpg://thoughtsync:thoughtsync@db:5432/thoughtsync - THOUGHTSYNC_DATA_DIR: /data volumes: - - thoughtsync-data:/data + - thoughtsync-data:/var/thoughtsync ports: - "5000:5000" diff --git a/src/thoughtsync/config.py b/src/thoughtsync/config.py index 1e5f9b8..1fcdc32 100644 --- a/src/thoughtsync/config.py +++ b/src/thoughtsync/config.py @@ -8,19 +8,21 @@ class Config: """Bootstrap configuration. For a basic install, ``THOUGHTSYNC_DATABASE_URL`` is the ONLY required env var — - every other tunable lives in the DB-backed Settings UI (rule 25). The remaining - env vars are optional "break-glass" / bootstrap items: + every other tunable lives in the DB-backed Settings UI (rule 25). The only other + env var is an optional "break-glass" item: - ``THOUGHTSYNC_SECRET_KEY`` — optional override for the cookie-signing secret. If unset, a key is generated and persisted in the DB (see ``thoughtsync.settings.load_or_create_secret_key``), so sessions survive restarts with no volume required. - - ``THOUGHTSYNC_DATA_DIR`` — optional, defaults to ``/var/thoughtsync``. Only - used for uploaded media (M2); irrelevant to a basic text-notes install. + + Uploaded media lives under ``DATA_DIR`` — a fixed, authoritative path + (``/var/thoughtsync``), intentionally NOT configurable (a mutable data path only + invites breakage). Mount a volume there if you want uploads to persist across + container recreation; a text-notes-only install never writes to it. """ - DATA_DIR = os.environ.get("THOUGHTSYNC_DATA_DIR", "/var/thoughtsync") - MEDIA_ROOT = os.environ.get("THOUGHTSYNC_MEDIA_ROOT", "") + DATA_DIR = "/var/thoughtsync" DATABASE_URL = os.environ.get( "THOUGHTSYNC_DATABASE_URL", @@ -29,7 +31,7 @@ class Config: @classmethod def media_root(cls) -> Path: - return Path(cls.MEDIA_ROOT or os.path.join(cls.DATA_DIR, "media")) + return Path(cls.DATA_DIR) / "media" @classmethod def secret_key_env(cls) -> str | None: diff --git a/tests/conftest.py b/tests/conftest.py index ce0bdc5..817c4ff 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5,9 +5,8 @@ from thoughtsync.config import Config @pytest.fixture(autouse=True) def _isolated_data_dir(tmp_path, monkeypatch): - """Point DATA_DIR at a writable temp dir so create_app() can generate its - signing key without needing /var/thoughtsync to exist (DB-free unit tests).""" - data_dir = tmp_path / "data" - monkeypatch.setattr(Config, "DATA_DIR", str(data_dir)) - monkeypatch.setattr(Config, "MEDIA_ROOT", str(data_dir / "media")) + """Keep any media writes on an isolated temp dir rather than the fixed + /var/thoughtsync. DB-free unit tests never actually hit it (create_app takes its + signing key from env-or-random, not a file), but this stays defensive.""" + monkeypatch.setattr(Config, "DATA_DIR", str(tmp_path / "data")) yield