chore: implement 2fa auth

from #2786
This commit is contained in:
Shishkevich D. 2025-05-03 08:03:09 +00:00
parent 3d54e33051
commit 2c53007580
29 changed files with 193 additions and 290 deletions

View file

@ -22,7 +22,6 @@ var db *gorm.DB
const ( const (
defaultUsername = "admin" defaultUsername = "admin"
defaultPassword = "admin" defaultPassword = "admin"
defaultSecret = ""
) )
func initModels() error { func initModels() error {
@ -53,7 +52,6 @@ func initUser() error {
user := &model.User{ user := &model.User{
Username: defaultUsername, Username: defaultUsername,
Password: defaultPassword, Password: defaultPassword,
LoginSecret: defaultSecret,
} }
return db.Create(user).Error return db.Create(user).Error
} }

View file

@ -24,7 +24,6 @@ type User struct {
Id int `json:"id" gorm:"primaryKey;autoIncrement"` Id int `json:"id" gorm:"primaryKey;autoIncrement"`
Username string `json:"username"` Username string `json:"username"`
Password string `json:"password"` Password string `json:"password"`
LoginSecret string `json:"loginSecret"`
} }
type Inbound struct { type Inbound struct {

1
go.mod
View file

@ -78,6 +78,7 @@ require (
github.com/valyala/fastjson v1.6.4 // indirect github.com/valyala/fastjson v1.6.4 // indirect
github.com/vishvananda/netlink v1.3.0 // indirect github.com/vishvananda/netlink v1.3.0 // indirect
github.com/vishvananda/netns v0.0.5 // indirect github.com/vishvananda/netns v0.0.5 // indirect
github.com/xlzd/gotp v0.1.0 // indirect
github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463 // indirect github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.uber.org/automaxprocs v1.6.0 // indirect go.uber.org/automaxprocs v1.6.0 // indirect

2
go.sum
View file

@ -187,6 +187,8 @@ github.com/vishvananda/netlink v1.3.0/go.mod h1:i6NetklAujEcC6fK0JPjT8qSwWyO0HLn
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY= github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
github.com/xlzd/gotp v0.1.0 h1:37blvlKCh38s+fkem+fFh7sMnceltoIEBYTVXyoa5Po=
github.com/xlzd/gotp v0.1.0/go.mod h1:ndLJ3JKzi3xLmUProq4LLxCuECL93dG9WASNLpHz8qg=
github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463 h1:g1Cj7d+my6k/HHxLAyxPwyX8i7FGRr6ulBDMkBzg2BM= github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463 h1:g1Cj7d+my6k/HHxLAyxPwyX8i7FGRr6ulBDMkBzg2BM=
github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463/go.mod h1:BjIOLmkEEtAgloAiVUcYj0Mt+YU00JARZw8AEU0IwAg= github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463/go.mod h1:BjIOLmkEEtAgloAiVUcYj0Mt+YU00JARZw8AEU0IwAg=
github.com/xtls/xray-core v1.250306.1-0.20250430044058-87ab8e512882 h1:O/aN4TCrJ+fmaDOBoQhtTRev2hVHIENy2EJ70jQcyEY= github.com/xtls/xray-core v1.250306.1-0.20250430044058-87ab8e512882 h1:O/aN4TCrJ+fmaDOBoQhtTRev2hVHIENy2EJ70jQcyEY=

35
main.go
View file

@ -343,36 +343,6 @@ func migrateDb() {
fmt.Println("Migration done!") fmt.Println("Migration done!")
} }
func removeSecret() {
userService := service.UserService{}
secretExists, err := userService.CheckSecretExistence()
if err != nil {
fmt.Println("Error checking secret existence:", err)
return
}
if !secretExists {
fmt.Println("No secret exists to remove.")
return
}
err = userService.RemoveUserSecret()
if err != nil {
fmt.Println("Error removing secret:", err)
return
}
settingService := service.SettingService{}
err = settingService.SetSecretStatus(false)
if err != nil {
fmt.Println("Error updating secret status:", err)
return
}
fmt.Println("Secret removed successfully.")
}
func main() { func main() {
if len(os.Args) < 2 { if len(os.Args) < 2 {
runWebServer() runWebServer()
@ -400,10 +370,8 @@ func main() {
var reset bool var reset bool
var show bool var show bool
var getCert bool var getCert bool
var remove_secret bool
settingCmd.BoolVar(&reset, "reset", false, "Reset all settings") settingCmd.BoolVar(&reset, "reset", false, "Reset all settings")
settingCmd.BoolVar(&show, "show", false, "Display current settings") settingCmd.BoolVar(&show, "show", false, "Display current settings")
settingCmd.BoolVar(&remove_secret, "remove_secret", false, "Remove secret key")
settingCmd.IntVar(&port, "port", 0, "Set panel port number") settingCmd.IntVar(&port, "port", 0, "Set panel port number")
settingCmd.StringVar(&username, "username", "", "Set login username") settingCmd.StringVar(&username, "username", "", "Set login username")
settingCmd.StringVar(&password, "password", "", "Set login password") settingCmd.StringVar(&password, "password", "", "Set login password")
@ -467,9 +435,6 @@ func main() {
if (tgbottoken != "") || (tgbotchatid != "") || (tgbotRuntime != "") { if (tgbottoken != "") || (tgbotchatid != "") || (tgbotRuntime != "") {
updateTgbotSetting(tgbottoken, tgbotchatid, tgbotRuntime) updateTgbotSetting(tgbottoken, tgbotchatid, tgbotRuntime)
} }
if remove_secret {
removeSecret()
}
if enabletgbot { if enabletgbot {
updateTgbotEnableSts(enabletgbot) updateTgbotEnableSts(enabletgbot)
} }

View file

@ -23,8 +23,9 @@ class AllSetting {
this.tgBotLoginNotify = true; this.tgBotLoginNotify = true;
this.tgCpu = 80; this.tgCpu = 80;
this.tgLang = "en-US"; this.tgLang = "en-US";
this.twoFactorEnable = false;
this.twoFactorToken = "";
this.xrayTemplateConfig = ""; this.xrayTemplateConfig = "";
this.secretEnable = false;
this.subEnable = false; this.subEnable = false;
this.subTitle = ""; this.subTitle = "";
this.subListen = ""; this.subListen = "";

View file

@ -145,6 +145,33 @@ class RandomUtil {
return Base64.alternativeEncode(String.fromCharCode(...array)); return Base64.alternativeEncode(String.fromCharCode(...array));
} }
static randomBase32String(length = 16) {
const array = new Uint8Array(length);
window.crypto.getRandomValues(array);
const base32Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
let result = '';
let bits = 0;
let buffer = 0;
for (let i = 0; i < array.length; i++) {
buffer = (buffer << 8) | array[i];
bits += 8;
while (bits >= 5) {
bits -= 5;
result += base32Chars[(buffer >>> bits) & 0x1F];
}
}
if (bits > 0) {
result += base32Chars[(buffer << (5 - bits)) & 0x1F];
}
return result;
}
} }
class ObjectUtil { class ObjectUtil {

View file

@ -14,9 +14,9 @@ import (
) )
type LoginForm struct { type LoginForm struct {
Username string `json:"username" form:"username"` Username string `json:"username" form:"username"`
Password string `json:"password" form:"password"` Password string `json:"password" form:"password"`
LoginSecret string `json:"loginSecret" form:"loginSecret"` TwoFactorCode string `json:"twoFactorCode" form:"twoFactorCode"`
} }
type IndexController struct { type IndexController struct {
@ -37,7 +37,7 @@ func (a *IndexController) initRouter(g *gin.RouterGroup) {
g.GET("/", a.index) g.GET("/", a.index)
g.POST("/login", a.login) g.POST("/login", a.login)
g.GET("/logout", a.logout) g.GET("/logout", a.logout)
g.POST("/getSecretStatus", a.getSecretStatus) g.POST("/getTwoFactorEnable", a.getTwoFactorEnable)
} }
func (a *IndexController) index(c *gin.Context) { func (a *IndexController) index(c *gin.Context) {
@ -64,14 +64,13 @@ func (a *IndexController) login(c *gin.Context) {
return return
} }
user := a.userService.CheckUser(form.Username, form.Password, form.LoginSecret) user := a.userService.CheckUser(form.Username, form.Password, form.TwoFactorCode)
timeStr := time.Now().Format("2006-01-02 15:04:05") timeStr := time.Now().Format("2006-01-02 15:04:05")
safeUser := template.HTMLEscapeString(form.Username) safeUser := template.HTMLEscapeString(form.Username)
safePass := template.HTMLEscapeString(form.Password) safePass := template.HTMLEscapeString(form.Password)
safeSecret := template.HTMLEscapeString(form.LoginSecret)
if user == nil { if user == nil {
logger.Warningf("wrong username: \"%s\", password: \"%s\", secret: \"%s\", IP: \"%s\"", safeUser, safePass, safeSecret, getRemoteIp(c)) logger.Warningf("wrong username: \"%s\", password: \"%s\", IP: \"%s\"", safeUser, safePass, getRemoteIp(c))
a.tgbot.UserLoginNotify(safeUser, safePass, getRemoteIp(c), timeStr, 0) a.tgbot.UserLoginNotify(safeUser, safePass, getRemoteIp(c), timeStr, 0)
pureJsonMsg(c, http.StatusOK, false, I18nWeb(c, "pages.login.toasts.wrongUsernameOrPassword")) pureJsonMsg(c, http.StatusOK, false, I18nWeb(c, "pages.login.toasts.wrongUsernameOrPassword"))
return return
@ -108,8 +107,8 @@ func (a *IndexController) logout(c *gin.Context) {
c.Redirect(http.StatusTemporaryRedirect, c.GetString("base_path")) c.Redirect(http.StatusTemporaryRedirect, c.GetString("base_path"))
} }
func (a *IndexController) getSecretStatus(c *gin.Context) { func (a *IndexController) getTwoFactorEnable(c *gin.Context) {
status, err := a.settingService.GetSecretStatus() status, err := a.settingService.GetTwoFactorEnable()
if err == nil { if err == nil {
jsonObj(c, status, nil) jsonObj(c, status, nil)
} }

View file

@ -18,10 +18,6 @@ type updateUserForm struct {
NewPassword string `json:"newPassword" form:"newPassword"` NewPassword string `json:"newPassword" form:"newPassword"`
} }
type updateSecretForm struct {
LoginSecret string `json:"loginSecret" form:"loginSecret"`
}
type SettingController struct { type SettingController struct {
settingService service.SettingService settingService service.SettingService
userService service.UserService userService service.UserService
@ -43,8 +39,6 @@ func (a *SettingController) initRouter(g *gin.RouterGroup) {
g.POST("/updateUser", a.updateUser) g.POST("/updateUser", a.updateUser)
g.POST("/restartPanel", a.restartPanel) g.POST("/restartPanel", a.restartPanel)
g.GET("/getDefaultJsonConfig", a.getDefaultXrayConfig) g.GET("/getDefaultJsonConfig", a.getDefaultXrayConfig)
g.POST("/updateUserSecret", a.updateSecret)
g.POST("/getUserSecret", a.getUserSecret)
} }
func (a *SettingController) getAllSetting(c *gin.Context) { func (a *SettingController) getAllSetting(c *gin.Context) {
@ -106,29 +100,6 @@ func (a *SettingController) restartPanel(c *gin.Context) {
jsonMsg(c, I18nWeb(c, "pages.settings.restartPanel"), err) jsonMsg(c, I18nWeb(c, "pages.settings.restartPanel"), err)
} }
func (a *SettingController) updateSecret(c *gin.Context) {
form := &updateSecretForm{}
err := c.ShouldBind(form)
if err != nil {
jsonMsg(c, I18nWeb(c, "pages.settings.toasts.modifySettings"), 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, I18nWeb(c, "pages.settings.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)
}
}
func (a *SettingController) getDefaultXrayConfig(c *gin.Context) { func (a *SettingController) getDefaultXrayConfig(c *gin.Context) {
defaultJsonConfig, err := a.settingService.GetDefaultXrayConfig() defaultJsonConfig, err := a.settingService.GetDefaultXrayConfig()
if err != nil { if err != nil {

View file

@ -38,7 +38,8 @@ type AllSetting struct {
TgCpu int `json:"tgCpu" form:"tgCpu"` TgCpu int `json:"tgCpu" form:"tgCpu"`
TgLang string `json:"tgLang" form:"tgLang"` TgLang string `json:"tgLang" form:"tgLang"`
TimeLocation string `json:"timeLocation" form:"timeLocation"` TimeLocation string `json:"timeLocation" form:"timeLocation"`
SecretEnable bool `json:"secretEnable" form:"secretEnable"` TwoFactorEnable bool `json:"twoFactorEnable" form:"twoFactorEnable"`
TwoFactorToken string `json:"twoFactorToken" form:"twoFactorToken"`
SubEnable bool `json:"subEnable" form:"subEnable"` SubEnable bool `json:"subEnable" form:"subEnable"`
SubTitle string `json:"subTitle" form:"subTitle"` SubTitle string `json:"subTitle" form:"subTitle"`
SubListen string `json:"subListen" form:"subListen"` SubListen string `json:"subListen" form:"subListen"`

View file

@ -512,11 +512,11 @@
<a-icon slot="prefix" type="lock" :style="{ fontSize: '1rem' }"></a-icon> <a-icon slot="prefix" type="lock" :style="{ fontSize: '1rem' }"></a-icon>
</a-input-password> </a-input-password>
</a-form-item> </a-form-item>
<a-form-item v-if="secretEnable"> <a-form-item v-if="twoFactorEnable">
<a-input-password autocomplete="secret" name="secret" v-model.trim="user.loginSecret" <a-input autocomplete="totp" name="twoFactorCode" v-model.trim="user.twoFactorCode"
placeholder='{{ i18n "secretToken" }}' @keydown.enter.native="login"> placeholder='{{ i18n "twoFactorCode" }}' @keydown.enter.native="login">
<a-icon slot="prefix" type="key" :style="{ fontSize: '1rem' }"></a-icon> <a-icon slot="prefix" type="key" :style="{ fontSize: '1rem' }"></a-icon>
</a-input-password> </a-input>
</a-form-item> </a-form-item>
<a-form-item> <a-form-item>
<a-row justify="center" class="centered"> <a-row justify="center" class="centered">
@ -549,14 +549,14 @@
user: { user: {
username: "", username: "",
password: "", password: "",
loginSecret: "" twoFactorCode: ""
}, },
secretEnable: false, twoFactorEnable: false,
lang: "" lang: ""
}, },
async mounted() { async mounted() {
this.lang = LanguageManager.getLanguage(); this.lang = LanguageManager.getLanguage();
this.secretEnable = await this.getSecretStatus(); this.twoFactorEnable = await this.getTwoFactorEnable();
}, },
methods: { methods: {
async login() { async login() {
@ -567,12 +567,12 @@
location.href = basePath + 'panel/'; location.href = basePath + 'panel/';
} }
}, },
async getSecretStatus() { async getTwoFactorEnable() {
this.loading = true; this.loading = true;
const msg = await HttpUtil.post('/getSecretStatus'); const msg = await HttpUtil.post('/getTwoFactorEnable');
this.loading = false; this.loading = false;
if (msg.success) { if (msg.success) {
this.secretEnable = msg.obj; this.twoFactorEnable = msg.obj;
return msg.obj; return msg.obj;
} }
}, },

View file

@ -133,7 +133,6 @@
data: { data: {
themeSwitcher, themeSwitcher,
spinning: false, spinning: false,
changeSecret: false,
oldAllSetting: new AllSetting(), oldAllSetting: new AllSetting(),
allSetting: new AllSetting(), allSetting: new AllSetting(),
saveBtnDisable: true, saveBtnDisable: true,
@ -258,7 +257,6 @@
app.changeRemarkSample(); app.changeRemarkSample();
this.saveBtnDisable = true; this.saveBtnDisable = true;
} }
await this.fetchUserSecret();
}, },
async updateAllSetting() { async updateAllSetting() {
this.loading(true); this.loading(true);
@ -302,38 +300,11 @@
window.location.replace(url); window.location.replace(url);
} }
}, },
async fetchUserSecret() { toggleTwoFactor(newValue) {
this.loading(true); if (newValue) {
const userMessage = await HttpUtil.post("/panel/setting/getUserSecret", this.user); this.allSetting.twoFactorToken = RandomUtil.randomBase32String();
if (userMessage.success) {
this.user = userMessage.obj;
}
this.loading(false);
},
async updateSecret() {
this.loading(true);
const msg = await HttpUtil.post("/panel/setting/updateUserSecret", this.user);
if (msg && msg.obj) {
this.user = msg.obj;
}
this.loading(false);
await this.updateAllSetting();
},
async getNewSecret() {
if (!this.changeSecret) {
this.changeSecret = true;
this.user.loginSecret = '';
const newSecret = RandomUtil.randomSeq(64);
await PromiseUtil.sleep(1000);
this.user.loginSecret = newSecret;
this.changeSecret = false;
}
},
async toggleToken(value) {
if (value) {
await this.getNewSecret();
} else { } else {
this.user.loginSecret = ""; this.allSetting.twoFactorToken = "";
} }
}, },
addNoise() { addNoise() {

View file

@ -31,30 +31,23 @@
</a-space> </a-space>
</a-list-item> </a-list-item>
</a-collapse-panel> </a-collapse-panel>
<a-collapse-panel key="2" header='{{ i18n "pages.settings.security.secret"}}'> <a-collapse-panel key="2" header='{{ i18n "pages.settings.security.twoFactor" }}'>
<a-setting-list-item paddings="small"> <a-setting-list-item paddings="small">
<template #title>{{ i18n "pages.settings.security.loginSecurity" }}</template> <template #title>{{ i18n "pages.settings.security.twoFactorEnable" }}</template>
<template #description>{{ i18n "pages.settings.security.loginSecurityDesc" }}</template> <template #description>{{ i18n "pages.settings.security.twoFactorEnableDesc" }}</template>
<template #control> <template #control>
<a-switch @change="toggleToken(allSetting.secretEnable)" v-model="allSetting.secretEnable"></a-switch> <a-switch @change="toggleTwoFactor" v-model="allSetting.twoFactorEnable"></a-switch>
<a-icon :style="{ marginLeft: '1rem' }" v-if="allSetting.secretEnable" :spin="this.changeSecret" type="sync"
@click="getNewSecret"></a-icon>
</template> </template>
</a-setting-list-item> </a-setting-list-item>
<a-setting-list-item paddings="small"> <a-setting-list-item paddings="small" v-if="allSetting.twoFactorEnable">
<template #title>{{ i18n "pages.settings.security.secretToken" }}</template> <template #title>{{ i18n "pages.settings.security.twoFactorToken" }}</template>
<template #description>{{ i18n "pages.settings.security.secretTokenDesc" }}</template> <template #description>{{ i18n "pages.settings.security.twoFactorTokenDesc" }}</template>
<template #control> <template #control>
<a-textarea type="text" :disabled="!allSetting.secretEnable" v-model="user.loginSecret"></a-textarea> <a-input disabled="disabled" v-model="allSetting.twoFactorToken" :style="{ cursor: 'text' }">
<a-icon slot="addonAfter" type="copy" @click="ClipboardManager.copyText(allSetting.twoFactorToken)"/>
</a-input>
</template> </template>
</a-setting-list-item> </a-setting-list-item>
<a-list-item>
<a-space direction="horizontal" :style="{ padding: '0 20px' }">
<a-button type="primary" :loading="this.changeSecret" @click="updateSecret">
<span>{{ i18n "confirm"}}</span>
</a-button>
</a-space>
</a-list-item>
</a-collapse-panel> </a-collapse-panel>
</a-collapse> </a-collapse>
{{end}} {{end}}

View file

@ -48,7 +48,8 @@ var defaultValueMap = map[string]string{
"tgBotLoginNotify": "true", "tgBotLoginNotify": "true",
"tgCpu": "80", "tgCpu": "80",
"tgLang": "en-US", "tgLang": "en-US",
"secretEnable": "false", "twoFactorEnable": "false",
"twoFactorToken": "",
"subEnable": "false", "subEnable": "false",
"subTitle": "", "subTitle": "",
"subListen": "", "subListen": "",
@ -166,8 +167,7 @@ func (s *SettingService) ResetSettings() error {
return err return err
} }
return db.Model(model.User{}). return db.Model(model.User{}).
Where("1 = 1"). Where("1 = 1").Error
Update("login_secret", "").Error
} }
func (s *SettingService) getSetting(key string) (*model.Setting, error) { func (s *SettingService) getSetting(key string) (*model.Setting, error) {
@ -318,6 +318,14 @@ func (s *SettingService) GetTgLang() (string, error) {
return s.getString("tgLang") return s.getString("tgLang")
} }
func (s *SettingService) GetTwoFactorEnable() (bool, error) {
return s.getBool("twoFactorEnable")
}
func (s *SettingService) GetTwoFactorToken() (string, error) {
return s.getString("twoFactorToken")
}
func (s *SettingService) GetPort() (int, error) { func (s *SettingService) GetPort() (int, error) {
return s.getInt("webPort") return s.getInt("webPort")
} }
@ -358,14 +366,6 @@ func (s *SettingService) GetRemarkModel() (string, error) {
return s.getString("remarkModel") return s.getString("remarkModel")
} }
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) { func (s *SettingService) GetSecret() ([]byte, error) {
secret, err := s.getString("secret") secret, err := s.getString("secret")
if secret == defaultValueMap["secret"] { if secret == defaultValueMap["secret"] {

View file

@ -8,9 +8,12 @@ import (
"x-ui/logger" "x-ui/logger"
"gorm.io/gorm" "gorm.io/gorm"
"github.com/xlzd/gotp"
) )
type UserService struct{} type UserService struct {
settingService SettingService
}
func (s *UserService) GetFirstUser() (*model.User, error) { func (s *UserService) GetFirstUser() (*model.User, error) {
db := database.GetDB() db := database.GetDB()
@ -25,12 +28,12 @@ func (s *UserService) GetFirstUser() (*model.User, error) {
return user, nil return user, nil
} }
func (s *UserService) CheckUser(username string, password string, secret string) *model.User { func (s *UserService) CheckUser(username string, password string, twoFactorCode string) *model.User {
db := database.GetDB() db := database.GetDB()
user := &model.User{} user := &model.User{}
err := db.Model(model.User{}). err := db.Model(model.User{}).
Where("username = ? and password = ? and login_secret = ?", username, password, secret). Where("username = ? and password = ?", username, password).
First(user). First(user).
Error Error
if err == gorm.ErrRecordNotFound { if err == gorm.ErrRecordNotFound {
@ -39,6 +42,24 @@ func (s *UserService) CheckUser(username string, password string, secret string)
logger.Warning("check user err:", err) logger.Warning("check user err:", err)
return nil return nil
} }
twoFactorEnable, err := s.settingService.GetTwoFactorEnable();
if err != nil {
logger.Warning("check two factor err:", err)
return nil
}
if twoFactorEnable {
twoFactorToken, err := s.settingService.GetTwoFactorToken();
if err != nil {
logger.Warning("check two factor token err:", err)
return nil
}
if gotp.NewDefaultTOTP(twoFactorToken).Now() != twoFactorCode {
return nil
}
}
return user return user
} }
@ -50,50 +71,6 @@ func (s *UserService) UpdateUser(id int, username string, password string) error
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) CheckSecretExistence() (bool, error) {
db := database.GetDB()
var count int64
err := db.Model(model.User{}).
Where("login_secret IS NOT NULL").
Count(&count).
Error
if err != nil {
return false, err
}
return count > 0, nil
}
func (s *UserService) UpdateFirstUser(username string, password string) error { func (s *UserService) UpdateFirstUser(username string, password string) error {
if username == "" { if username == "" {
return errors.New("username can not be empty") return errors.New("username can not be empty")

View file

@ -51,7 +51,7 @@
"install" = "تثبيت" "install" = "تثبيت"
"clients" = "عملاء" "clients" = "عملاء"
"usage" = "استخدام" "usage" = "استخدام"
"secretToken" = "توكن سري" "twoFactorCode" = "الكود"
"remained" = "المتبقي" "remained" = "المتبقي"
"security" = "أمان" "security" = "أمان"
"secAlertTitle" = "تنبيه أمني" "secAlertTitle" = "تنبيه أمني"
@ -87,7 +87,7 @@
"invalidFormData" = "تنسيق البيانات المدخلة مش صحيح." "invalidFormData" = "تنسيق البيانات المدخلة مش صحيح."
"emptyUsername" = "اسم المستخدم مطلوب" "emptyUsername" = "اسم المستخدم مطلوب"
"emptyPassword" = "الباسورد مطلوب" "emptyPassword" = "الباسورد مطلوب"
"wrongUsernameOrPassword" = "اسم المستخدم أو الباسورد أو السر مش صحيح." "wrongUsernameOrPassword" = "اسم المستخدم أو كلمة المرور أو كود المصادقة الثنائية غير صحيح."
"successLogin" = "تسجيل دخول ناجح" "successLogin" = "تسجيل دخول ناجح"
[pages.index] [pages.index]
@ -497,11 +497,11 @@
[pages.settings.security] [pages.settings.security]
"admin" = "بيانات الأدمن" "admin" = "بيانات الأدمن"
"secret" = "توكن سري" "twoFactor" = "المصادقة الثنائية"
"loginSecurity" = "أمان تسجيل الدخول" "twoFactorEnable" = "تفعيل المصادقة الثنائية"
"loginSecurityDesc" = "بيضيف طبقة مصادقة إضافية لزيادة الأمان." "twoFactorEnableDesc" = "يضيف طبقة إضافية من المصادقة لتعزيز الأمان."
"secretToken" = "توكن سري" "twoFactorToken" = "رمز المصادقة الثنائية"
"secretTokenDesc" = "احتفظ بالتوكن ده في مكان آمن. التوكن ده مطلوب لتسجيل الدخول ومش ممكن تسترجعه لو ضاع." "twoFactorTokenDesc" = "يرجى حفظ هذا الرمز بشكل آمن في تطبيق المصادقة."
[pages.settings.toasts] [pages.settings.toasts]
"modifySettings" = "تعديل الإعدادات" "modifySettings" = "تعديل الإعدادات"

View file

@ -51,7 +51,7 @@
"install" = "Install" "install" = "Install"
"clients" = "Clients" "clients" = "Clients"
"usage" = "Usage" "usage" = "Usage"
"secretToken" = "Secret Token" "twoFactorCode" = "Code"
"remained" = "Remained" "remained" = "Remained"
"security" = "Security" "security" = "Security"
"secAlertTitle" = "Security Alert" "secAlertTitle" = "Security Alert"
@ -87,7 +87,7 @@
"invalidFormData" = "The Input data format is invalid." "invalidFormData" = "The Input data format is invalid."
"emptyUsername" = "Username is required" "emptyUsername" = "Username is required"
"emptyPassword" = "Password is required" "emptyPassword" = "Password is required"
"wrongUsernameOrPassword" = "Invalid username or password or secret." "wrongUsernameOrPassword" = "Invalid username or password or two-factor code."
"successLogin" = "Login" "successLogin" = "Login"
[pages.index] [pages.index]
@ -497,11 +497,11 @@
[pages.settings.security] [pages.settings.security]
"admin" = "Admin credentials" "admin" = "Admin credentials"
"secret" = "Secret Token" "twoFactor" = "Two-factor authentication"
"loginSecurity" = "Secure Login" "twoFactorEnable" = "Enable 2FA"
"loginSecurityDesc" = "Adds an additional layer of authentication to provide more security." "twoFactorEnableDesc" = "Adds an additional layer of authentication to provide more security."
"secretToken" = "Secret Token" "twoFactorToken" = "2FA token"
"secretTokenDesc" = "Please securely store this token in a safe place. This token is required for login and cannot be recovered." "twoFactorTokenDesc" = "Please securely store this token in a authentication app."
[pages.settings.toasts] [pages.settings.toasts]
"modifySettings" = "Modify Settings" "modifySettings" = "Modify Settings"

View file

@ -51,7 +51,7 @@
"install" = "Instalar" "install" = "Instalar"
"clients" = "Clientes" "clients" = "Clientes"
"usage" = "Uso" "usage" = "Uso"
"secretToken" = "Token Secreto" "twoFactorCode" = "Código"
"remained" = "Restante" "remained" = "Restante"
"security" = "Seguridad" "security" = "Seguridad"
"secAlertTitle" = "Alerta de Seguridad" "secAlertTitle" = "Alerta de Seguridad"
@ -87,7 +87,7 @@
"invalidFormData" = "El formato de los datos de entrada es inválido." "invalidFormData" = "El formato de los datos de entrada es inválido."
"emptyUsername" = "Por favor ingresa el nombre de usuario." "emptyUsername" = "Por favor ingresa el nombre de usuario."
"emptyPassword" = "Por favor ingresa la contraseña." "emptyPassword" = "Por favor ingresa la contraseña."
"wrongUsernameOrPassword" = "Nombre de usuario o contraseña inválidos." "wrongUsernameOrPassword" = "Nombre de usuario, contraseña o código de dos factores incorrecto."
"successLogin" = "Inicio de Sesión Exitoso" "successLogin" = "Inicio de Sesión Exitoso"
[pages.index] [pages.index]
@ -499,11 +499,11 @@
[pages.settings.security] [pages.settings.security]
"admin" = "Credenciales de administrador" "admin" = "Credenciales de administrador"
"secret" = "Token Secreto" "twoFactor" = "Autenticación de dos factores"
"loginSecurity" = "Seguridad de Inicio de Sesión" "twoFactorEnable" = "Habilitar 2FA"
"loginSecurityDesc" = "Habilitar un paso adicional de seguridad para el inicio de sesión de usuarios." "twoFactorEnableDesc" = "Añade una capa adicional de autenticación para mayor seguridad."
"secretToken" = "Token Secreto" "twoFactorToken" = "Token de 2FA"
"secretTokenDesc" = "Por favor, copia y guarda este token de forma segura en un lugar seguro. Este token es necesario para iniciar sesión y no se puede recuperar con la herramienta de comando x-ui." "twoFactorTokenDesc" = "Guarde este token de forma segura en una aplicación de autenticación."
[pages.settings.toasts] [pages.settings.toasts]
"modifySettings" = "Modificar Configuraciones " "modifySettings" = "Modificar Configuraciones "

View file

@ -51,7 +51,7 @@
"install" = "نصب" "install" = "نصب"
"clients" = "کاربران" "clients" = "کاربران"
"usage" = "استفاده" "usage" = "استفاده"
"secretToken" = "توکن امنیتی" "twoFactorCode" = "کد"
"remained" = "باقی‌مانده" "remained" = "باقی‌مانده"
"security" = "امنیت" "security" = "امنیت"
"secAlertTitle" = "هشدار‌امنیتی" "secAlertTitle" = "هشدار‌امنیتی"
@ -87,7 +87,7 @@
"invalidFormData" = "اطلاعات به‌درستی وارد نشده‌است" "invalidFormData" = "اطلاعات به‌درستی وارد نشده‌است"
"emptyUsername" = "لطفا یک نام‌کاربری وارد کنید‌" "emptyUsername" = "لطفا یک نام‌کاربری وارد کنید‌"
"emptyPassword" = "لطفا یک رمزعبور وارد کنید" "emptyPassword" = "لطفا یک رمزعبور وارد کنید"
"wrongUsernameOrPassword" = "نام‌کاربری یا رمزعبور‌اشتباه‌است" "wrongUsernameOrPassword" = "نام کاربری، رمز عبور یا کد دو مرحله‌ای نامعتبر است."
"successLogin" = "ورود" "successLogin" = "ورود"
[pages.index] [pages.index]
@ -499,11 +499,11 @@
[pages.settings.security] [pages.settings.security]
"admin" = "اعتبارنامه‌های ادمین" "admin" = "اعتبارنامه‌های ادمین"
"secret" = "توکن مخفی" "twoFactor" = "احراز هویت دو مرحله‌ای"
"loginSecurity" = "ورود ایمن" "twoFactorEnable" = "فعال‌سازی 2FA"
"loginSecurityDesc" = "یک لایه اضافی از احراز هویت برای ایجاد امنیت بیشتر اضافه می کند" "twoFactorEnableDesc" = "یک لایه اضافی امنیتی برای احراز هویت فراهم می‌کند."
"secretToken" = "توکن مخفی" "twoFactorToken" = "توکن 2FA"
"secretTokenDesc" = "لطفاً این توکن را در مکانی امن ذخیره کنید. این توکن برای ورود به سیستم مورد نیاز است و قابل بازیابی نیست" "twoFactorTokenDesc" = "لطفاً این توکن را در یک برنامه احراز هویت ذخیره کنید."
[pages.settings.toasts] [pages.settings.toasts]
"modifySettings" = "ویرایش تنظیمات" "modifySettings" = "ویرایش تنظیمات"

View file

@ -51,7 +51,7 @@
"install" = "Instal" "install" = "Instal"
"clients" = "Klien" "clients" = "Klien"
"usage" = "Penggunaan" "usage" = "Penggunaan"
"secretToken" = "Token Rahasia" "twoFactorCode" = "Kode"
"remained" = "Tersisa" "remained" = "Tersisa"
"security" = "Keamanan" "security" = "Keamanan"
"secAlertTitle" = "Peringatan keamanan" "secAlertTitle" = "Peringatan keamanan"
@ -87,7 +87,7 @@
"invalidFormData" = "Format data input tidak valid." "invalidFormData" = "Format data input tidak valid."
"emptyUsername" = "Nama Pengguna diperlukan" "emptyUsername" = "Nama Pengguna diperlukan"
"emptyPassword" = "Kata Sandi diperlukan" "emptyPassword" = "Kata Sandi diperlukan"
"wrongUsernameOrPassword" = "Nama pengguna atau kata sandi tidak valid." "wrongUsernameOrPassword" = "Username, kata sandi, atau kode dua faktor tidak valid."
"successLogin" = "Login berhasil" "successLogin" = "Login berhasil"
[pages.index] [pages.index]
@ -499,11 +499,11 @@
[pages.settings.security] [pages.settings.security]
"admin" = "Kredensial admin" "admin" = "Kredensial admin"
"secret" = "Token Rahasia" "twoFactor" = "Autentikasi dua faktor"
"loginSecurity" = "Login Aman" "twoFactorEnable" = "Aktifkan 2FA"
"loginSecurityDesc" = "Menambahkan lapisan otentikasi tambahan untuk memberikan keamanan lebih." "twoFactorEnableDesc" = "Menambahkan lapisan autentikasi tambahan untuk keamanan lebih."
"secretToken" = "Token Rahasia" "twoFactorToken" = "Token 2FA"
"secretTokenDesc" = "Simpan token ini dengan aman di tempat yang aman. Token ini diperlukan untuk login dan tidak dapat dipulihkan." "twoFactorTokenDesc" = "Harap simpan token ini dengan aman di aplikasi autentikasi."
[pages.settings.toasts] [pages.settings.toasts]
"modifySettings" = "Ubah Pengaturan" "modifySettings" = "Ubah Pengaturan"

View file

@ -51,7 +51,7 @@
"install" = "インストール" "install" = "インストール"
"clients" = "クライアント" "clients" = "クライアント"
"usage" = "利用状況" "usage" = "利用状況"
"secretToken" = "シークレットトークン" "twoFactorCode" = "コード"
"remained" = "残り" "remained" = "残り"
"security" = "セキュリティ" "security" = "セキュリティ"
"secAlertTitle" = "セキュリティアラート" "secAlertTitle" = "セキュリティアラート"
@ -87,7 +87,7 @@
"invalidFormData" = "データ形式エラー" "invalidFormData" = "データ形式エラー"
"emptyUsername" = "ユーザー名を入力してください" "emptyUsername" = "ユーザー名を入力してください"
"emptyPassword" = "パスワードを入力してください" "emptyPassword" = "パスワードを入力してください"
"wrongUsernameOrPassword" = "ユーザー名またはパスワードが間違っています" "wrongUsernameOrPassword" = "ユーザー名、パスワード、または二段階認証コードが無効です。"
"successLogin" = "ログイン成功" "successLogin" = "ログイン成功"
[pages.index] [pages.index]
@ -499,11 +499,11 @@
[pages.settings.security] [pages.settings.security]
"admin" = "管理者の資格情報" "admin" = "管理者の資格情報"
"secret" = "セキュリティトークン" "twoFactor" = "二段階認証"
"loginSecurity" = "ログインセキュリティ" "twoFactorEnable" = "2FAを有効化"
"loginSecurityDesc" = "追加の認証を追加してセキュリティを向上させる" "twoFactorEnableDesc" = "セキュリティを強化するために追加の認証層を追加します。"
"secretToken" = "セキュリティトークン" "twoFactorToken" = "2FAトークン"
"secretTokenDesc" = "このトークンを安全な場所に保管してください。このトークンはログインに使用され、紛失すると回復できません。" "twoFactorTokenDesc" = "このトークンを認証アプリに安全に保存してください。"
[pages.settings.toasts] [pages.settings.toasts]
"modifySettings" = "設定を変更" "modifySettings" = "設定を変更"

View file

@ -51,7 +51,7 @@
"install" = "Instalar" "install" = "Instalar"
"clients" = "Clientes" "clients" = "Clientes"
"usage" = "Uso" "usage" = "Uso"
"secretToken" = "Token Secreto" "twoFactorCode" = "Código"
"remained" = "Restante" "remained" = "Restante"
"security" = "Segurança" "security" = "Segurança"
"secAlertTitle" = "Alerta de Segurança" "secAlertTitle" = "Alerta de Segurança"
@ -87,7 +87,7 @@
"invalidFormData" = "O formato dos dados de entrada é inválido." "invalidFormData" = "O formato dos dados de entrada é inválido."
"emptyUsername" = "Nome de usuário é obrigatório" "emptyUsername" = "Nome de usuário é obrigatório"
"emptyPassword" = "Senha é obrigatória" "emptyPassword" = "Senha é obrigatória"
"wrongUsernameOrPassword" = "Nome de usuário, senha ou segredo inválidos." "wrongUsernameOrPassword" = "Nome de usuário, senha ou código de dois fatores inválido."
"successLogin" = "Login realizado com sucesso" "successLogin" = "Login realizado com sucesso"
[pages.index] [pages.index]
@ -499,11 +499,11 @@
[pages.settings.security] [pages.settings.security]
"admin" = "Credenciais de administrador" "admin" = "Credenciais de administrador"
"secret" = "Token Secreto" "twoFactor" = "Autenticação de dois fatores"
"loginSecurity" = "Login Seguro" "twoFactorEnable" = "Ativar 2FA"
"loginSecurityDesc" = "Adiciona uma camada extra de autenticação para fornecer mais segurança." "twoFactorEnableDesc" = "Adiciona uma camada extra de autenticação para mais segurança."
"secretToken" = "Token Secreto" "twoFactorToken" = "Token 2FA"
"secretTokenDesc" = "Por favor, armazene este token em um local seguro. Este token é necessário para o login e não pode ser recuperado." "twoFactorTokenDesc" = "Guarde este token com segurança em um aplicativo de autenticação."
[pages.settings.toasts] [pages.settings.toasts]
"modifySettings" = "Modificar Configurações" "modifySettings" = "Modificar Configurações"

View file

@ -51,7 +51,7 @@
"install" = "Установка" "install" = "Установка"
"clients" = "Клиенты" "clients" = "Клиенты"
"usage" = "Использование" "usage" = "Использование"
"secretToken" = "Секретный токен" "twoFactorCode" = "Код"
"remained" = "Остаток" "remained" = "Остаток"
"security" = "Безопасность" "security" = "Безопасность"
"secAlertTitle" = "Предупреждение системы безопасности" "secAlertTitle" = "Предупреждение системы безопасности"
@ -87,7 +87,7 @@
"invalidFormData" = "Недопустимый формат данных" "invalidFormData" = "Недопустимый формат данных"
"emptyUsername" = "Введите имя пользователя" "emptyUsername" = "Введите имя пользователя"
"emptyPassword" = "Введите пароль" "emptyPassword" = "Введите пароль"
"wrongUsernameOrPassword" = "Неверное имя пользователя, пароль или секретный токен." "wrongUsernameOrPassword" = "Неверное имя пользователя, пароль или код двухфакторной аутентификации."
"successLogin" = "Успешный вход" "successLogin" = "Успешный вход"
[pages.index] [pages.index]
@ -499,11 +499,11 @@
[pages.settings.security] [pages.settings.security]
"admin" = "Учетные данные администратора" "admin" = "Учетные данные администратора"
"secret" = "Секретный токен" "twoFactor" = "Двухфакторная аутентификация"
"loginSecurity" = "Безопасность входа" "twoFactorEnable" = "Включить 2FA"
"loginSecurityDesc" = "Включить дополнительные меры безопасности входа пользователя" "twoFactorEnableDesc" = "Добавляет дополнительный уровень аутентификации для повышения безопасности."
"secretToken" = "Секретный токен" "twoFactorToken" = "Токен 2FA"
"secretTokenDesc" = "Пожалуйста, скопируйте и сохраните этот токен в безопасном месте. Этот токен необходим для входа в систему и не может быть восстановлен с помощью инструмента x-ui" "twoFactorTokenDesc" = "Пожалуйста, сохраните этот токен в приложении для аутентификации."
[pages.settings.toasts] [pages.settings.toasts]
"modifySettings" = "Настройки изменены" "modifySettings" = "Настройки изменены"

View file

@ -51,7 +51,7 @@
"install" = "Yükle" "install" = "Yükle"
"clients" = "Müşteriler" "clients" = "Müşteriler"
"usage" = "Kullanım" "usage" = "Kullanım"
"secretToken" = "Gizli Anahtar" "twoFactorCode" = "Kod"
"remained" = "Kalan" "remained" = "Kalan"
"security" = "Güvenlik" "security" = "Güvenlik"
"secAlertTitle" = "Güvenlik Uyarısı" "secAlertTitle" = "Güvenlik Uyarısı"
@ -87,7 +87,7 @@
"invalidFormData" = "Girdi verisi formatı geçersiz." "invalidFormData" = "Girdi verisi formatı geçersiz."
"emptyUsername" = "Kullanıcı adı gerekli" "emptyUsername" = "Kullanıcı adı gerekli"
"emptyPassword" = "Şifre gerekli" "emptyPassword" = "Şifre gerekli"
"wrongUsernameOrPassword" = "Geçersiz kullanıcı adı veya şifre veya gizli anahtar." "wrongUsernameOrPassword" = "Geçersiz kullanıcı adı, şifre veya iki adımlı doğrulama kodu."
"successLogin" = "Giriş Başarılı" "successLogin" = "Giriş Başarılı"
[pages.index] [pages.index]
@ -499,11 +499,11 @@
[pages.settings.security] [pages.settings.security]
"admin" = "Yönetici kimlik bilgileri" "admin" = "Yönetici kimlik bilgileri"
"secret" = "Gizli Anahtar" "twoFactor" = "İki adımlı doğrulama"
"loginSecurity" = "Güvenli Giriş" "twoFactorEnable" = "2FA'yı Etkinleştir"
"loginSecurityDesc" = "Daha fazla güvenlik sağlamak için ek bir kimlik doğrulama katmanı ekler." "twoFactorEnableDesc" = "Daha fazla güvenlik için ek bir doğrulama katmanı ekler."
"secretToken" = "Gizli Anahtar" "twoFactorToken" = "2FA Token"
"secretTokenDesc" = "Bu anahtarı güvenli bir yerde saklayın. Bu anahtar giriş için gereklidir ve geri alınamaz." "twoFactorTokenDesc" = "Lütfen bu token'ı bir doğrulama uygulamasında güvenle saklayın."
[pages.settings.toasts] [pages.settings.toasts]
"modifySettings" = "Ayarları Değiştir" "modifySettings" = "Ayarları Değiştir"

View file

@ -51,7 +51,7 @@
"install" = "Встановити" "install" = "Встановити"
"clients" = "Клієнти" "clients" = "Клієнти"
"usage" = "Використання" "usage" = "Використання"
"secretToken" = "Секретний маркер" "twoFactorCode" = "Код"
"remained" = "Залишилося" "remained" = "Залишилося"
"security" = "Беспека" "security" = "Беспека"
"secAlertTitle" = "Попередження системи безпеки" "secAlertTitle" = "Попередження системи безпеки"
@ -87,7 +87,7 @@
"invalidFormData" = "Формат вхідних даних недійсний." "invalidFormData" = "Формат вхідних даних недійсний."
"emptyUsername" = "Потрібне ім'я користувача" "emptyUsername" = "Потрібне ім'я користувача"
"emptyPassword" = "Потрібен пароль" "emptyPassword" = "Потрібен пароль"
"wrongUsernameOrPassword" = "Невірне ім'я користувача або пароль." "wrongUsernameOrPassword" = "Невірне ім’я користувача, пароль або код двофакторної аутентифікації."
"successLogin" = "Вхід" "successLogin" = "Вхід"
[pages.index] [pages.index]
@ -499,11 +499,11 @@
[pages.settings.security] [pages.settings.security]
"admin" = "Облікові дані адміністратора" "admin" = "Облікові дані адміністратора"
"secret" = "Секретний маркер" "twoFactor" = "Двофакторна аутентифікація"
"loginSecurity" = "Безпечний вхід" "twoFactorEnable" = "Увімкнути 2FA"
"loginSecurityDesc" = "Додає додатковий рівень автентифікації для забезпечення більшої безпеки." "twoFactorEnableDesc" = "Додає додатковий рівень аутентифікації для підвищення безпеки."
"secretToken" = "Секретний маркер" "twoFactorToken" = "Токен 2FA"
"secretTokenDesc" = "Будь ласка, надійно зберігайте цей маркер у безпечному місці. Цей маркер потрібен для входу, і його неможливо відновити." "twoFactorTokenDesc" = "Будь ласка, збережіть цей токен у захищеному додатку для аутентифікації."
[pages.settings.toasts] [pages.settings.toasts]
"modifySettings" = "Змінити налаштування" "modifySettings" = "Змінити налаштування"

View file

@ -51,7 +51,7 @@
"install" = "Cài đặt" "install" = "Cài đặt"
"clients" = "Các khách hàng" "clients" = "Các khách hàng"
"usage" = "Sử dụng" "usage" = "Sử dụng"
"secretToken" = "Mã bí mật" "twoFactorCode" = "Mã"
"remained" = "Còn lại" "remained" = "Còn lại"
"security" = "Bảo vệ" "security" = "Bảo vệ"
"secAlertTitle" = "Cảnh báo an ninh-Tiếng Việt by Ohoang7" "secAlertTitle" = "Cảnh báo an ninh-Tiếng Việt by Ohoang7"
@ -87,7 +87,7 @@
"invalidFormData" = "Dạng dữ liệu nhập không hợp lệ." "invalidFormData" = "Dạng dữ liệu nhập không hợp lệ."
"emptyUsername" = "Vui lòng nhập tên người dùng." "emptyUsername" = "Vui lòng nhập tên người dùng."
"emptyPassword" = "Vui lòng nhập mật khẩu." "emptyPassword" = "Vui lòng nhập mật khẩu."
"wrongUsernameOrPassword" = "Tên người dùng hoặc mật khẩu không đúng." "wrongUsernameOrPassword" = "Tên người dùng, mật khẩu hoặc mã xác thực hai yếu tố không hợp lệ."
"successLogin" = "Đăng nhập thành công." "successLogin" = "Đăng nhập thành công."
[pages.index] [pages.index]
@ -499,11 +499,11 @@
[pages.settings.security] [pages.settings.security]
"admin" = "Thông tin đăng nhập quản trị viên" "admin" = "Thông tin đăng nhập quản trị viên"
"secret" = "Mã thông báo bí mật" "twoFactor" = "Xác thực hai yếu tố"
"loginSecurity" = "Bảo mật đăng nhập" "twoFactorEnable" = "Bật 2FA"
"loginSecurityDesc" = "Bật bước bảo mật đăng nhập bổ sung cho người dùng" "twoFactorEnableDesc" = "Thêm một lớp bảo mật bổ sung để tăng cường an toàn."
"secretToken" = "Mã bí mật" "twoFactorToken" = "Token 2FA"
"secretTokenDesc" = "Vui lòng sao chép và lưu trữ mã này một cách an toàn ở nơi an toàn. Mã này cần thiết để đăng nhập và không thể phục hồi từ công cụ lệnh x-ui." "twoFactorTokenDesc" = "Vui lòng lưu token này vào ứng dụng xác thực một cách an toàn."
[pages.settings.toasts] [pages.settings.toasts]
"modifySettings" = "Chỉnh sửa cài đặt " "modifySettings" = "Chỉnh sửa cài đặt "

View file

@ -51,7 +51,7 @@
"install" = "安装" "install" = "安装"
"clients" = "客户端" "clients" = "客户端"
"usage" = "使用情况" "usage" = "使用情况"
"secretToken" = "安全密钥" "twoFactorCode" = "代码"
"remained" = "剩余" "remained" = "剩余"
"security" = "安全" "security" = "安全"
"secAlertTitle" = "安全警报" "secAlertTitle" = "安全警报"
@ -87,7 +87,7 @@
"invalidFormData" = "数据格式错误" "invalidFormData" = "数据格式错误"
"emptyUsername" = "请输入用户名" "emptyUsername" = "请输入用户名"
"emptyPassword" = "请输入密码" "emptyPassword" = "请输入密码"
"wrongUsernameOrPassword" = "用户名或密码错误" "wrongUsernameOrPassword" = "用户名、密码或双重验证码无效。"
"successLogin" = "登录" "successLogin" = "登录"
[pages.index] [pages.index]
@ -499,11 +499,11 @@
[pages.settings.security] [pages.settings.security]
"admin" = "管理员凭据" "admin" = "管理员凭据"
"secret" = "安全令牌" "twoFactor" = "双重验证"
"loginSecurity" = "登录安全" "twoFactorEnable" = "启用2FA"
"loginSecurityDesc" = "添加额外的身份验证以提高安全性" "twoFactorEnableDesc" = "增加额外的验证层以提高安全性。"
"secretToken" = "安全令牌" "twoFactorToken" = "2FA令牌"
"secretTokenDesc" = "请将此令牌存储在安全的地方。此令牌用于登录,丢失无法恢复。" "twoFactorTokenDesc" = "请将此令牌安全地保存在验证应用中。"
[pages.settings.toasts] [pages.settings.toasts]
"modifySettings" = "修改设置" "modifySettings" = "修改设置"

View file

@ -51,7 +51,7 @@
"install" = "安裝" "install" = "安裝"
"clients" = "客戶端" "clients" = "客戶端"
"usage" = "使用情況" "usage" = "使用情況"
"secretToken" = "安全金鑰" "twoFactorCode" = "代碼"
"remained" = "剩餘" "remained" = "剩餘"
"security" = "安全" "security" = "安全"
"secAlertTitle" = "安全警報" "secAlertTitle" = "安全警報"
@ -87,7 +87,7 @@
"invalidFormData" = "資料格式錯誤" "invalidFormData" = "資料格式錯誤"
"emptyUsername" = "請輸入使用者名稱" "emptyUsername" = "請輸入使用者名稱"
"emptyPassword" = "請輸入密碼" "emptyPassword" = "請輸入密碼"
"wrongUsernameOrPassword" = "使用者名稱或密碼錯誤" "wrongUsernameOrPassword" = "用戶名、密碼或雙重驗證碼無效。"
"successLogin" = "登入" "successLogin" = "登入"
[pages.index] [pages.index]
@ -499,11 +499,11 @@
[pages.settings.security] [pages.settings.security]
"admin" = "管理員憑證" "admin" = "管理員憑證"
"secret" = "安全令牌" "twoFactor" = "雙重驗證"
"loginSecurity" = "登入安全" "twoFactorEnable" = "啟用2FA"
"loginSecurityDesc" = "新增額外的身份驗證以提高安全性" "twoFactorEnableDesc" = "增加額外的驗證層以提高安全性。"
"secretToken" = "安全令牌" "twoFactorToken" = "2FA令牌"
"secretTokenDesc" = "請將此令牌儲存在安全的地方。此令牌用於登入,丟失無法恢復。" "twoFactorTokenDesc" = "請將此令牌安全地保存在驗證應用中。"
[pages.settings.toasts] [pages.settings.toasts]
"modifySettings" = "修改設定" "modifySettings" = "修改設定"

View file

@ -184,10 +184,8 @@ reset_user() {
read -rp "Please set the login password [default is a random password]: " config_password 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) [[ -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 -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 username has been reset to: ${green} ${config_account} ${plain}"
echo -e "Panel login password has been reset to: ${green} ${config_password} ${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}" echo -e "${green} Please use the new login username and password to access the X-UI panel. Also remember them! ${plain}"
confirm_restart confirm_restart
} }
@ -1731,7 +1729,7 @@ show_menu() {
${green}4.${plain} Legacy Version │ ${green}4.${plain} Legacy Version │
${green}5.${plain} Uninstall │ ${green}5.${plain} Uninstall │
│────────────────────────────────────────────────│ │────────────────────────────────────────────────│
${green}6.${plain} Reset Username & Password & Secret Token ${green}6.${plain} Reset Username & Password
${green}7.${plain} Reset Web Base Path │ ${green}7.${plain} Reset Web Base Path │
${green}8.${plain} Reset Settings │ ${green}8.${plain} Reset Settings │
${green}9.${plain} Change Port │ ${green}9.${plain} Change Port │