Merge branch 'chore/rename-subfeed-to-siftlode' into dev

This commit is contained in:
npeter83 2026-06-21 06:53:12 +02:00
commit 4e0c35738a
31 changed files with 67 additions and 67 deletions

View file

@ -1,9 +1,9 @@
# ---- Copy this file to .env and fill in the values ----
# Postgres (used by docker-compose for the db service and the DATABASE_URL)
POSTGRES_USER=subfeed
POSTGRES_USER=siftlode
POSTGRES_PASSWORD=change-this-password
POSTGRES_DB=subfeed
POSTGRES_DB=siftlode
# Host port the app is exposed on (http://localhost:<APP_PORT>)
APP_PORT=8080
@ -51,7 +51,7 @@ SMTP_FROM=
# --- Deployment role ---
# DATABASE_URL is set automatically by docker-compose.yml / docker-compose.server.yml to the
# bundled `db` service. Override it only for local dev against the central server DB, e.g.:
# DATABASE_URL=postgresql+psycopg://subfeed:<password>@your-db-host:5432/subfeed
# DATABASE_URL=postgresql+psycopg://siftlode:<password>@your-db-host:5432/siftlode
# Exactly one instance may run the background scheduler. The central server keeps it on; every
# other instance (local dev, etc.) must set SCHEDULER_ENABLED=false. The compose files set this
# for you (server=true via docker-compose.server.yml, local=false via docker-compose.localdev.yml).

View file

@ -90,7 +90,7 @@ sync.
depend on a refresh token (which expires after 7 days while the OAuth screen is in *Testing*).
- **Local dev** (`docker-compose.localdev.yml`): webapp only, no local DB, no scheduler. In `.env`
set `DATABASE_URL` to the central DB (e.g. `…@your-db-host:5432/subfeed`), then
set `DATABASE_URL` to the central DB (e.g. `…@your-db-host:5432/siftlode`), then
`docker compose -f docker-compose.localdev.yml up --build` and browse http://localhost:8080.
**Exactly one instance may run the scheduler.** The server keeps `SCHEDULER_ENABLED=true`; every

View file

@ -71,7 +71,7 @@ BASE_SCOPES = "openid email profile"
READ_SCOPES = f"{BASE_SCOPES} {READ_SCOPE}"
WRITE_SCOPES = f"{BASE_SCOPES} {READ_SCOPE} {WRITE_SCOPE}"
log = logging.getLogger("subfeed.auth")
log = logging.getLogger("siftlode.auth")
router = APIRouter(prefix="/auth", tags=["auth"])

View file

@ -30,7 +30,7 @@ class Settings(BaseSettings):
git_sha: str = "unknown"
build_date: str = ""
database_url: str = "postgresql+psycopg://subfeed:subfeed@db:5432/subfeed"
database_url: str = "postgresql+psycopg://siftlode:siftlode@db:5432/siftlode"
# Session cookie signing key.
secret_key: str = _DEFAULT_SECRET_KEY

View file

@ -14,7 +14,7 @@ from app import sysconfig
from app.config import settings
from app.db import SessionLocal
log = logging.getLogger("subfeed.email")
log = logging.getLogger("siftlode.email")
def _smtp() -> dict:

View file

@ -5,20 +5,20 @@ from pathlib import Path
from fastapi import FastAPI, HTTPException
# When started via uvicorn --log-config the "subfeed" logger is already configured
# When started via uvicorn --log-config the "siftlode" logger is already configured
# (see log_config.json). This block is a timestamped fallback for other entrypoints
# (tests, scripts) so our logs are never silently dropped.
_subfeed_logger = logging.getLogger("subfeed")
if not _subfeed_logger.handlers:
_siftlode_logger = logging.getLogger("siftlode")
if not _siftlode_logger.handlers:
_handler = logging.StreamHandler(sys.stdout)
_handler.setFormatter(
logging.Formatter("%(asctime)s %(levelname)-5s [%(name)s] %(message)s")
)
_subfeed_logger.addHandler(_handler)
_subfeed_logger.setLevel(logging.INFO)
_subfeed_logger.propagate = False
_siftlode_logger.addHandler(_handler)
_siftlode_logger.setLevel(logging.INFO)
_siftlode_logger.propagate = False
log = logging.getLogger("subfeed.app")
log = logging.getLogger("siftlode.app")
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse, JSONResponse
from fastapi.staticfiles import StaticFiles

View file

@ -28,7 +28,7 @@ from app.youtube.client import YouTubeClient, YouTubeError
router = APIRouter(prefix="/api/channels", tags=["channels"])
log = logging.getLogger("subfeed.api")
log = logging.getLogger("siftlode.api")
@router.get("")

View file

@ -27,7 +27,7 @@ from app.sync.runner import (
run_subscription_resync,
)
logger = logging.getLogger("subfeed.scheduler")
logger = logging.getLogger("siftlode.scheduler")
_scheduler: BackgroundScheduler | None = None

View file

@ -8,7 +8,7 @@ from sqlalchemy.orm import Session
from app.config import settings
from app.models import AppState
log = logging.getLogger("subfeed.state")
log = logging.getLogger("siftlode.state")
# Bounds for the admin-set maintenance re-validation batch (videos re-checked per run).
MAINTENANCE_BATCH_MIN = 100

View file

@ -11,7 +11,7 @@ import re
from sqlalchemy import and_, exists, func, select
from sqlalchemy.orm import Session
log = logging.getLogger("subfeed.autotag")
log = logging.getLogger("siftlode.autotag")
from app import progress, sysconfig
from app.models import Channel, ChannelTag, Tag, Video

View file

@ -34,7 +34,7 @@ from app.sync.runner import get_service_user
from app.sync.videos import apply_video_details, parse_dt
from app.youtube.client import YouTubeClient
log = logging.getLogger("subfeed.maintenance")
log = logging.getLogger("siftlode.maintenance")
# Reasons recorded on Video.unavailable_reason. "removed" = absent from videos.list
# (deleted or made private); "abandoned" = an upcoming premiere that never went live.

View file

@ -18,7 +18,7 @@ from app.models import Channel, OAuthToken, Playlist, PlaylistItem, User, Video
from app.sync.videos import apply_video_details, parse_dt
from app.youtube.client import YouTubeClient, YouTubeError
log = logging.getLogger("subfeed.sync")
log = logging.getLogger("siftlode.sync")
# Cost of a single YouTube write op (playlists/playlistItems insert/update/delete).
WRITE_UNIT_COST = 50

View file

@ -12,7 +12,7 @@ from sqlalchemy.orm import Session
from app import progress, quota, sysconfig
log = logging.getLogger("subfeed.sync")
log = logging.getLogger("siftlode.sync")
from app.models import Channel, OAuthToken, Subscription, User
from app.sync.subscriptions import import_subscriptions
from app.sync.videos import (

View file

@ -8,7 +8,7 @@ from sqlalchemy.orm import Session
from app.models import Channel, Subscription, User
from app.youtube.client import YouTubeClient, best_thumbnail
log = logging.getLogger("subfeed.sync")
log = logging.getLogger("siftlode.sync")
def _to_int(value) -> int | None:

View file

@ -16,7 +16,7 @@ from app.config import settings
from app.models import User
from app.security import decrypt
log = logging.getLogger("subfeed.youtube")
log = logging.getLogger("siftlode.youtube")
GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token"
API_BASE = "https://www.googleapis.com/youtube/v3"

View file

@ -35,6 +35,6 @@
"uvicorn": { "handlers": ["default"], "level": "INFO", "propagate": false },
"uvicorn.error": { "handlers": ["default"], "level": "INFO", "propagate": false },
"uvicorn.access": { "handlers": ["access"], "level": "INFO", "propagate": false },
"subfeed": { "handlers": ["plain"], "level": "INFO", "propagate": false }
"siftlode": { "handlers": ["plain"], "level": "INFO", "propagate": false }
}
}

View file

@ -15,15 +15,15 @@ services:
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: ${POSTGRES_USER:-subfeed}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-subfeed}
POSTGRES_DB: ${POSTGRES_DB:-subfeed}
POSTGRES_USER: ${POSTGRES_USER:-siftlode}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-siftlode}
POSTGRES_DB: ${POSTGRES_DB:-siftlode}
volumes:
- pgdata:/var/lib/postgresql/data
security_opt:
- apparmor:unconfined
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-subfeed} -d ${POSTGRES_DB:-subfeed}"]
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-siftlode} -d ${POSTGRES_DB:-siftlode}"]
interval: 5s
timeout: 5s
retries: 12
@ -40,7 +40,7 @@ services:
env_file: .env
environment:
# Own local database (overrides whatever DATABASE_URL is in .env).
DATABASE_URL: postgresql+psycopg://${POSTGRES_USER:-subfeed}:${POSTGRES_PASSWORD:-subfeed}@db:5432/${POSTGRES_DB:-subfeed}
DATABASE_URL: postgresql+psycopg://${POSTGRES_USER:-siftlode}:${POSTGRES_PASSWORD:-siftlode}@db:5432/${POSTGRES_DB:-siftlode}
# This instance owns its scheduler now (its own DB, so no double-write concern).
SCHEDULER_ENABLED: "true"
depends_on:

View file

@ -16,18 +16,18 @@ services:
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: ${POSTGRES_USER:-subfeed}
POSTGRES_USER: ${POSTGRES_USER:-siftlode}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD must be set in the env file}
POSTGRES_DB: ${POSTGRES_DB:-subfeed}
POSTGRES_DB: ${POSTGRES_DB:-siftlode}
volumes:
- subfeed_pgdata:/var/lib/postgresql/data
- siftlode_pgdata:/var/lib/postgresql/data
networks: [internal]
mem_limit: 512m
cpus: 0.75
security_opt:
- no-new-privileges:true
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-subfeed} -d ${POSTGRES_DB:-subfeed}"]
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-siftlode} -d ${POSTGRES_DB:-siftlode}"]
interval: 10s
timeout: 5s
retries: 12
@ -41,7 +41,7 @@ services:
env_file:
- /srv/siftlode/.env
environment:
DATABASE_URL: postgresql+psycopg://${POSTGRES_USER:-subfeed}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB:-subfeed}
DATABASE_URL: postgresql+psycopg://${POSTGRES_USER:-siftlode}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB:-siftlode}
# The public instance owns the background scheduler (single writer).
SCHEDULER_ENABLED: "true"
depends_on:
@ -70,7 +70,7 @@ services:
restart: unless-stopped
volumes:
subfeed_pgdata:
siftlode_pgdata:
networks:
internal:

View file

@ -14,16 +14,16 @@ services:
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: ${POSTGRES_USER:-subfeed}
POSTGRES_USER: ${POSTGRES_USER:-siftlode}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD must be set (run install.sh)}
POSTGRES_DB: ${POSTGRES_DB:-subfeed}
POSTGRES_DB: ${POSTGRES_DB:-siftlode}
volumes:
- siftlode_pgdata:/var/lib/postgresql/data
networks: [internal]
security_opt:
- no-new-privileges:true
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-subfeed} -d ${POSTGRES_DB:-subfeed}"]
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-siftlode} -d ${POSTGRES_DB:-siftlode}"]
interval: 10s
timeout: 5s
retries: 12
@ -34,7 +34,7 @@ services:
env_file:
- .env
environment:
DATABASE_URL: postgresql+psycopg://${POSTGRES_USER:-subfeed}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB:-subfeed}
DATABASE_URL: postgresql+psycopg://${POSTGRES_USER:-siftlode}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB:-siftlode}
# This instance owns the background scheduler (single writer).
SCHEDULER_ENABLED: "true"
depends_on:

View file

@ -10,7 +10,7 @@
# export COMPOSE_FILE=docker-compose.server.yml # or put it in .env
# docker compose up -d --build
#
# Postgres data lives in ./pgdata (a host-visible bind mount under /docker/subfeed)
# Postgres data lives in ./pgdata (a host-visible bind mount under /docker/siftlode)
# so it is covered by the host's /docker backups; logical pg_dump backups via
# scripts/backup.sh remain the portable mechanism for moving between hosts.
@ -18,9 +18,9 @@ services:
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: ${POSTGRES_USER:-subfeed}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-subfeed}
POSTGRES_DB: ${POSTGRES_DB:-subfeed}
POSTGRES_USER: ${POSTGRES_USER:-siftlode}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-siftlode}
POSTGRES_DB: ${POSTGRES_DB:-siftlode}
volumes:
- ./pgdata:/var/lib/postgresql/data
ports:
@ -31,7 +31,7 @@ services:
security_opt:
- apparmor:unconfined
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-subfeed} -d ${POSTGRES_DB:-subfeed}"]
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-siftlode} -d ${POSTGRES_DB:-siftlode}"]
interval: 5s
timeout: 5s
retries: 12
@ -47,7 +47,7 @@ services:
BUILD_DATE: ${BUILD_DATE:-}
env_file: .env
environment:
DATABASE_URL: postgresql+psycopg://${POSTGRES_USER:-subfeed}:${POSTGRES_PASSWORD:-subfeed}@db:5432/${POSTGRES_DB:-subfeed}
DATABASE_URL: postgresql+psycopg://${POSTGRES_USER:-siftlode}:${POSTGRES_PASSWORD:-siftlode}@db:5432/${POSTGRES_DB:-siftlode}
# This instance owns the background sync. Keep it true here and false everywhere else.
SCHEDULER_ENABLED: "true"
depends_on:

View file

@ -2,13 +2,13 @@ services:
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: ${POSTGRES_USER:-subfeed}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-subfeed}
POSTGRES_DB: ${POSTGRES_DB:-subfeed}
POSTGRES_USER: ${POSTGRES_USER:-siftlode}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-siftlode}
POSTGRES_DB: ${POSTGRES_DB:-siftlode}
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-subfeed} -d ${POSTGRES_DB:-subfeed}"]
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-siftlode} -d ${POSTGRES_DB:-siftlode}"]
interval: 5s
timeout: 5s
retries: 12
@ -24,7 +24,7 @@ services:
BUILD_DATE: ${BUILD_DATE:-}
env_file: .env
environment:
DATABASE_URL: postgresql+psycopg://${POSTGRES_USER:-subfeed}:${POSTGRES_PASSWORD:-subfeed}@db:5432/${POSTGRES_DB:-subfeed}
DATABASE_URL: postgresql+psycopg://${POSTGRES_USER:-siftlode}:${POSTGRES_PASSWORD:-siftlode}@db:5432/${POSTGRES_DB:-siftlode}
depends_on:
db:
condition: service_healthy

View file

@ -57,9 +57,9 @@ const DEFAULT_FILTERS: FeedFilters = {
show: "unwatched",
};
const FILTERS_KEY = "subfeed.filters";
const FILTERS_KEY = "siftlode.filters";
const PAGE_KEY = "siftlode.page";
const PERF_KEY = "subfeed.perfMode";
const PERF_KEY = "siftlode.perfMode";
// The preferences edited on the Settings page. They apply locally for instant preview but
// persist to the server only on an explicit Save — so App holds both the live "draft" (the

View file

@ -1,7 +1,7 @@
// App-wide "hints" toggle: when on, Tooltip components reveal short explanatory
// captions on hover so the UI is somewhat self-documenting for new users. Experienced
// users can turn it off in Settings. Persisted to localStorage (+ server preferences).
const KEY = "subfeed.hints";
const KEY = "siftlode.hints";
let enabled = load();
let listeners: Array<() => void> = [];

View file

@ -71,8 +71,8 @@ export interface NotifyInput {
transient?: boolean; // live status: sticky toast, not persisted (see Notification.transient)
}
const HISTORY_KEY = "subfeed.notifications";
const SETTINGS_KEY = "subfeed.notifSettings";
const HISTORY_KEY = "siftlode.notifications";
const SETTINGS_KEY = "siftlode.notifSettings";
const MAX_HISTORY = 100;
const ERROR_TTL = 15000;

View file

@ -12,8 +12,8 @@
// suppresses the auto-popup on future logins (they
// can still reopen it from Settings → Account).
export const ONBOARD_ACTIVE = "subfeed.onboarding.active";
export const ONBOARD_DISMISSED = "subfeed.onboarding.dismissed";
export const ONBOARD_ACTIVE = "siftlode.onboarding.active";
export const ONBOARD_DISMISSED = "siftlode.onboarding.dismissed";
export function shouldAutoOpenOnboarding(me: {
can_read: boolean;

View file

@ -27,7 +27,7 @@ export const DEFAULT_LAYOUT: SidebarLayout = {
hidden: {},
};
const KEY = "subfeed.sidebarLayout";
const KEY = "siftlode.sidebarLayout";
// Tolerate stale/partial data: keep only known widgets and append any that are missing
// (e.g. a widget added in a later version) so nothing silently disappears.

View file

@ -27,7 +27,7 @@ export function applyTheme(t: ThemePrefs): void {
el.style.setProperty("--font-scale", String(t.fontScale));
}
const KEY = "subfeed.theme";
const KEY = "siftlode.theme";
export function loadLocalTheme(): ThemePrefs {
try {

View file

@ -1,8 +1,8 @@
# Dumps the Siftlode Postgres database to backupssiftlode-<timestamp>.dump (custom format).
# Run from the project root: .\scripts\backup.ps1
$ErrorActionPreference = "Stop"
$user = if ($env:POSTGRES_USER) { $env:POSTGRES_USER } else { "subfeed" }
$dbname = if ($env:POSTGRES_DB) { $env:POSTGRES_DB } else { "subfeed" }
$user = if ($env:POSTGRES_USER) { $env:POSTGRES_USER } else { "siftlode" }
$dbname = if ($env:POSTGRES_DB) { $env:POSTGRES_DB } else { "siftlode" }
New-Item -ItemType Directory -Force -Path "backups" | Out-Null
$ts = Get-Date -Format "yyyyMMdd-HHmmss"
$out = "backups\siftlode-$ts.dump"

View file

@ -2,8 +2,8 @@
# Dumps the Siftlode Postgres database to backups/siftlode-<timestamp>.dump (custom format).
# Run from the project root: ./scripts/backup.sh
set -e
USER_NAME="${POSTGRES_USER:-subfeed}"
DB_NAME="${POSTGRES_DB:-subfeed}"
USER_NAME="${POSTGRES_USER:-siftlode}"
DB_NAME="${POSTGRES_DB:-siftlode}"
mkdir -p backups
TS=$(date +%Y%m%d-%H%M%S)
OUT="backups/siftlode-$TS.dump"

View file

@ -2,8 +2,8 @@
# Usage: .\scripts\restore.ps1 backupssiftlode-YYYYMMDD-HHMMSS.dump
param([Parameter(Mandatory = $true)][string]$DumpFile)
$ErrorActionPreference = "Stop"
$user = if ($env:POSTGRES_USER) { $env:POSTGRES_USER } else { "subfeed" }
$dbname = if ($env:POSTGRES_DB) { $env:POSTGRES_DB } else { "subfeed" }
$user = if ($env:POSTGRES_USER) { $env:POSTGRES_USER } else { "siftlode" }
$dbname = if ($env:POSTGRES_DB) { $env:POSTGRES_DB } else { "siftlode" }
if (-not (Test-Path $DumpFile)) { throw "Dump file not found: $DumpFile" }
Get-Content -Encoding Byte $DumpFile | docker compose exec -T db pg_restore -U $user -d $dbname --clean --if-exists
Write-Output "Restored $DumpFile"

View file

@ -5,7 +5,7 @@ set -e
FILE="$1"
if [ -z "$FILE" ]; then echo "Usage: restore.sh <dumpfile>"; exit 1; fi
if [ ! -f "$FILE" ]; then echo "Dump file not found: $FILE"; exit 1; fi
USER_NAME="${POSTGRES_USER:-subfeed}"
DB_NAME="${POSTGRES_DB:-subfeed}"
USER_NAME="${POSTGRES_USER:-siftlode}"
DB_NAME="${POSTGRES_DB:-siftlode}"
docker compose exec -T db pg_restore -U "$USER_NAME" -d "$DB_NAME" --clean --if-exists < "$FILE"
echo "Restored $FILE"