diff --git a/README.ar_EG.md b/README.ar_EG.md index 2a309545..f42df1e1 100644 --- a/README.ar_EG.md +++ b/README.ar_EG.md @@ -33,7 +33,7 @@ لتثبيت المشروع أو تحديثه، نفذ الأمر ده: ```bash -bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) +bash <(curl -Ls https://raw.githubusercontent.com/MHSanaei/3x-ui/refs/tags/v2.5.8/install.sh) ``` ## تثبيت النسخة القديمة (مش موصى بيها) diff --git a/README.es_ES.md b/README.es_ES.md index afa1b8e3..60dbd305 100644 --- a/README.es_ES.md +++ b/README.es_ES.md @@ -32,7 +32,7 @@ ## Instalar y Actualizar ``` -bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) +bash <(curl -Ls https://raw.githubusercontent.com/MHSanaei/3x-ui/refs/tags/v2.5.8/install.sh) ``` ## Instalar versión antigua (no recomendamos) diff --git a/README.fa_IR.md b/README.fa_IR.md index 806f9e69..f69f54fa 100644 --- a/README.fa_IR.md +++ b/README.fa_IR.md @@ -32,7 +32,7 @@ ## نصب و ارتقا ``` -bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) +bash <(curl -Ls https://raw.githubusercontent.com/MHSanaei/3x-ui/refs/tags/v2.5.8/install.sh) ``` ## نصب نسخه‌های قدیمی (توصیه نمی‌شود) diff --git a/README.md b/README.md index 0cace84a..cb8fef8c 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ ## Install & Upgrade ``` -bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) +bash <(curl -Ls https://raw.githubusercontent.com/MHSanaei/3x-ui/refs/tags/v2.5.8/install.sh) ``` ## Install legacy Version (we don't recommend) diff --git a/README.ru_RU.md b/README.ru_RU.md index bc0170bb..c75dba91 100644 --- a/README.ru_RU.md +++ b/README.ru_RU.md @@ -32,7 +32,7 @@ ## Установка и обновление ``` -bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) +bash <(curl -Ls https://raw.githubusercontent.com/MHSanaei/3x-ui/refs/tags/v2.5.8/install.sh) ``` ## Установить старую версию (мы не рекомендуем) diff --git a/README.zh_CN.md b/README.zh_CN.md index 995a6ee1..703b6733 100644 --- a/README.zh_CN.md +++ b/README.zh_CN.md @@ -32,7 +32,7 @@ ## 安装 & 升级 ``` -bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) +bash <(curl -Ls https://raw.githubusercontent.com/MHSanaei/3x-ui/refs/tags/v2.5.8/install.sh) ``` ## 安装旧版本 (我们不建议) diff --git a/database/db.go b/database/db.go index 744f1401..2fe18478 100644 --- a/database/db.go +++ b/database/db.go @@ -7,9 +7,11 @@ import ( "log" "os" "path" + "slices" "x-ui/config" "x-ui/database/model" + "x-ui/util/crypto" "x-ui/xray" "gorm.io/driver/sqlite" @@ -33,6 +35,7 @@ func initModels() error { &model.Setting{}, &model.InboundClientIps{}, &xray.ClientTraffic{}, + &model.HistoryOfSeeders{}, } for _, model := range models { if err := db.AutoMigrate(model); err != nil { @@ -50,9 +53,16 @@ func initUser() error { return err } if empty { + hashedPassword, err := crypto.HashPasswordAsBcrypt(defaultPassword) + + if err != nil { + log.Printf("Error hashing default password: %v", err) + return err + } + user := &model.User{ Username: defaultUsername, - Password: defaultPassword, + Password: hashedPassword, LoginSecret: defaultSecret, } return db.Create(user).Error @@ -60,6 +70,45 @@ func initUser() error { return nil } +func runSeeders(isUsersEmpty bool) error { + empty, err := isTableEmpty("history_of_seeders") + if err != nil { + log.Printf("Error checking if users table is empty: %v", err) + return err + } + + if empty && isUsersEmpty { + hashSeeder := &model.HistoryOfSeeders{ + SeederName: "UserPasswordHash", + } + return db.Create(hashSeeder).Error + } else { + var seedersHistory []string + db.Model(&model.HistoryOfSeeders{}).Pluck("seeder_name", &seedersHistory) + + if !slices.Contains(seedersHistory, "UserPasswordHash") && !isUsersEmpty { + var users []model.User + db.Find(&users) + + for _, user := range users { + hashedPassword, err := crypto.HashPasswordAsBcrypt(user.Password) + if err != nil { + log.Printf("Error hashing password for user '%s': %v", user.Username, err) + return err + } + db.Model(&user).Update("password", hashedPassword) + } + + hashSeeder := &model.HistoryOfSeeders{ + SeederName: "UserPasswordHash", + } + return db.Create(hashSeeder).Error + } + } + + return nil +} + func isTableEmpty(tableName string) (bool, error) { var count int64 err := db.Table(tableName).Count(&count).Error @@ -92,11 +141,13 @@ func InitDB(dbPath string) error { if err := initModels(); err != nil { return err } + + isUsersEmpty, err := isTableEmpty("users") + if err := initUser(); err != nil { return err } - - return nil + return runSeeders(isUsersEmpty) } func CloseDB() error { diff --git a/database/model/model.go b/database/model/model.go index e9d1836f..7a20de16 100644 --- a/database/model/model.go +++ b/database/model/model.go @@ -63,6 +63,11 @@ type InboundClientIps struct { Ips string `json:"ips" form:"ips"` } +type HistoryOfSeeders struct { + Id int `json:"id" gorm:"primaryKey;autoIncrement"` + SeederName string `json:"seederName"` +} + func (i *Inbound) GenXrayInboundConfig() *xray.InboundConfig { listen := i.Listen if listen != "" { diff --git a/install.sh b/install.sh index 92f62417..0398285d 100644 --- a/install.sh +++ b/install.sh @@ -82,14 +82,13 @@ gen_random_string() { } config_after_install() { - local existing_username=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'username: .+' | awk '{print $2}') - local existing_password=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'password: .+' | awk '{print $2}') + local existing_hasDefaultCredential=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'hasDefaultCredential: .+' | awk '{print $2}') local existing_webBasePath=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'webBasePath: .+' | awk '{print $2}') local existing_port=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'port: .+' | awk '{print $2}') local server_ip=$(curl -s https://api.ipify.org) if [[ ${#existing_webBasePath} -lt 4 ]]; then - if [[ "$existing_username" == "admin" && "$existing_password" == "admin" ]]; then + if [[ "$existing_hasDefaultCredential" == "true" ]]; then local config_webBasePath=$(gen_random_string 15) local config_username=$(gen_random_string 10) local config_password=$(gen_random_string 10) @@ -112,7 +111,6 @@ config_after_install() { echo -e "${green}WebBasePath: ${config_webBasePath}${plain}" echo -e "${green}Access URL: http://${server_ip}:${config_port}/${config_webBasePath}${plain}" echo -e "###############################################" - echo -e "${yellow}If you forgot your login info, you can type 'x-ui settings' to check${plain}" else local config_webBasePath=$(gen_random_string 15) echo -e "${yellow}WebBasePath is missing or too short. Generating a new one...${plain}" @@ -121,7 +119,7 @@ config_after_install() { echo -e "${green}Access URL: http://${server_ip}:${existing_port}/${config_webBasePath}${plain}" fi else - if [[ "$existing_username" == "admin" && "$existing_password" == "admin" ]]; then + if [[ "$existing_hasDefaultCredential" == "true" ]]; then local config_username=$(gen_random_string 10) local config_password=$(gen_random_string 10) @@ -132,7 +130,6 @@ config_after_install() { echo -e "${green}Username: ${config_username}${plain}" echo -e "${green}Password: ${config_password}${plain}" echo -e "###############################################" - echo -e "${yellow}If you forgot your login info, you can type 'x-ui settings' to check${plain}" else echo -e "${green}Username, Password, and WebBasePath are properly set. Exiting...${plain}" fi diff --git a/main.go b/main.go index 84ffca6e..ed30a11f 100644 --- a/main.go +++ b/main.go @@ -16,6 +16,7 @@ import ( "x-ui/web" "x-ui/web/global" "x-ui/web/service" + "x-ui/util/crypto" "github.com/op/go-logging" ) @@ -151,9 +152,7 @@ func showSetting(show bool) { fmt.Println("get current user info failed, error info:", err) } - username := userModel.Username - userpasswd := userModel.Password - if username == "" || userpasswd == "" { + if userModel.Username == "" || userModel.Password == "" { fmt.Println("current username or password is empty") } @@ -163,8 +162,12 @@ func showSetting(show bool) { } else { fmt.Println("Panel is secure with SSL") } - fmt.Println("username:", username) - fmt.Println("password:", userpasswd) + + hasDefaultCredential := func() bool { + return userModel.Username == "admin" && crypto.CheckPasswordHash(userModel.Password, "admin") + }() + + fmt.Println("hasDefaultCredential:", hasDefaultCredential) fmt.Println("port:", port) fmt.Println("webBasePath:", webBasePath) } diff --git a/util/crypto/crypto.go b/util/crypto/crypto.go new file mode 100644 index 00000000..f600e7a6 --- /dev/null +++ b/util/crypto/crypto.go @@ -0,0 +1,15 @@ +package crypto + +import ( + "golang.org/x/crypto/bcrypt" +) + +func HashPasswordAsBcrypt(password string) (string, error) { + hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + return string(hash), err +} + +func CheckPasswordHash(hash, password string) bool { + err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) + return err == nil +} diff --git a/web/controller/server.go b/web/controller/server.go index fc7756ba..5d89e356 100644 --- a/web/controller/server.go +++ b/web/controller/server.go @@ -44,6 +44,7 @@ func (a *ServerController) initRouter(g *gin.RouterGroup) { g.POST("/stopXrayService", a.stopXrayService) g.POST("/restartXrayService", a.restartXrayService) g.POST("/installXray/:version", a.installXray) + g.POST("/updateGeofile/:fileName", a.updateGeofile) g.POST("/logs/:count", a.getLogs) g.POST("/getConfigJson", a.getConfigJson) g.GET("/getDb", a.getDb) @@ -95,7 +96,13 @@ func (a *ServerController) getXrayVersion(c *gin.Context) { func (a *ServerController) installXray(c *gin.Context) { version := c.Param("version") err := a.serverService.UpdateXray(version) - jsonMsg(c, I18nWeb(c, "install")+" xray", err) + jsonMsg(c, I18nWeb(c, "pages.index.xraySwitchVersionPopover"), err) +} + +func (a *ServerController) updateGeofile(c *gin.Context) { + fileName := c.Param("fileName") + err := a.serverService.UpdateGeofile(fileName) + jsonMsg(c, I18nWeb(c, "pages.index.geofileUpdatePopover"), err) } func (a *ServerController) stopXrayService(c *gin.Context) { diff --git a/web/controller/setting.go b/web/controller/setting.go index d04969dc..1ca65b07 100644 --- a/web/controller/setting.go +++ b/web/controller/setting.go @@ -4,6 +4,7 @@ import ( "errors" "time" + "x-ui/util/crypto" "x-ui/web/entity" "x-ui/web/service" "x-ui/web/session" @@ -84,7 +85,7 @@ func (a *SettingController) updateUser(c *gin.Context) { return } user := session.GetLoginUser(c) - if user.Username != form.OldUsername || user.Password != form.OldPassword { + if user.Username != form.OldUsername || !crypto.CheckPasswordHash(user.Password, form.OldPassword) { jsonMsg(c, I18nWeb(c, "pages.settings.toasts.modifyUser"), errors.New(I18nWeb(c, "pages.settings.toasts.originalUserPassIncorrect"))) return } @@ -95,7 +96,7 @@ func (a *SettingController) updateUser(c *gin.Context) { err = a.userService.UpdateUser(user.Id, form.NewUsername, form.NewPassword) if err == nil { user.Username = form.NewUsername - user.Password = form.NewPassword + user.Password, _ = crypto.HashPasswordAsBcrypt(form.NewPassword) session.SetLoginUser(c, user) } jsonMsg(c, I18nWeb(c, "pages.settings.toasts.modifyUser"), err) diff --git a/web/html/index.html b/web/html/index.html index 574fc539..d17cd1e0 100644 --- a/web/html/index.html +++ b/web/html/index.html @@ -22,11 +22,14 @@ .ant-backup-list-item { gap: 10px; } - .ant-xray-version-list-item { + .ant-version-list-item { --padding: 12px; padding: var(--padding) !important; gap: var(--padding); } + .dark .ant-version-list-item svg{ + color: var(--dark-color-text-primary); + } .dark .ant-backup-list-item svg, .dark .ant-badge-status-text, .dark .ant-card-extra { @@ -43,7 +46,7 @@ border-color: var(--color-primary-100); } .dark .ant-backup-list, - .dark .ant-xray-version-list, + .dark .ant-version-list, .dark .ant-card-actions, .dark .ant-card-actions>li:not(:last-child) { border-color: var(--dark-color-stroke); @@ -353,14 +356,25 @@ - - - - [[ version ]] - - - + + + + + + [[ version ]] + + + + + + + + [[ file ]] + + + + + { + versionModal.hide(); + this.loading(true, '{{ i18n "pages.index.dontRefresh"}}'); + await HttpUtil.post(`/server/updateGeofile/${fileName}`); + this.loading(false); + }, + }); + }, async stopXrayService() { this.loading(true); const msg = await HttpUtil.post('server/stopXrayService'); diff --git a/web/html/xray.html b/web/html/xray.html index 0dfbdf50..fc94c31a 100644 --- a/web/html/xray.html +++ b/web/html/xray.html @@ -287,6 +287,7 @@ { label: 'Malware 🇮🇷', value: 'ext:geosite_IR.dat:malware' }, { label: 'Phishing 🇮🇷', value: 'ext:geosite_IR.dat:phishing' }, { label: 'Cryptominers 🇮🇷', value: 'ext:geosite_IR.dat:cryptominers' }, + { label: 'Adult +18', value: 'geosite:category-porn' }, { label: '🇮🇷 Iran', value: 'ext:geosite_IR.dat:ir' }, { label: '🇮🇷 .ir', value: 'regexp:.*\\.ir$' }, { label: '🇮🇷 .ایران', value: 'regexp:.*\\.xn--mgba3a4f16a$' }, diff --git a/web/service/server.go b/web/service/server.go index 99e2a97b..23167fb3 100644 --- a/web/service/server.go +++ b/web/service/server.go @@ -591,6 +591,66 @@ func (s *ServerService) ImportDB(file multipart.File) error { return nil } +func (s *ServerService) UpdateGeofile(fileName string) error { + files := []struct { + URL string + FileName string + }{ + {"https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat", "geoip.dat"}, + {"https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat", "geosite.dat"}, + {"https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geoip.dat", "geoip_IR.dat"}, + {"https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geosite.dat", "geosite_IR.dat"}, + {"https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geoip.dat", "geoip_RU.dat"}, + {"https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geosite.dat", "geosite_RU.dat"}, + } + + downloadFile := func(url, destPath string) error { + resp, err := http.Get(url) + if err != nil { + return common.NewErrorf("Failed to download Geofile from %s: %v", url, err) + } + defer resp.Body.Close() + + file, err := os.Create(destPath) + if err != nil { + return common.NewErrorf("Failed to create Geofile %s: %v", destPath, err) + } + defer file.Close() + + _, err = io.Copy(file, resp.Body) + if err != nil { + return common.NewErrorf("Failed to save Geofile %s: %v", destPath, err) + } + + return nil + } + + var fileURL string + for _, file := range files { + if file.FileName == fileName { + fileURL = file.URL + break + } + } + + if fileURL == "" { + return common.NewErrorf("File '%s' not found in the list of Geofiles", fileName) + } + + destPath := fmt.Sprintf("%s/%s", config.GetBinFolderPath(), fileName) + + if err := downloadFile(fileURL, destPath); err != nil { + return common.NewErrorf("Error downloading Geofile '%s': %v", fileName, err) + } + + err := s.RestartXrayService() + if err != nil { + return common.NewErrorf("Updated Geofile '%s' but Failed to start Xray: %v", fileName, err) + } + + return nil +} + func (s *ServerService) GetNewX25519Cert() (any, error) { // Run the command cmd := exec.Command(xray.GetBinaryPath(), "x25519") diff --git a/web/service/user.go b/web/service/user.go index 7438cf1a..72ae25a2 100644 --- a/web/service/user.go +++ b/web/service/user.go @@ -6,6 +6,7 @@ import ( "x-ui/database" "x-ui/database/model" "x-ui/logger" + "x-ui/util/crypto" "gorm.io/gorm" ) @@ -29,8 +30,9 @@ func (s *UserService) CheckUser(username string, password string, secret string) db := database.GetDB() user := &model.User{} + err := db.Model(model.User{}). - Where("username = ? and password = ? and login_secret = ?", username, password, secret). + Where("username = ? and login_secret = ?", username, secret). First(user). Error if err == gorm.ErrRecordNotFound { @@ -39,14 +41,25 @@ func (s *UserService) CheckUser(username string, password string, secret string) logger.Warning("check user err:", err) return nil } - return user + + if crypto.CheckPasswordHash(user.Password, password) { + return user + } + + return nil } func (s *UserService) UpdateUser(id int, username string, password string) error { db := database.GetDB() + hashedPassword, err := crypto.HashPasswordAsBcrypt(password) + + if err != nil { + return err + } + return db.Model(model.User{}). Where("id = ?", id). - Updates(map[string]any{"username": username, "password": password}). + Updates(map[string]any{"username": username, "password": hashedPassword}). Error } @@ -100,17 +113,23 @@ func (s *UserService) UpdateFirstUser(username string, password string) error { } else if password == "" { return errors.New("password can not be empty") } + hashedPassword, er := crypto.HashPasswordAsBcrypt(password) + + if er != nil { + return er + } + db := database.GetDB() user := &model.User{} err := db.Model(model.User{}).First(user).Error if database.IsNotFound(err) { user.Username = username - user.Password = password + user.Password = hashedPassword return db.Model(model.User{}).Create(user).Error } else if err != nil { return err } user.Username = username - user.Password = password + user.Password = hashedPassword return db.Save(user).Error } diff --git a/web/translation/translate.ar_EG.toml b/web/translation/translate.ar_EG.toml index 57c2b774..30d57552 100644 --- a/web/translation/translate.ar_EG.toml +++ b/web/translation/translate.ar_EG.toml @@ -122,8 +122,12 @@ "totalData" = "إجمالي البيانات" "sent" = "مرسل" "received" = "مستقبل" -"xraySwitchVersionDialog" = "تغيير نسخة Xray" -"xraySwitchVersionDialogDesc" = "متأكد إنك عايز تغير نسخة Xray لـ" +"xraySwitchVersionDialog" = "هل تريد حقًا تغيير إصدار Xray؟" +"xraySwitchVersionDialogDesc" = "سيؤدي هذا إلى تغيير إصدار Xray إلى #version#." +"xraySwitchVersionPopover" = "تم تحديث Xray بنجاح" +"geofileUpdateDialog" = "هل تريد حقًا تحديث ملف الجغرافيا؟" +"geofileUpdateDialogDesc" = "سيؤدي هذا إلى تحديث ملف #filename#." +"geofileUpdatePopover" = "تم تحديث ملف الجغرافيا بنجاح" "dontRefresh" = "التثبيت شغال، متعملش Refresh للصفحة" "logs" = "السجلات" "config" = "الإعدادات" diff --git a/web/translation/translate.en_US.toml b/web/translation/translate.en_US.toml index e35c41c5..ac40d7a2 100644 --- a/web/translation/translate.en_US.toml +++ b/web/translation/translate.en_US.toml @@ -122,8 +122,12 @@ "totalData" = "Total Data" "sent" = "Sent" "received" = "Received" -"xraySwitchVersionDialog" = "Change Xray Version" -"xraySwitchVersionDialogDesc" = "Are you sure you want to change the Xray version to" +"xraySwitchVersionDialog" = "Do you really want to change the Xray version?" +"xraySwitchVersionDialogDesc" = "This will change the Xray version to #version#." +"xraySwitchVersionPopover" = "Xray updated successfully" +"geofileUpdateDialog" = "Do you really want to update the geofile?" +"geofileUpdateDialogDesc" = "This will update the #filename# file." +"geofileUpdatePopover" = "Geofile updated successfully" "dontRefresh" = "Installation is in progress, please do not refresh this page" "logs" = "Logs" "config" = "Config" diff --git a/web/translation/translate.es_ES.toml b/web/translation/translate.es_ES.toml index 3d7d9a5c..cff6c252 100644 --- a/web/translation/translate.es_ES.toml +++ b/web/translation/translate.es_ES.toml @@ -124,8 +124,12 @@ "totalData" = "Datos totales" "sent" = "Enviado" "received" = "Recibido" -"xraySwitchVersionDialog" = "Cambiar Versión de Xray" -"xraySwitchVersionDialogDesc" = "¿Estás seguro de que deseas cambiar la versión de Xray a" +"xraySwitchVersionDialog" = "¿Realmente deseas cambiar la versión de Xray?" +"xraySwitchVersionDialogDesc" = "Esto cambiará la versión de Xray a #version#." +"xraySwitchVersionPopover" = "Xray se actualizó correctamente" +"geofileUpdateDialog" = "¿Realmente deseas actualizar el geofichero?" +"geofileUpdateDialogDesc" = "Esto actualizará el archivo #filename#." +"geofileUpdatePopover" = "Geofichero actualizado correctamente" "dontRefresh" = "La instalación está en progreso, por favor no actualices esta página." "logs" = "Registros" "config" = "Configuración" diff --git a/web/translation/translate.fa_IR.toml b/web/translation/translate.fa_IR.toml index ec1c5ef2..f203c615 100644 --- a/web/translation/translate.fa_IR.toml +++ b/web/translation/translate.fa_IR.toml @@ -124,8 +124,12 @@ "totalData" = "داده‌های کل" "sent" = "ارسال شده" "received" = "دریافت شده" -"xraySwitchVersionDialog" = "تغییر نسخه ایکس‌ری" -"xraySwitchVersionDialogDesc" = "آیا از تغییر نسخه‌ مطمئن هستید؟" +"xraySwitchVersionDialog" = "آیا واقعاً می‌خواهید نسخه Xray را تغییر دهید؟" +"xraySwitchVersionDialogDesc" = "این کار نسخه Xray را به #version# تغییر می‌دهد." +"xraySwitchVersionPopover" = "Xray با موفقیت به‌روز شد" +"geofileUpdateDialog" = "آیا واقعاً می‌خواهید فایل جغرافیایی را به‌روز کنید؟" +"geofileUpdateDialogDesc" = "این عمل فایل #filename# را به‌روز می‌کند." +"geofileUpdatePopover" = "فایل جغرافیایی با موفقیت به‌روز شد" "dontRefresh" = "در حال نصب، لطفا صفحه را رفرش نکنید" "logs" = "گزارش‌ها" "config" = "پیکربندی" diff --git a/web/translation/translate.id_ID.toml b/web/translation/translate.id_ID.toml index 2dd97591..cde8ece6 100644 --- a/web/translation/translate.id_ID.toml +++ b/web/translation/translate.id_ID.toml @@ -124,8 +124,12 @@ "totalData" = "Total data" "sent" = "Dikirim" "received" = "Diterima" -"xraySwitchVersionDialog" = "Ganti Versi Xray" -"xraySwitchVersionDialogDesc" = "Apakah Anda yakin ingin mengubah versi Xray menjadi" +"xraySwitchVersionDialog" = "Apakah Anda yakin ingin mengubah versi Xray?" +"xraySwitchVersionDialogDesc" = "Ini akan mengubah versi Xray ke #version#." +"xraySwitchVersionPopover" = "Xray berhasil diperbarui" +"geofileUpdateDialog" = "Apakah Anda yakin ingin memperbarui geofile?" +"geofileUpdateDialogDesc" = "Ini akan memperbarui file #filename#." +"geofileUpdatePopover" = "Geofile berhasil diperbarui" "dontRefresh" = "Instalasi sedang berlangsung, harap jangan menyegarkan halaman ini" "logs" = "Log" "config" = "Konfigurasi" diff --git a/web/translation/translate.ja_JP.toml b/web/translation/translate.ja_JP.toml index 90f34dba..9369923b 100644 --- a/web/translation/translate.ja_JP.toml +++ b/web/translation/translate.ja_JP.toml @@ -124,8 +124,12 @@ "totalData" = "総データ量" "sent" = "送信" "received" = "受信" -"xraySwitchVersionDialog" = "Xrayバージョン切り替え" -"xraySwitchVersionDialogDesc" = "Xrayのバージョンを切り替えますか?" +"xraySwitchVersionDialog" = "Xrayのバージョンを本当に変更しますか?" +"xraySwitchVersionDialogDesc" = "Xrayのバージョンが#version#に変更されます。" +"xraySwitchVersionPopover" = "Xrayの更新が成功しました" +"geofileUpdateDialog" = "ジオファイルを本当に更新しますか?" +"geofileUpdateDialogDesc" = "これにより#filename#ファイルが更新されます。" +"geofileUpdatePopover" = "ジオファイルの更新が成功しました" "dontRefresh" = "インストール中、このページをリロードしないでください" "logs" = "ログ" "config" = "設定" diff --git a/web/translation/translate.pt_BR.toml b/web/translation/translate.pt_BR.toml index 882db24e..6fc8b921 100644 --- a/web/translation/translate.pt_BR.toml +++ b/web/translation/translate.pt_BR.toml @@ -124,8 +124,12 @@ "totalData" = "Dados totais" "sent" = "Enviado" "received" = "Recebido" -"xraySwitchVersionDialog" = "Alterar Versão do Xray" -"xraySwitchVersionDialogDesc" = "Tem certeza de que deseja alterar a versão do Xray para" +"xraySwitchVersionDialog" = "Você realmente deseja alterar a versão do Xray?" +"xraySwitchVersionDialogDesc" = "Isso mudará a versão do Xray para #version#." +"xraySwitchVersionPopover" = "Xray atualizado com sucesso" +"geofileUpdateDialog" = "Você realmente deseja atualizar o geofile?" +"geofileUpdateDialogDesc" = "Isso atualizará o arquivo #filename#." +"geofileUpdatePopover" = "Geofile atualizado com sucesso" "dontRefresh" = "Instalação em andamento, por favor não atualize a página" "logs" = "Logs" "config" = "Configuração" diff --git a/web/translation/translate.ru_RU.toml b/web/translation/translate.ru_RU.toml index 13a39e79..7d99d037 100644 --- a/web/translation/translate.ru_RU.toml +++ b/web/translation/translate.ru_RU.toml @@ -124,8 +124,12 @@ "totalData" = "Общий объем данных" "sent" = "Отправлено" "received" = "Получено" -"xraySwitchVersionDialog" = "Переключить версию Xray" -"xraySwitchVersionDialogDesc" = "Вы точно хотите сменить версию Xray?" +"xraySwitchVersionDialog" = "Вы действительно хотите изменить версию Xray?" +"xraySwitchVersionDialogDesc" = "Это изменит версию Xray на #version#." +"xraySwitchVersionPopover" = "Xray успешно обновлён" +"geofileUpdateDialog" = "Вы действительно хотите обновить геофайл?" +"geofileUpdateDialogDesc" = "Это обновит файл #filename#." +"geofileUpdatePopover" = "Геофайл успешно обновлён" "dontRefresh" = "Установка в процессе. Не обновляйте страницу" "logs" = "Журнал" "config" = "Конфигурация" diff --git a/web/translation/translate.tr_TR.toml b/web/translation/translate.tr_TR.toml index 9b790cba..57f5084a 100644 --- a/web/translation/translate.tr_TR.toml +++ b/web/translation/translate.tr_TR.toml @@ -124,8 +124,12 @@ "totalData" = "Toplam veri" "sent" = "Gönderilen" "received" = "Alınan" -"xraySwitchVersionDialog" = "Xray Sürümünü Değiştir" -"xraySwitchVersionDialogDesc" = "Xray sürümünü değiştirmek istediğinizden emin misiniz" +"xraySwitchVersionDialog" = "Xray sürümünü gerçekten değiştirmek istiyor musunuz?" +"xraySwitchVersionDialogDesc" = "Bu işlem Xray sürümünü #version# olarak değiştirecektir." +"xraySwitchVersionPopover" = "Xray başarıyla güncellendi" +"geofileUpdateDialog" = "Geofile'ı gerçekten güncellemek istiyor musunuz?" +"geofileUpdateDialogDesc" = "Bu işlem #filename# dosyasını güncelleyecektir." +"geofileUpdatePopover" = "Geofile başarıyla güncellendi" "dontRefresh" = "Kurulum devam ediyor, lütfen bu sayfayı yenilemeyin" "logs" = "Günlükler" "config" = "Yapılandırma" diff --git a/web/translation/translate.uk_UA.toml b/web/translation/translate.uk_UA.toml index fcebcd6c..234c60c6 100644 --- a/web/translation/translate.uk_UA.toml +++ b/web/translation/translate.uk_UA.toml @@ -124,8 +124,12 @@ "totalData" = "Загальний обсяг даних" "sent" = "Відправлено" "received" = "Отримано" -"xraySwitchVersionDialog" = "Змінити версію Xray" -"xraySwitchVersionDialogDesc" = "Ви впевнені, що бажаєте змінити версію Xray на" +"xraySwitchVersionDialog" = "Ви дійсно хочете змінити версію Xray?" +"xraySwitchVersionDialogDesc" = "Це змінить версію Xray на #version#." +"xraySwitchVersionPopover" = "Xray успішно оновлено" +"geofileUpdateDialog" = "Ви дійсно хочете оновити геофайл?" +"geofileUpdateDialogDesc" = "Це оновить файл #filename#." +"geofileUpdatePopover" = "Геофайл успішно оновлено" "dontRefresh" = "Інсталяція триває, будь ласка, не оновлюйте цю сторінку" "logs" = "Журнали" "config" = "Конфігурація" diff --git a/web/translation/translate.vi_VN.toml b/web/translation/translate.vi_VN.toml index 541a4781..635d9923 100644 --- a/web/translation/translate.vi_VN.toml +++ b/web/translation/translate.vi_VN.toml @@ -124,8 +124,12 @@ "totalData" = "Tổng dữ liệu" "sent" = "Đã gửi" "received" = "Đã nhận" -"xraySwitchVersionDialog" = "Chuyển đổi Phiên bản Xray" -"xraySwitchVersionDialogDesc" = "Bạn có chắc chắn muốn chuyển đổi phiên bản Xray sang" +"xraySwitchVersionDialog" = "Bạn có chắc chắn muốn thay đổi phiên bản Xray không?" +"xraySwitchVersionDialogDesc" = "Hành động này sẽ thay đổi phiên bản Xray thành #version#." +"xraySwitchVersionPopover" = "Xray đã được cập nhật thành công" +"geofileUpdateDialog" = "Bạn có chắc chắn muốn cập nhật geofile không?" +"geofileUpdateDialogDesc" = "Hành động này sẽ cập nhật tệp #filename#." +"geofileUpdatePopover" = "Geofile đã được cập nhật thành công" "dontRefresh" = "Đang tiến hành cài đặt, vui lòng không làm mới trang này." "logs" = "Nhật ký" "config" = "Cấu hình" diff --git a/web/translation/translate.zh_CN.toml b/web/translation/translate.zh_CN.toml index f74655f8..587b0fe7 100644 --- a/web/translation/translate.zh_CN.toml +++ b/web/translation/translate.zh_CN.toml @@ -124,8 +124,12 @@ "totalData" = "总数据" "sent" = "已发送" "received" = "已接收" -"xraySwitchVersionDialog" = "切换 Xray 版本" -"xraySwitchVersionDialogDesc" = "是否切换 Xray 版本至" +"xraySwitchVersionDialog" = "您确定要更改Xray版本吗?" +"xraySwitchVersionDialogDesc" = "这将把Xray版本更改为#version#。" +"xraySwitchVersionPopover" = "Xray 更新成功" +"geofileUpdateDialog" = "您确定要更新地理文件吗?" +"geofileUpdateDialogDesc" = "这将更新 #filename# 文件。" +"geofileUpdatePopover" = "地理文件更新成功" "dontRefresh" = "安装中,请勿刷新此页面" "logs" = "日志" "config" = "配置" diff --git a/web/translation/translate.zh_TW.toml b/web/translation/translate.zh_TW.toml index 4bd6a524..ed0aedfe 100644 --- a/web/translation/translate.zh_TW.toml +++ b/web/translation/translate.zh_TW.toml @@ -124,8 +124,12 @@ "totalData" = "總數據" "sent" = "已發送" "received" = "已接收" -"xraySwitchVersionDialog" = "切換 Xray 版本" -"xraySwitchVersionDialogDesc" = "是否切換 Xray 版本至" +"xraySwitchVersionDialog" = "您確定要變更Xray版本嗎?" +"xraySwitchVersionDialogDesc" = "這將會把Xray版本變更為#version#。" +"xraySwitchVersionPopover" = "Xray 更新成功" +"geofileUpdateDialog" = "您確定要更新地理檔案嗎?" +"geofileUpdateDialogDesc" = "這將更新 #filename# 檔案。" +"geofileUpdatePopover" = "地理檔案更新成功" "dontRefresh" = "安裝中,請勿重新整理此頁面" "logs" = "日誌" "config" = "配置"