feat(notify-bridge): phase 6 - database models and server API
New database schema with ServiceProvider abstraction: - ServiceProvider (replaces ImmichServer): type + JSON config - Tracker (replaces AlbumTracker): owns tracking_config_id - TrackingConfig: provider_type scoped, owned by Tracker - TemplateConfig: provider_type scoped, owned by Target - NotificationTarget: owns template_config_id (not tracking_config_id) - TrackerState, EventLog, User, TelegramBot, TelegramChat Full FastAPI server: - /api/providers: CRUD + test connection + list collections - /api/trackers: CRUD - /api/tracking-configs: CRUD with provider_type filter - /api/template-configs: CRUD with provider_type filter, system defaults - /api/targets: CRUD - /api/template-vars: variable docs filtered by provider type - /api/auth: setup, login, refresh, me, password change - /api/health: health check - Default template seeding on first startup (EN/RU for Immich) - pydantic-settings with NOTIFY_BRIDGE_ env prefix Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
"""NotificationTarget CRUD API routes."""
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from pydantic import BaseModel
|
||||
from sqlmodel import select
|
||||
from sqlmodel.ext.asyncio.session import AsyncSession
|
||||
from typing import Any
|
||||
|
||||
from ..auth.dependencies import get_current_user
|
||||
from ..database.engine import get_session
|
||||
from ..database.models import NotificationTarget, User
|
||||
|
||||
router = APIRouter(prefix="/api/targets", tags=["targets"])
|
||||
|
||||
|
||||
class TargetCreate(BaseModel):
|
||||
type: str
|
||||
name: str
|
||||
icon: str = ""
|
||||
config: dict[str, Any] = {}
|
||||
template_config_id: int | None = None
|
||||
|
||||
|
||||
class TargetUpdate(BaseModel):
|
||||
name: str | None = None
|
||||
icon: str | None = None
|
||||
config: dict[str, Any] | None = None
|
||||
template_config_id: int | None = None
|
||||
|
||||
|
||||
@router.get("")
|
||||
async def list_targets(
|
||||
user: User = Depends(get_current_user),
|
||||
session: AsyncSession = Depends(get_session),
|
||||
):
|
||||
result = await session.exec(
|
||||
select(NotificationTarget).where(NotificationTarget.user_id == user.id)
|
||||
)
|
||||
return result.all()
|
||||
|
||||
|
||||
@router.post("", status_code=201)
|
||||
async def create_target(
|
||||
body: TargetCreate,
|
||||
user: User = Depends(get_current_user),
|
||||
session: AsyncSession = Depends(get_session),
|
||||
):
|
||||
target = NotificationTarget(user_id=user.id, **body.model_dump())
|
||||
session.add(target)
|
||||
await session.commit()
|
||||
await session.refresh(target)
|
||||
return target
|
||||
|
||||
|
||||
@router.get("/{target_id}")
|
||||
async def get_target(
|
||||
target_id: int,
|
||||
user: User = Depends(get_current_user),
|
||||
session: AsyncSession = Depends(get_session),
|
||||
):
|
||||
target = await session.get(NotificationTarget, target_id)
|
||||
if not target or target.user_id != user.id:
|
||||
raise HTTPException(status_code=404, detail="Target not found")
|
||||
return target
|
||||
|
||||
|
||||
@router.put("/{target_id}")
|
||||
async def update_target(
|
||||
target_id: int,
|
||||
body: TargetUpdate,
|
||||
user: User = Depends(get_current_user),
|
||||
session: AsyncSession = Depends(get_session),
|
||||
):
|
||||
target = await session.get(NotificationTarget, target_id)
|
||||
if not target or target.user_id != user.id:
|
||||
raise HTTPException(status_code=404, detail="Target not found")
|
||||
|
||||
for field, value in body.model_dump(exclude_unset=True).items():
|
||||
setattr(target, field, value)
|
||||
|
||||
session.add(target)
|
||||
await session.commit()
|
||||
await session.refresh(target)
|
||||
return target
|
||||
|
||||
|
||||
@router.delete("/{target_id}", status_code=204)
|
||||
async def delete_target(
|
||||
target_id: int,
|
||||
user: User = Depends(get_current_user),
|
||||
session: AsyncSession = Depends(get_session),
|
||||
):
|
||||
target = await session.get(NotificationTarget, target_id)
|
||||
if not target or target.user_id != user.id:
|
||||
raise HTTPException(status_code=404, detail="Target not found")
|
||||
await session.delete(target)
|
||||
await session.commit()
|
||||
Reference in New Issue
Block a user