Add standalone FastAPI server backend (Phase 3)
Some checks failed
Validate / Hassfest (push) Has been cancelled

Build a complete standalone web server for Immich album change
notifications, independent of Home Assistant. Uses the shared
core library from Phase 1.

Server features:
- FastAPI with async SQLite (SQLModel + aiosqlite)
- Multi-user auth with JWT (admin/user roles, setup wizard)
- CRUD APIs: Immich servers, album trackers, message templates,
  notification targets (Telegram + webhook), user management
- APScheduler background polling per tracker
- Jinja2 template rendering with live preview
- Album browser proxied from Immich API
- Event logging and dashboard status endpoint
- Docker deployment (single container, SQLite in volume)

39 API routes, 14 integration tests passing.

Also adds Phase 6 (Claude AI Telegram bot enhancement) to the
primary plan as an optional future phase.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-19 12:56:22 +03:00
parent b107cfe67f
commit 58b2281dc6
28 changed files with 1982 additions and 1 deletions

View File

@@ -0,0 +1,94 @@
"""FastAPI application entry point."""
from __future__ import annotations
import logging
from contextlib import asynccontextmanager
from pathlib import Path
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from .config import settings
from .database.engine import init_db
from .services.scheduler import start_scheduler, stop_scheduler
from .auth.routes import router as auth_router
from .api.servers import router as servers_router
from .api.trackers import router as trackers_router
from .api.templates import router as templates_router
from .api.targets import router as targets_router
from .api.users import router as users_router
from .api.status import router as status_router
logging.basicConfig(
level=logging.DEBUG if settings.debug else logging.INFO,
format="%(asctime)s %(levelname)s [%(name)s] %(message)s",
)
_LOGGER = logging.getLogger(__name__)
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Application lifespan: startup and shutdown."""
# Startup
settings.data_dir.mkdir(parents=True, exist_ok=True)
await init_db()
_LOGGER.info("Database initialized at %s", settings.effective_database_url)
await start_scheduler()
yield
# Shutdown
await stop_scheduler()
app = FastAPI(
title="Immich Watcher",
description="Standalone Immich album change notification server",
version="0.1.0",
lifespan=lifespan,
)
# CORS for frontend dev server
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# API routes
app.include_router(auth_router)
app.include_router(servers_router)
app.include_router(trackers_router)
app.include_router(templates_router)
app.include_router(targets_router)
app.include_router(users_router)
app.include_router(status_router)
# Serve frontend static files if available
_frontend_dist = Path(__file__).parent / "frontend"
if _frontend_dist.is_dir():
app.mount("/", StaticFiles(directory=_frontend_dist, html=True), name="frontend")
@app.get("/api/health")
async def health():
"""Health check endpoint."""
return {"status": "ok", "version": "0.1.0"}
def run():
"""Run the server (entry point for `immich-watcher` CLI command)."""
import uvicorn
uvicorn.run(
"immich_watcher_server.main:app",
host=settings.host,
port=settings.port,
reload=settings.debug,
)