From 4a629db036ad0aca480119986b169c4328356b4c Mon Sep 17 00:00:00 2001 From: ThorSP Date: Sat, 22 Aug 2026 13:27:12 +0300 Subject: [PATCH] chore: point the app at thorsp.net instead of the DDNS hostname MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Telemetry, feedback and the overrides manifest now call thorsp.net. Same nginx, same certificate (it covers thorsp.ddns.net, thorsp.net and www.thorsp.net), same WAN address - just a real domain rather than a DDNS hostname. ⚠ thorsp.ddns.net cannot be retired. Every build already in the field has that hostname compiled in and will keep calling it forever, so it has to keep resolving and serving. This only changes where *new* releases point. Verified against the live server through the real client code: the overrides manifest fetch returns both overrides with their quality variants, the feedback GET returns 200, and a telemetry ping returns 200. Also documents in CLAUDE.md which paths are public and which are restricted: the admin surface (/login, /feedback-dashboard, /feedback-detail/, /user/, /feedback/image/) is now LAN + WireGuard only, while the client API paths stay deliberately public. The feedback dashboard had never had the restriction the other two dashboards did, so its login form and every user's feedback were reachable from the internet - and a logged-in session can post replies that reach users as if from the operator. Confirmed from an off-network machine before and after: 302 then 403, with /overrides, /feedback/ and /override-file/ still 200. 289 tests, full dry-run set. --- CLAUDE.md | 11 +++++++---- app/backend/feedback.py | 7 ++++++- app/backend/overrides.py | 7 ++++++- app/backend/telemetry.py | 7 ++++++- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index ed3b8e4..17adbc8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -62,9 +62,9 @@ The build bundles config defaults from `config/`, platform binaries for mpv/ffmp - `auto_downloader.py`: Automatic download scheduling for upcoming Sabbath (Fri/Sat). - `updater.py`: Talks to the GitHub releases API (`ThorSPB/YoutubeWeekly`). Provides current-release check, paginated listing of available versions for rollback (`MIN_ROLLBACK_VERSION = 1.1.0`), and asset downloader with progress callback. Per-platform asset selection (`win64`, `macos-arm64`, `macos-intel`, `linux-x64`). - `update_bootstrap.py`: External process that swaps a downloaded ZIP over the running install. Uses `WaitForSingleObject` on Windows for reliable exit detection; renames the running bootstrap before extraction so it can update itself. - - `telemetry.py`: Anonymous usage analytics. Persistent install ID in `CONFIG_DIR/install_id`. Geo lookup happens **on the client** via `ip-api.com` (HTTP, free tier) — only the resolved `city`/`country` are sent, never the IP. Posts to `https://thorsp.ddns.net/ytw-telemetry/ping`. Gated on `send_telemetry` setting. - - `overrides.py`: Server-driven video overrides. When a channel titles a video with the wrong date (a wrong *year*, typically) the date matcher can't find it, so the operator publishes an override from the telemetry dashboard pointing at the right video. Two modes: **fallback** (used only when the app's own search finds nothing) and **force** (beats the search, and replaces a video already downloaded for that Sabbath). Sources are a link (yt-dlp) or a video file hosted on the Pi (streamed over HTTP with yt-dlp-shaped progress events). Discovery is a conditional GET against `https://thorsp.ddns.net/ytw-telemetry/overrides` — an unchanged poll is a bodyless 304. Identity-free (no install ID), so it runs regardless of the telemetry opt-out. See "Video Overrides" below. - - `feedback.py`: In-app feedback. Local thread cache at `CONFIG_DIR/feedback.json`. Screenshots compressed to JPEG (≤500 KB, max 1280×720) before upload. Posts to `https://thorsp.ddns.net/ytw-telemetry/feedback`. Reuses install ID + sanitized settings from telemetry module so a single anonymous identity links pings and feedback. + - `telemetry.py`: Anonymous usage analytics. Persistent install ID in `CONFIG_DIR/install_id`. Geo lookup happens **on the client** via `ip-api.com` (HTTP, free tier) — only the resolved `city`/`country` are sent, never the IP. Posts to `https://thorsp.net/ytw-telemetry/ping`. Gated on `send_telemetry` setting. + - `overrides.py`: Server-driven video overrides. When a channel titles a video with the wrong date (a wrong *year*, typically) the date matcher can't find it, so the operator publishes an override from the telemetry dashboard pointing at the right video. Two modes: **fallback** (used only when the app's own search finds nothing) and **force** (beats the search, and replaces a video already downloaded for that Sabbath). Sources are a link (yt-dlp) or a video file hosted on the Pi (streamed over HTTP with yt-dlp-shaped progress events). Discovery is a conditional GET against `https://thorsp.net/ytw-telemetry/overrides` — an unchanged poll is a bodyless 304. Identity-free (no install ID), so it runs regardless of the telemetry opt-out. See "Video Overrides" below. + - `feedback.py`: In-app feedback. Local thread cache at `CONFIG_DIR/feedback.json`. Screenshots compressed to JPEG (≤500 KB, max 1280×720) before upload. Posts to `https://thorsp.net/ytw-telemetry/feedback`. Reuses install ID + sanitized settings from telemetry module so a single anonymous identity links pings and feedback. - `progress.py`: Download-progress model — see Key Design Patterns #3. No tkinter import, so headless callers and tests can use it. - `changelog.py`: Locates the changelog (localized `CHANGELOG_.md` first, English as fallback), splits it into `## ` sections and works out which are **new to this user**. `notes_since(content, previous, current)` is inclusive of the current version and exclusive of the one they had, so a 1.4.0 → 1.5.1 jump shows the 1.5.0 notes too; `all_notes()` backs the Settings → Advanced → Release Notes reader. Non-version headings (`## Unreleased`) are parsed but excluded from both. - `logger.py`: Logging setup with timestamped log files. @@ -219,6 +219,9 @@ matcher can't bridge a five-year gap. - Optional: Integrated mpv with custom arguments - Supports fullscreen (via Lua script), volume (0–130), screen selection, and arbitrary mpv args +### Server hostname +The app calls `https://thorsp.net/ytw-telemetry/...` (telemetry, feedback, overrides). It used to use `thorsp.ddns.net`, and **releases already in the field still do** — both names resolve to the same nginx and are on the same certificate, so that hostname must keep working indefinitely rather than being retired. Admin pages (`/login`, `/feedback-dashboard`, `/feedback-detail/`, `/user/`, `/feedback/image/`) are restricted to LAN + WireGuard; the client API paths are deliberately public. + ### Update Checking - On startup, queries the GitHub releases API for newer versions - Compares semantic version from `config.__version__` against the latest tag (skipped when `__version__ == "dev"`) @@ -232,7 +235,7 @@ matcher can't bridge a five-year gap. - Threaded conversation view with the developer; reply support; unread badge - Up to multiple screenshots per message (drag-and-drop) - System info (OS, version, hardware via `psutil`) is included with each thread to help diagnose issues -- Local cache at `CONFIG_DIR/feedback.json`; server at `https://thorsp.ddns.net/ytw-telemetry/feedback` +- Local cache at `CONFIG_DIR/feedback.json`; server at `https://thorsp.net/ytw-telemetry/feedback` ## Configuration Files diff --git a/app/backend/feedback.py b/app/backend/feedback.py index b27b98f..f6a1a9f 100644 --- a/app/backend/feedback.py +++ b/app/backend/feedback.py @@ -14,7 +14,12 @@ from app.backend.config import CONFIG_DIR, __version__ from app.backend.telemetry import _get_install_id, _get_location, _sanitize_settings -FEEDBACK_URL = "https://thorsp.ddns.net/ytw-telemetry/feedback" +# thorsp.net, not thorsp.ddns.net: same nginx, same certificate (the cert +# covers thorsp.ddns.net, thorsp.net and www.thorsp.net), but a real domain +# rather than a DDNS hostname. Builds already in the field keep calling the +# thorsp.ddns.net form, so **that hostname has to keep working indefinitely** +# - it cannot be retired once a release has shipped with it baked in. +FEEDBACK_URL = "https://thorsp.net/ytw-telemetry/feedback" FEEDBACK_FILE = os.path.join(CONFIG_DIR, "feedback.json") diff --git a/app/backend/overrides.py b/app/backend/overrides.py index ec3e707..bf027c6 100644 --- a/app/backend/overrides.py +++ b/app/backend/overrides.py @@ -48,7 +48,12 @@ from app.backend.config import CONFIG_DIR from app.backend.progress import PROGRESS_PLAN_STATUS -OVERRIDES_URL = "https://thorsp.ddns.net/ytw-telemetry/overrides" +# thorsp.net, not thorsp.ddns.net: same nginx, same certificate (the cert +# covers thorsp.ddns.net, thorsp.net and www.thorsp.net), but a real domain +# rather than a DDNS hostname. Builds already in the field keep calling the +# thorsp.ddns.net form, so **that hostname has to keep working indefinitely** +# - it cannot be retired once a release has shipped with it baked in. +OVERRIDES_URL = "https://thorsp.net/ytw-telemetry/overrides" OVERRIDES_CACHE_FILE = os.path.join(CONFIG_DIR, "overrides.json") # Which override produced which file, per Sabbath. Deliberately NOT stored in diff --git a/app/backend/telemetry.py b/app/backend/telemetry.py index 5111f6d..7fac04c 100644 --- a/app/backend/telemetry.py +++ b/app/backend/telemetry.py @@ -11,7 +11,12 @@ from app.backend.config import CONFIG_DIR, __version__, load_settings -TELEMETRY_URL = "https://thorsp.ddns.net/ytw-telemetry/ping" +# thorsp.net, not thorsp.ddns.net: same nginx, same certificate (the cert +# covers thorsp.ddns.net, thorsp.net and www.thorsp.net), but a real domain +# rather than a DDNS hostname. Builds already in the field keep calling the +# thorsp.ddns.net form, so **that hostname has to keep working indefinitely** +# - it cannot be retired once a release has shipped with it baked in. +TELEMETRY_URL = "https://thorsp.net/ytw-telemetry/ping" INSTALL_ID_FILE = os.path.join(CONFIG_DIR, "install_id") GEO_API_URL = "http://ip-api.com/json/?fields=city,country" # Free tier requires HTTP