mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-08-29 06:27:14 +00:00
feat(notifications): event bus architecture with Telegram and SMTP subscribers (#5326)
* feat(notifications): event bus architecture with Telegram and SMTP subscribers
- Event bus core with buffered channel, fan-out, panic recovery
- Telegram subscriber with HTML formatting and rate limiting
- Email subscriber with SMTP/TLS/STARTTLS support and stage diagnostics
- 5 event types: outbound.down/up, xray.crash, cpu.high, login.attempt
- CPU threshold checks per subscriber (tgCpu for TG, smtpCpu for Email)
- SystemMetricData struct for raw metric values in events
- i18n keys for en-US, ru-RU, and English defaults for other locales
* fix
* fix(notifications): repair crash/CPU alerts, harden secrets, add node alerts
Bug fixes:
- Xray crash notifications were permanently suppressed after the first crash:
XrayStateTracker latched state="down" with no reset and no recovery event,
so only the first crash per process lifetime ever notified. Removed the
tracker; the existing 1/min rate limiter already dedupes crash-loop spam.
- Email CPU alerts could never fire unless Telegram was also enabled, because
the CPU job was registered only inside the tgbot block. Register it whenever
either Telegram or SMTP wants cpu.high (new cpuAlarmWanted gate) and relax
the cadence to @every 1m (cpu.Percent already samples over a full minute).
- SMTP password (and, pre-existing, all other secrets) were shipped to the
browser in plaintext: GetAllSettingView was dead code and /setting/all
returned the raw model. Wire getAllSetting -> GetAllSettingView, redact
smtpPassword with a hasSmtpPassword presence flag, and preserve it on blank
save. Closes the leak for tgBotToken/ldapPassword/2FA token too.
Polish:
- email Send: use nil SMTP auth when no credentials (Go refuses PlainAuth over
the unencrypted "none" transport).
- Remove unused EventClientDepleted; fix inaccurate bus.go doc comments; drop
stale tgBotLoginNotify from the frontend schema; gofmt alignment.
Feature - node online/offline alerts:
- Emit node.down/node.up from the heartbeat job on a real status transition
(with a startup-spam guard), reusing NodeHealthData. Formatted by both the
Telegram and email subscribers and selectable in the settings UI.
Regenerated frontend types (hasSmtpPassword). New i18n keys added to en-US;
other locales fall back to English (bundle default) until translated.
* fix(settings): use antd Space orientation instead of deprecated direction
Ant Design 6 deprecated Space's `direction` prop in favor of `orientation`,
which logged a console warning from the Telegram/Email notification tabs. Brings
these two tabs in line with the rest of the codebase, which already uses
`orientation`.
* i18n(notifications): translate the notification feature into all locales
The notifications PR shipped ~99 new strings (SMTP settings, event labels,
Telegram/email message templates) as English placeholders in every non-English
locale. Translate them — plus the node-alert keys added during this review —
into all 12 locales: Arabic, Spanish, Persian, Indonesian, Japanese,
Portuguese-BR, Russian, Turkish, Ukrainian, Vietnamese, and Simplified/
Traditional Chinese.
Go-template placeholders ({{ .Tag }}, {{ .Name }}, etc.) are preserved exactly;
tgbot message values carry no leading status emoji (the bot/email code adds
those, so an emoji in the value would duplicate it); product/protocol names
(SMTP, STARTTLS, TLS, CPU, Xray, Telegram) are kept as-is.
---------
Co-authored-by: Sanaei <ho3ein.sanaei@gmail.com>
This commit is contained in:
@@ -1200,7 +1200,69 @@
|
||||
"userPassMustBeNotEmpty": "Tên người dùng mới và mật khẩu mới không thể để trống",
|
||||
"getOutboundTrafficError": "Lỗi khi lấy lưu lượng truy cập đi",
|
||||
"resetOutboundTrafficError": "Lỗi khi đặt lại lưu lượng truy cập đi"
|
||||
}
|
||||
},
|
||||
"emailNotifications": "Thông báo",
|
||||
"emailSettings": "Email",
|
||||
"eventCPUHigh": "CPU cao (%)",
|
||||
"eventGroupOutbound": "Outbound",
|
||||
"eventGroupSecurity": "Bảo mật",
|
||||
"eventGroupSystem": "Hệ thống",
|
||||
"eventGroupXray": "Xray Core",
|
||||
"eventLoginAttempt": "Lần thử đăng nhập",
|
||||
"eventOutboundDown": "Ngừng hoạt động",
|
||||
"eventOutboundUp": "Hoạt động",
|
||||
"eventXrayCrash": "Sự cố",
|
||||
"requestFailed": "Yêu cầu thất bại",
|
||||
"smtpEnable": "Bật thông báo qua email",
|
||||
"smtpEnableDesc": "Bật thông báo qua email bằng SMTP",
|
||||
"smtpEncryption": "Mã hóa",
|
||||
"smtpEncryptionDesc": "Phương thức mã hóa kết nối SMTP",
|
||||
"smtpEncryptionNone": "Không (văn bản thuần)",
|
||||
"smtpEncryptionStartTLS": "STARTTLS",
|
||||
"smtpEncryptionTLS": "TLS (ngầm định)",
|
||||
"smtpEventBusNotify": "Thông báo sự kiện qua email",
|
||||
"smtpEventBusNotifyDesc": "Chọn những sự kiện nào sẽ kích hoạt thông báo qua email",
|
||||
"smtpHost": "Máy chủ SMTP",
|
||||
"smtpHostDesc": "Tên máy chủ SMTP (ví dụ: smtp.gmail.com)",
|
||||
"smtpHostNotConfigured": "Chưa cấu hình máy chủ SMTP",
|
||||
"smtpNoRecipients": "Chưa cấu hình người nhận",
|
||||
"smtpNotInitialized": "SMTP chưa được khởi tạo",
|
||||
"smtpPassword": "Mật khẩu SMTP",
|
||||
"smtpPasswordDesc": "Mật khẩu xác thực SMTP",
|
||||
"smtpPort": "Cổng SMTP",
|
||||
"smtpPortDesc": "Cổng máy chủ SMTP (mặc định: 587)",
|
||||
"smtpSettings": "Cài đặt SMTP",
|
||||
"smtpStageAuth": "Xác thực",
|
||||
"smtpStageConnect": "Kết nối",
|
||||
"smtpStageSend": "Gửi",
|
||||
"smtpTestSuccess": "Đã gửi email thử nghiệm thành công",
|
||||
"smtpTo": "Người nhận",
|
||||
"smtpToDesc": "Các địa chỉ email người nhận, phân cách bằng dấu phẩy",
|
||||
"smtpUsername": "Tên đăng nhập SMTP",
|
||||
"smtpUsernameDesc": "Tên đăng nhập xác thực SMTP",
|
||||
"telegramTokenConfigured": "Đã cấu hình; để trống để giữ token hiện tại.",
|
||||
"telegramTokenPlaceholder": "Đã cấu hình - nhập token mới để thay thế",
|
||||
"testSmtp": "Gửi email thử nghiệm",
|
||||
"testTgBot": "Gửi tin nhắn thử nghiệm",
|
||||
"tgBotNotEnabled": "Bot Telegram chưa được bật",
|
||||
"tgBotNotRunning": "Bot Telegram không hoạt động",
|
||||
"tgEventBusNotify": "Thông báo sự kiện qua Telegram",
|
||||
"tgEventBusNotifyDesc": "Chọn những sự kiện nào sẽ kích hoạt thông báo qua Telegram",
|
||||
"tgTestFailed": "Thử nghiệm Telegram thất bại",
|
||||
"tgTestSuccess": "Đã gửi tin nhắn thử nghiệm tới Telegram",
|
||||
"smtpErrorAuth": "Xác thực thất bại — kiểm tra tên đăng nhập và mật khẩu",
|
||||
"smtpErrorStarttls": "Máy chủ yêu cầu STARTTLS — thay đổi kiểu mã hóa",
|
||||
"smtpErrorTls": "Máy chủ yêu cầu TLS — thay đổi kiểu mã hóa",
|
||||
"smtpErrorRefused": "Kết nối bị từ chối — kiểm tra máy chủ và cổng",
|
||||
"smtpErrorTimeout": "Hết thời gian kết nối — không thể truy cập máy chủ",
|
||||
"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 }}",
|
||||
"eventGroupNode": "Node",
|
||||
"eventNodeDown": "Ngừng hoạt động",
|
||||
"eventNodeUp": "Hoạt động",
|
||||
"smtpPasswordConfigured": "Đã cấu hình; để trống để giữ mật khẩu hiện tại.",
|
||||
"smtpPasswordPlaceholder": "Đã cấu hình - nhập mật khẩu mới để thay thế"
|
||||
},
|
||||
"xray": {
|
||||
"title": "Cài đặt Xray",
|
||||
@@ -1703,7 +1765,18 @@
|
||||
"AreYouSure": "Bạn có chắc không? 🤔",
|
||||
"SuccessResetTraffic": "📧 Email: {{ .ClientEmail }}\n🏁 Kết quả: ✅ Thành công",
|
||||
"FailedResetTraffic": "📧 Email: {{ .ClientEmail }}\n🏁 Kết quả: ❌ Thất bại \n\n🛠️ Lỗi: [ {{ .ErrorMessage }} ]",
|
||||
"FinishProcess": "🔚 Quá trình đặt lại lưu lượng đã hoàn tất cho tất cả khách hàng."
|
||||
"FinishProcess": "🔚 Quá trình đặt lại lưu lượng đã hoàn tất cho tất cả khách hàng.",
|
||||
"eventCPUHigh": "CPU cao",
|
||||
"eventCPUHighDetail": "CPU: {{ .Detail }}",
|
||||
"eventDelayDetail": "Độ trễ: {{ .Delay }}ms",
|
||||
"eventErrorDetail": "Lỗi: {{ .Error }}",
|
||||
"eventLoginFallback": "Đăng nhập thất bại từ {{ .Source }}",
|
||||
"eventOutboundDown": "Outbound {{ .Tag }} đã NGỪNG HOẠT ĐỘNG",
|
||||
"eventOutboundUp": "Outbound {{ .Tag }} đã HOẠT ĐỘNG",
|
||||
"eventXrayCrash": "Xray GẶP SỰ CỐ",
|
||||
"eventXrayCrashError": "Lỗi: {{ .Error }}",
|
||||
"eventNodeDown": "Node {{ .Name }} đã NGỪNG HOẠT ĐỘNG",
|
||||
"eventNodeUp": "Node {{ .Name }} đã HOẠT ĐỘNG"
|
||||
},
|
||||
"buttons": {
|
||||
"closeKeyboard": "❌ Đóng Bàn Phím",
|
||||
@@ -1773,5 +1846,57 @@
|
||||
"chooseClient": "Chọn một Khách hàng cho Inbound {{ .Inbound }}",
|
||||
"chooseInbound": "Chọn một Inbound"
|
||||
}
|
||||
},
|
||||
"email": {
|
||||
"labelDelay": "Độ trễ",
|
||||
"labelDetail": "Chi tiết",
|
||||
"labelError": "Lỗi",
|
||||
"labelIP": "IP",
|
||||
"labelOutbound": "Outbound",
|
||||
"labelReason": "Lý do",
|
||||
"labelSource": "Nguồn",
|
||||
"labelStatus": "Trạng thái",
|
||||
"labelTime": "Thời gian",
|
||||
"labelUsername": "Tên đăng nhập",
|
||||
"statusBanned": "BANNED",
|
||||
"statusCrashed": "GẶP SỰ CỐ",
|
||||
"statusDown": "NGỪNG HOẠT ĐỘNG",
|
||||
"statusFailed": "THẤT BẠI",
|
||||
"statusFull": "FULL",
|
||||
"statusHigh": "CAO",
|
||||
"statusOffline": "OFFLINE",
|
||||
"statusOnline": "ONLINE",
|
||||
"statusRunning": "Đang chạy",
|
||||
"statusSuccess": "THÀNH CÔNG",
|
||||
"statusUp": "HOẠT ĐỘNG",
|
||||
"statusXrayDown": "Xray DOWN",
|
||||
"statusXrayUp": "Xray UP",
|
||||
"subjectCPUHigh": "CPU cao",
|
||||
"subjectDiskFull": "Disk full",
|
||||
"subjectIPBanned": "IP banned: {{ .IP }}",
|
||||
"subjectLoginFailed": "Đăng nhập thất bại",
|
||||
"subjectLoginSuccess": "Đăng nhập thành công",
|
||||
"subjectNodeOffline": "Node {{ .Node }} is OFFLINE",
|
||||
"subjectNodeOnline": "Node {{ .Node }} is ONLINE",
|
||||
"subjectNodeXrayDown": "Node {{ .Node }} Xray is DOWN",
|
||||
"subjectNodeXrayUp": "Node {{ .Node }} Xray is UP",
|
||||
"subjectOutboundDown": "Outbound {{ .Tag }} đã NGỪNG HOẠT ĐỘNG",
|
||||
"subjectOutboundUp": "Outbound {{ .Tag }} đã HOẠT ĐỘNG",
|
||||
"subjectXrayCrash": "Xray GẶP SỰ CỐ",
|
||||
"subjectXrayUp": "Xray is UP",
|
||||
"titleCPUHigh": "CPU cao",
|
||||
"titleDiskFull": "Disk full",
|
||||
"titleIPBanned": "IP banned",
|
||||
"titleLoginFailed": "Đăng nhập thất bại",
|
||||
"titleLoginSuccess": "Đăng nhập thành công",
|
||||
"titleNodeOffline": "Node OFFLINE",
|
||||
"titleNodeOnline": "Node ONLINE",
|
||||
"titleNodeXrayDown": "Node Xray DOWN",
|
||||
"titleNodeXrayUp": "Node Xray UP",
|
||||
"titleOutboundDown": "Outbound NGỪNG HOẠT ĐỘNG",
|
||||
"titleOutboundUp": "Outbound HOẠT ĐỘNG",
|
||||
"titleXrayCrash": "Xray GẶP SỰ CỐ",
|
||||
"titleXrayUp": "Xray UP",
|
||||
"labelNode": "Node"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user