diff options
| -rw-r--r-- | database/db.go | 5 | ||||
| -rw-r--r-- | database/model/model.go | 7 | ||||
| -rw-r--r-- | install.sh | 39 | ||||
| -rw-r--r-- | main.go | 22 | ||||
| -rw-r--r-- | web/assets/js/model/models.js | 2 | ||||
| -rw-r--r-- | web/controller/index.go | 21 | ||||
| -rw-r--r-- | web/controller/setting.go | 28 | ||||
| -rw-r--r-- | web/entity/entity.go | 1 | ||||
| -rw-r--r-- | web/html/login.html | 16 | ||||
| -rw-r--r-- | web/html/xui/setting.html | 98 | ||||
| -rw-r--r-- | web/service/setting.go | 17 | ||||
| -rw-r--r-- | web/service/user.go | 33 | ||||
| -rw-r--r-- | web/translation/translate.en_US.toml | 5 | ||||
| -rw-r--r-- | web/translation/translate.fa_IR.toml | 5 | ||||
| -rw-r--r-- | web/translation/translate.zh_Hans.toml | 5 | ||||
| -rw-r--r-- | x-ui.sh | 77 |
16 files changed, 314 insertions, 67 deletions
diff --git a/database/db.go b/database/db.go index b9c16be8..ae42a6de 100644 --- a/database/db.go +++ b/database/db.go @@ -27,8 +27,9 @@ func initUser() error { } if count == 0 { user := &model.User{ - Username: "admin", - Password: "admin", + Username: "admin", + Password: "admin", + LoginSecret: "", } return db.Create(user).Error } diff --git a/database/model/model.go b/database/model/model.go index 778ad9b6..d1498b06 100644 --- a/database/model/model.go +++ b/database/model/model.go @@ -18,9 +18,10 @@ const ( ) type User struct { - Id int `json:"id" gorm:"primaryKey;autoIncrement"` - Username string `json:"username"` - Password string `json:"password"` + Id int `json:"id" gorm:"primaryKey;autoIncrement"` + Username string `json:"username"` + Password string `json:"password"` + LoginSecret string `json:"loginSecret"` } type Inbound struct { @@ -23,23 +23,14 @@ else fi echo "The OS release is: $release" -arch=$(arch) - -if [[ $arch == "x86_64" || $arch == "x64" || $arch == "amd64" ]]; then - arch="amd64" -elif [[ $arch == "aarch64" || $arch == "arm64" ]]; then - arch="arm64" -else - arch="amd64" - echo -e "${red} Failed to check system arch, will use default arch: ${arch}${plain}" -fi - -echo "arch: ${arch}" - -if [ $(getconf WORD_BIT) != '32' ] && [ $(getconf LONG_BIT) != '64' ]; then - echo "x-ui dosen't support 32-bit(x86) system, please use 64 bit operating system(x86_64) instead, if there is something wrong, please get in touch with me!" - exit -1 -fi +arch3xui() { + case "$(uname -m)" in + x86_64 | x64 | amd64 ) echo 'amd64' ;; + armv8 | arm64 | aarch64 ) echo 'arm64' ;; + * ) echo -e "${green}Unsupported CPU architecture! ${plain}" && rm -f install.sh && exit 1 ;; + esac +} +echo "arch: $(arch3xui)" os_version="" os_version=$(grep -i version_id /etc/os-release | cut -d \" -f2 | cut -d . -f1) @@ -122,18 +113,18 @@ install_x-ui() { exit 1 fi echo -e "Got x-ui latest version: ${last_version}, beginning the installation..." - wget -N --no-check-certificate -O /usr/local/x-ui-linux-${arch}.tar.gz https://github.com/MHSanaei/3x-ui/releases/download/${last_version}/x-ui-linux-${arch}.tar.gz + wget -N --no-check-certificate -O /usr/local/x-ui-linux-$(arch3xui).tar.gz https://github.com/MHSanaei/3x-ui/releases/download/${last_version}/x-ui-linux-$(arch3xui).tar.gz if [[ $? -ne 0 ]]; then echo -e "${red}Downloading x-ui failed, please be sure that your server can access Github ${plain}" exit 1 fi else last_version=$1 - url="https://github.com/MHSanaei/3x-ui/releases/download/${last_version}/x-ui-linux-${arch}.tar.gz" + url="https://github.com/MHSanaei/3x-ui/releases/download/${last_version}/x-ui-linux-$(arch3xui).tar.gz" echo -e "Begining to install x-ui $1" - wget -N --no-check-certificate -O /usr/local/x-ui-linux-${arch}.tar.gz ${url} + wget -N --no-check-certificate -O /usr/local/x-ui-linux-$(arch3xui).tar.gz ${url} if [[ $? -ne 0 ]]; then - echo -e "${red}Download x-ui $1 failed,please check the version exists${plain}" + echo -e "${red}Download x-ui $1 failed,please check the version exists ${plain}" exit 1 fi fi @@ -142,10 +133,10 @@ install_x-ui() { rm /usr/local/x-ui/ -rf fi - tar zxvf x-ui-linux-${arch}.tar.gz - rm x-ui-linux-${arch}.tar.gz -f + tar zxvf x-ui-linux-$(arch3xui).tar.gz + rm x-ui-linux-$(arch3xui).tar.gz -f cd x-ui - chmod +x x-ui bin/xray-linux-${arch} + chmod +x x-ui bin/xray-linux-$(arch3xui) cp -f x-ui.service /etc/systemd/system/ wget --no-check-certificate -O /usr/bin/x-ui https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.sh chmod +x /usr/local/x-ui/x-ui.sh @@ -204,6 +204,24 @@ func updateSetting(port int, username string, password string) { } } +func removeSecret() { + err := database.InitDB(config.GetDBPath()) + if err != nil { + fmt.Println(err) + return + } + userService := service.UserService{} + err = userService.RemoveUserSecret() + if err != nil { + fmt.Println(err) + } + settingService := service.SettingService{} + err = settingService.SetSecretStatus(false) + if err != nil { + fmt.Println(err) + } +} + func main() { if len(os.Args) < 2 { runWebServer() @@ -229,6 +247,7 @@ func main() { var tgbotRuntime string var reset bool var show bool + var remove_secret bool settingCmd.BoolVar(&reset, "reset", false, "reset all settings") settingCmd.BoolVar(&show, "show", false, "show current settings") settingCmd.IntVar(&port, "port", 0, "set panel port") @@ -290,6 +309,9 @@ func main() { if (tgbottoken != "") || (tgbotchatid != "") || (tgbotRuntime != "") { updateTgbotSetting(tgbottoken, tgbotchatid, tgbotRuntime) } + if remove_secret { + removeSecret() + } if enabletgbot { updateTgbotEnableSts(enabletgbot) } diff --git a/web/assets/js/model/models.js b/web/assets/js/model/models.js index 1de76850..ace99f48 100644 --- a/web/assets/js/model/models.js +++ b/web/assets/js/model/models.js @@ -3,6 +3,7 @@ class User { constructor() { this.username = ""; this.password = ""; + this.LoginSecret = ""; } } @@ -180,6 +181,7 @@ class AllSetting { this.tgBotBackup = false; this.tgCpu = ""; this.xrayTemplateConfig = ""; + this.secretEnable = false; this.timeLocation = "Asia/Tehran"; diff --git a/web/controller/index.go b/web/controller/index.go index b4f981e8..c19ee799 100644 --- a/web/controller/index.go +++ b/web/controller/index.go @@ -11,15 +11,17 @@ import ( ) type LoginForm struct { - Username string `json:"username" form:"username"` - Password string `json:"password" form:"password"` + Username string `json:"username" form:"username"` + Password string `json:"password" form:"password"` + LoginSecret string `json:"loginSecret" form:"loginSecret"` } type IndexController struct { BaseController - userService service.UserService - tgbot service.Tgbot + settingService service.SettingService + userService service.UserService + tgbot service.Tgbot } func NewIndexController(g *gin.RouterGroup) *IndexController { @@ -32,6 +34,7 @@ func (a *IndexController) initRouter(g *gin.RouterGroup) { g.GET("/", a.index) g.POST("/login", a.login) g.GET("/logout", a.logout) + g.POST("/getSecretStatus", a.getSecretStatus) } func (a *IndexController) index(c *gin.Context) { @@ -57,7 +60,7 @@ func (a *IndexController) login(c *gin.Context) { pureJsonMsg(c, false, I18n(c, "pages.login.toasts.emptyPassword")) return } - user := a.userService.CheckUser(form.Username, form.Password) + user := a.userService.CheckUser(form.Username, form.Password, form.LoginSecret) timeStr := time.Now().Format("2006-01-02 15:04:05") if user == nil { a.tgbot.UserLoginNotify(form.Username, getRemoteIp(c), timeStr, 0) @@ -82,3 +85,11 @@ func (a *IndexController) logout(c *gin.Context) { session.ClearSession(c) c.Redirect(http.StatusTemporaryRedirect, c.GetString("base_path")) } + +func (a *IndexController) getSecretStatus(c *gin.Context) { + status, err := a.settingService.GetSecretStatus() + if err == nil { + jsonObj(c, status, nil) + } + +} diff --git a/web/controller/setting.go b/web/controller/setting.go index 1de55ab6..2726c228 100644 --- a/web/controller/setting.go +++ b/web/controller/setting.go @@ -17,6 +17,10 @@ type updateUserForm struct { NewPassword string `json:"newPassword" form:"newPassword"` } +type updateSecretForm struct { + LoginSecret string `json:"loginSecret" form:"loginSecret"` +} + type SettingController struct { settingService service.SettingService userService service.UserService @@ -38,6 +42,8 @@ func (a *SettingController) initRouter(g *gin.RouterGroup) { g.POST("/updateUser", a.updateUser) g.POST("/restartPanel", a.restartPanel) g.GET("/getDefaultJsonConfig", a.getDefaultJsonConfig) + g.POST("/updateUserSecret", a.updateSecret) + g.POST("/getUserSecret", a.getUserSecret) } func (a *SettingController) getAllSetting(c *gin.Context) { @@ -128,3 +134,25 @@ func (a *SettingController) restartPanel(c *gin.Context) { err := a.panelService.RestartPanel(time.Second * 3) jsonMsg(c, I18n(c, "pages.setting.restartPanel"), err) } + +func (a *SettingController) updateSecret(c *gin.Context) { + form := &updateSecretForm{} + err := c.ShouldBind(form) + if err != nil { + jsonMsg(c, I18n(c, "pages.setting.toasts.modifySetting"), err) + } + user := session.GetLoginUser(c) + err = a.userService.UpdateUserSecret(user.Id, form.LoginSecret) + if err == nil { + user.LoginSecret = form.LoginSecret + session.SetLoginUser(c, user) + } + jsonMsg(c, I18n(c, "pages.setting.toasts.modifyUser"), err) +} +func (a *SettingController) getUserSecret(c *gin.Context) { + loginUser := session.GetLoginUser(c) + user := a.userService.GetUserSecret(loginUser.Id) + if user != nil { + jsonObj(c, user, nil) + } +} diff --git a/web/entity/entity.go b/web/entity/entity.go index b464de00..f1b24520 100644 --- a/web/entity/entity.go +++ b/web/entity/entity.go @@ -42,6 +42,7 @@ type AllSetting struct { TgCpu int `json:"tgCpu" form:"tgCpu"` XrayTemplateConfig string `json:"xrayTemplateConfig" form:"xrayTemplateConfig"` TimeLocation string `json:"timeLocation" form:"timeLocation"` + SecretEnable bool `json:"secretEnable" form:"secretEnable"` } func (s *AllSetting) CheckValid() error { diff --git a/web/html/login.html b/web/html/login.html index 4218793c..2f4cb3e6 100644 --- a/web/html/login.html +++ b/web/html/login.html @@ -57,6 +57,11 @@ <a-icon slot="prefix" type="lock" style="color: rgba(0,0,0,.25)"/> </a-input> </a-form-item> + <a-form-item v-if="secretEnable"> + <a-input type="text" placeholder='{{ i18n "secretToken" }}' v-model.trim="user.loginSecret" @keydown.enter.native="login"> + <a-icon slot="prefix" type="key" style="color: rgba(0,0,0,.25)"/> + </a-input> + </a-form-item> <a-form-item> <a-button block @click="login" :loading="loading">{{ i18n "login" }}</a-button> </a-form-item> @@ -98,10 +103,12 @@ data: { loading: false, user: new User(), + secretEnable: false, lang : "" }, created(){ this.lang = getLang(); + this.secretEnable = this.getSecretStatus(); }, methods: { async login() { @@ -111,6 +118,15 @@ if (msg.success) { location.href = basePath + 'xui/'; } + }, + async getSecretStatus() { + this.loading= true; + const msg = await HttpUtil.post('/getSecretStatus'); + this.loading = false; + if (msg.success){ + this.secretEnable = msg.obj; + return msg.obj; + } } } }); diff --git a/web/html/xui/setting.html b/web/html/xui/setting.html index eaaf8b25..db318ee4 100644 --- a/web/html/xui/setting.html +++ b/web/html/xui/setting.html @@ -91,8 +91,39 @@ <a-button type="primary" @click="updateUser">{{ i18n "confirm" }}</a-button> </a-form-item> </a-form> + <a-form :style="siderDrawer.isDarkTheme ? 'color: hsla(0,0%,100%,.65); padding: 20px;': 'background: white; padding: 20px;'"> + <a-list-item style="padding: 20px"> + <a-row> + <a-col :lg="24" :xl="12"> + <a-list-item-meta title='{{ i18n "pages.setting.loginSecurity" }}' description='{{ i18n "pages.setting.loginSecurityDesc" }}'/> + </a-col> + <a-col :lg="24" :xl="12"> + <template> + <a-switch @change="toggleToken(allSetting.secretEnable)" v-model="allSetting.secretEnable"></a-switch> + </template> + </a-col> + </a-row> + </a-list-item> + <a-list-item style="padding: 20px"> + <a-row> + <a-col :lg="24" :xl="12"> + <a-list-item-meta title='{{ i18n "pages.setting.secretToken" }}' description='{{ i18n "pages.setting.secretTokenDesc" }}'/> + + </a-col> + <a-col :lg="24" :xl="12"> + <svg + @click="getNewSecret" + xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="anticon anticon-question-circle" viewBox="0 0 16 16"> <path d="M11.534 7h3.932a.25.25 0 0 1 .192.41l-1.966 2.36a.25.25 0 0 1-.384 0l-1.966-2.36a.25.25 0 0 1 .192-.41zm-11 2h3.932a.25.25 0 0 0 .192-.41L2.692 6.23a.25.25 0 0 0-.384 0L.342 8.59A.25.25 0 0 0 .534 9z"/> <path fill-rule="evenodd" d="M8 3c-1.552 0-2.94.707-3.857 1.818a.5.5 0 1 1-.771-.636A6.002 6.002 0 0 1 13.917 7H12.9A5.002 5.002 0 0 0 8 3zM3.1 9a5.002 5.002 0 0 0 8.757 2.182.5.5 0 1 1 .771.636A6.002 6.002 0 0 1 2.083 9H3.1z"/> + </svg> + <template> + <a-textarea type="text" id='token' :disabled="!allSetting.secretEnable" v-model="user.loginSecret"></a-textarea> + </template> + </a-col> + </a-row> + </a-list-item> + <a-button type="primary" @click="updateSecret">{{ i18n "confirm" }}</a-button> + </a-form> </a-tab-pane> - <a-tab-pane key="3" tab='{{ i18n "pages.setting.xrayConfiguration"}}'> <a-list item-layout="horizontal" :style="siderDrawer.isDarkTheme ? 'color: hsla(0,0%,100%,.65);': 'background: white;'"> <a-divider>{{ i18n "pages.setting.actions"}}</a-divider> @@ -205,7 +236,7 @@ oldAllSetting: new AllSetting(), allSetting: new AllSetting(), saveBtnDisable: true, - user: {}, + user: new User(), lang: getLang(), ipv4Settings: { tag: "IPv4", @@ -262,31 +293,33 @@ } }, methods: { - loading(spinning = true) { - this.spinning = spinning; + loading(spinning = true , obj) { + if(obj == null) + this.spinning = spinning; }, async getAllSetting() { - this.loading(true); + this.loading(true,{}); const msg = await HttpUtil.post("/xui/setting/all"); - this.loading(false); + this.loading(false,null); if (msg.success) { this.oldAllSetting = new AllSetting(msg.obj); this.allSetting = new AllSetting(msg.obj); this.saveBtnDisable = true; } + await this.getUserSecret(); }, async updateAllSetting() { - this.loading(true); + this.loading(true,{}); const msg = await HttpUtil.post("/xui/setting/update", this.allSetting); - this.loading(false); + this.loading(false,null); if (msg.success) { await this.getAllSetting(); } }, async updateUser() { - this.loading(true); + this.loading(true,{}); const msg = await HttpUtil.post("/xui/setting/updateUser", this.user); - this.loading(false); + this.loading(false,null); if (msg.success) { this.user = {}; } @@ -301,19 +334,54 @@ onOk: () => resolve(), }); }); - this.loading(true); + this.loading(true,{}); const msg = await HttpUtil.post("/xui/setting/restartPanel"); - this.loading(false); + this.loading(false,null); if (msg.success) { - this.loading(true); + this.loading(true,{}); await PromiseUtil.sleep(5000); location.reload(); } }, + async getUserSecret(){ + const user_msg = await HttpUtil.post("/xui/setting/getUserSecret", this.user); + if (user_msg.success){ + this.user = user_msg.obj; + } + this.loading(false); + }, + async updateSecret(){ + this.loading(true,{}); + const msg = await HttpUtil.post("/xui/setting/updateUserSecret", this.user); + if (msg.success){ + this.user = msg.obj; + } + this.loading(false,null); + await this.updateAllSetting(); + }, + async getNewSecret(){ + this.loading(true,{}); + await PromiseUtil.sleep(1000); + var chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'; + var string = ''; + var len = 64; + for(var ii=0; ii<len; ii++){ + string += chars[Math.floor(Math.random() * chars.length)]; + } + this.user.loginSecret = string; + document.getElementById('token').value =this.user.loginSecret; + this.loading(false,null); + }, + async toggleToken(value){ + if(value) + this.getNewSecret(); + else + this.user.loginSecret = ""; + }, async resetXrayConfigToDefault() { - this.loading(true); + this.loading(true,{}); const msg = await HttpUtil.get("/xui/setting/getDefaultJsonConfig"); - this.loading(false); + this.loading(false,null); if (msg.success) { this.templateSettings = JSON.parse(JSON.stringify(msg.obj, null, 2)); this.saveBtnDisable = true; diff --git a/web/service/setting.go b/web/service/setting.go index 9d92685e..6e305536 100644 --- a/web/service/setting.go +++ b/web/service/setting.go @@ -38,6 +38,7 @@ var defaultValueMap = map[string]string{ "tgRunTime": "@daily", "tgBotBackup": "false", "tgCpu": "0", + "secretEnable": "false", } type SettingService struct { @@ -129,7 +130,13 @@ func (s *SettingService) GetAllSetting() (*entity.AllSetting, error) { func (s *SettingService) ResetSettings() error { db := database.GetDB() - return db.Where("1 = 1").Delete(model.Setting{}).Error + err := db.Where("1 = 1").Delete(model.Setting{}).Error + if err != nil { + return err + } + return db.Model(model.User{}). + Where("1 = 1"). + Update("login_secret", "").Error } func (s *SettingService) getSetting(key string) (*model.Setting, error) { @@ -288,6 +295,14 @@ func (s *SettingService) SetgetTrafficDiff(value int) error { return s.setInt("trafficDiff", value) } +func (s *SettingService) GetSecretStatus() (bool, error) { + return s.getBool("secretEnable") +} + +func (s *SettingService) SetSecretStatus(value bool) error { + return s.setBool("secretEnable", value) +} + func (s *SettingService) GetSecret() ([]byte, error) { secret, err := s.getString("secret") if secret == defaultValueMap["secret"] { diff --git a/web/service/user.go b/web/service/user.go index 6da8bd1e..f1868424 100644 --- a/web/service/user.go +++ b/web/service/user.go @@ -25,12 +25,12 @@ func (s *UserService) GetFirstUser() (*model.User, error) { return user, nil } -func (s *UserService) CheckUser(username string, password string) *model.User { +func (s *UserService) CheckUser(username string, password string, secret string) *model.User { db := database.GetDB() user := &model.User{} err := db.Model(model.User{}). - Where("username = ? and password = ?", username, password). + Where("username = ? and password = ? and login_secret = ?", username, password, secret). First(user). Error if err == gorm.ErrRecordNotFound { @@ -50,6 +50,35 @@ func (s *UserService) UpdateUser(id int, username string, password string) error Error } +func (s *UserService) UpdateUserSecret(id int, secret string) error { + db := database.GetDB() + return db.Model(model.User{}). + Where("id = ?", id). + Update("login_secret", secret). + Error +} + +func (s *UserService) RemoveUserSecret() error { + db := database.GetDB() + return db.Model(model.User{}). + Where("1 = 1"). + Update("login_secret", ""). + Error +} + +func (s *UserService) GetUserSecret(id int) *model.User { + db := database.GetDB() + user := &model.User{} + err := db.Model(model.User{}). + Where("id = ?", id). + First(user). + Error + if err == gorm.ErrRecordNotFound { + return nil + } + return user +} + func (s *UserService) UpdateFirstUser(username string, password string) error { if username == "" { return errors.New("username can not be empty") diff --git a/web/translation/translate.en_US.toml b/web/translation/translate.en_US.toml index c5809442..70bf8251 100644 --- a/web/translation/translate.en_US.toml +++ b/web/translation/translate.en_US.toml @@ -48,6 +48,7 @@ "install" = "Install" "clients" = "Clients" "usage" = "Usage" +"secretToken" = "Secret token" [menu] "dashboard" = "System Status" @@ -288,6 +289,10 @@ "tgNotifyCpuDesc" = "This telegram bot will send you a notification if CPU usage is more than this percentage (unit:%)" "timeZonee" = "Time Zone" "timeZoneDesc" = "The scheduled task runs according to the time in the time zone, and restarts the panel to take effect" +"loginSecurity" = "Login security" +"loginSecurityDesc" = "Toggle additional step in user login page" +"secretToken" = "Secret Token" +"secretTokenDesc" = "Copy this secret token and keep it in a safe place; without this you won't be able to login. This can not be recovered from x-ui command tool neither" [pages.setting.toasts] "modifySetting" = "Modify setting" diff --git a/web/translation/translate.fa_IR.toml b/web/translation/translate.fa_IR.toml index 24e6d02a..28ed1747 100644 --- a/web/translation/translate.fa_IR.toml +++ b/web/translation/translate.fa_IR.toml @@ -48,6 +48,7 @@ "install" = "نصب" "clients" = "کاربران" "usage" = "استفاده" +"secretToken" = "توکن امنیتی" [menu] "dashboard" = "وضعیت سیستم" @@ -286,6 +287,10 @@ "tgNotifyCpuDesc" = "این ربات تلگرام در صورت استفاده پردازنده بیشتر از این درصد برای شما پیام ارسال می کند.(واحد: درصد)" "timeZonee" = "منظقه زمانی" "timeZoneDesc" = "وظایف برنامه ریزی شده بر اساس این منطقه زمانی اجرا می شوند. پنل را مجدداً راه اندازی می کند تا اعمال شود" +"loginSecurity" = "لاگین ایمن" +"loginSecurityDesc" = "افزودن یک مرحله دیگر به فرآیند لاگین" +"secretToken" = "توکن امنیتی" +"secretTokenDesc" = "این کد امنیتی را نزد خود در این جای امن نگه داری، بدون این کد امکان ورود به پنل را نخواهید داشت. امکان بازیابی آن وجود ندارد!" [pages.setting.toasts] "modifySetting" = "ویرایش تنظیمات" diff --git a/web/translation/translate.zh_Hans.toml b/web/translation/translate.zh_Hans.toml index 08241bdb..f79ce75f 100644 --- a/web/translation/translate.zh_Hans.toml +++ b/web/translation/translate.zh_Hans.toml @@ -48,6 +48,7 @@ "install" = "安装" "clients" = "客户端" "usage" = "用法" +"secretToken" = "秘密令牌" [menu] "dashboard" = "系统状态" @@ -286,6 +287,10 @@ "tgNotifyCpuDesc" = "如果 CPU 使用率超过此百分比(单位:%),此 talegram bot 将向您发送通知" "timeZonee" = "时区" "timeZoneDesc" = "定时任务按照该时区的时间运行,重启面板生效" +"loginSecurity" = "登录安全" +"loginSecurityDesc" = "在用户登录页面中切换附加步骤" +"secretToken" = "秘密令牌" +"secretTokenDesc" = "复制此秘密令牌并将其保存在安全的地方;没有这个你将无法登录。这也无法从 x-ui 命令工具中恢复" [pages.setting.toasts] "modifySetting" = "修改设置" @@ -56,6 +56,14 @@ elif [[ "${release}" == "debian" ]]; then fi fi +arch3xui() { + case "$(uname -m)" in + x86_64 | x64 | amd64 ) echo 'amd64' ;; + armv8 | arm64 | aarch64 ) echo 'arm64' ;; + * ) echo -e "${red} Unsupported CPU architecture!${plain}" && exit 1 ;; + esac +} + confirm() { if [[ $# > 1 ]]; then echo && read -p "$1 [Default$2]: " temp @@ -98,18 +106,49 @@ install() { } update() { - confirm "This function will forcefully reinstall the latest version, and the data will not be lost. Do you want to continue?" "n" - if [[ $? != 0 ]]; then - LOGE "Cancelled" - if [[ $# == 0 ]]; then - before_show_menu + read -rp "This function will update the X-UI panel to the latest version. Data will not be lost. Whether to continues? [Y/N]: " yn + if [[ $yn =~ "Y"|"y" ]]; then + systemctl stop x-ui + if [[ -e /usr/local/x-ui/ ]]; then + cd + rm -rf /usr/local/x-ui/ fi - return 0 - fi - bash <(curl -Ls https://raw.githubusercontent.com/MHSanaei/3x-ui/main/install.sh) - if [[ $? == 0 ]]; then - LOGI "Update is complete, Panel has automatically restarted " - exit 0 + + last_version=$(curl -Ls "https://api.github.com/repos/MHSanaei/3x-ui/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') || last_version=$(curl -sm8 https://raw.githubusercontent.com/MHSanaei/3x-ui/main/config/version) + if [[ -z "$last_version" ]]; then + echo -e "${red}Detecting the X-UI version failed, please make sure your server can connect to the GitHub API ${plain}" + exit 1 + fi + + echo -e "${yellow}The latest version of X-UI is: ${last_version}, starting update...${plain}" + wget -N --no-check-certificate -O /usr/local/x-ui-linux-$(arch3xui).tar.gz https://github.com/MHSanaei/3x-ui/releases/download/${last_version}/x-ui-linux-$(arch3xui).tar.gz + if [[ $? -ne 0 ]]; then + echo -e "${red}Download the X-UI failure, please make sure your server can connect and download the files from github ${plain}" + exit 1 + fi + + cd /usr/local/ + tar zxvf x-ui-linux-$(arch3xui).tar.gz + rm -f x-ui-linux-$(arch3xui).tar.gz + + cd x-ui + chmod +x x-ui bin/xray-linux-$(arch3xui) + cp -f x-ui.service /etc/systemd/system/ + + wget -N --no-check-certificate https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.sh -O /usr/bin/x-ui + chmod +x /usr/local/x-ui/x-ui.sh + chmod +x /usr/bin/x-ui + + systemctl daemon-reload + systemctl enable x-ui >/dev/null 2>&1 + systemctl start x-ui + systemctl restart x-ui + + echo -e "${green}The update is completed, and the X-UI panel has been automatically restarted ${plain}" + exit 1 + else + echo -e "${red}The upgrade X-UI panel has been canceled! ${plain}" + exit 1 fi } @@ -139,15 +178,23 @@ uninstall() { } reset_user() { - confirm "Reset your username and password to admin?" "n" + confirm "Are you sure to reset the username and password of the panel?" "n" if [[ $? != 0 ]]; then if [[ $# == 0 ]]; then show_menu fi return 0 fi - /usr/local/x-ui/x-ui setting -username admin -password admin - echo -e "Username and password have been reset to ${green}admin${plain}, Please restart the panel now." + read -rp "Please set the login username [default is a random username]: " config_account + [[ -z $config_account ]] && config_account=$(date +%s%N | md5sum | cut -c 1-8) + read -rp "Please set the login password [default is a random password]: " config_password + [[ -z $config_password ]] && config_password=$(date +%s%N | md5sum | cut -c 1-8) + /usr/local/x-ui/x-ui setting -username ${config_account} -password ${config_password} >/dev/null 2>&1 + /usr/local/x-ui/x-ui setting -remove_secret >/dev/null 2>&1 + echo -e "Panel login username has been reset to: ${green} ${config_account} ${plain}" + echo -e "Panel login password has been reset to: ${green} ${config_password} ${plain}" + echo -e "${yellow} Panel login secret token disabled ${plain}" + echo -e "${green} Please use the new login username and password to access the X-UI panel. Also remember them! ${plain}" confirm_restart } @@ -781,7 +828,7 @@ show_menu() { ${green}2.${plain} Update x-ui ${green}3.${plain} Uninstall x-ui ———————————————— - ${green}4.${plain} Reset Username And Password + ${green}4.${plain} Reset Username & Password & Secret Token ${green}5.${plain} Reset Panel Settings ${green}6.${plain} Change Panel Port ${green}7.${plain} View Current Panel Settings |
