mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-28 00:24:19 +00:00
feat(memory): add memory threshold alerts (#5366)
* feat(memory): add memory threshold alerts
Add memory (RAM) threshold alerts following the same architecture as
CPU alerts: CheckMemJob with @every 1m cadence, memoryAlarmWanted gate,
tgMemory/smtpMemory per-subscriber settings (default 80%), EventBusCheckboxes
with inline threshold input, i18n for en-US/ru-RU with English defaults.
# Conflicts:
# internal/web/translation/ar-EG.json
# internal/web/translation/es-ES.json
# internal/web/translation/fa-IR.json
# internal/web/translation/id-ID.json
# internal/web/translation/ja-JP.json
# internal/web/translation/pt-BR.json
# internal/web/translation/ru-RU.json
# internal/web/translation/tr-TR.json
# internal/web/translation/uk-UA.json
# internal/web/translation/vi-VN.json
# internal/web/translation/zh-CN.json
# internal/web/translation/zh-TW.json
* fix: address code review findings for memory alerts
- Remove dead settingService field from CheckMemJob
- Fix cpuThreshold double-emoji in 12 locale files (code prepends 🔴)
- Align TgCpu/TgMemory fields in entity.go
- Add missing SetTgMemory function
* fix: restore settingService in CheckMemJob for consistency with CheckCpuJob
This commit is contained in:
@@ -160,6 +160,12 @@
|
||||
"description": "SMTP server host",
|
||||
"type": "string"
|
||||
},
|
||||
"smtpMemory": {
|
||||
"description": "Memory threshold for email notifications",
|
||||
"maximum": 100,
|
||||
"minimum": 0,
|
||||
"type": "integer"
|
||||
},
|
||||
"smtpPassword": {
|
||||
"description": "SMTP password",
|
||||
"type": "string"
|
||||
@@ -335,6 +341,12 @@
|
||||
"description": "Telegram bot language",
|
||||
"type": "string"
|
||||
},
|
||||
"tgMemory": {
|
||||
"description": "Memory usage threshold for alerts (percent)",
|
||||
"maximum": 100,
|
||||
"minimum": 0,
|
||||
"type": "integer"
|
||||
},
|
||||
"tgRunTime": {
|
||||
"description": "Cron schedule for Telegram notifications",
|
||||
"type": "string"
|
||||
@@ -428,6 +440,7 @@
|
||||
"smtpEnabledEvents",
|
||||
"smtpEncryptionType",
|
||||
"smtpHost",
|
||||
"smtpMemory",
|
||||
"smtpPassword",
|
||||
"smtpPort",
|
||||
"smtpTo",
|
||||
@@ -470,6 +483,7 @@
|
||||
"tgCpu",
|
||||
"tgEnabledEvents",
|
||||
"tgLang",
|
||||
"tgMemory",
|
||||
"tgRunTime",
|
||||
"timeLocation",
|
||||
"trafficDiff",
|
||||
@@ -641,6 +655,12 @@
|
||||
"description": "SMTP server host",
|
||||
"type": "string"
|
||||
},
|
||||
"smtpMemory": {
|
||||
"description": "Memory threshold for email notifications",
|
||||
"maximum": 100,
|
||||
"minimum": 0,
|
||||
"type": "integer"
|
||||
},
|
||||
"smtpPassword": {
|
||||
"description": "SMTP password",
|
||||
"type": "string"
|
||||
@@ -816,6 +836,12 @@
|
||||
"description": "Telegram bot language",
|
||||
"type": "string"
|
||||
},
|
||||
"tgMemory": {
|
||||
"description": "Memory usage threshold for alerts (percent)",
|
||||
"maximum": 100,
|
||||
"minimum": 0,
|
||||
"type": "integer"
|
||||
},
|
||||
"tgRunTime": {
|
||||
"description": "Cron schedule for Telegram notifications",
|
||||
"type": "string"
|
||||
@@ -916,6 +942,7 @@
|
||||
"smtpEnabledEvents",
|
||||
"smtpEncryptionType",
|
||||
"smtpHost",
|
||||
"smtpMemory",
|
||||
"smtpPassword",
|
||||
"smtpPort",
|
||||
"smtpTo",
|
||||
@@ -958,6 +985,7 @@
|
||||
"tgCpu",
|
||||
"tgEnabledEvents",
|
||||
"tgLang",
|
||||
"tgMemory",
|
||||
"tgRunTime",
|
||||
"timeLocation",
|
||||
"trafficDiff",
|
||||
|
||||
@@ -41,6 +41,14 @@ const GROUPS: NotificationGroupConfig[] = [
|
||||
<InputNumber size="small" min={0} max={100} value={value} onChange={onChange} style={{ width: 80 }} />
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'memory.high',
|
||||
label: 'eventMemoryHigh',
|
||||
settingKey: 'smtpMemory',
|
||||
extra: ({ value, onChange }) => (
|
||||
<InputNumber size="small" min={0} max={100} value={value} onChange={onChange} style={{ width: 80 }} />
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -41,6 +41,14 @@ const GROUPS: NotificationGroupConfig[] = [
|
||||
<InputNumber size="small" min={0} max={100} value={value} onChange={onChange} style={{ width: 80 }} />
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'memory.high',
|
||||
label: 'eventMemoryHigh',
|
||||
settingKey: 'tgMemory',
|
||||
extra: ({ value, onChange }) => (
|
||||
<InputNumber size="small" min={0} max={100} value={value} onChange={onChange} style={{ width: 80 }} />
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -35,6 +35,7 @@ export const EXAMPLES: Record<string, unknown> = {
|
||||
"smtpEnabledEvents": "",
|
||||
"smtpEncryptionType": "",
|
||||
"smtpHost": "",
|
||||
"smtpMemory": 0,
|
||||
"smtpPassword": "",
|
||||
"smtpPort": 1,
|
||||
"smtpTo": "",
|
||||
@@ -77,6 +78,7 @@ export const EXAMPLES: Record<string, unknown> = {
|
||||
"tgCpu": 0,
|
||||
"tgEnabledEvents": "",
|
||||
"tgLang": "",
|
||||
"tgMemory": 0,
|
||||
"tgRunTime": "",
|
||||
"timeLocation": "",
|
||||
"trafficDiff": 0,
|
||||
@@ -133,6 +135,7 @@ export const EXAMPLES: Record<string, unknown> = {
|
||||
"smtpEnabledEvents": "",
|
||||
"smtpEncryptionType": "",
|
||||
"smtpHost": "",
|
||||
"smtpMemory": 0,
|
||||
"smtpPassword": "",
|
||||
"smtpPort": 1,
|
||||
"smtpTo": "",
|
||||
@@ -175,6 +178,7 @@ export const EXAMPLES: Record<string, unknown> = {
|
||||
"tgCpu": 0,
|
||||
"tgEnabledEvents": "",
|
||||
"tgLang": "",
|
||||
"tgMemory": 0,
|
||||
"tgRunTime": "",
|
||||
"timeLocation": "",
|
||||
"trafficDiff": 0,
|
||||
|
||||
@@ -134,6 +134,12 @@ export const SCHEMAS: Record<string, unknown> = {
|
||||
"description": "SMTP server host",
|
||||
"type": "string"
|
||||
},
|
||||
"smtpMemory": {
|
||||
"description": "Memory threshold for email notifications",
|
||||
"maximum": 100,
|
||||
"minimum": 0,
|
||||
"type": "integer"
|
||||
},
|
||||
"smtpPassword": {
|
||||
"description": "SMTP password",
|
||||
"type": "string"
|
||||
@@ -309,6 +315,12 @@ export const SCHEMAS: Record<string, unknown> = {
|
||||
"description": "Telegram bot language",
|
||||
"type": "string"
|
||||
},
|
||||
"tgMemory": {
|
||||
"description": "Memory usage threshold for alerts (percent)",
|
||||
"maximum": 100,
|
||||
"minimum": 0,
|
||||
"type": "integer"
|
||||
},
|
||||
"tgRunTime": {
|
||||
"description": "Cron schedule for Telegram notifications",
|
||||
"type": "string"
|
||||
@@ -402,6 +414,7 @@ export const SCHEMAS: Record<string, unknown> = {
|
||||
"smtpEnabledEvents",
|
||||
"smtpEncryptionType",
|
||||
"smtpHost",
|
||||
"smtpMemory",
|
||||
"smtpPassword",
|
||||
"smtpPort",
|
||||
"smtpTo",
|
||||
@@ -444,6 +457,7 @@ export const SCHEMAS: Record<string, unknown> = {
|
||||
"tgCpu",
|
||||
"tgEnabledEvents",
|
||||
"tgLang",
|
||||
"tgMemory",
|
||||
"tgRunTime",
|
||||
"timeLocation",
|
||||
"trafficDiff",
|
||||
@@ -615,6 +629,12 @@ export const SCHEMAS: Record<string, unknown> = {
|
||||
"description": "SMTP server host",
|
||||
"type": "string"
|
||||
},
|
||||
"smtpMemory": {
|
||||
"description": "Memory threshold for email notifications",
|
||||
"maximum": 100,
|
||||
"minimum": 0,
|
||||
"type": "integer"
|
||||
},
|
||||
"smtpPassword": {
|
||||
"description": "SMTP password",
|
||||
"type": "string"
|
||||
@@ -790,6 +810,12 @@ export const SCHEMAS: Record<string, unknown> = {
|
||||
"description": "Telegram bot language",
|
||||
"type": "string"
|
||||
},
|
||||
"tgMemory": {
|
||||
"description": "Memory usage threshold for alerts (percent)",
|
||||
"maximum": 100,
|
||||
"minimum": 0,
|
||||
"type": "integer"
|
||||
},
|
||||
"tgRunTime": {
|
||||
"description": "Cron schedule for Telegram notifications",
|
||||
"type": "string"
|
||||
@@ -890,6 +916,7 @@ export const SCHEMAS: Record<string, unknown> = {
|
||||
"smtpEnabledEvents",
|
||||
"smtpEncryptionType",
|
||||
"smtpHost",
|
||||
"smtpMemory",
|
||||
"smtpPassword",
|
||||
"smtpPort",
|
||||
"smtpTo",
|
||||
@@ -932,6 +959,7 @@ export const SCHEMAS: Record<string, unknown> = {
|
||||
"tgCpu",
|
||||
"tgEnabledEvents",
|
||||
"tgLang",
|
||||
"tgMemory",
|
||||
"tgRunTime",
|
||||
"timeLocation",
|
||||
"trafficDiff",
|
||||
|
||||
@@ -41,6 +41,7 @@ export interface AllSetting {
|
||||
smtpEnabledEvents: string;
|
||||
smtpEncryptionType: string;
|
||||
smtpHost: string;
|
||||
smtpMemory: number;
|
||||
smtpPassword: string;
|
||||
smtpPort: number;
|
||||
smtpTo: string;
|
||||
@@ -83,6 +84,7 @@ export interface AllSetting {
|
||||
tgCpu: number;
|
||||
tgEnabledEvents: string;
|
||||
tgLang: string;
|
||||
tgMemory: number;
|
||||
tgRunTime: string;
|
||||
timeLocation: string;
|
||||
trafficDiff: number;
|
||||
@@ -140,6 +142,7 @@ export interface AllSettingView {
|
||||
smtpEnabledEvents: string;
|
||||
smtpEncryptionType: string;
|
||||
smtpHost: string;
|
||||
smtpMemory: number;
|
||||
smtpPassword: string;
|
||||
smtpPort: number;
|
||||
smtpTo: string;
|
||||
@@ -182,6 +185,7 @@ export interface AllSettingView {
|
||||
tgCpu: number;
|
||||
tgEnabledEvents: string;
|
||||
tgLang: string;
|
||||
tgMemory: number;
|
||||
tgRunTime: string;
|
||||
timeLocation: string;
|
||||
trafficDiff: number;
|
||||
|
||||
@@ -53,6 +53,7 @@ export const AllSettingSchema = z.object({
|
||||
smtpEnabledEvents: z.string(),
|
||||
smtpEncryptionType: z.string(),
|
||||
smtpHost: z.string(),
|
||||
smtpMemory: z.number().int().min(0).max(100),
|
||||
smtpPassword: z.string(),
|
||||
smtpPort: z.number().int().min(1).max(65535),
|
||||
smtpTo: z.string(),
|
||||
@@ -95,6 +96,7 @@ export const AllSettingSchema = z.object({
|
||||
tgCpu: z.number().int().min(0).max(100),
|
||||
tgEnabledEvents: z.string(),
|
||||
tgLang: z.string(),
|
||||
tgMemory: z.number().int().min(0).max(100),
|
||||
tgRunTime: z.string(),
|
||||
timeLocation: z.string(),
|
||||
trafficDiff: z.number().int().min(0).max(100),
|
||||
@@ -153,6 +155,7 @@ export const AllSettingViewSchema = z.object({
|
||||
smtpEnabledEvents: z.string(),
|
||||
smtpEncryptionType: z.string(),
|
||||
smtpHost: z.string(),
|
||||
smtpMemory: z.number().int().min(0).max(100),
|
||||
smtpPassword: z.string(),
|
||||
smtpPort: z.number().int().min(1).max(65535),
|
||||
smtpTo: z.string(),
|
||||
@@ -195,6 +198,7 @@ export const AllSettingViewSchema = z.object({
|
||||
tgCpu: z.number().int().min(0).max(100),
|
||||
tgEnabledEvents: z.string(),
|
||||
tgLang: z.string(),
|
||||
tgMemory: z.number().int().min(0).max(100),
|
||||
tgRunTime: z.string(),
|
||||
timeLocation: z.string(),
|
||||
trafficDiff: z.number().int().min(0).max(100),
|
||||
|
||||
@@ -22,6 +22,7 @@ export class AllSetting {
|
||||
tgRunTime = '@daily';
|
||||
tgBotBackup = false;
|
||||
tgCpu = 80;
|
||||
tgMemory = 80;
|
||||
tgLang = 'en-US';
|
||||
twoFactorEnable = false;
|
||||
twoFactorToken = '';
|
||||
@@ -91,6 +92,7 @@ export class AllSetting {
|
||||
smtpEncryptionType = 'starttls';
|
||||
smtpEnabledEvents = '';
|
||||
smtpCpu = 80;
|
||||
smtpMemory = 80;
|
||||
hasTgBotToken = false;
|
||||
hasTwoFactorToken = false;
|
||||
hasLdapPassword = false;
|
||||
|
||||
@@ -18,7 +18,8 @@ const (
|
||||
EventNodeUp EventType = "node.up"
|
||||
|
||||
// System health
|
||||
EventCPUHigh EventType = "cpu.high"
|
||||
EventCPUHigh EventType = "cpu.high"
|
||||
EventMemoryHigh EventType = "memory.high"
|
||||
|
||||
// Security
|
||||
EventLoginAttempt EventType = "login.attempt"
|
||||
|
||||
@@ -47,6 +47,7 @@ type AllSetting struct {
|
||||
TgRunTime string `json:"tgRunTime" form:"tgRunTime"` // Cron schedule for Telegram notifications
|
||||
TgBotBackup bool `json:"tgBotBackup" form:"tgBotBackup"` // Enable database backup via Telegram
|
||||
TgCpu int `json:"tgCpu" form:"tgCpu" validate:"gte=0,lte=100"` // CPU usage threshold for alerts (percent)
|
||||
TgMemory int `json:"tgMemory" form:"tgMemory" validate:"gte=0,lte=100"` // Memory usage threshold for alerts (percent)
|
||||
TgLang string `json:"tgLang" form:"tgLang"` // Telegram bot language
|
||||
TgEnabledEvents string `json:"tgEnabledEvents" form:"tgEnabledEvents"` // Comma-separated event types to send via Telegram
|
||||
|
||||
@@ -59,7 +60,8 @@ type AllSetting struct {
|
||||
SmtpTo string `json:"smtpTo" form:"smtpTo"` // Comma-separated recipient emails
|
||||
SmtpEncryptionType string `json:"smtpEncryptionType" form:"smtpEncryptionType"` // SMTP encryption: none, starttls, tls
|
||||
SmtpEnabledEvents string `json:"smtpEnabledEvents" form:"smtpEnabledEvents"` // Comma-separated event types to send via email
|
||||
SmtpCpu int `json:"smtpCpu" form:"smtpCpu" validate:"gte=0,lte=100"` // CPU threshold for email notifications
|
||||
SmtpCpu int `json:"smtpCpu" form:"smtpCpu" validate:"gte=0,lte=100"` // CPU threshold for email notifications
|
||||
SmtpMemory int `json:"smtpMemory" form:"smtpMemory" validate:"gte=0,lte=100"` // Memory threshold for email notifications
|
||||
|
||||
// Security settings
|
||||
TimeLocation string `json:"timeLocation" form:"timeLocation"` // Time zone location
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package job
|
||||
|
||||
import (
|
||||
"github.com/mhsanaei/3x-ui/v3/internal/eventbus"
|
||||
"github.com/mhsanaei/3x-ui/v3/internal/web/service"
|
||||
|
||||
"github.com/shirou/gopsutil/v4/mem"
|
||||
)
|
||||
|
||||
// CheckMemJob monitors memory usage and publishes events when threshold is exceeded.
|
||||
type CheckMemJob struct {
|
||||
settingService service.SettingService
|
||||
}
|
||||
|
||||
// NewCheckMemJob creates a new memory monitoring job instance.
|
||||
func NewCheckMemJob() *CheckMemJob {
|
||||
return new(CheckMemJob)
|
||||
}
|
||||
|
||||
// Run checks memory usage and publishes a memory.high event with raw metric data.
|
||||
func (j *CheckMemJob) Run() {
|
||||
memInfo, err := mem.VirtualMemory()
|
||||
if err != nil || memInfo == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if EventBus != nil {
|
||||
EventBus.Publish(eventbus.Event{
|
||||
Type: eventbus.EventMemoryHigh,
|
||||
Data: &eventbus.SystemMetricData{
|
||||
Percent: memInfo.UsedPercent,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -148,6 +148,19 @@ func (s *Subscriber) formatMessage(e eventbus.Event) (subject, body string) {
|
||||
body = wrap(subject, content)
|
||||
}
|
||||
|
||||
case eventbus.EventMemoryHigh:
|
||||
if data, ok := e.Data.(*eventbus.SystemMetricData); ok {
|
||||
smtpMemory, err := s.settingService.GetSmtpMemory()
|
||||
if err != nil || smtpMemory <= 0 || data.Percent <= float64(smtpMemory) {
|
||||
return
|
||||
}
|
||||
subject = host + " " + i18n("tgbot.messages.memoryThreshold",
|
||||
"Percent=="+strconv.FormatFloat(data.Percent, 'f', 2, 64),
|
||||
"Threshold=="+fmt.Sprintf("%d", smtpMemory))
|
||||
content := kv(i18n("email.labelStatus"), `<span style="color:orange">`+i18n("email.statusHigh")+`</span>`)
|
||||
body = wrap(subject, content)
|
||||
}
|
||||
|
||||
case eventbus.EventLoginAttempt:
|
||||
if data, ok := e.Data.(*eventbus.LoginEventData); ok {
|
||||
if data.Status == "success" {
|
||||
|
||||
@@ -63,6 +63,7 @@ var defaultValueMap = map[string]string{
|
||||
"tgRunTime": "@daily",
|
||||
"tgBotBackup": "false",
|
||||
"tgCpu": "80",
|
||||
"tgMemory": "80",
|
||||
"tgLang": "en-US",
|
||||
"twoFactorEnable": "false",
|
||||
"twoFactorToken": "",
|
||||
@@ -131,6 +132,7 @@ var defaultValueMap = map[string]string{
|
||||
"tgEnabledEvents": "login.attempt,cpu.high",
|
||||
"smtpEnabledEvents": "login.attempt,cpu.high",
|
||||
"smtpCpu": "80",
|
||||
"smtpMemory": "80",
|
||||
|
||||
// Email (SMTP) notifications
|
||||
"smtpEnable": "false",
|
||||
@@ -531,6 +533,14 @@ func (s *SettingService) GetTgCpu() (int, error) {
|
||||
return s.getInt("tgCpu")
|
||||
}
|
||||
|
||||
func (s *SettingService) GetTgMemory() (int, error) {
|
||||
return s.getInt("tgMemory")
|
||||
}
|
||||
|
||||
func (s *SettingService) SetTgMemory(value int) error {
|
||||
return s.setInt("tgMemory", value)
|
||||
}
|
||||
|
||||
func (s *SettingService) GetTgLang() (string, error) {
|
||||
return s.getString("tgLang")
|
||||
}
|
||||
@@ -1017,6 +1027,14 @@ func (s *SettingService) SetSmtpCpu(value int) error {
|
||||
return s.setInt("smtpCpu", value)
|
||||
}
|
||||
|
||||
func (s *SettingService) GetSmtpMemory() (int, error) {
|
||||
return s.getInt("smtpMemory")
|
||||
}
|
||||
|
||||
func (s *SettingService) SetSmtpMemory(value int) error {
|
||||
return s.setInt("smtpMemory", value)
|
||||
}
|
||||
|
||||
func (s *SettingService) UpdateAllSetting(allSetting *entity.AllSetting) error {
|
||||
if err := s.preserveRedactedSecrets(allSetting); err != nil {
|
||||
return err
|
||||
|
||||
@@ -123,6 +123,18 @@ func (t *Tgbot) formatEventMessage(e eventbus.Event) string {
|
||||
}
|
||||
return ""
|
||||
|
||||
case eventbus.EventMemoryHigh:
|
||||
if data, ok := e.Data.(*eventbus.SystemMetricData); ok {
|
||||
tgMemory, err := t.settingService.GetTgMemory()
|
||||
if err != nil || tgMemory <= 0 || data.Percent <= float64(tgMemory) {
|
||||
return ""
|
||||
}
|
||||
return header + "🔴 " + t.I18nBot("tgbot.messages.memoryThreshold",
|
||||
"Percent=="+strconv.FormatFloat(data.Percent, 'f', 2, 64),
|
||||
"Threshold=="+strconv.Itoa(tgMemory))
|
||||
}
|
||||
return ""
|
||||
|
||||
case eventbus.EventLoginAttempt:
|
||||
if data, ok := e.Data.(*eventbus.LoginEventData); ok {
|
||||
if data.Status == "success" {
|
||||
|
||||
@@ -1309,6 +1309,7 @@
|
||||
"smtpErrorRelay": "الخادم يرفض الإرسال من هذا العنوان",
|
||||
"smtpErrorEof": "تم إغلاق الاتصال من قبل الخادم",
|
||||
"smtpErrorUnknown": "خطأ SMTP: {{ .Error }}",
|
||||
"eventMemoryHigh": "ارتفاع استخدام الذاكرة (%)",
|
||||
"remarkTemplate": "قالب الملاحظة",
|
||||
"remarkTemplateDesc": "عند تعيينه، يحل هذا محل نموذج الملاحظة لكل رابط اشتراك — اكتب صيغتك الخاصة باستخدام رموز المتغيرات (استخدم الزر لإدراجها). اتركه فارغاً لاستخدام النموذج أعلاه."
|
||||
},
|
||||
@@ -1865,7 +1866,7 @@
|
||||
"idDesc": "عرض معرف Telegram الخاص بك"
|
||||
},
|
||||
"messages": {
|
||||
"cpuThreshold": "🔴 حمل المعالج {{ .Percent }}% عدى الحد المسموح ({{ .Threshold }}%)",
|
||||
"cpuThreshold": "حمل المعالج {{ .Percent }}% عدى الحد المسموح ({{ .Threshold }}%)",
|
||||
"selectUserFailed": "❌ حصل خطأ في اختيار المستخدم!",
|
||||
"userSaved": "✅ حفظت بيانات مستخدم Telegram.",
|
||||
"loginSuccess": "✅ تسجيل الدخول للبانل تم بنجاح.\r\n",
|
||||
@@ -1940,7 +1941,8 @@
|
||||
"eventNodeUp": "العقدة {{ .Name }} متصلة",
|
||||
"eventCPUHigh": "ارتفاع استخدام المعالج",
|
||||
"eventCPUHighDetail": "المعالج: {{ .Detail }}",
|
||||
"eventLoginFallback": "فشل تسجيل الدخول من {{ .Source }}"
|
||||
"eventLoginFallback": "فشل تسجيل الدخول من {{ .Source }}",
|
||||
"memoryThreshold": "استخدام الذاكرة {{ .Percent }}% يتجاوز الحد {{ .Threshold }}%"
|
||||
},
|
||||
"buttons": {
|
||||
"closeKeyboard": "❌ اقفل الكيبورد",
|
||||
|
||||
@@ -1418,7 +1418,8 @@
|
||||
"smtpErrorTimeout": "Connection timeout — host unreachable",
|
||||
"smtpErrorRelay": "Server rejects sending from this address",
|
||||
"smtpErrorEof": "Connection closed by server",
|
||||
"smtpErrorUnknown": "SMTP error: {{ .Error }}"
|
||||
"smtpErrorUnknown": "SMTP error: {{ .Error }}",
|
||||
"eventMemoryHigh": "Memory high (%)"
|
||||
},
|
||||
"xray": {
|
||||
"title": "Xray Configs",
|
||||
@@ -1865,7 +1866,7 @@
|
||||
"idDesc": "Show your Telegram ID"
|
||||
},
|
||||
"messages": {
|
||||
"cpuThreshold": "🔴 CPU Load {{ .Percent }}% exceeds the threshold of {{ .Threshold }}%",
|
||||
"cpuThreshold": "CPU Load {{ .Percent }}% exceeds the threshold of {{ .Threshold }}%",
|
||||
"selectUserFailed": "❌ Error in user selection!",
|
||||
"userSaved": "✅ Telegram User saved.",
|
||||
"loginSuccess": "✅ Logged in to the panel successfully.\r\n",
|
||||
@@ -1940,7 +1941,8 @@
|
||||
"eventNodeUp": "Node {{ .Name }} is UP",
|
||||
"eventCPUHigh": "CPU high",
|
||||
"eventCPUHighDetail": "CPU: {{ .Detail }}",
|
||||
"eventLoginFallback": "Login failed from {{ .Source }}"
|
||||
"eventLoginFallback": "Login failed from {{ .Source }}",
|
||||
"memoryThreshold": "Memory Load {{ .Percent }}% exceeds the threshold of {{ .Threshold }}%"
|
||||
},
|
||||
"buttons": {
|
||||
"closeKeyboard": "❌ Close Keyboard",
|
||||
|
||||
@@ -1309,6 +1309,7 @@
|
||||
"smtpErrorRelay": "El servidor rechaza el envío desde esta dirección",
|
||||
"smtpErrorEof": "Conexión cerrada por el servidor",
|
||||
"smtpErrorUnknown": "Error de SMTP: {{ .Error }}",
|
||||
"eventMemoryHigh": "Uso de memoria alto (%)",
|
||||
"remarkTemplate": "Plantilla de notas",
|
||||
"remarkTemplateDesc": "Cuando se define, esto reemplaza el modelo de notas para cada enlace de suscripción — escribe tu propio formato con los tokens de variable (usa el botón para insertarlos). Déjalo vacío para usar el modelo anterior."
|
||||
},
|
||||
@@ -1865,7 +1866,7 @@
|
||||
"idDesc": "Mostrar tu ID de Telegram"
|
||||
},
|
||||
"messages": {
|
||||
"cpuThreshold": "🔴 El uso de CPU {{ .Percent }}% es mayor que el umbral {{ .Threshold }}%",
|
||||
"cpuThreshold": "El uso de CPU {{ .Percent }}% es mayor que el umbral {{ .Threshold }}%",
|
||||
"selectUserFailed": "❌ ¡Error al seleccionar usuario!",
|
||||
"userSaved": "✅ Usuario de Telegram guardado.",
|
||||
"loginSuccess": "✅ Has iniciado sesión en el panel con éxito.\r\n",
|
||||
@@ -1940,7 +1941,8 @@
|
||||
"eventNodeUp": "El nodo {{ .Name }} está ACTIVO",
|
||||
"eventCPUHigh": "CPU alta",
|
||||
"eventCPUHighDetail": "CPU: {{ .Detail }}",
|
||||
"eventLoginFallback": "Inicio de sesión fallido desde {{ .Source }}"
|
||||
"eventLoginFallback": "Inicio de sesión fallido desde {{ .Source }}",
|
||||
"memoryThreshold": "Uso de memoria {{ .Percent }}% supera el umbral de {{ .Threshold }}%"
|
||||
},
|
||||
"buttons": {
|
||||
"closeKeyboard": "❌ Cerrar Teclado",
|
||||
@@ -2043,4 +2045,4 @@
|
||||
"statusDown": "CAÍDO",
|
||||
"statusUp": "ACTIVO"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1310,7 +1310,8 @@
|
||||
"smtpErrorTimeout": "مهلت اتصال به پایان رسید — میزبان در دسترس نیست",
|
||||
"smtpErrorRelay": "سرور ارسال از این آدرس را رد میکند",
|
||||
"smtpErrorEof": "اتصال توسط سرور بسته شد",
|
||||
"smtpErrorUnknown": "خطای SMTP: {{ .Error }}"
|
||||
"smtpErrorUnknown": "خطای SMTP: {{ .Error }}",
|
||||
"eventMemoryHigh": "مصرف حافظه بالا (%)"
|
||||
},
|
||||
"xray": {
|
||||
"title": "پیکربندی ایکسری",
|
||||
@@ -1865,7 +1866,7 @@
|
||||
"idDesc": "نمایش شناسه تلگرام شما"
|
||||
},
|
||||
"messages": {
|
||||
"cpuThreshold": "🔴 بار پردازنده {{ .Percent }}% بیشتر از آستانه است {{ .Threshold }}%",
|
||||
"cpuThreshold": "بار پردازنده {{ .Percent }}% بیشتر از آستانه است {{ .Threshold }}%",
|
||||
"selectUserFailed": "❌ خطا در انتخاب کاربر!",
|
||||
"userSaved": "✅ کاربر تلگرام ذخیره شد.",
|
||||
"loginSuccess": "✅ با موفقیت به پنل وارد شدید.\r\n",
|
||||
@@ -1940,7 +1941,8 @@
|
||||
"eventNodeUp": "نود {{ .Name }} وصل است",
|
||||
"eventCPUHigh": "بالا بودن CPU",
|
||||
"eventCPUHighDetail": "CPU: {{ .Detail }}",
|
||||
"eventLoginFallback": "ورود ناموفق از {{ .Source }}"
|
||||
"eventLoginFallback": "ورود ناموفق از {{ .Source }}",
|
||||
"memoryThreshold": "مصرف حافظه {{ .Percent }}% از حد آستانه {{ .Threshold }}% فراتر رفته است"
|
||||
},
|
||||
"buttons": {
|
||||
"closeKeyboard": "❌ بستن کیبورد",
|
||||
@@ -2043,4 +2045,4 @@
|
||||
"statusDown": "قطع",
|
||||
"statusUp": "وصل"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1309,6 +1309,7 @@
|
||||
"smtpErrorRelay": "Server menolak pengiriman dari alamat ini",
|
||||
"smtpErrorEof": "Koneksi ditutup oleh server",
|
||||
"smtpErrorUnknown": "Kesalahan SMTP: {{ .Error }}",
|
||||
"eventMemoryHigh": "Penggunaan memori tinggi (%)",
|
||||
"remarkTemplate": "Templat Catatan",
|
||||
"remarkTemplateDesc": "Jika diatur, ini menggantikan model catatan untuk setiap tautan langganan — tulis format Anda sendiri dengan token variabel (gunakan tombol untuk menyisipkannya). Biarkan kosong untuk memakai model di atas."
|
||||
},
|
||||
@@ -1865,7 +1866,7 @@
|
||||
"idDesc": "Tampilkan ID Telegram Anda"
|
||||
},
|
||||
"messages": {
|
||||
"cpuThreshold": "🔴 Beban CPU {{ .Percent }}% melebihi batas {{ .Threshold }}%",
|
||||
"cpuThreshold": "Beban CPU {{ .Percent }}% melebihi batas {{ .Threshold }}%",
|
||||
"selectUserFailed": "❌ Kesalahan dalam pemilihan pengguna!",
|
||||
"userSaved": "✅ Pengguna Telegram tersimpan.",
|
||||
"loginSuccess": "✅ Berhasil masuk ke panel.\r\n",
|
||||
@@ -1940,7 +1941,8 @@
|
||||
"eventNodeUp": "Node {{ .Name }} AKTIF",
|
||||
"eventCPUHigh": "CPU tinggi",
|
||||
"eventCPUHighDetail": "CPU: {{ .Detail }}",
|
||||
"eventLoginFallback": "Gagal masuk dari {{ .Source }}"
|
||||
"eventLoginFallback": "Gagal masuk dari {{ .Source }}",
|
||||
"memoryThreshold": "Penggunaan memori {{ .Percent }}% melebihi ambang batas {{ .Threshold }}%"
|
||||
},
|
||||
"buttons": {
|
||||
"closeKeyboard": "❌ Tutup Papan Ketik",
|
||||
|
||||
@@ -1309,6 +1309,7 @@
|
||||
"smtpErrorRelay": "サーバーはこのアドレスからの送信を拒否しています",
|
||||
"smtpErrorEof": "サーバーによって接続が閉じられました",
|
||||
"smtpErrorUnknown": "SMTPエラー: {{ .Error }}",
|
||||
"eventMemoryHigh": "メモリ使用率が高い (%)",
|
||||
"remarkTemplate": "備考テンプレート",
|
||||
"remarkTemplateDesc": "設定すると、すべてのサブスクリプションリンクの備考モデルを置き換えます — 変数トークンを使って独自の形式を記述してください(ボタンで挿入できます)。空欄にすると上記のモデルが使用されます。"
|
||||
},
|
||||
@@ -1865,7 +1866,7 @@
|
||||
"idDesc": "Telegram IDを表示"
|
||||
},
|
||||
"messages": {
|
||||
"cpuThreshold": "🔴 CPU使用率は{{ .Percent }}%、しきい値{{ .Threshold }}%を超えました",
|
||||
"cpuThreshold": "CPU使用率は{{ .Percent }}%、しきい値{{ .Threshold }}%を超えました",
|
||||
"selectUserFailed": "❌ ユーザーの選択に失敗しました!",
|
||||
"userSaved": "✅ Telegramユーザーが保存されました。",
|
||||
"loginSuccess": "✅ パネルに正常にログインしました。\r\n",
|
||||
@@ -1940,7 +1941,8 @@
|
||||
"eventNodeUp": "ノード {{ .Name }} が復旧しました",
|
||||
"eventCPUHigh": "CPU高負荷",
|
||||
"eventCPUHighDetail": "CPU: {{ .Detail }}",
|
||||
"eventLoginFallback": "{{ .Source }} からのログインに失敗しました"
|
||||
"eventLoginFallback": "{{ .Source }} からのログインに失敗しました",
|
||||
"memoryThreshold": "メモリ使用率 {{ .Percent }}% がしきい値 {{ .Threshold }}% を超えました"
|
||||
},
|
||||
"buttons": {
|
||||
"closeKeyboard": "❌ キーボードを閉じる",
|
||||
@@ -2043,4 +2045,4 @@
|
||||
"statusDown": "ダウン",
|
||||
"statusUp": "アップ"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1309,6 +1309,7 @@
|
||||
"smtpErrorRelay": "O servidor rejeita o envio a partir deste endereço",
|
||||
"smtpErrorEof": "Conexão encerrada pelo servidor",
|
||||
"smtpErrorUnknown": "Erro de SMTP: {{ .Error }}",
|
||||
"eventMemoryHigh": "Uso de memória alto (%)",
|
||||
"remarkTemplate": "Modelo de Observação",
|
||||
"remarkTemplateDesc": "Quando definido, isto substitui o modelo de observação de cada link de assinatura — escreva seu próprio formato com os tokens de variáveis (use o botão para inseri-los). Deixe vazio para usar o modelo acima."
|
||||
},
|
||||
@@ -1865,7 +1866,7 @@
|
||||
"idDesc": "Mostrar seu ID do Telegram"
|
||||
},
|
||||
"messages": {
|
||||
"cpuThreshold": "🔴 A carga da CPU {{ .Percent }}% excede o limite de {{ .Threshold }}%",
|
||||
"cpuThreshold": "A carga da CPU {{ .Percent }}% excede o limite de {{ .Threshold }}%",
|
||||
"selectUserFailed": "❌ Erro na seleção do usuário!",
|
||||
"userSaved": "✅ Usuário do Telegram salvo.",
|
||||
"loginSuccess": "✅ Conectado ao painel com sucesso.\r\n",
|
||||
@@ -1940,7 +1941,8 @@
|
||||
"eventNodeUp": "O nó {{ .Name }} está ATIVO",
|
||||
"eventCPUHigh": "CPU alta",
|
||||
"eventCPUHighDetail": "CPU: {{ .Detail }}",
|
||||
"eventLoginFallback": "Falha de login a partir de {{ .Source }}"
|
||||
"eventLoginFallback": "Falha de login a partir de {{ .Source }}",
|
||||
"memoryThreshold": "Uso de memória {{ .Percent }}% excede o limite de {{ .Threshold }}%"
|
||||
},
|
||||
"buttons": {
|
||||
"closeKeyboard": "❌ Fechar teclado",
|
||||
@@ -2043,4 +2045,4 @@
|
||||
"statusDown": "INATIVO",
|
||||
"statusUp": "ATIVO"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1309,6 +1309,7 @@
|
||||
"smtpErrorRelay": "Сервер отклоняет отправку с этого адреса",
|
||||
"smtpErrorEof": "Соединение закрыто сервером",
|
||||
"smtpErrorUnknown": "Ошибка SMTP: {{ .Error }}",
|
||||
"eventMemoryHigh": "Превышение порога памяти (%)",
|
||||
"remarkTemplate": "Шаблон примечания",
|
||||
"remarkTemplateDesc": "Если задан, заменяет модель примечания для каждой ссылки подписки — задайте собственный формат с помощью токенов переменных (используйте кнопку для их вставки). Оставьте пустым, чтобы использовать модель выше."
|
||||
},
|
||||
@@ -1865,7 +1866,7 @@
|
||||
"idDesc": "Показать ваш Telegram ID"
|
||||
},
|
||||
"messages": {
|
||||
"cpuThreshold": "🔴 Загрузка процессора составляет {{ .Percent }}%, что превышает пороговое значение {{ .Threshold }}%",
|
||||
"cpuThreshold": "Загрузка процессора составляет {{ .Percent }}%, что превышает пороговое значение {{ .Threshold }}%",
|
||||
"selectUserFailed": "❌ Ошибка при выборе пользователя.",
|
||||
"userSaved": "✅ Пользователь Telegram сохранен.",
|
||||
"loginSuccess": "✅ Успешный вход в панель.\r\n",
|
||||
@@ -1940,7 +1941,8 @@
|
||||
"eventNodeUp": "Узел {{ .Name }} В СЕТИ",
|
||||
"eventCPUHigh": "Высокая загрузка CPU",
|
||||
"eventCPUHighDetail": "CPU: {{ .Detail }}",
|
||||
"eventLoginFallback": "Неудачный вход с {{ .Source }}"
|
||||
"eventLoginFallback": "Неудачный вход с {{ .Source }}",
|
||||
"memoryThreshold": "🔴 Использование памяти {{ .Percent }}% превышает пороговое значение {{ .Threshold }}%"
|
||||
},
|
||||
"buttons": {
|
||||
"closeKeyboard": "❌ Закрыть клавиатуру",
|
||||
@@ -2043,4 +2045,4 @@
|
||||
"statusDown": "НЕДОСТУПЕН",
|
||||
"statusUp": "РАБОТАЕТ"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1309,6 +1309,7 @@
|
||||
"smtpErrorRelay": "Sunucu bu adresten gönderimi reddediyor",
|
||||
"smtpErrorEof": "Bağlantı sunucu tarafından kapatıldı",
|
||||
"smtpErrorUnknown": "SMTP hatası: {{ .Error }}",
|
||||
"eventMemoryHigh": "Bellek kullanımı yüksek (%)",
|
||||
"remarkTemplate": "Açıklama Şablonu",
|
||||
"remarkTemplateDesc": "Ayarlandığında, her abonelik bağlantısının açıklama modelinin yerini alır — değişken belirteçleriyle kendi formatınızı yazın (eklemek için düğmeyi kullanın). Yukarıdaki modeli kullanmak için boş bırakın."
|
||||
},
|
||||
@@ -1865,7 +1866,7 @@
|
||||
"idDesc": "Telegram Kimliğinizi gösterir"
|
||||
},
|
||||
"messages": {
|
||||
"cpuThreshold": "🔴 CPU Yükü ({{ .Percent }}%), {{ .Threshold }}% eşiğini aşıyor",
|
||||
"cpuThreshold": "CPU Yükü ({{ .Percent }}%), {{ .Threshold }}% eşiğini aşıyor",
|
||||
"selectUserFailed": "❌ Kullanıcı seçiminde hata!",
|
||||
"userSaved": "✅ Telegram Kullanıcısı kaydedildi.",
|
||||
"loginSuccess": "✅ Panele başarıyla giriş yapıldı.\r\n",
|
||||
@@ -1940,7 +1941,8 @@
|
||||
"eventNodeUp": "{{ .Name }} düğümü ÇEVRİMİÇİ",
|
||||
"eventCPUHigh": "Yüksek CPU",
|
||||
"eventCPUHighDetail": "CPU: {{ .Detail }}",
|
||||
"eventLoginFallback": "{{ .Source }} adresinden oturum açma başarısız"
|
||||
"eventLoginFallback": "{{ .Source }} adresinden oturum açma başarısız",
|
||||
"memoryThreshold": "Bellek kullanımı {{ .Percent }}% eşiği {{ .Threshold }}% aşıyor"
|
||||
},
|
||||
"buttons": {
|
||||
"closeKeyboard": "❌ Klavyeyi Kapat",
|
||||
|
||||
@@ -1309,6 +1309,7 @@
|
||||
"smtpErrorRelay": "Сервер відхиляє надсилання з цієї адреси",
|
||||
"smtpErrorEof": "З'єднання закрито сервером",
|
||||
"smtpErrorUnknown": "Помилка SMTP: {{ .Error }}",
|
||||
"eventMemoryHigh": "Високе використання пам'яті (%)",
|
||||
"remarkTemplate": "Шаблон примітки",
|
||||
"remarkTemplateDesc": "Якщо задано, це замінює модель примітки для кожного посилання підписки — напишіть власний формат із токенами змінних (використовуйте кнопку для їх вставлення). Залиште порожнім, щоб використовувати модель вище."
|
||||
},
|
||||
@@ -1865,7 +1866,7 @@
|
||||
"idDesc": "Показати ваш Telegram ID"
|
||||
},
|
||||
"messages": {
|
||||
"cpuThreshold": "🔴 Навантаження ЦП {{ .Percent }}% перевищує порогове значення {{ .Threshold }}%",
|
||||
"cpuThreshold": "Навантаження ЦП {{ .Percent }}% перевищує порогове значення {{ .Threshold }}%",
|
||||
"selectUserFailed": "❌ Помилка під час вибору користувача!",
|
||||
"userSaved": "✅ Користувача Telegram збережено.",
|
||||
"loginSuccess": "✅ Успішно ввійшли в панель\r\n",
|
||||
@@ -1940,7 +1941,8 @@
|
||||
"eventNodeUp": "Вузол {{ .Name }} ДОСТУПНИЙ",
|
||||
"eventCPUHigh": "Високе навантаження на CPU",
|
||||
"eventCPUHighDetail": "CPU: {{ .Detail }}",
|
||||
"eventLoginFallback": "Невдала спроба входу з {{ .Source }}"
|
||||
"eventLoginFallback": "Невдала спроба входу з {{ .Source }}",
|
||||
"memoryThreshold": "Використання пам'яті {{ .Percent }}% перевищує порогове значення {{ .Threshold }}%"
|
||||
},
|
||||
"buttons": {
|
||||
"closeKeyboard": "❌ Закрити клавіатуру",
|
||||
@@ -2043,4 +2045,4 @@
|
||||
"statusDown": "НЕДОСТУПНО",
|
||||
"statusUp": "ДОСТУПНО"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1309,6 +1309,7 @@
|
||||
"smtpErrorRelay": "Máy chủ từ chối gửi từ địa chỉ này",
|
||||
"smtpErrorEof": "Kết nối đã bị máy chủ đóng",
|
||||
"smtpErrorUnknown": "Lỗi SMTP: {{ .Error }}",
|
||||
"eventMemoryHigh": "Sử dụng bộ nhớ cao (%)",
|
||||
"remarkTemplate": "Mẫu ghi chú",
|
||||
"remarkTemplateDesc": "Khi được đặt, mục này thay thế mô hình ghi chú cho mọi liên kết đăng ký — hãy viết định dạng riêng của bạn bằng các token biến (dùng nút để chèn chúng). Để trống để dùng mô hình ở trên."
|
||||
},
|
||||
@@ -1865,7 +1866,7 @@
|
||||
"idDesc": "Hiển thị ID Telegram của bạn"
|
||||
},
|
||||
"messages": {
|
||||
"cpuThreshold": "🔴 Sử dụng CPU {{ .Percent }}% vượt quá ngưỡng {{ .Threshold }}%",
|
||||
"cpuThreshold": "Sử dụng CPU {{ .Percent }}% vượt quá ngưỡng {{ .Threshold }}%",
|
||||
"selectUserFailed": "❌ Lỗi khi chọn người dùng!",
|
||||
"userSaved": "✅ Người dùng Telegram đã được lưu.",
|
||||
"loginSuccess": "✅ Đăng nhập thành công vào bảng điều khiển.\r\n",
|
||||
@@ -1940,7 +1941,8 @@
|
||||
"eventNodeUp": "Node {{ .Name }} đã HOẠT ĐỘNG",
|
||||
"eventCPUHigh": "CPU cao",
|
||||
"eventCPUHighDetail": "CPU: {{ .Detail }}",
|
||||
"eventLoginFallback": "Đăng nhập thất bại từ {{ .Source }}"
|
||||
"eventLoginFallback": "Đăng nhập thất bại từ {{ .Source }}",
|
||||
"memoryThreshold": "Sử dụng bộ nhớ {{ .Percent }}% vượt quá ngưỡng {{ .Threshold }}%"
|
||||
},
|
||||
"buttons": {
|
||||
"closeKeyboard": "❌ Đóng Bàn Phím",
|
||||
@@ -2043,4 +2045,4 @@
|
||||
"statusDown": "NGỪNG HOẠT ĐỘNG",
|
||||
"statusUp": "HOẠT ĐỘNG"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1309,6 +1309,7 @@
|
||||
"smtpErrorRelay": "服务器拒绝从此地址发送",
|
||||
"smtpErrorEof": "连接被服务器关闭",
|
||||
"smtpErrorUnknown": "SMTP 错误:{{ .Error }}",
|
||||
"eventMemoryHigh": "内存使用率高 (%)",
|
||||
"remarkTemplate": "备注模板",
|
||||
"remarkTemplateDesc": "设置后,将替换每个订阅链接的备注模型 — 使用变量标记编写您自己的格式(用按钮插入它们)。留空则使用上方的模型。"
|
||||
},
|
||||
@@ -1865,7 +1866,7 @@
|
||||
"idDesc": "显示您的 Telegram ID"
|
||||
},
|
||||
"messages": {
|
||||
"cpuThreshold": "🔴 CPU 使用率为 {{ .Percent }}%,超过阈值 {{ .Threshold }}%",
|
||||
"cpuThreshold": "CPU 使用率为 {{ .Percent }}%,超过阈值 {{ .Threshold }}%",
|
||||
"selectUserFailed": "❌ 用户选择错误!",
|
||||
"userSaved": "✅ 电报用户已保存。",
|
||||
"loginSuccess": "✅ 成功登录到面板。\r\n",
|
||||
@@ -1940,7 +1941,8 @@
|
||||
"eventNodeUp": "节点 {{ .Name }} 已上线",
|
||||
"eventCPUHigh": "CPU 占用过高",
|
||||
"eventCPUHighDetail": "CPU:{{ .Detail }}",
|
||||
"eventLoginFallback": "来自 {{ .Source }} 的登录失败"
|
||||
"eventLoginFallback": "来自 {{ .Source }} 的登录失败",
|
||||
"memoryThreshold": "内存使用率 {{ .Percent }}% 超过阈值 {{ .Threshold }}%"
|
||||
},
|
||||
"buttons": {
|
||||
"closeKeyboard": "❌ 关闭键盘",
|
||||
|
||||
@@ -1309,6 +1309,7 @@
|
||||
"smtpErrorRelay": "伺服器拒絕從此地址傳送",
|
||||
"smtpErrorEof": "連線已被伺服器關閉",
|
||||
"smtpErrorUnknown": "SMTP 錯誤:{{ .Error }}",
|
||||
"eventMemoryHigh": "記憶體使用率高 (%)",
|
||||
"remarkTemplate": "備註範本",
|
||||
"remarkTemplateDesc": "設定後,這將取代每個訂閱連結的備註模型——使用變數標記撰寫您自己的格式(使用按鈕來插入)。留空則使用上方的模型。"
|
||||
},
|
||||
@@ -1865,7 +1866,7 @@
|
||||
"idDesc": "顯示您的 Telegram ID"
|
||||
},
|
||||
"messages": {
|
||||
"cpuThreshold": "🔴 CPU 使用率為 {{ .Percent }}%,超過閾值 {{ .Threshold }}%",
|
||||
"cpuThreshold": "CPU 使用率為 {{ .Percent }}%,超過閾值 {{ .Threshold }}%",
|
||||
"selectUserFailed": "❌ 使用者選擇錯誤!",
|
||||
"userSaved": "✅ 電報使用者已儲存。",
|
||||
"loginSuccess": "✅ 成功登入到面板。\r\n",
|
||||
@@ -1940,7 +1941,8 @@
|
||||
"eventNodeUp": "節點 {{ .Name }} 已上線",
|
||||
"eventCPUHigh": "CPU 偏高",
|
||||
"eventCPUHighDetail": "CPU:{{ .Detail }}",
|
||||
"eventLoginFallback": "來自 {{ .Source }} 的登入失敗"
|
||||
"eventLoginFallback": "來自 {{ .Source }} 的登入失敗",
|
||||
"memoryThreshold": "記憶體使用率 {{ .Percent }}% 超過閾值 {{ .Threshold }}%"
|
||||
},
|
||||
"buttons": {
|
||||
"closeKeyboard": "❌ 關閉鍵盤",
|
||||
@@ -2043,4 +2045,4 @@
|
||||
"statusDown": "中斷",
|
||||
"statusUp": "恢復"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+36
-1
@@ -290,7 +290,8 @@ const (
|
||||
cadenceCheckHash = "@every 2m"
|
||||
// cpu.Percent samples over a full minute (blocking), so a finer cadence just
|
||||
// stacks overlapping samplers; subscribers rate-limit alerts to 1/min anyway.
|
||||
cadenceCPUAlarm = "@every 1m"
|
||||
cadenceCPUAlarm = "@every 1m"
|
||||
cadenceMemoryAlarm = "@every 1m"
|
||||
)
|
||||
|
||||
// startTask schedules background jobs (Xray checks, traffic jobs, cron
|
||||
@@ -385,6 +386,10 @@ func (s *Server) startTask(restartXray bool) {
|
||||
if s.cpuAlarmWanted() {
|
||||
s.cron.AddJob(cadenceCPUAlarm, job.NewCheckCpuJob())
|
||||
}
|
||||
// Memory monitor publishes memory.high events; register it whenever any notifier wants them.
|
||||
if s.memoryAlarmWanted() {
|
||||
s.cron.AddJob(cadenceMemoryAlarm, job.NewCheckMemJob())
|
||||
}
|
||||
}
|
||||
|
||||
// cpuAlarmWanted reports whether any notifier is configured to receive cpu.high
|
||||
@@ -418,6 +423,36 @@ func (s *Server) cpuAlarmWanted() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// memoryAlarmWanted reports whether any notifier is configured to receive memory.high alerts.
|
||||
func (s *Server) memoryAlarmWanted() bool {
|
||||
wants := func(events string, threshold int) bool {
|
||||
if threshold <= 0 {
|
||||
return false
|
||||
}
|
||||
for _, e := range strings.Split(events, ",") {
|
||||
if strings.TrimSpace(e) == string(eventbus.EventMemoryHigh) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
if on, _ := s.settingService.GetTgbotEnabled(); on {
|
||||
events, _ := s.settingService.GetTgEnabledEvents()
|
||||
mem, _ := s.settingService.GetTgMemory()
|
||||
if wants(events, mem) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if on, _ := s.settingService.GetSmtpEnable(); on {
|
||||
events, _ := s.settingService.GetSmtpEnabledEvents()
|
||||
mem, _ := s.settingService.GetSmtpMemory()
|
||||
if wants(events, mem) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Start initializes and starts the web server with configured settings, routes, and background jobs.
|
||||
func (s *Server) Start() (err error) {
|
||||
return s.start(true, true)
|
||||
|
||||
Reference in New Issue
Block a user