Add standalone FastAPI server backend (Phase 3)
Some checks failed
Validate / Hassfest (push) Has been cancelled
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:
@@ -0,0 +1 @@
|
||||
"""Webhook notification provider."""
|
||||
41
packages/server/src/immich_watcher_server/webhook/client.py
Normal file
41
packages/server/src/immich_watcher_server/webhook/client.py
Normal file
@@ -0,0 +1,41 @@
|
||||
"""Generic webhook notification client."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
import aiohttp
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class WebhookClient:
|
||||
"""Send JSON payloads to a webhook URL."""
|
||||
|
||||
def __init__(self, session: aiohttp.ClientSession, url: str, headers: dict[str, str] | None = None) -> None:
|
||||
self._session = session
|
||||
self._url = url
|
||||
self._headers = headers or {}
|
||||
|
||||
async def send(self, payload: dict[str, Any]) -> dict[str, Any]:
|
||||
"""POST JSON payload to the webhook URL.
|
||||
|
||||
Returns:
|
||||
Dict with success status and optional error.
|
||||
"""
|
||||
try:
|
||||
async with self._session.post(
|
||||
self._url,
|
||||
json=payload,
|
||||
headers={"Content-Type": "application/json", **self._headers},
|
||||
) as response:
|
||||
if 200 <= response.status < 300:
|
||||
_LOGGER.debug("Webhook sent successfully to %s (HTTP %d)", self._url, response.status)
|
||||
return {"success": True, "status_code": response.status}
|
||||
body = await response.text()
|
||||
_LOGGER.error("Webhook failed: HTTP %d - %s", response.status, body[:200])
|
||||
return {"success": False, "error": f"HTTP {response.status}", "body": body[:200]}
|
||||
except aiohttp.ClientError as err:
|
||||
_LOGGER.error("Webhook request failed: %s", err)
|
||||
return {"success": False, "error": str(err)}
|
||||
Reference in New Issue
Block a user