feat: provider-strict configs, slot-based templates, broadcast targets, email bots, command templates
Major architectural improvements: - Provider-type enforcement: configs validated against provider type at assignment - TemplateConfig migrated to slot-based pattern (TemplateSlot child table) - Broadcast targets: TargetReceiver child table for multi-receiver dispatch - EmailBot: first-class email sender entity with SMTP config, test connection - CommandTemplateConfig: generic slot-based command response templates - Provider capability registry: dynamic slot/event/command definitions per provider - CommandTracker play/pause button matches NotificationTracker style
This commit is contained in:
@@ -20,13 +20,16 @@ from .api.notification_tracker_targets import router as notification_tracker_tar
|
||||
from .api.tracking_configs import router as tracking_configs_router
|
||||
from .api.template_configs import router as template_configs_router
|
||||
from .api.targets import router as targets_router
|
||||
from .api.target_receivers import router as target_receivers_router
|
||||
from .api.telegram_bots import router as telegram_bots_router
|
||||
from .api.email_bots import router as email_bots_router
|
||||
from .api.users import router as users_router
|
||||
from .api.status import router as status_router
|
||||
from .api.template_vars import router as template_vars_router
|
||||
from .api.app_settings import router as app_settings_router
|
||||
from .api.command_configs import router as command_configs_router
|
||||
from .api.command_trackers import router as command_trackers_router
|
||||
from .api.command_template_configs import router as command_template_configs_router
|
||||
from .commands.webhook import router as webhook_router, set_webhook_secret
|
||||
|
||||
|
||||
@@ -35,11 +38,13 @@ async def lifespan(app: FastAPI):
|
||||
await init_db()
|
||||
# Run data migrations (idempotent)
|
||||
from .database.engine import get_engine
|
||||
from .database.migrations import migrate_schema, migrate_tracker_targets, migrate_entity_refactor
|
||||
from .database.migrations import migrate_schema, migrate_tracker_targets, migrate_entity_refactor, migrate_template_slots, migrate_target_receivers
|
||||
engine = get_engine()
|
||||
await migrate_schema(engine)
|
||||
await migrate_tracker_targets(engine)
|
||||
await migrate_entity_refactor(engine)
|
||||
await migrate_template_slots(engine)
|
||||
await migrate_target_receivers(engine)
|
||||
await _seed_default_templates()
|
||||
# Configure webhook secret from DB setting (falls back to env var)
|
||||
from sqlmodel.ext.asyncio.session import AsyncSession as _AS
|
||||
@@ -63,12 +68,15 @@ app.include_router(notification_tracker_targets_router)
|
||||
app.include_router(tracking_configs_router)
|
||||
app.include_router(template_configs_router)
|
||||
app.include_router(targets_router)
|
||||
app.include_router(target_receivers_router)
|
||||
app.include_router(telegram_bots_router)
|
||||
app.include_router(email_bots_router)
|
||||
app.include_router(users_router)
|
||||
app.include_router(status_router)
|
||||
app.include_router(app_settings_router)
|
||||
app.include_router(command_configs_router)
|
||||
app.include_router(command_trackers_router)
|
||||
app.include_router(command_template_configs_router)
|
||||
app.include_router(webhook_router)
|
||||
|
||||
|
||||
@@ -78,11 +86,14 @@ async def health():
|
||||
|
||||
|
||||
async def _seed_default_templates():
|
||||
"""Seed or update default (system-owned) templates on startup."""
|
||||
"""Seed or update default (system-owned) templates on startup.
|
||||
|
||||
Uses TemplateSlot child rows for template content.
|
||||
"""
|
||||
from sqlmodel import func, select
|
||||
from sqlmodel.ext.asyncio.session import AsyncSession
|
||||
from .database.engine import get_engine
|
||||
from .database.models import TemplateConfig
|
||||
from .database.models import TemplateConfig, TemplateSlot
|
||||
from notify_bridge_core.templates.defaults import load_default_templates
|
||||
|
||||
engine = get_engine()
|
||||
@@ -102,9 +113,15 @@ async def _seed_default_templates():
|
||||
provider_type="immich",
|
||||
name=name,
|
||||
description=f"Default Immich templates ({locale.upper()})",
|
||||
**slots,
|
||||
)
|
||||
session.add(config)
|
||||
await session.flush() # get config.id
|
||||
for slot_name, template_text in slots.items():
|
||||
session.add(TemplateSlot(
|
||||
config_id=config.id,
|
||||
slot_name=slot_name,
|
||||
template=template_text,
|
||||
))
|
||||
else:
|
||||
# Update existing system-owned templates from files
|
||||
result = await session.exec(
|
||||
@@ -116,9 +133,24 @@ async def _seed_default_templates():
|
||||
slots = load_default_templates(locale)
|
||||
if not slots:
|
||||
continue
|
||||
for key, value in slots.items():
|
||||
setattr(config, key, value)
|
||||
session.add(config)
|
||||
for slot_name, template_text in slots.items():
|
||||
# Upsert: find existing slot or create new
|
||||
slot_result = await session.exec(
|
||||
select(TemplateSlot).where(
|
||||
TemplateSlot.config_id == config.id,
|
||||
TemplateSlot.slot_name == slot_name,
|
||||
)
|
||||
)
|
||||
existing = slot_result.first()
|
||||
if existing:
|
||||
existing.template = template_text
|
||||
session.add(existing)
|
||||
else:
|
||||
session.add(TemplateSlot(
|
||||
config_id=config.id,
|
||||
slot_name=slot_name,
|
||||
template=template_text,
|
||||
))
|
||||
|
||||
await session.commit()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user