From b889526b0d5470456d5cc946da779384806e4aba Mon Sep 17 00:00:00 2001 From: Muse Date: Thu, 22 Oct 2026 23:56:27 +0000 Subject: [PATCH] B6 frontend: notification settings + cf-probe retirement checklist on the public-page tab; probeNotify i18n x13 --- frontend/src/pages/settings/PublicPageTab.tsx | 318 ++++++++++++++++++ internal/web/translation/ar-EG.json | 36 ++ internal/web/translation/en-US.json | 36 ++ internal/web/translation/es-ES.json | 36 ++ internal/web/translation/fa-IR.json | 36 ++ internal/web/translation/id-ID.json | 36 ++ internal/web/translation/ja-JP.json | 36 ++ internal/web/translation/pt-BR.json | 36 ++ internal/web/translation/ru-RU.json | 36 ++ internal/web/translation/tr-TR.json | 36 ++ internal/web/translation/uk-UA.json | 36 ++ internal/web/translation/vi-VN.json | 36 ++ internal/web/translation/zh-CN.json | 36 ++ internal/web/translation/zh-TW.json | 36 ++ 14 files changed, 786 insertions(+) diff --git a/frontend/src/pages/settings/PublicPageTab.tsx b/frontend/src/pages/settings/PublicPageTab.tsx index 96bf6bd..1108f0b 100644 --- a/frontend/src/pages/settings/PublicPageTab.tsx +++ b/frontend/src/pages/settings/PublicPageTab.tsx @@ -18,6 +18,7 @@ import { } from 'antd'; import { DeleteOutlined, SettingOutlined, UploadOutlined } from '@ant-design/icons'; import { HttpUtil } from '@/utils'; +import { onNumber } from '@/utils/onNumber'; // Item 7 B5: the public status page's settings + theme store. Like // the probe-meta section this surface is self-contained — it loads @@ -60,6 +61,35 @@ interface ThemesPayload { touchIcon: string; } +// Notify settings wire shape (B6) — mirrors probenotify.SettingsView. +// The webhook URL and headers are secrets: the GET answers only +// whether they are set, and the PUT omits them unless the admin +// edited the field (an explicit clear sends ""). +interface NotifySettingsWire { + notifyGrace: number; + notifyTraffic: number; + notifyExpiry: number; + notifyLogin: string; + notifyTelegramText: string; + notifyWebhookBody: string; + telegramConfigured: boolean; + webhookConfigured: boolean; + webhookUrlSet: boolean; + webhookHeadersSet: boolean; +} + +interface CoverageRow { + name: string; + online: boolean; + hasProbe: boolean; + probeAt: number; +} + +interface NotifyPayload { + settings: NotifySettingsWire; + coverage: CoverageRow[]; +} + const JSON_HEADERS = { headers: { 'Content-Type': 'application/json' } }; const UPLOAD_CHUNK = 1 << 20; const MAX_ICON_BYTES = 20 * 1024; @@ -123,6 +153,14 @@ export default function PublicPageTab() { const [installing, setInstalling] = useState(false); const [uploadPct, setUploadPct] = useState(null); const [busyShort, setBusyShort] = useState(''); + const [notify, setNotify] = useState(null); + const [coverage, setCoverage] = useState([]); + const [webhookUrl, setWebhookUrl] = useState(''); + const [webhookHeaders, setWebhookHeaders] = useState(''); + const [urlTouched, setUrlTouched] = useState(false); + const [headersTouched, setHeadersTouched] = useState(false); + const [notifySaving, setNotifySaving] = useState(false); + const [notifyTesting, setNotifyTesting] = useState(false); const fileRef = useRef(null); const faviconRef = useRef(null); const touchRef = useRef(null); @@ -144,11 +182,71 @@ export default function PublicPageTab() { HttpUtil.get('/panel/api/probe/themes', { silent: true }).then((msg) => { if (!cancelled && msg?.success && msg.obj) applyPayload(msg.obj); }); + HttpUtil.get('/panel/api/probe/notify', { silent: true }).then((msg) => { + if (!cancelled && msg?.success && msg.obj) { + setNotify(msg.obj.settings); + setCoverage(msg.obj.coverage ?? []); + } + }); return () => { cancelled = true; }; }, [applyPayload]); + async function saveNotify() { + if (!notify) return; + setNotifySaving(true); + try { + const body: Record = { + notifyGrace: notify.notifyGrace, + notifyTraffic: notify.notifyTraffic, + notifyExpiry: notify.notifyExpiry, + notifyLogin: notify.notifyLogin, + notifyTelegramText: notify.notifyTelegramText, + notifyWebhookBody: notify.notifyWebhookBody, + }; + // Secrets ride along only when the field was edited; an + // edited-but-empty field clears the stored value server-side. + if (urlTouched) body.notifyWebhookUrl = webhookUrl; + if (headersTouched) body.notifyWebhookHeaders = webhookHeaders; + const msg = await HttpUtil.put('/panel/api/probe/notify', body, { + silent: true, + ...JSON_HEADERS, + }); + if (msg?.success && msg.obj) { + setNotify(msg.obj.settings); + setCoverage(msg.obj.coverage ?? []); + setWebhookUrl(''); + setWebhookHeaders(''); + setUrlTouched(false); + setHeadersTouched(false); + messageApi.success(t('pages.settings.probeThemes.saved')); + } else { + messageApi.error(msg?.msg || t('pages.settings.probeThemes.saveFailed')); + } + } finally { + setNotifySaving(false); + } + } + + async function testNotify() { + setNotifyTesting(true); + try { + const msg = await HttpUtil.post('/panel/api/probe/notify/test', undefined, { + silent: true, + }); + if (msg?.success) { + messageApi.success( + t('pages.settings.probeNotify.testSent', { channels: (msg.obj ?? []).join('、') }), + ); + } else { + messageApi.error(msg?.msg || t('pages.settings.probeThemes.saveFailed')); + } + } finally { + setNotifyTesting(false); + } + } + async function saveSettings() { setSaving(true); try { @@ -474,6 +572,226 @@ export default function PublicPageTab() { ))} + + + {t('pages.settings.probeNotify.notifyTitle')} + + + {t('pages.settings.probeNotify.notifyHint')} + + {notify ? ( + <> + + +
{t('pages.settings.probeNotify.grace')}
+ setNotify({ ...notify, notifyGrace: v }))} + /> +
+ + {t('pages.settings.probeNotify.graceHint')} + +
+ + +
{t('pages.settings.probeNotify.trafficPct')}
+ setNotify({ ...notify, notifyTraffic: v }))} + /> +
+ + {t('pages.settings.probeNotify.trafficHint')} + +
+ + +
{t('pages.settings.probeNotify.expiryDays')}
+ setNotify({ ...notify, notifyExpiry: v }))} + /> +
+ + {t('pages.settings.probeNotify.expiryHint')} + +
+ + +
{t('pages.settings.probeNotify.loginAlert')}
+ setNotify({ ...notify, notifyLogin: v ? 'on' : 'off' })} + /> + + +
+ {t('pages.settings.probeNotify.telegramChannel')} +
+ {notify.telegramConfigured ? ( + {t('pages.settings.probeNotify.configured')} + ) : ( + {t('pages.settings.probeNotify.notConfigured')} + )} +
+ + {t('pages.settings.probeNotify.telegramHint')} + +
+ + +
+ {t('pages.settings.probeNotify.webhookChannel')} +
+ {notify.webhookConfigured ? ( + {t('pages.settings.probeNotify.configured')} + ) : ( + {t('pages.settings.probeNotify.notConfigured')} + )} + + +
{t('pages.settings.probeNotify.telegramText')}
+ setNotify({ ...notify, notifyTelegramText: e.target.value })} + /> + + +
{t('pages.settings.probeNotify.webhookUrl')}
+ { + setWebhookUrl(e.target.value); + setUrlTouched(true); + }} + /> + {notify.webhookUrlSet && !urlTouched ? ( + + ) : null} + + +
+ {t('pages.settings.probeNotify.webhookHeaders')} +
+ { + setWebhookHeaders(e.target.value); + setHeadersTouched(true); + }} + /> + {notify.webhookHeadersSet && !headersTouched ? ( + + ) : null} + + +
{t('pages.settings.probeNotify.webhookBody')}
+ setNotify({ ...notify, notifyWebhookBody: e.target.value })} + /> +
+ + {t('pages.settings.probeNotify.templateHint')} + +
+ +
+ + + + + + ) : null} + + + {t('pages.settings.probeNotify.retireTitle')} + + + {t('pages.settings.probeNotify.retireHint')} + + + + {t('pages.settings.probeNotify.retirePublicPage')}: + {settings.publicPage ? ( + {t('pages.settings.probeNotify.configured')} + ) : ( + {t('pages.settings.probeNotify.retirePublicPageOff')} + )} + + {coverage.map((row) => ( + + {row.name}: + {row.hasProbe ? ( + {t('pages.settings.probeNotify.retireHasProbe')} + ) : ( + {t('pages.settings.probeNotify.retireNoProbe')} + )} + {row.online ? ( + {t('pages.settings.probeNotify.retireOnline')} + ) : ( + {t('pages.settings.probeNotify.retireOffline')} + )} + {row.probeAt > 0 ? ( + + {t('pages.settings.probeNotify.retireProbeAt', { + time: new Date(row.probeAt * 1000).toLocaleString(), + })} + + ) : null} + + ))} + {coverage.length > 0 && coverage.every((row) => row.hasProbe) ? ( + + {t('pages.settings.probeNotify.retireAllReady')} + + ) : ( + + {t('pages.settings.probeNotify.retireNotReady')} + + )} + ); } diff --git a/internal/web/translation/ar-EG.json b/internal/web/translation/ar-EG.json index ff6268b..2d41e06 100644 --- a/internal/web/translation/ar-EG.json +++ b/internal/web/translation/ar-EG.json @@ -1019,6 +1019,42 @@ "alreadyLatest": "Already the latest version", "config": "Theme settings" }, + "probeNotify": { + "notifyTitle": "Notifications", + "notifyHint": "Offline, traffic and expiry alerts, delivered to the channels below (Telegram reuses the panel bot settings).", + "grace": "Offline grace (minutes)", + "graceHint": "An alert fires only after reporting has stopped for this long; a recovery inside the grace stays silent.", + "trafficPct": "Traffic alert threshold (%)", + "trafficHint": "Alerts once at this percentage and once more at 100%, once per billing period each; 0 disables traffic alerts.", + "expiryDays": "Expiry alert (days)", + "expiryHint": "A daily digest of nodes expiring within this many days; online nodes roll over automatically by billing cycle; 0 disables.", + "loginAlert": "Panel sign-in alerts", + "telegramChannel": "Telegram channel", + "telegramHint": "The token and chat IDs are configured in the Telegram bot settings.", + "webhookChannel": "Webhook channel", + "configured": "Configured", + "notConfigured": "Not configured", + "telegramText": "Telegram text template", + "webhookUrl": "Webhook URL", + "webhookHeaders": "Headers (one Name: value per line)", + "webhookBody": "Body template (JSON)", + "secretSet": "Set (leave empty to keep)", + "clearSecret": "Clear", + "templateHint": "Placeholders: {{title}} {{message}} {{node}} {{event}} {{time}} {{count}} {{site}}", + "testSend": "Send a test alert", + "testSent": "Test alert sent: {{channels}}", + "retireTitle": "cf-probe retirement checklist", + "retireHint": "Only once every node is served by the panel may the old public page be switched off, in the order given in the deployment notes.", + "retirePublicPage": "Public page", + "retirePublicPageOff": "Off (enable it above)", + "retireHasProbe": "Probe reporting", + "retireNoProbe": "No probe data", + "retireOnline": "Online", + "retireOffline": "Offline", + "retireProbeAt": "Last report {{time}}", + "retireAllReady": "Every node is served by the panel; cf-probe can be retired in the documented order.", + "retireNotReady": "Some nodes have no probe data yet; do not take cf-probe down." + }, "defaultTag": "افتراضي", "title": "إعدادات البانل", "save": "حفظ", diff --git a/internal/web/translation/en-US.json b/internal/web/translation/en-US.json index b4f7f3a..28e0200 100644 --- a/internal/web/translation/en-US.json +++ b/internal/web/translation/en-US.json @@ -1159,6 +1159,42 @@ "alreadyLatest": "Already the latest version", "config": "Theme settings" }, + "probeNotify": { + "notifyTitle": "Notifications", + "notifyHint": "Offline, traffic and expiry alerts, delivered to the channels below (Telegram reuses the panel bot settings).", + "grace": "Offline grace (minutes)", + "graceHint": "An alert fires only after reporting has stopped for this long; a recovery inside the grace stays silent.", + "trafficPct": "Traffic alert threshold (%)", + "trafficHint": "Alerts once at this percentage and once more at 100%, once per billing period each; 0 disables traffic alerts.", + "expiryDays": "Expiry alert (days)", + "expiryHint": "A daily digest of nodes expiring within this many days; online nodes roll over automatically by billing cycle; 0 disables.", + "loginAlert": "Panel sign-in alerts", + "telegramChannel": "Telegram channel", + "telegramHint": "The token and chat IDs are configured in the Telegram bot settings.", + "webhookChannel": "Webhook channel", + "configured": "Configured", + "notConfigured": "Not configured", + "telegramText": "Telegram text template", + "webhookUrl": "Webhook URL", + "webhookHeaders": "Headers (one Name: value per line)", + "webhookBody": "Body template (JSON)", + "secretSet": "Set (leave empty to keep)", + "clearSecret": "Clear", + "templateHint": "Placeholders: {{title}} {{message}} {{node}} {{event}} {{time}} {{count}} {{site}}", + "testSend": "Send a test alert", + "testSent": "Test alert sent: {{channels}}", + "retireTitle": "cf-probe retirement checklist", + "retireHint": "Only once every node is served by the panel may the old public page be switched off, in the order given in the deployment notes.", + "retirePublicPage": "Public page", + "retirePublicPageOff": "Off (enable it above)", + "retireHasProbe": "Probe reporting", + "retireNoProbe": "No probe data", + "retireOnline": "Online", + "retireOffline": "Offline", + "retireProbeAt": "Last report {{time}}", + "retireAllReady": "Every node is served by the panel; cf-probe can be retired in the documented order.", + "retireNotReady": "Some nodes have no probe data yet; do not take cf-probe down." + }, "defaultTag": "Default", "title": "Panel Settings", "save": "Save", diff --git a/internal/web/translation/es-ES.json b/internal/web/translation/es-ES.json index 120680c..4e81899 100644 --- a/internal/web/translation/es-ES.json +++ b/internal/web/translation/es-ES.json @@ -1019,6 +1019,42 @@ "alreadyLatest": "Already the latest version", "config": "Theme settings" }, + "probeNotify": { + "notifyTitle": "Notifications", + "notifyHint": "Offline, traffic and expiry alerts, delivered to the channels below (Telegram reuses the panel bot settings).", + "grace": "Offline grace (minutes)", + "graceHint": "An alert fires only after reporting has stopped for this long; a recovery inside the grace stays silent.", + "trafficPct": "Traffic alert threshold (%)", + "trafficHint": "Alerts once at this percentage and once more at 100%, once per billing period each; 0 disables traffic alerts.", + "expiryDays": "Expiry alert (days)", + "expiryHint": "A daily digest of nodes expiring within this many days; online nodes roll over automatically by billing cycle; 0 disables.", + "loginAlert": "Panel sign-in alerts", + "telegramChannel": "Telegram channel", + "telegramHint": "The token and chat IDs are configured in the Telegram bot settings.", + "webhookChannel": "Webhook channel", + "configured": "Configured", + "notConfigured": "Not configured", + "telegramText": "Telegram text template", + "webhookUrl": "Webhook URL", + "webhookHeaders": "Headers (one Name: value per line)", + "webhookBody": "Body template (JSON)", + "secretSet": "Set (leave empty to keep)", + "clearSecret": "Clear", + "templateHint": "Placeholders: {{title}} {{message}} {{node}} {{event}} {{time}} {{count}} {{site}}", + "testSend": "Send a test alert", + "testSent": "Test alert sent: {{channels}}", + "retireTitle": "cf-probe retirement checklist", + "retireHint": "Only once every node is served by the panel may the old public page be switched off, in the order given in the deployment notes.", + "retirePublicPage": "Public page", + "retirePublicPageOff": "Off (enable it above)", + "retireHasProbe": "Probe reporting", + "retireNoProbe": "No probe data", + "retireOnline": "Online", + "retireOffline": "Offline", + "retireProbeAt": "Last report {{time}}", + "retireAllReady": "Every node is served by the panel; cf-probe can be retired in the documented order.", + "retireNotReady": "Some nodes have no probe data yet; do not take cf-probe down." + }, "defaultTag": "Predeterminado", "title": "Configuraciones", "save": "Guardar", diff --git a/internal/web/translation/fa-IR.json b/internal/web/translation/fa-IR.json index 828803e..ee62030 100644 --- a/internal/web/translation/fa-IR.json +++ b/internal/web/translation/fa-IR.json @@ -1019,6 +1019,42 @@ "alreadyLatest": "Already the latest version", "config": "Theme settings" }, + "probeNotify": { + "notifyTitle": "Notifications", + "notifyHint": "Offline, traffic and expiry alerts, delivered to the channels below (Telegram reuses the panel bot settings).", + "grace": "Offline grace (minutes)", + "graceHint": "An alert fires only after reporting has stopped for this long; a recovery inside the grace stays silent.", + "trafficPct": "Traffic alert threshold (%)", + "trafficHint": "Alerts once at this percentage and once more at 100%, once per billing period each; 0 disables traffic alerts.", + "expiryDays": "Expiry alert (days)", + "expiryHint": "A daily digest of nodes expiring within this many days; online nodes roll over automatically by billing cycle; 0 disables.", + "loginAlert": "Panel sign-in alerts", + "telegramChannel": "Telegram channel", + "telegramHint": "The token and chat IDs are configured in the Telegram bot settings.", + "webhookChannel": "Webhook channel", + "configured": "Configured", + "notConfigured": "Not configured", + "telegramText": "Telegram text template", + "webhookUrl": "Webhook URL", + "webhookHeaders": "Headers (one Name: value per line)", + "webhookBody": "Body template (JSON)", + "secretSet": "Set (leave empty to keep)", + "clearSecret": "Clear", + "templateHint": "Placeholders: {{title}} {{message}} {{node}} {{event}} {{time}} {{count}} {{site}}", + "testSend": "Send a test alert", + "testSent": "Test alert sent: {{channels}}", + "retireTitle": "cf-probe retirement checklist", + "retireHint": "Only once every node is served by the panel may the old public page be switched off, in the order given in the deployment notes.", + "retirePublicPage": "Public page", + "retirePublicPageOff": "Off (enable it above)", + "retireHasProbe": "Probe reporting", + "retireNoProbe": "No probe data", + "retireOnline": "Online", + "retireOffline": "Offline", + "retireProbeAt": "Last report {{time}}", + "retireAllReady": "Every node is served by the panel; cf-probe can be retired in the documented order.", + "retireNotReady": "Some nodes have no probe data yet; do not take cf-probe down." + }, "defaultTag": "پیش‌فرض", "title": "تنظیمات پنل", "save": "ذخیره", diff --git a/internal/web/translation/id-ID.json b/internal/web/translation/id-ID.json index 5acb4dc..c8e770f 100644 --- a/internal/web/translation/id-ID.json +++ b/internal/web/translation/id-ID.json @@ -1019,6 +1019,42 @@ "alreadyLatest": "Already the latest version", "config": "Theme settings" }, + "probeNotify": { + "notifyTitle": "Notifications", + "notifyHint": "Offline, traffic and expiry alerts, delivered to the channels below (Telegram reuses the panel bot settings).", + "grace": "Offline grace (minutes)", + "graceHint": "An alert fires only after reporting has stopped for this long; a recovery inside the grace stays silent.", + "trafficPct": "Traffic alert threshold (%)", + "trafficHint": "Alerts once at this percentage and once more at 100%, once per billing period each; 0 disables traffic alerts.", + "expiryDays": "Expiry alert (days)", + "expiryHint": "A daily digest of nodes expiring within this many days; online nodes roll over automatically by billing cycle; 0 disables.", + "loginAlert": "Panel sign-in alerts", + "telegramChannel": "Telegram channel", + "telegramHint": "The token and chat IDs are configured in the Telegram bot settings.", + "webhookChannel": "Webhook channel", + "configured": "Configured", + "notConfigured": "Not configured", + "telegramText": "Telegram text template", + "webhookUrl": "Webhook URL", + "webhookHeaders": "Headers (one Name: value per line)", + "webhookBody": "Body template (JSON)", + "secretSet": "Set (leave empty to keep)", + "clearSecret": "Clear", + "templateHint": "Placeholders: {{title}} {{message}} {{node}} {{event}} {{time}} {{count}} {{site}}", + "testSend": "Send a test alert", + "testSent": "Test alert sent: {{channels}}", + "retireTitle": "cf-probe retirement checklist", + "retireHint": "Only once every node is served by the panel may the old public page be switched off, in the order given in the deployment notes.", + "retirePublicPage": "Public page", + "retirePublicPageOff": "Off (enable it above)", + "retireHasProbe": "Probe reporting", + "retireNoProbe": "No probe data", + "retireOnline": "Online", + "retireOffline": "Offline", + "retireProbeAt": "Last report {{time}}", + "retireAllReady": "Every node is served by the panel; cf-probe can be retired in the documented order.", + "retireNotReady": "Some nodes have no probe data yet; do not take cf-probe down." + }, "defaultTag": "Bawaan", "title": "Pengaturan Panel", "save": "Simpan", diff --git a/internal/web/translation/ja-JP.json b/internal/web/translation/ja-JP.json index 87be6c0..b6d4f34 100644 --- a/internal/web/translation/ja-JP.json +++ b/internal/web/translation/ja-JP.json @@ -1019,6 +1019,42 @@ "alreadyLatest": "Already the latest version", "config": "Theme settings" }, + "probeNotify": { + "notifyTitle": "Notifications", + "notifyHint": "Offline, traffic and expiry alerts, delivered to the channels below (Telegram reuses the panel bot settings).", + "grace": "Offline grace (minutes)", + "graceHint": "An alert fires only after reporting has stopped for this long; a recovery inside the grace stays silent.", + "trafficPct": "Traffic alert threshold (%)", + "trafficHint": "Alerts once at this percentage and once more at 100%, once per billing period each; 0 disables traffic alerts.", + "expiryDays": "Expiry alert (days)", + "expiryHint": "A daily digest of nodes expiring within this many days; online nodes roll over automatically by billing cycle; 0 disables.", + "loginAlert": "Panel sign-in alerts", + "telegramChannel": "Telegram channel", + "telegramHint": "The token and chat IDs are configured in the Telegram bot settings.", + "webhookChannel": "Webhook channel", + "configured": "Configured", + "notConfigured": "Not configured", + "telegramText": "Telegram text template", + "webhookUrl": "Webhook URL", + "webhookHeaders": "Headers (one Name: value per line)", + "webhookBody": "Body template (JSON)", + "secretSet": "Set (leave empty to keep)", + "clearSecret": "Clear", + "templateHint": "Placeholders: {{title}} {{message}} {{node}} {{event}} {{time}} {{count}} {{site}}", + "testSend": "Send a test alert", + "testSent": "Test alert sent: {{channels}}", + "retireTitle": "cf-probe retirement checklist", + "retireHint": "Only once every node is served by the panel may the old public page be switched off, in the order given in the deployment notes.", + "retirePublicPage": "Public page", + "retirePublicPageOff": "Off (enable it above)", + "retireHasProbe": "Probe reporting", + "retireNoProbe": "No probe data", + "retireOnline": "Online", + "retireOffline": "Offline", + "retireProbeAt": "Last report {{time}}", + "retireAllReady": "Every node is served by the panel; cf-probe can be retired in the documented order.", + "retireNotReady": "Some nodes have no probe data yet; do not take cf-probe down." + }, "defaultTag": "デフォルト", "title": "パネル設定", "save": "保存", diff --git a/internal/web/translation/pt-BR.json b/internal/web/translation/pt-BR.json index 75a9d40..49d8044 100644 --- a/internal/web/translation/pt-BR.json +++ b/internal/web/translation/pt-BR.json @@ -1019,6 +1019,42 @@ "alreadyLatest": "Already the latest version", "config": "Theme settings" }, + "probeNotify": { + "notifyTitle": "Notifications", + "notifyHint": "Offline, traffic and expiry alerts, delivered to the channels below (Telegram reuses the panel bot settings).", + "grace": "Offline grace (minutes)", + "graceHint": "An alert fires only after reporting has stopped for this long; a recovery inside the grace stays silent.", + "trafficPct": "Traffic alert threshold (%)", + "trafficHint": "Alerts once at this percentage and once more at 100%, once per billing period each; 0 disables traffic alerts.", + "expiryDays": "Expiry alert (days)", + "expiryHint": "A daily digest of nodes expiring within this many days; online nodes roll over automatically by billing cycle; 0 disables.", + "loginAlert": "Panel sign-in alerts", + "telegramChannel": "Telegram channel", + "telegramHint": "The token and chat IDs are configured in the Telegram bot settings.", + "webhookChannel": "Webhook channel", + "configured": "Configured", + "notConfigured": "Not configured", + "telegramText": "Telegram text template", + "webhookUrl": "Webhook URL", + "webhookHeaders": "Headers (one Name: value per line)", + "webhookBody": "Body template (JSON)", + "secretSet": "Set (leave empty to keep)", + "clearSecret": "Clear", + "templateHint": "Placeholders: {{title}} {{message}} {{node}} {{event}} {{time}} {{count}} {{site}}", + "testSend": "Send a test alert", + "testSent": "Test alert sent: {{channels}}", + "retireTitle": "cf-probe retirement checklist", + "retireHint": "Only once every node is served by the panel may the old public page be switched off, in the order given in the deployment notes.", + "retirePublicPage": "Public page", + "retirePublicPageOff": "Off (enable it above)", + "retireHasProbe": "Probe reporting", + "retireNoProbe": "No probe data", + "retireOnline": "Online", + "retireOffline": "Offline", + "retireProbeAt": "Last report {{time}}", + "retireAllReady": "Every node is served by the panel; cf-probe can be retired in the documented order.", + "retireNotReady": "Some nodes have no probe data yet; do not take cf-probe down." + }, "defaultTag": "Padrão", "title": "Configurações do Painel", "save": "Salvar", diff --git a/internal/web/translation/ru-RU.json b/internal/web/translation/ru-RU.json index 37559d0..f865501 100644 --- a/internal/web/translation/ru-RU.json +++ b/internal/web/translation/ru-RU.json @@ -1019,6 +1019,42 @@ "alreadyLatest": "Already the latest version", "config": "Theme settings" }, + "probeNotify": { + "notifyTitle": "Notifications", + "notifyHint": "Offline, traffic and expiry alerts, delivered to the channels below (Telegram reuses the panel bot settings).", + "grace": "Offline grace (minutes)", + "graceHint": "An alert fires only after reporting has stopped for this long; a recovery inside the grace stays silent.", + "trafficPct": "Traffic alert threshold (%)", + "trafficHint": "Alerts once at this percentage and once more at 100%, once per billing period each; 0 disables traffic alerts.", + "expiryDays": "Expiry alert (days)", + "expiryHint": "A daily digest of nodes expiring within this many days; online nodes roll over automatically by billing cycle; 0 disables.", + "loginAlert": "Panel sign-in alerts", + "telegramChannel": "Telegram channel", + "telegramHint": "The token and chat IDs are configured in the Telegram bot settings.", + "webhookChannel": "Webhook channel", + "configured": "Configured", + "notConfigured": "Not configured", + "telegramText": "Telegram text template", + "webhookUrl": "Webhook URL", + "webhookHeaders": "Headers (one Name: value per line)", + "webhookBody": "Body template (JSON)", + "secretSet": "Set (leave empty to keep)", + "clearSecret": "Clear", + "templateHint": "Placeholders: {{title}} {{message}} {{node}} {{event}} {{time}} {{count}} {{site}}", + "testSend": "Send a test alert", + "testSent": "Test alert sent: {{channels}}", + "retireTitle": "cf-probe retirement checklist", + "retireHint": "Only once every node is served by the panel may the old public page be switched off, in the order given in the deployment notes.", + "retirePublicPage": "Public page", + "retirePublicPageOff": "Off (enable it above)", + "retireHasProbe": "Probe reporting", + "retireNoProbe": "No probe data", + "retireOnline": "Online", + "retireOffline": "Offline", + "retireProbeAt": "Last report {{time}}", + "retireAllReady": "Every node is served by the panel; cf-probe can be retired in the documented order.", + "retireNotReady": "Some nodes have no probe data yet; do not take cf-probe down." + }, "defaultTag": "По умолчанию", "title": "Настройки", "save": "Сохранить", diff --git a/internal/web/translation/tr-TR.json b/internal/web/translation/tr-TR.json index 6cb1e55..7c9b3a1 100644 --- a/internal/web/translation/tr-TR.json +++ b/internal/web/translation/tr-TR.json @@ -1019,6 +1019,42 @@ "alreadyLatest": "Already the latest version", "config": "Theme settings" }, + "probeNotify": { + "notifyTitle": "Notifications", + "notifyHint": "Offline, traffic and expiry alerts, delivered to the channels below (Telegram reuses the panel bot settings).", + "grace": "Offline grace (minutes)", + "graceHint": "An alert fires only after reporting has stopped for this long; a recovery inside the grace stays silent.", + "trafficPct": "Traffic alert threshold (%)", + "trafficHint": "Alerts once at this percentage and once more at 100%, once per billing period each; 0 disables traffic alerts.", + "expiryDays": "Expiry alert (days)", + "expiryHint": "A daily digest of nodes expiring within this many days; online nodes roll over automatically by billing cycle; 0 disables.", + "loginAlert": "Panel sign-in alerts", + "telegramChannel": "Telegram channel", + "telegramHint": "The token and chat IDs are configured in the Telegram bot settings.", + "webhookChannel": "Webhook channel", + "configured": "Configured", + "notConfigured": "Not configured", + "telegramText": "Telegram text template", + "webhookUrl": "Webhook URL", + "webhookHeaders": "Headers (one Name: value per line)", + "webhookBody": "Body template (JSON)", + "secretSet": "Set (leave empty to keep)", + "clearSecret": "Clear", + "templateHint": "Placeholders: {{title}} {{message}} {{node}} {{event}} {{time}} {{count}} {{site}}", + "testSend": "Send a test alert", + "testSent": "Test alert sent: {{channels}}", + "retireTitle": "cf-probe retirement checklist", + "retireHint": "Only once every node is served by the panel may the old public page be switched off, in the order given in the deployment notes.", + "retirePublicPage": "Public page", + "retirePublicPageOff": "Off (enable it above)", + "retireHasProbe": "Probe reporting", + "retireNoProbe": "No probe data", + "retireOnline": "Online", + "retireOffline": "Offline", + "retireProbeAt": "Last report {{time}}", + "retireAllReady": "Every node is served by the panel; cf-probe can be retired in the documented order.", + "retireNotReady": "Some nodes have no probe data yet; do not take cf-probe down." + }, "defaultTag": "Varsayılan", "title": "Panel Ayarları", "save": "Kaydet", diff --git a/internal/web/translation/uk-UA.json b/internal/web/translation/uk-UA.json index e0f656f..6f28471 100644 --- a/internal/web/translation/uk-UA.json +++ b/internal/web/translation/uk-UA.json @@ -1019,6 +1019,42 @@ "alreadyLatest": "Already the latest version", "config": "Theme settings" }, + "probeNotify": { + "notifyTitle": "Notifications", + "notifyHint": "Offline, traffic and expiry alerts, delivered to the channels below (Telegram reuses the panel bot settings).", + "grace": "Offline grace (minutes)", + "graceHint": "An alert fires only after reporting has stopped for this long; a recovery inside the grace stays silent.", + "trafficPct": "Traffic alert threshold (%)", + "trafficHint": "Alerts once at this percentage and once more at 100%, once per billing period each; 0 disables traffic alerts.", + "expiryDays": "Expiry alert (days)", + "expiryHint": "A daily digest of nodes expiring within this many days; online nodes roll over automatically by billing cycle; 0 disables.", + "loginAlert": "Panel sign-in alerts", + "telegramChannel": "Telegram channel", + "telegramHint": "The token and chat IDs are configured in the Telegram bot settings.", + "webhookChannel": "Webhook channel", + "configured": "Configured", + "notConfigured": "Not configured", + "telegramText": "Telegram text template", + "webhookUrl": "Webhook URL", + "webhookHeaders": "Headers (one Name: value per line)", + "webhookBody": "Body template (JSON)", + "secretSet": "Set (leave empty to keep)", + "clearSecret": "Clear", + "templateHint": "Placeholders: {{title}} {{message}} {{node}} {{event}} {{time}} {{count}} {{site}}", + "testSend": "Send a test alert", + "testSent": "Test alert sent: {{channels}}", + "retireTitle": "cf-probe retirement checklist", + "retireHint": "Only once every node is served by the panel may the old public page be switched off, in the order given in the deployment notes.", + "retirePublicPage": "Public page", + "retirePublicPageOff": "Off (enable it above)", + "retireHasProbe": "Probe reporting", + "retireNoProbe": "No probe data", + "retireOnline": "Online", + "retireOffline": "Offline", + "retireProbeAt": "Last report {{time}}", + "retireAllReady": "Every node is served by the panel; cf-probe can be retired in the documented order.", + "retireNotReady": "Some nodes have no probe data yet; do not take cf-probe down." + }, "defaultTag": "Типово", "title": "Параметри панелі", "save": "Зберегти", diff --git a/internal/web/translation/vi-VN.json b/internal/web/translation/vi-VN.json index d85bbed..fa9bbae 100644 --- a/internal/web/translation/vi-VN.json +++ b/internal/web/translation/vi-VN.json @@ -1019,6 +1019,42 @@ "alreadyLatest": "Already the latest version", "config": "Theme settings" }, + "probeNotify": { + "notifyTitle": "Notifications", + "notifyHint": "Offline, traffic and expiry alerts, delivered to the channels below (Telegram reuses the panel bot settings).", + "grace": "Offline grace (minutes)", + "graceHint": "An alert fires only after reporting has stopped for this long; a recovery inside the grace stays silent.", + "trafficPct": "Traffic alert threshold (%)", + "trafficHint": "Alerts once at this percentage and once more at 100%, once per billing period each; 0 disables traffic alerts.", + "expiryDays": "Expiry alert (days)", + "expiryHint": "A daily digest of nodes expiring within this many days; online nodes roll over automatically by billing cycle; 0 disables.", + "loginAlert": "Panel sign-in alerts", + "telegramChannel": "Telegram channel", + "telegramHint": "The token and chat IDs are configured in the Telegram bot settings.", + "webhookChannel": "Webhook channel", + "configured": "Configured", + "notConfigured": "Not configured", + "telegramText": "Telegram text template", + "webhookUrl": "Webhook URL", + "webhookHeaders": "Headers (one Name: value per line)", + "webhookBody": "Body template (JSON)", + "secretSet": "Set (leave empty to keep)", + "clearSecret": "Clear", + "templateHint": "Placeholders: {{title}} {{message}} {{node}} {{event}} {{time}} {{count}} {{site}}", + "testSend": "Send a test alert", + "testSent": "Test alert sent: {{channels}}", + "retireTitle": "cf-probe retirement checklist", + "retireHint": "Only once every node is served by the panel may the old public page be switched off, in the order given in the deployment notes.", + "retirePublicPage": "Public page", + "retirePublicPageOff": "Off (enable it above)", + "retireHasProbe": "Probe reporting", + "retireNoProbe": "No probe data", + "retireOnline": "Online", + "retireOffline": "Offline", + "retireProbeAt": "Last report {{time}}", + "retireAllReady": "Every node is served by the panel; cf-probe can be retired in the documented order.", + "retireNotReady": "Some nodes have no probe data yet; do not take cf-probe down." + }, "defaultTag": "Mặc định", "title": "Cài đặt", "save": "Lưu", diff --git a/internal/web/translation/zh-CN.json b/internal/web/translation/zh-CN.json index 60ba24e..ca8450d 100644 --- a/internal/web/translation/zh-CN.json +++ b/internal/web/translation/zh-CN.json @@ -1041,6 +1041,42 @@ "alreadyLatest": "已是最新版本", "config": "主题设置" }, + "probeNotify": { + "notifyTitle": "通知", + "notifyHint": "节点掉线、流量超限与到期提醒,发送到下方配置的渠道(Telegram 复用面板机器人设置)。", + "grace": "离线宽限(分钟)", + "graceHint": "停止上报超过该时长才报离线;宽限内恢复不报警。", + "trafficPct": "流量提醒阈值(%)", + "trafficHint": "达到该百分比提醒一次、达到 100% 再提醒一次,每个计费周期内各一次;0 = 关闭流量提醒。", + "expiryDays": "到期提醒(天)", + "expiryHint": "每天汇总一次将在该天数内到期的节点;在线节点到期后按计费周期自动续期;0 = 关闭。", + "loginAlert": "面板登录提醒", + "telegramChannel": "Telegram 渠道", + "telegramHint": "令牌与会话 ID 在 Telegram 机器人设置中配置。", + "webhookChannel": "Webhook 渠道", + "configured": "已配置", + "notConfigured": "未配置", + "telegramText": "Telegram 文案模板", + "webhookUrl": "Webhook 地址", + "webhookHeaders": "请求头(每行 名: 值)", + "webhookBody": "请求体模板(JSON)", + "secretSet": "已设置(留空不改)", + "clearSecret": "清除", + "templateHint": "可用占位符:{{title}} {{message}} {{node}} {{event}} {{time}} {{count}} {{site}}", + "testSend": "发送测试通知", + "testSent": "测试通知已发送:{{channels}}", + "retireTitle": "cf-probe 退役前提", + "retireHint": "全部节点都由面板供数后,旧公开页才可按部署文档顺序切换/下线。", + "retirePublicPage": "公开页", + "retirePublicPageOff": "未开启(在上方开启)", + "retireHasProbe": "探针已上报", + "retireNoProbe": "探针未上报", + "retireOnline": "在线", + "retireOffline": "离线", + "retireProbeAt": "最近上报 {{time}}", + "retireAllReady": "全部节点已由主控供数,可按文档顺序退役 cf-probe。", + "retireNotReady": "仍有节点未供数,先不要下线 cf-probe。" + }, "defaultTag": "默认", "title": "面板设置", "save": "保存", diff --git a/internal/web/translation/zh-TW.json b/internal/web/translation/zh-TW.json index bc0acf1..1b717ad 100644 --- a/internal/web/translation/zh-TW.json +++ b/internal/web/translation/zh-TW.json @@ -1019,6 +1019,42 @@ "alreadyLatest": "Already the latest version", "config": "Theme settings" }, + "probeNotify": { + "notifyTitle": "Notifications", + "notifyHint": "Offline, traffic and expiry alerts, delivered to the channels below (Telegram reuses the panel bot settings).", + "grace": "Offline grace (minutes)", + "graceHint": "An alert fires only after reporting has stopped for this long; a recovery inside the grace stays silent.", + "trafficPct": "Traffic alert threshold (%)", + "trafficHint": "Alerts once at this percentage and once more at 100%, once per billing period each; 0 disables traffic alerts.", + "expiryDays": "Expiry alert (days)", + "expiryHint": "A daily digest of nodes expiring within this many days; online nodes roll over automatically by billing cycle; 0 disables.", + "loginAlert": "Panel sign-in alerts", + "telegramChannel": "Telegram channel", + "telegramHint": "The token and chat IDs are configured in the Telegram bot settings.", + "webhookChannel": "Webhook channel", + "configured": "Configured", + "notConfigured": "Not configured", + "telegramText": "Telegram text template", + "webhookUrl": "Webhook URL", + "webhookHeaders": "Headers (one Name: value per line)", + "webhookBody": "Body template (JSON)", + "secretSet": "Set (leave empty to keep)", + "clearSecret": "Clear", + "templateHint": "Placeholders: {{title}} {{message}} {{node}} {{event}} {{time}} {{count}} {{site}}", + "testSend": "Send a test alert", + "testSent": "Test alert sent: {{channels}}", + "retireTitle": "cf-probe retirement checklist", + "retireHint": "Only once every node is served by the panel may the old public page be switched off, in the order given in the deployment notes.", + "retirePublicPage": "Public page", + "retirePublicPageOff": "Off (enable it above)", + "retireHasProbe": "Probe reporting", + "retireNoProbe": "No probe data", + "retireOnline": "Online", + "retireOffline": "Offline", + "retireProbeAt": "Last report {{time}}", + "retireAllReady": "Every node is served by the panel; cf-probe can be retired in the documented order.", + "retireNotReady": "Some nodes have no probe data yet; do not take cf-probe down." + }, "defaultTag": "預設", "title": "面板設定", "save": "儲存",