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:
@@ -58,6 +58,14 @@ export const memorySourceItems = (): GridItem[] => [
|
||||
{ value: 'native', icon: 'mdiMemory', label: t('trackingConfig.memorySourceNative'), desc: t('gridDesc.memorySourceNative') },
|
||||
];
|
||||
|
||||
// --- Webhook auth mode ---
|
||||
|
||||
export const webhookAuthModeItems = (): GridItem[] => [
|
||||
{ value: 'none', icon: 'mdiLockOpen', label: t('providers.authNone'), desc: t('gridDesc.authNone') },
|
||||
{ value: 'bearer_token', icon: 'mdiKey', label: t('providers.authBearer'), desc: t('gridDesc.authBearer') },
|
||||
{ value: 'hmac_sha256', icon: 'mdiShieldKey', label: t('providers.authHmac'), desc: t('gridDesc.authHmac') },
|
||||
];
|
||||
|
||||
// --- Locale ---
|
||||
|
||||
export const localeItems = (): GridItem[] => [
|
||||
|
||||
@@ -133,6 +133,9 @@
|
||||
"plankaWebhookUrlHint": "Set this as the Webhook URL in Planka environment config (relative to your bridge host).",
|
||||
"authMode": "Authentication Mode",
|
||||
"authModeHint": "Choose hmac_sha256, bearer_token, or none",
|
||||
"authNone": "None",
|
||||
"authBearer": "Bearer Token",
|
||||
"authHmac": "HMAC-SHA256",
|
||||
"genericWebhookSecretHint": "Secret for HMAC-SHA256 or Bearer token authentication. Leave empty for no authentication.",
|
||||
"maxStoredPayloads": "Max stored payloads",
|
||||
"maxStoredPayloadsHint": "Number of recent payloads to keep for debugging (0 = disabled, max 100)",
|
||||
@@ -658,6 +661,9 @@
|
||||
"webhookSecretHint": "Secret token to verify webhook requests from Telegram",
|
||||
"cacheTtl": "Media Cache TTL (hours)",
|
||||
"cacheTtlHint": "How long to cache uploaded Telegram file_ids before re-uploading",
|
||||
"locales": "Template Languages",
|
||||
"supportedLocales": "Supported Locales",
|
||||
"supportedLocalesHint": "Comma-separated locale codes for template editing (e.g. en,ru,de,fr)",
|
||||
"saved": "Settings saved"
|
||||
},
|
||||
"hints": {
|
||||
@@ -926,6 +932,9 @@
|
||||
"message_ups_overload": "UPS load exceeded capacity"
|
||||
},
|
||||
"gridDesc": {
|
||||
"authNone": "No authentication required",
|
||||
"authBearer": "Verify requests with a Bearer token",
|
||||
"authHmac": "Verify payload signature with HMAC-SHA256",
|
||||
"sortNone": "No sorting applied",
|
||||
"sortDate": "Sort by creation date",
|
||||
"sortRating": "Sort by star rating",
|
||||
|
||||
@@ -133,6 +133,9 @@
|
||||
"plankaWebhookUrlHint": "Укажите этот URL в конфигурации Planka (относительно хоста bridge).",
|
||||
"authMode": "Режим аутентификации",
|
||||
"authModeHint": "Выберите hmac_sha256, bearer_token или none",
|
||||
"authNone": "Без аутентификации",
|
||||
"authBearer": "Bearer Token",
|
||||
"authHmac": "HMAC-SHA256",
|
||||
"genericWebhookSecretHint": "Секрет для HMAC-SHA256 или Bearer token аутентификации. Оставьте пустым для режима без аутентификации.",
|
||||
"maxStoredPayloads": "Макс. сохранённых запросов",
|
||||
"maxStoredPayloadsHint": "Количество сохраняемых запросов для отладки (0 = отключено, макс. 100)",
|
||||
@@ -658,6 +661,9 @@
|
||||
"webhookSecretHint": "Секретный токен для проверки запросов вебхука от Telegram",
|
||||
"cacheTtl": "TTL кэша медиа (часы)",
|
||||
"cacheTtlHint": "Сколько хранить кэш Telegram file_id перед повторной загрузкой",
|
||||
"locales": "Языки шаблонов",
|
||||
"supportedLocales": "Поддерживаемые локали",
|
||||
"supportedLocalesHint": "Коды локалей через запятую для редактирования шаблонов (например en,ru,de,fr)",
|
||||
"saved": "Настройки сохранены"
|
||||
},
|
||||
"hints": {
|
||||
@@ -926,6 +932,9 @@
|
||||
"message_ups_overload": "ИБП перегружен"
|
||||
},
|
||||
"gridDesc": {
|
||||
"authNone": "Аутентификация не требуется",
|
||||
"authBearer": "Проверка запросов по Bearer-токену",
|
||||
"authHmac": "Проверка подписи через HMAC-SHA256",
|
||||
"sortNone": "Без сортировки",
|
||||
"sortDate": "По дате создания",
|
||||
"sortRating": "По рейтингу",
|
||||
|
||||
@@ -56,5 +56,5 @@ export const giteaDescriptor: ProviderDescriptor = {
|
||||
desc: () => '',
|
||||
},
|
||||
|
||||
webhookUrlPattern: '/api/webhooks/gitea/{id}',
|
||||
webhookUrlPattern: '/api/webhooks/gitea/{token}',
|
||||
};
|
||||
|
||||
@@ -62,5 +62,5 @@ export const plankaDescriptor: ProviderDescriptor = {
|
||||
desc: () => '',
|
||||
},
|
||||
|
||||
webhookUrlPattern: '/api/webhooks/planka/{id}',
|
||||
webhookUrlPattern: '/api/webhooks/planka/{token}',
|
||||
};
|
||||
|
||||
@@ -20,7 +20,10 @@ export interface ConfigField {
|
||||
configKey?: string;
|
||||
/** i18n key for the field label. */
|
||||
label: string;
|
||||
type: 'text' | 'password' | 'number';
|
||||
type: 'text' | 'password' | 'number' | 'grid-select';
|
||||
/** Grid-select item source function name from grid-items.ts. */
|
||||
gridItems?: string;
|
||||
gridColumns?: number;
|
||||
placeholder?: string;
|
||||
/**
|
||||
* - `true` → always required
|
||||
|
||||
@@ -10,10 +10,10 @@ export const webhookDescriptor: ProviderDescriptor = {
|
||||
{
|
||||
key: 'auth_mode', configKey: 'auth_mode',
|
||||
label: 'providers.authMode',
|
||||
type: 'text',
|
||||
placeholder: 'hmac_sha256 | bearer_token | none',
|
||||
type: 'grid-select',
|
||||
gridItems: 'webhookAuthModeItems',
|
||||
gridColumns: 3,
|
||||
defaultValue: 'none',
|
||||
hint: 'providers.authModeHint',
|
||||
},
|
||||
{
|
||||
key: 'webhook_secret', configKey: 'webhook_secret',
|
||||
@@ -57,6 +57,6 @@ export const webhookDescriptor: ProviderDescriptor = {
|
||||
|
||||
collectionMeta: null,
|
||||
webhookBased: true,
|
||||
webhookUrlPattern: '/api/webhooks/webhook/{id}',
|
||||
webhookUrlPattern: '/api/webhooks/webhook/{token}',
|
||||
payloadHistory: true,
|
||||
};
|
||||
|
||||
@@ -74,6 +74,23 @@ export const capabilitiesCache = (() => {
|
||||
};
|
||||
})();
|
||||
|
||||
/** Supported template locales — fetched from app settings. */
|
||||
export const supportedLocalesCache = (() => {
|
||||
let data = $state<string[]>(['en', 'ru']);
|
||||
let fetchedAt = $state(0);
|
||||
const TTL = 300_000; // 5 minutes
|
||||
return {
|
||||
get items() { return data; },
|
||||
async fetch(force = false): Promise<string[]> {
|
||||
if (!force && fetchedAt > 0 && Date.now() - fetchedAt < TTL) return data;
|
||||
const { api } = await import('$lib/api');
|
||||
data = await api('/settings/locales');
|
||||
fetchedAt = Date.now();
|
||||
return data;
|
||||
},
|
||||
};
|
||||
})();
|
||||
|
||||
/**
|
||||
* All caches keyed by entity type — for search palette and crosslink resolution.
|
||||
*/
|
||||
|
||||
@@ -6,6 +6,7 @@ export interface ServiceProvider {
|
||||
name: string;
|
||||
icon: string;
|
||||
config: Record<string, any>;
|
||||
webhook_token: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user