mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-06 13:14:11 +00:00
fix(tgbot): resolve client creation race conditions and localization bugs
- Refactored Telegram bot client creation state to use a concurrent-safe map (\clientStates map[int64]*ClientState\), replacing package-level global variables. This prevents data races when multiple administrators interact with the bot simultaneously. - Fixed hardcoded English strings in \BuildInboundClientDataMessage\ by utilizing the \ .I18nBot()\ localization wrapper. - Implemented \UpdateBotLocalizer\ to dynamically refresh the bot's language whenever the \ gLang\ setting is updated in the web panel, eliminating the need for a service restart. - Synchronized missing translation keys for \Sub ID\ and \Flow\ across all non-English/Russian localization files to prevent missing interface elements.
This commit is contained in:
parent
63a7931862
commit
29fa28bf75
13 changed files with 7566 additions and 6920 deletions
|
|
@ -6,6 +6,7 @@ import (
|
|||
|
||||
"github.com/mhsanaei/3x-ui/v3/util/crypto"
|
||||
"github.com/mhsanaei/3x-ui/v3/web/entity"
|
||||
"github.com/mhsanaei/3x-ui/v3/web/locale"
|
||||
"github.com/mhsanaei/3x-ui/v3/web/service"
|
||||
"github.com/mhsanaei/3x-ui/v3/web/session"
|
||||
|
||||
|
|
@ -77,6 +78,9 @@ func (a *SettingController) updateSetting(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
err = a.settingService.UpdateAllSetting(allSetting)
|
||||
if err == nil {
|
||||
locale.UpdateBotLocalizer(allSetting.TgLang)
|
||||
}
|
||||
jsonMsg(c, I18nWeb(c, "pages.settings.toasts.modifySettings"), err)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -111,11 +111,20 @@ func initTGBotLocalizer(settingService SettingService) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Infof("Initializing TG Bot localizer with language: %s", botLang)
|
||||
|
||||
LocalizerBot = i18n.NewLocalizer(i18nBundle, botLang)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateBotLocalizer dynamically updates the bot localizer language.
|
||||
func UpdateBotLocalizer(botLang string) {
|
||||
if i18nBundle != nil {
|
||||
logger.Infof("Updating TG Bot localizer with language: %s", botLang)
|
||||
LocalizerBot = i18n.NewLocalizer(i18nBundle, botLang)
|
||||
}
|
||||
}
|
||||
|
||||
// LocalizerMiddleware returns a Gin middleware that sets up localization for web requests.
|
||||
// It determines the user's language from cookies or Accept-Language header,
|
||||
// creates a localizer instance, and stores it in the Gin context for use in handlers.
|
||||
|
|
@ -161,7 +170,9 @@ func loadTranslationsFromDisk(bundle *i18n.Bundle) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = bundle.ParseMessageFileBytes(data, path)
|
||||
filename := d.Name()
|
||||
logger.Infof("Parsing translation file from disk: %s", filename)
|
||||
_, err = bundle.ParseMessageFileBytes(data, filename)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
|
@ -183,7 +194,9 @@ func parseTranslationFiles(i18nFS embed.FS, i18nBundle *i18n.Bundle) error {
|
|||
return err
|
||||
}
|
||||
|
||||
_, err = i18nBundle.ParseMessageFileBytes(data, path)
|
||||
filename := d.Name()
|
||||
logger.Infof("Parsing translation file from embed: %s (path: %s)", filename, path)
|
||||
_, err = i18nBundle.ParseMessageFileBytes(data, filename)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
|
|
|
|||
1124
web/service/tgbot.go
1124
web/service/tgbot.go
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -873,6 +873,11 @@
|
|||
"exhaustedMsg": "🚨 Exhausted {{ .Type }}:\r\n",
|
||||
"exhaustedCount": "🚨 Exhausted {{ .Type }} count:\r\n",
|
||||
"onlinesCount": "🌐 Online Clients: {{ .Count }}\r\n",
|
||||
"cpu": "CPU: {{ .Usage }}%\r\n",
|
||||
"mem": "RAM: {{ .Usage }}/{{ .Total }}\r\n",
|
||||
"swap": "Swap: {{ .Usage }}/{{ .Total }}\r\n",
|
||||
"disk": "Disk: {{ .Usage }}/{{ .Total }}\r\n",
|
||||
"uptime": "Uptime: {{ .Time }}\r\n",
|
||||
"disabled": "🛑 Disabled: {{ .Disabled }}\r\n",
|
||||
"depleteSoon": "🔜 Deplete Soon: {{ .Deplete }}\r\n\r\n",
|
||||
"backupTime": "🗄 Backup Time: {{ .Time }}\r\n",
|
||||
|
|
@ -947,6 +952,8 @@
|
|||
"change_subid": "📝 Sub ID",
|
||||
"change_flow": "🌊 Flow",
|
||||
"flow_none": "None",
|
||||
"qrCode": "QR Code",
|
||||
"selectTGUser": "Select Telegram User",
|
||||
"ResetAllTraffics": "Reset All Traffics",
|
||||
"SortedTrafficUsageReport": "Sorted Traffic Usage Report"
|
||||
},
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -873,6 +873,11 @@
|
|||
"exhaustedMsg": "🚨 Исчерпаны {{ .Type }}:\r\n",
|
||||
"exhaustedCount": "🚨 Количество исчерпанных {{ .Type }}:\r\n",
|
||||
"onlinesCount": "🌐 Клиентов онлайн: {{ .Count }}\r\n",
|
||||
"cpu": "ЦП: {{ .Usage }}%\r\n",
|
||||
"mem": "ОЗУ: {{ .Usage }}/{{ .Total }}\r\n",
|
||||
"swap": "Swap: {{ .Usage }}/{{ .Total }}\r\n",
|
||||
"disk": "Диск: {{ .Usage }}/{{ .Total }}\r\n",
|
||||
"uptime": "Время работы: {{ .Time }}\r\n",
|
||||
"disabled": "🛑 Отключено: {{ .Disabled }}\r\n",
|
||||
"depleteSoon": "🔜 Клиенты, у которых скоро исчерпание: {{ .Deplete }}\r\n\r\n",
|
||||
"backupTime": "🗄 Время резервного копирования: {{ .Time }}\r\n",
|
||||
|
|
@ -947,6 +952,7 @@
|
|||
"change_subid": "📝 Sub ID",
|
||||
"change_flow": "🌊 Flow",
|
||||
"flow_none": "Отсутствует",
|
||||
"qrCode": "QR-код",
|
||||
"ResetAllTraffics": "Сбросить весь трафик",
|
||||
"SortedTrafficUsageReport": "Отсортированный отчет об использовании трафика"
|
||||
},
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue