B6 frontend: notification settings + cf-probe retirement checklist on the public-page tab; probeNotify i18n x13

This commit is contained in:
Muse
2026-10-22 23:56:27 +00:00
parent 031b422365
commit b889526b0d
14 changed files with 786 additions and 0 deletions
@@ -18,6 +18,7 @@ import {
} from 'antd'; } from 'antd';
import { DeleteOutlined, SettingOutlined, UploadOutlined } from '@ant-design/icons'; import { DeleteOutlined, SettingOutlined, UploadOutlined } from '@ant-design/icons';
import { HttpUtil } from '@/utils'; import { HttpUtil } from '@/utils';
import { onNumber } from '@/utils/onNumber';
// Item 7 B5: the public status page's settings + theme store. Like // Item 7 B5: the public status page's settings + theme store. Like
// the probe-meta section this surface is self-contained — it loads // the probe-meta section this surface is self-contained — it loads
@@ -60,6 +61,35 @@ interface ThemesPayload {
touchIcon: string; 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 JSON_HEADERS = { headers: { 'Content-Type': 'application/json' } };
const UPLOAD_CHUNK = 1 << 20; const UPLOAD_CHUNK = 1 << 20;
const MAX_ICON_BYTES = 20 * 1024; const MAX_ICON_BYTES = 20 * 1024;
@@ -123,6 +153,14 @@ export default function PublicPageTab() {
const [installing, setInstalling] = useState(false); const [installing, setInstalling] = useState(false);
const [uploadPct, setUploadPct] = useState<number | null>(null); const [uploadPct, setUploadPct] = useState<number | null>(null);
const [busyShort, setBusyShort] = useState(''); const [busyShort, setBusyShort] = useState('');
const [notify, setNotify] = useState<NotifySettingsWire | null>(null);
const [coverage, setCoverage] = useState<CoverageRow[]>([]);
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<HTMLInputElement>(null); const fileRef = useRef<HTMLInputElement>(null);
const faviconRef = useRef<HTMLInputElement>(null); const faviconRef = useRef<HTMLInputElement>(null);
const touchRef = useRef<HTMLInputElement>(null); const touchRef = useRef<HTMLInputElement>(null);
@@ -144,11 +182,71 @@ export default function PublicPageTab() {
HttpUtil.get<ThemesPayload>('/panel/api/probe/themes', { silent: true }).then((msg) => { HttpUtil.get<ThemesPayload>('/panel/api/probe/themes', { silent: true }).then((msg) => {
if (!cancelled && msg?.success && msg.obj) applyPayload(msg.obj); if (!cancelled && msg?.success && msg.obj) applyPayload(msg.obj);
}); });
HttpUtil.get<NotifyPayload>('/panel/api/probe/notify', { silent: true }).then((msg) => {
if (!cancelled && msg?.success && msg.obj) {
setNotify(msg.obj.settings);
setCoverage(msg.obj.coverage ?? []);
}
});
return () => { return () => {
cancelled = true; cancelled = true;
}; };
}, [applyPayload]); }, [applyPayload]);
async function saveNotify() {
if (!notify) return;
setNotifySaving(true);
try {
const body: Record<string, unknown> = {
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<NotifyPayload>('/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<string[]>('/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() { async function saveSettings() {
setSaving(true); setSaving(true);
try { try {
@@ -474,6 +572,226 @@ export default function PublicPageTab() {
</Col> </Col>
))} ))}
</Row> </Row>
<Typography.Title level={5} style={{ marginTop: 28 }}>
{t('pages.settings.probeNotify.notifyTitle')}
</Typography.Title>
<Typography.Paragraph type="secondary">
{t('pages.settings.probeNotify.notifyHint')}
</Typography.Paragraph>
{notify ? (
<>
<Row gutter={[16, 12]}>
<Col xs={24} md={8}>
<div style={{ marginBottom: 4 }}>{t('pages.settings.probeNotify.grace')}</div>
<InputNumber
min={1}
max={30}
value={notify.notifyGrace}
onChange={onNumber((v) => setNotify({ ...notify, notifyGrace: v }))}
/>
<div>
<Typography.Text type="secondary">
{t('pages.settings.probeNotify.graceHint')}
</Typography.Text>
</div>
</Col>
<Col xs={24} md={8}>
<div style={{ marginBottom: 4 }}>{t('pages.settings.probeNotify.trafficPct')}</div>
<InputNumber
min={0}
max={100}
value={notify.notifyTraffic}
onChange={onNumber((v) => setNotify({ ...notify, notifyTraffic: v }))}
/>
<div>
<Typography.Text type="secondary">
{t('pages.settings.probeNotify.trafficHint')}
</Typography.Text>
</div>
</Col>
<Col xs={24} md={8}>
<div style={{ marginBottom: 4 }}>{t('pages.settings.probeNotify.expiryDays')}</div>
<InputNumber
min={0}
max={365}
value={notify.notifyExpiry}
onChange={onNumber((v) => setNotify({ ...notify, notifyExpiry: v }))}
/>
<div>
<Typography.Text type="secondary">
{t('pages.settings.probeNotify.expiryHint')}
</Typography.Text>
</div>
</Col>
<Col xs={24} md={8}>
<div style={{ marginBottom: 4 }}>{t('pages.settings.probeNotify.loginAlert')}</div>
<Switch
checked={notify.notifyLogin !== 'off'}
onChange={(v) => setNotify({ ...notify, notifyLogin: v ? 'on' : 'off' })}
/>
</Col>
<Col xs={24} md={8}>
<div style={{ marginBottom: 4 }}>
{t('pages.settings.probeNotify.telegramChannel')}
</div>
{notify.telegramConfigured ? (
<Tag color="green">{t('pages.settings.probeNotify.configured')}</Tag>
) : (
<Tag>{t('pages.settings.probeNotify.notConfigured')}</Tag>
)}
<div>
<Typography.Text type="secondary">
{t('pages.settings.probeNotify.telegramHint')}
</Typography.Text>
</div>
</Col>
<Col xs={24} md={8}>
<div style={{ marginBottom: 4 }}>
{t('pages.settings.probeNotify.webhookChannel')}
</div>
{notify.webhookConfigured ? (
<Tag color="green">{t('pages.settings.probeNotify.configured')}</Tag>
) : (
<Tag>{t('pages.settings.probeNotify.notConfigured')}</Tag>
)}
</Col>
<Col xs={24}>
<div style={{ marginBottom: 4 }}>{t('pages.settings.probeNotify.telegramText')}</div>
<Input.TextArea
rows={3}
value={notify.notifyTelegramText}
placeholder={'{{title}}\n{{message}}'}
onChange={(e) => setNotify({ ...notify, notifyTelegramText: e.target.value })}
/>
</Col>
<Col xs={24} md={12}>
<div style={{ marginBottom: 4 }}>{t('pages.settings.probeNotify.webhookUrl')}</div>
<Input
value={webhookUrl}
placeholder={
notify.webhookUrlSet ? t('pages.settings.probeNotify.secretSet') : 'https://'
}
onChange={(e) => {
setWebhookUrl(e.target.value);
setUrlTouched(true);
}}
/>
{notify.webhookUrlSet && !urlTouched ? (
<Button
type="link"
size="small"
onClick={() => {
setWebhookUrl('');
setUrlTouched(true);
}}
>
{t('pages.settings.probeNotify.clearSecret')}
</Button>
) : null}
</Col>
<Col xs={24} md={12}>
<div style={{ marginBottom: 4 }}>
{t('pages.settings.probeNotify.webhookHeaders')}
</div>
<Input.TextArea
rows={2}
value={webhookHeaders}
placeholder={
notify.webhookHeadersSet
? t('pages.settings.probeNotify.secretSet')
: 'Authorization: Bearer …'
}
onChange={(e) => {
setWebhookHeaders(e.target.value);
setHeadersTouched(true);
}}
/>
{notify.webhookHeadersSet && !headersTouched ? (
<Button
type="link"
size="small"
onClick={() => {
setWebhookHeaders('');
setHeadersTouched(true);
}}
>
{t('pages.settings.probeNotify.clearSecret')}
</Button>
) : null}
</Col>
<Col xs={24}>
<div style={{ marginBottom: 4 }}>{t('pages.settings.probeNotify.webhookBody')}</div>
<Input.TextArea
rows={3}
value={notify.notifyWebhookBody}
onChange={(e) => setNotify({ ...notify, notifyWebhookBody: e.target.value })}
/>
<div>
<Typography.Text type="secondary">
{t('pages.settings.probeNotify.templateHint')}
</Typography.Text>
</div>
</Col>
</Row>
<Space style={{ marginTop: 12 }}>
<Button type="primary" loading={notifySaving} onClick={saveNotify}>
{t('save')}
</Button>
<Button loading={notifyTesting} onClick={testNotify}>
{t('pages.settings.probeNotify.testSend')}
</Button>
</Space>
</>
) : null}
<Typography.Title level={5} style={{ marginTop: 28 }}>
{t('pages.settings.probeNotify.retireTitle')}
</Typography.Title>
<Typography.Paragraph type="secondary">
{t('pages.settings.probeNotify.retireHint')}
</Typography.Paragraph>
<Space direction="vertical">
<span>
{t('pages.settings.probeNotify.retirePublicPage')}:
{settings.publicPage ? (
<Tag color="green">{t('pages.settings.probeNotify.configured')}</Tag>
) : (
<Tag color="orange">{t('pages.settings.probeNotify.retirePublicPageOff')}</Tag>
)}
</span>
{coverage.map((row) => (
<span key={row.name}>
{row.name}:
{row.hasProbe ? (
<Tag color="green">{t('pages.settings.probeNotify.retireHasProbe')}</Tag>
) : (
<Tag color="red">{t('pages.settings.probeNotify.retireNoProbe')}</Tag>
)}
{row.online ? (
<Tag color="green">{t('pages.settings.probeNotify.retireOnline')}</Tag>
) : (
<Tag>{t('pages.settings.probeNotify.retireOffline')}</Tag>
)}
{row.probeAt > 0 ? (
<Typography.Text type="secondary">
{t('pages.settings.probeNotify.retireProbeAt', {
time: new Date(row.probeAt * 1000).toLocaleString(),
})}
</Typography.Text>
) : null}
</span>
))}
{coverage.length > 0 && coverage.every((row) => row.hasProbe) ? (
<Typography.Text type="success">
{t('pages.settings.probeNotify.retireAllReady')}
</Typography.Text>
) : (
<Typography.Text type="warning">
{t('pages.settings.probeNotify.retireNotReady')}
</Typography.Text>
)}
</Space>
</div> </div>
); );
} }
+36
View File
@@ -1019,6 +1019,42 @@
"alreadyLatest": "Already the latest version", "alreadyLatest": "Already the latest version",
"config": "Theme settings" "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": "افتراضي", "defaultTag": "افتراضي",
"title": "إعدادات البانل", "title": "إعدادات البانل",
"save": "حفظ", "save": "حفظ",
+36
View File
@@ -1159,6 +1159,42 @@
"alreadyLatest": "Already the latest version", "alreadyLatest": "Already the latest version",
"config": "Theme settings" "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", "defaultTag": "Default",
"title": "Panel Settings", "title": "Panel Settings",
"save": "Save", "save": "Save",
+36
View File
@@ -1019,6 +1019,42 @@
"alreadyLatest": "Already the latest version", "alreadyLatest": "Already the latest version",
"config": "Theme settings" "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", "defaultTag": "Predeterminado",
"title": "Configuraciones", "title": "Configuraciones",
"save": "Guardar", "save": "Guardar",
+36
View File
@@ -1019,6 +1019,42 @@
"alreadyLatest": "Already the latest version", "alreadyLatest": "Already the latest version",
"config": "Theme settings" "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": "پیش‌فرض", "defaultTag": "پیش‌فرض",
"title": "تنظیمات پنل", "title": "تنظیمات پنل",
"save": "ذخیره", "save": "ذخیره",
+36
View File
@@ -1019,6 +1019,42 @@
"alreadyLatest": "Already the latest version", "alreadyLatest": "Already the latest version",
"config": "Theme settings" "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", "defaultTag": "Bawaan",
"title": "Pengaturan Panel", "title": "Pengaturan Panel",
"save": "Simpan", "save": "Simpan",
+36
View File
@@ -1019,6 +1019,42 @@
"alreadyLatest": "Already the latest version", "alreadyLatest": "Already the latest version",
"config": "Theme settings" "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": "デフォルト", "defaultTag": "デフォルト",
"title": "パネル設定", "title": "パネル設定",
"save": "保存", "save": "保存",
+36
View File
@@ -1019,6 +1019,42 @@
"alreadyLatest": "Already the latest version", "alreadyLatest": "Already the latest version",
"config": "Theme settings" "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", "defaultTag": "Padrão",
"title": "Configurações do Painel", "title": "Configurações do Painel",
"save": "Salvar", "save": "Salvar",
+36
View File
@@ -1019,6 +1019,42 @@
"alreadyLatest": "Already the latest version", "alreadyLatest": "Already the latest version",
"config": "Theme settings" "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": "По умолчанию", "defaultTag": "По умолчанию",
"title": "Настройки", "title": "Настройки",
"save": "Сохранить", "save": "Сохранить",
+36
View File
@@ -1019,6 +1019,42 @@
"alreadyLatest": "Already the latest version", "alreadyLatest": "Already the latest version",
"config": "Theme settings" "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", "defaultTag": "Varsayılan",
"title": "Panel Ayarları", "title": "Panel Ayarları",
"save": "Kaydet", "save": "Kaydet",
+36
View File
@@ -1019,6 +1019,42 @@
"alreadyLatest": "Already the latest version", "alreadyLatest": "Already the latest version",
"config": "Theme settings" "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": "Типово", "defaultTag": "Типово",
"title": "Параметри панелі", "title": "Параметри панелі",
"save": "Зберегти", "save": "Зберегти",
+36
View File
@@ -1019,6 +1019,42 @@
"alreadyLatest": "Already the latest version", "alreadyLatest": "Already the latest version",
"config": "Theme settings" "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", "defaultTag": "Mặc định",
"title": "Cài đặt", "title": "Cài đặt",
"save": "Lưu", "save": "Lưu",
+36
View File
@@ -1041,6 +1041,42 @@
"alreadyLatest": "已是最新版本", "alreadyLatest": "已是最新版本",
"config": "主题设置" "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": "默认", "defaultTag": "默认",
"title": "面板设置", "title": "面板设置",
"save": "保存", "save": "保存",
+36
View File
@@ -1019,6 +1019,42 @@
"alreadyLatest": "Already the latest version", "alreadyLatest": "Already the latest version",
"config": "Theme settings" "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": "預設", "defaultTag": "預設",
"title": "面板設定", "title": "面板設定",
"save": "儲存", "save": "儲存",