# Phase 3: Build Standalone Server Backend **Status**: In progress **Parent**: [primary-plan.md](primary-plan.md) --- ## Goal Build a standalone FastAPI web server that provides Immich album change notifications without Home Assistant, using the shared core library from Phase 1. --- ## Tech Stack - **Framework**: FastAPI (async-native, OpenAPI docs) - **Database**: SQLite via SQLModel (SQLAlchemy + Pydantic) - **Auth**: bcrypt + JWT (multi-user, admin/user roles) - **Scheduler**: APScheduler (async jobs per tracker) - **Notifications**: Core TelegramClient + new WebhookClient - **Templates**: Jinja2 (same syntax as HA blueprint) - **Deployment**: Docker (single container, SQLite in volume) --- ## Directory Structure ``` packages/server/ pyproject.toml src/immich_watcher_server/ __init__.py main.py # FastAPI app, lifespan, static mount config.py # Settings from env vars database/ __init__.py models.py # SQLModel table definitions engine.py # Engine creation, session factory auth/ __init__.py jwt.py # JWT token creation/validation dependencies.py # FastAPI dependency for current_user routes.py # /api/auth/* endpoints api/ __init__.py servers.py # CRUD /api/servers/* trackers.py # CRUD /api/trackers/* templates.py # CRUD /api/templates/* targets.py # CRUD /api/targets/* users.py # CRUD /api/users/* (admin) status.py # GET /api/status services/ __init__.py scheduler.py # APScheduler setup, job management watcher.py # Album polling + change detection notifier.py # Dispatch notifications (Telegram/webhook) webhook/ __init__.py client.py # Generic webhook POST JSON Dockerfile docker-compose.yml ``` --- ## Tasks ### 1. Package setup `[ ]` - pyproject.toml with deps: fastapi, uvicorn, sqlmodel, pyjwt, bcrypt, apscheduler, jinja2, immich-watcher-core ### 2. Database models `[ ]` - `User`: id, username, hashed_password, role (admin/user), created_at - `ImmichServer`: id, user_id, name, url, api_key, external_domain, created_at - `NotificationTarget`: id, user_id, type (telegram/webhook), name, config JSON, created_at - `MessageTemplate`: id, user_id, name, body (Jinja2), is_default, created_at - `AlbumTracker`: id, user_id, server_id FK, name, album_ids JSON, event_types JSON, target_ids JSON, template_id FK (nullable), scan_interval, enabled, quiet_hours_start, quiet_hours_end, created_at - `AlbumState`: id, tracker_id FK, album_id, asset_ids JSON, last_updated - `EventLog`: id, tracker_id FK, event_type, album_name, details JSON, created_at ### 3. Auth `[ ]` - POST /api/auth/setup (create first admin, only when no users exist) - POST /api/auth/login (returns JWT access + refresh tokens) - GET /api/auth/me (current user info) - JWT dependency for all protected routes ### 4. API endpoints `[ ]` - CRUD for servers, trackers, templates, targets - GET /api/servers/{id}/albums (proxy to Immich) - POST /api/templates/{id}/preview (render with sample data) - POST /api/targets/{id}/test (send test notification) - POST /api/trackers/{id}/trigger (force check) - GET /api/trackers/{id}/history (recent events) - GET /api/status (dashboard data) - CRUD /api/users (admin only) ### 5. Background scheduler `[ ]` - APScheduler with AsyncIOScheduler - One job per enabled tracker at its scan_interval - Job: fetch album -> detect changes -> render template -> notify - Start/stop jobs when trackers are created/updated/deleted ### 6. Notification dispatch `[ ]` - TelegramClient from core (reuse) - WebhookClient: POST JSON to configured URL with event data - Jinja2 template rendering with same variables as blueprint ### 7. Docker `[ ]` - Multi-stage Dockerfile (build frontend -> Python runtime) - docker-compose.yml for standalone deployment --- ## Acceptance Criteria - [ ] Server starts, creates SQLite DB on first run - [ ] First-run setup creates admin account - [ ] Can add Immich server, browse albums - [ ] Can create tracker, template, and notification target - [ ] Scheduler polls albums and detects changes - [ ] Telegram notifications sent on album changes - [ ] Webhook notifications sent on album changes - [ ] JWT auth protects all endpoints - [ ] Docker build works