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:
2026-03-21 16:33:24 +03:00
parent 371ea70756
commit 846d480d38
27 changed files with 2355 additions and 205 deletions
+38 -1
View File
@@ -13,6 +13,7 @@
"targets": "Targets",
"commandConfigs": "Cmd Configs",
"commandTrackers": "Cmd Trackers",
"cmdTemplateConfigs": "Cmd Templates",
"users": "Users",
"settings": "Settings",
"logout": "Logout"
@@ -478,6 +479,36 @@
"botLocale": "Language for command descriptions in Telegram's menu and bot response messages.",
"rateLimits": "Cooldown in seconds between uses of each command category per chat. 0 = no limit."
},
"emailBot": {
"title": "Email Bots",
"description": "SMTP email senders for notifications",
"addBot": "Add Email Bot",
"name": "Name",
"namePlaceholder": "Family Notifications",
"email": "From Email",
"smtpHost": "SMTP Host",
"smtpPort": "Port",
"smtpUsername": "Username",
"smtpUsernamePlaceholder": "Same as email or app password",
"smtpPassword": "Password",
"passwordUnchanged": "(unchanged)",
"useTls": "Use TLS/SSL",
"testConnection": "Send test email",
"noBots": "No email bots yet.",
"confirmDelete": "Delete this email bot?"
},
"cmdTemplateConfig": {
"title": "Command Templates",
"description": "Customize command response messages with Jinja2 templates",
"newConfig": "New Config",
"name": "Name",
"namePlaceholder": "Default Immich Commands",
"descriptionPlaceholder": "e.g. Custom response formats for family bot",
"noConfigs": "No command template configs yet.",
"confirmDelete": "Delete this command template config?",
"commandResponses": "Command Responses",
"commandResponsesHint": "Leave a slot empty to use the default hardcoded response."
},
"commandConfig": {
"title": "Command Configs",
"description": "Define command settings for Telegram bot interactions",
@@ -558,7 +589,13 @@
"commandTrackerEnabled": "Command tracker enabled",
"commandTrackerDisabled": "Command tracker disabled",
"listenerAdded": "Listener added",
"listenerRemoved": "Listener removed"
"listenerRemoved": "Listener removed",
"cmdTemplateSaved": "Command template saved",
"cmdTemplateDeleted": "Command template deleted",
"emailBotCreated": "Email bot created",
"emailBotUpdated": "Email bot updated",
"emailBotDeleted": "Email bot deleted",
"emailBotTestSent": "Test email sent successfully"
},
"common": {
"loading": "Loading...",
+38 -1
View File
@@ -13,6 +13,7 @@
"targets": "Получатели",
"commandConfigs": "Конф. команд",
"commandTrackers": "Трекеры команд",
"cmdTemplateConfigs": "Шаблоны команд",
"users": "Пользователи",
"settings": "Настройки",
"logout": "Выход"
@@ -478,6 +479,36 @@
"botLocale": "Язык описаний команд в меню Telegram и ответов бота.",
"rateLimits": "Кулдаун в секундах между использованиями команд в каждом чате. 0 = без ограничений."
},
"emailBot": {
"title": "Email боты",
"description": "SMTP отправители для уведомлений по email",
"addBot": "Добавить Email бот",
"name": "Название",
"namePlaceholder": "Семейные уведомления",
"email": "Email отправителя",
"smtpHost": "SMTP сервер",
"smtpPort": "Порт",
"smtpUsername": "Имя пользователя",
"smtpUsernamePlaceholder": "Как email или пароль приложения",
"smtpPassword": "Пароль",
"passwordUnchanged": "(без изменений)",
"useTls": "Использовать TLS/SSL",
"testConnection": "Отправить тестовое письмо",
"noBots": "Email ботов пока нет.",
"confirmDelete": "Удалить этот email бот?"
},
"cmdTemplateConfig": {
"title": "Шаблоны команд",
"description": "Настройте ответы команд с помощью Jinja2 шаблонов",
"newConfig": "Новый шаблон",
"name": "Название",
"namePlaceholder": "Команды Immich по умолчанию",
"descriptionPlaceholder": "Например, пользовательские форматы ответов",
"noConfigs": "Шаблонов команд пока нет.",
"confirmDelete": "Удалить этот шаблон команд?",
"commandResponses": "Ответы команд",
"commandResponsesHint": "Оставьте слот пустым, чтобы использовать ответ по умолчанию."
},
"commandConfig": {
"title": "Конфигурации команд",
"description": "Настройки команд для взаимодействия с Telegram-ботами",
@@ -558,7 +589,13 @@
"commandTrackerEnabled": "Трекер команд включён",
"commandTrackerDisabled": "Трекер команд отключён",
"listenerAdded": "Слушатель добавлен",
"listenerRemoved": "Слушатель удалён"
"listenerRemoved": "Слушатель удалён",
"cmdTemplateSaved": "Шаблон команд сохранён",
"cmdTemplateDeleted": "Шаблон команд удалён",
"emailBotCreated": "Email бот создан",
"emailBotUpdated": "Email бот обновлён",
"emailBotDeleted": "Email бот удалён",
"emailBotTestSent": "Тестовое письмо отправлено"
},
"common": {
"loading": "Загрузка...",
+165
View File
@@ -0,0 +1,165 @@
/** Shared TypeScript interfaces for API entities. */
export interface ServiceProvider {
id: number;
type: string;
name: string;
icon: string;
config: Record<string, any>;
created_at: string;
}
export interface EmailBot {
id: number;
name: string;
icon: string;
email: string;
smtp_host: string;
smtp_port: number;
smtp_username: string;
smtp_password: string;
smtp_use_tls: boolean;
created_at: string;
}
export interface TelegramBot {
id: number;
name: string;
icon: string;
bot_username: string;
bot_id: number;
webhook_path_id: string;
commands_config: Record<string, any>;
token_preview: string;
created_at: string;
}
export interface TelegramChat {
id: number;
chat_id: string;
title: string;
type: string;
username: string;
discovered_at: string;
}
export interface TrackerTarget {
id: number;
target_id: number;
target_name: string | null;
target_type: string | null;
target_icon: string | null;
tracking_config_id: number | null;
template_config_id: number | null;
enabled: boolean;
quiet_hours_start: string | null;
quiet_hours_end: string | null;
commands_config: Record<string, any> | null;
created_at: string;
}
export interface Tracker {
id: number;
name: string;
icon: string;
provider_id: number;
collection_ids: string[];
scan_interval: number;
batch_duration: number;
enabled: boolean;
tracker_targets: TrackerTarget[];
created_at: string;
}
export interface NotificationTarget {
id: number;
type: string;
name: string;
icon: string;
config: Record<string, any>;
chat_name?: string;
created_at: string;
}
export interface TrackingConfig {
id: number;
provider_type: string;
name: string;
icon: string;
track_assets_added: boolean;
track_assets_removed: boolean;
track_collection_renamed: boolean;
track_collection_deleted: boolean;
track_sharing_changed: boolean;
track_images: boolean;
track_videos: boolean;
notify_favorites_only: boolean;
include_tags: boolean;
include_asset_details: boolean;
max_assets_to_show: number;
assets_order_by: string;
assets_order: string;
periodic_enabled: boolean;
periodic_interval_days: number;
periodic_start_date: string;
periodic_times: string;
scheduled_enabled: boolean;
scheduled_times: string;
scheduled_collection_mode: string;
scheduled_limit: number;
scheduled_favorite_only: boolean;
scheduled_asset_type: string;
scheduled_min_rating: number;
scheduled_order_by: string;
scheduled_order: string;
memory_enabled: boolean;
memory_source: string;
memory_times: string;
memory_collection_mode: string;
memory_limit: number;
memory_favorite_only: boolean;
memory_asset_type: string;
memory_min_rating: number;
created_at: string;
}
export interface TemplateConfig {
id: number;
user_id: number;
provider_type: string;
name: string;
description: string;
icon: string;
slots: Record<string, string>;
date_format: string;
date_only_format: string;
created_at: string;
}
export interface EventLog {
id: number;
event_type: string;
collection_id: string;
collection_name: string;
tracker_name: string;
provider_name: string;
provider_id: number | null;
assets_count: number;
details: Record<string, any>;
created_at: string;
}
export interface User {
id: number;
username: string;
role: string;
created_at: string;
}
export interface DashboardStatus {
providers: number;
trackers: { total: number; active: number };
targets: number;
total_events: number;
recent_events: EventLog[];
}