feat: UX improvements — secure webhooks, locale fixes, dynamic languages, UI polish

- Remove top paginator from dashboard events, keep only bottom
- Fix test message locale: pass UI locale to email/matrix bot tests
- Convert webhook auth mode from text input to icon grid selector
- Generate secure UUID tokens for webhook URLs instead of sequential IDs
- Move Recent Payloads into per-provider expandable container (lazy-loaded)
- Make template config languages dynamic via app settings instead of hardcoded
- Change default dev port to 5175
This commit is contained in:
2026-04-11 02:14:15 +03:00
parent 6b2211353d
commit 734e5c9340
29 changed files with 278 additions and 154 deletions
@@ -8,7 +8,7 @@ from pydantic import BaseModel
from sqlmodel import select
from sqlmodel.ext.asyncio.session import AsyncSession
from ..auth.dependencies import require_admin
from ..auth.dependencies import get_current_user, require_admin
from ..database.engine import get_session
from ..database.models import AppSetting, TelegramBot, User
@@ -21,12 +21,14 @@ _SETTING_KEYS = {
"external_url": "NOTIFY_BRIDGE_EXTERNAL_URL",
"telegram_webhook_secret": "NOTIFY_BRIDGE_TELEGRAM_WEBHOOK_SECRET",
"telegram_cache_ttl_hours": None, # no env fallback, default 48
"supported_locales": None, # comma-separated locale codes
}
_DEFAULTS = {
"external_url": "",
"telegram_webhook_secret": "",
"telegram_cache_ttl_hours": "48",
"supported_locales": "en,ru",
}
@@ -47,6 +49,7 @@ class SettingsUpdate(BaseModel):
external_url: str | None = None
telegram_webhook_secret: str | None = None
telegram_cache_ttl_hours: str | None = None
supported_locales: str | None = None
@router.get("")
@@ -105,6 +108,17 @@ async def update_settings(
return result
@router.get("/locales")
async def get_supported_locales(
user: User = Depends(get_current_user),
session: AsyncSession = Depends(get_session),
):
"""Return list of supported template locales (available to all users)."""
raw = await get_setting(session, "supported_locales")
locales = [loc.strip() for loc in raw.split(",") if loc.strip()]
return locales or ["en"]
async def _reregister_webhooks(
session: AsyncSession, base_url: str, secret: str
) -> None: