Browse Source

Merge pull request #332 from hamid-gh98/main

[Update] sub remark + settings UI
Ho3ein 1 year ago
parent
commit
e164c7e780

+ 5 - 1
web/assets/css/custom.css

@@ -180,7 +180,7 @@
 
 .ant-card-dark:hover {
     border-color: #e8e8e8;
-    box-shadow: 0 2px 8px rgba(255,255,255,.15);
+    /* box-shadow: 0 2px 8px rgba(255,255,255,.15); */
 }
 
 .ant-card-dark .ant-table-thead th {
@@ -236,6 +236,10 @@
     background-color: #1a212a; 
 }
 
+.ant-input-number {
+    min-width: 100px;
+}
+
 .ant-card-dark .ant-input,
 .ant-card-dark .ant-input-number,
 .ant-card-dark .ant-input-number-handler-wrap,

+ 12 - 12
web/controller/inbound.go

@@ -93,7 +93,7 @@ func (a *InboundController) addInbound(c *gin.Context) {
 	inbound := &model.Inbound{}
 	err := c.ShouldBind(inbound)
 	if err != nil {
-		jsonMsg(c, I18n(c, "pages.inbounds.addTo"), err)
+		jsonMsg(c, I18n(c, "pages.inbounds.create"), err)
 		return
 	}
 	user := session.GetLoginUser(c)
@@ -101,7 +101,7 @@ func (a *InboundController) addInbound(c *gin.Context) {
 	inbound.Enable = true
 	inbound.Tag = fmt.Sprintf("inbound-%v", inbound.Port)
 	inbound, err = a.inboundService.AddInbound(inbound)
-	jsonMsgObj(c, I18n(c, "pages.inbounds.addTo"), inbound, err)
+	jsonMsgObj(c, I18n(c, "pages.inbounds.create"), inbound, err)
 	if err == nil {
 		a.xrayService.SetToNeedRestart()
 	}
@@ -123,7 +123,7 @@ func (a *InboundController) delInbound(c *gin.Context) {
 func (a *InboundController) updateInbound(c *gin.Context) {
 	id, err := strconv.Atoi(c.Param("id"))
 	if err != nil {
-		jsonMsg(c, I18n(c, "pages.inbounds.revise"), err)
+		jsonMsg(c, I18n(c, "pages.inbounds.update"), err)
 		return
 	}
 	inbound := &model.Inbound{
@@ -131,11 +131,11 @@ func (a *InboundController) updateInbound(c *gin.Context) {
 	}
 	err = c.ShouldBind(inbound)
 	if err != nil {
-		jsonMsg(c, I18n(c, "pages.inbounds.revise"), err)
+		jsonMsg(c, I18n(c, "pages.inbounds.update"), err)
 		return
 	}
 	inbound, err = a.inboundService.UpdateInbound(inbound)
-	jsonMsgObj(c, I18n(c, "pages.inbounds.revise"), inbound, err)
+	jsonMsgObj(c, I18n(c, "pages.inbounds.update"), inbound, err)
 	if err == nil {
 		a.xrayService.SetToNeedRestart()
 	}
@@ -156,7 +156,7 @@ func (a *InboundController) clearClientIps(c *gin.Context) {
 
 	err := a.inboundService.ClearClientIps(email)
 	if err != nil {
-		jsonMsg(c, "Revise", err)
+		jsonMsg(c, "Update", err)
 		return
 	}
 	jsonMsg(c, "Log Cleared", nil)
@@ -165,7 +165,7 @@ func (a *InboundController) addInboundClient(c *gin.Context) {
 	data := &model.Inbound{}
 	err := c.ShouldBind(data)
 	if err != nil {
-		jsonMsg(c, I18n(c, "pages.inbounds.revise"), err)
+		jsonMsg(c, I18n(c, "pages.inbounds.update"), err)
 		return
 	}
 
@@ -183,7 +183,7 @@ func (a *InboundController) addInboundClient(c *gin.Context) {
 func (a *InboundController) delInboundClient(c *gin.Context) {
 	id, err := strconv.Atoi(c.Param("id"))
 	if err != nil {
-		jsonMsg(c, I18n(c, "pages.inbounds.revise"), err)
+		jsonMsg(c, I18n(c, "pages.inbounds.update"), err)
 		return
 	}
 	clientId := c.Param("clientId")
@@ -205,7 +205,7 @@ func (a *InboundController) updateInboundClient(c *gin.Context) {
 	inbound := &model.Inbound{}
 	err := c.ShouldBind(inbound)
 	if err != nil {
-		jsonMsg(c, I18n(c, "pages.inbounds.revise"), err)
+		jsonMsg(c, I18n(c, "pages.inbounds.update"), err)
 		return
 	}
 
@@ -223,7 +223,7 @@ func (a *InboundController) updateInboundClient(c *gin.Context) {
 func (a *InboundController) resetClientTraffic(c *gin.Context) {
 	id, err := strconv.Atoi(c.Param("id"))
 	if err != nil {
-		jsonMsg(c, I18n(c, "pages.inbounds.revise"), err)
+		jsonMsg(c, I18n(c, "pages.inbounds.update"), err)
 		return
 	}
 	email := c.Param("email")
@@ -251,7 +251,7 @@ func (a *InboundController) resetAllTraffics(c *gin.Context) {
 func (a *InboundController) resetAllClientTraffics(c *gin.Context) {
 	id, err := strconv.Atoi(c.Param("id"))
 	if err != nil {
-		jsonMsg(c, I18n(c, "pages.inbounds.revise"), err)
+		jsonMsg(c, I18n(c, "pages.inbounds.update"), err)
 		return
 	}
 
@@ -266,7 +266,7 @@ func (a *InboundController) resetAllClientTraffics(c *gin.Context) {
 func (a *InboundController) delDepletedClients(c *gin.Context) {
 	id, err := strconv.Atoi(c.Param("id"))
 	if err != nil {
-		jsonMsg(c, I18n(c, "pages.inbounds.revise"), err)
+		jsonMsg(c, I18n(c, "pages.inbounds.update"), err)
 		return
 	}
 	err = a.inboundService.DelDepletedClients(id)

+ 15 - 15
web/controller/setting.go

@@ -49,7 +49,7 @@ func (a *SettingController) initRouter(g *gin.RouterGroup) {
 func (a *SettingController) getAllSetting(c *gin.Context) {
 	allSetting, err := a.settingService.GetAllSetting()
 	if err != nil {
-		jsonMsg(c, I18n(c, "pages.setting.toasts.getSetting"), err)
+		jsonMsg(c, I18n(c, "pages.settings.toasts.getSettings"), err)
 		return
 	}
 	jsonObj(c, allSetting, nil)
@@ -58,7 +58,7 @@ func (a *SettingController) getAllSetting(c *gin.Context) {
 func (a *SettingController) getDefaultJsonConfig(c *gin.Context) {
 	defaultJsonConfig, err := a.settingService.GetDefaultJsonConfig()
 	if err != nil {
-		jsonMsg(c, I18n(c, "pages.setting.toasts.getSetting"), err)
+		jsonMsg(c, I18n(c, "pages.settings.toasts.getSettings"), err)
 		return
 	}
 	jsonObj(c, defaultJsonConfig, nil)
@@ -67,22 +67,22 @@ func (a *SettingController) getDefaultJsonConfig(c *gin.Context) {
 func (a *SettingController) getDefaultSettings(c *gin.Context) {
 	expireDiff, err := a.settingService.GetExpireDiff()
 	if err != nil {
-		jsonMsg(c, I18n(c, "pages.setting.toasts.getSetting"), err)
+		jsonMsg(c, I18n(c, "pages.settings.toasts.getSettings"), err)
 		return
 	}
 	trafficDiff, err := a.settingService.GetTrafficDiff()
 	if err != nil {
-		jsonMsg(c, I18n(c, "pages.setting.toasts.getSetting"), err)
+		jsonMsg(c, I18n(c, "pages.settings.toasts.getSettings"), err)
 		return
 	}
 	defaultCert, err := a.settingService.GetCertFile()
 	if err != nil {
-		jsonMsg(c, I18n(c, "pages.setting.toasts.getSetting"), err)
+		jsonMsg(c, I18n(c, "pages.settings.toasts.getSettings"), err)
 		return
 	}
 	defaultKey, err := a.settingService.GetKeyFile()
 	if err != nil {
-		jsonMsg(c, I18n(c, "pages.setting.toasts.getSetting"), err)
+		jsonMsg(c, I18n(c, "pages.settings.toasts.getSettings"), err)
 		return
 	}
 	result := map[string]interface{}{
@@ -98,27 +98,27 @@ func (a *SettingController) updateSetting(c *gin.Context) {
 	allSetting := &entity.AllSetting{}
 	err := c.ShouldBind(allSetting)
 	if err != nil {
-		jsonMsg(c, I18n(c, "pages.setting.toasts.modifySetting"), err)
+		jsonMsg(c, I18n(c, "pages.settings.toasts.modifySettings"), err)
 		return
 	}
 	err = a.settingService.UpdateAllSetting(allSetting)
-	jsonMsg(c, I18n(c, "pages.setting.toasts.modifySetting"), err)
+	jsonMsg(c, I18n(c, "pages.settings.toasts.modifySettings"), err)
 }
 
 func (a *SettingController) updateUser(c *gin.Context) {
 	form := &updateUserForm{}
 	err := c.ShouldBind(form)
 	if err != nil {
-		jsonMsg(c, I18n(c, "pages.setting.toasts.modifySetting"), err)
+		jsonMsg(c, I18n(c, "pages.settings.toasts.modifySettings"), err)
 		return
 	}
 	user := session.GetLoginUser(c)
 	if user.Username != form.OldUsername || user.Password != form.OldPassword {
-		jsonMsg(c, I18n(c, "pages.setting.toasts.modifyUser"), errors.New(I18n(c, "pages.setting.toasts.originalUserPassIncorrect")))
+		jsonMsg(c, I18n(c, "pages.settings.toasts.modifyUser"), errors.New(I18n(c, "pages.settings.toasts.originalUserPassIncorrect")))
 		return
 	}
 	if form.NewUsername == "" || form.NewPassword == "" {
-		jsonMsg(c, I18n(c, "pages.setting.toasts.modifyUser"), errors.New(I18n(c, "pages.setting.toasts.userPassMustBeNotEmpty")))
+		jsonMsg(c, I18n(c, "pages.settings.toasts.modifyUser"), errors.New(I18n(c, "pages.settings.toasts.userPassMustBeNotEmpty")))
 		return
 	}
 	err = a.userService.UpdateUser(user.Id, form.NewUsername, form.NewPassword)
@@ -127,19 +127,19 @@ func (a *SettingController) updateUser(c *gin.Context) {
 		user.Password = form.NewPassword
 		session.SetLoginUser(c, user)
 	}
-	jsonMsg(c, I18n(c, "pages.setting.toasts.modifyUser"), err)
+	jsonMsg(c, I18n(c, "pages.settings.toasts.modifyUser"), err)
 }
 
 func (a *SettingController) restartPanel(c *gin.Context) {
 	err := a.panelService.RestartPanel(time.Second * 3)
-	jsonMsg(c, I18n(c, "pages.setting.restartPanel"), err)
+	jsonMsg(c, I18n(c, "pages.settings.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)
+		jsonMsg(c, I18n(c, "pages.settings.toasts.modifySettings"), err)
 	}
 	user := session.GetLoginUser(c)
 	err = a.userService.UpdateUserSecret(user.Id, form.LoginSecret)
@@ -147,7 +147,7 @@ func (a *SettingController) updateSecret(c *gin.Context) {
 		user.LoginSecret = form.LoginSecret
 		session.SetLoginUser(c, user)
 	}
-	jsonMsg(c, I18n(c, "pages.setting.toasts.modifyUser"), err)
+	jsonMsg(c, I18n(c, "pages.settings.toasts.modifyUser"), err)
 }
 func (a *SettingController) getUserSecret(c *gin.Context) {
 	loginUser := session.GetLoginUser(c)

+ 3 - 3
web/controller/xui.go

@@ -23,7 +23,7 @@ func (a *XUIController) initRouter(g *gin.RouterGroup) {
 
 	g.GET("/", a.index)
 	g.GET("/inbounds", a.inbounds)
-	g.GET("/setting", a.setting)
+	g.GET("/settings", a.settings)
 
 	a.inboundController = NewInboundController(g)
 	a.settingController = NewSettingController(g)
@@ -37,6 +37,6 @@ func (a *XUIController) inbounds(c *gin.Context) {
 	html(c, "inbounds.html", "pages.inbounds.title", nil)
 }
 
-func (a *XUIController) setting(c *gin.Context) {
-	html(c, "setting.html", "pages.setting.title", nil)
+func (a *XUIController) settings(c *gin.Context) {
+	html(c, "settings.html", "pages.settings.title", nil)
 }

+ 3 - 3
web/html/xui/client_bulk_modal.html

@@ -43,7 +43,7 @@
                     <a-icon type="question-circle" theme="filled"></a-icon>
                 </a-tooltip>
             </span>
-            <a-input type="number" v-model.number="clientsBulkModal.limitIp" min="0" style="width: 70px;" ></a-input>
+            <a-input-number v-model="clientsBulkModal.limitIp" min="0"></a-input-number>
         </a-form-item>
         <a-form-item v-if="clientsBulkModal.inbound.xtls" label="Flow">
             <a-select v-model="clientsBulkModal.flow" style="width: 200px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
@@ -65,7 +65,7 @@
         </a-form-item>
         <a-form-item>
             <span slot="label">
-                <span >{{ i18n "pages.inbounds.totalFlow" }}</span>(GB)
+                <span >{{ i18n "pages.inbounds.totalFlow" }}</span> (GB)
                 <a-tooltip>
                     <template slot="title">
                         0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
@@ -79,7 +79,7 @@
             <a-switch v-model="clientsBulkModal.delayedStart" @click="clientsBulkModal.expiryTime=0"></a-switch>
         </a-form-item>
         <a-form-item label='{{ i18n "pages.client.expireDays" }}' v-if="clientsBulkModal.delayedStart">
-            <a-input type="number" v-model.number="delayedExpireDays" :min="0"></a-input>
+            <a-input-number v-model="delayedExpireDays" :min="0"></a-input-number>
         </a-form-item>
         <a-form-item v-else>
             <span slot="label">

+ 2 - 2
web/html/xui/common_sider.html

@@ -7,9 +7,9 @@
     <a-icon type="user"></a-icon>
     <span>{{ i18n "menu.inbounds"}}</span>
 </a-menu-item>
-<a-menu-item key="{{ .base_path }}xui/setting">
+<a-menu-item key="{{ .base_path }}xui/settings">
     <a-icon type="setting"></a-icon>
-    <span>{{ i18n "menu.setting"}}</span>
+    <span>{{ i18n "menu.settings"}}</span>
 </a-menu-item>
 <!--<a-menu-item key="{{ .base_path }}xui/clients">-->
 <!--    <a-icon type="laptop"></a-icon>-->

+ 1 - 1
web/html/xui/component/setting.html

@@ -9,7 +9,7 @@
                 <a-input :value="value" @input="$emit('input', $event.target.value)"></a-input>
             </template>
             <template v-else-if="type === 'number'">
-                <a-input type="number" :value="value" @input="$emit('input', $event.target.value)" :min="min"></a-input>
+                <a-input-number :value="value" @change="value => $emit('input', value)" :min="min" style="width: 100%;"></a-input-number>
             </template>
             <template v-else-if="type === 'textarea'">
                 <a-textarea :value="value" @input="$emit('input', $event.target.value)" :auto-size="{ minRows: 10, maxRows: 10 }"></a-textarea>

+ 4 - 4
web/html/xui/form/client.html

@@ -22,7 +22,7 @@
         <a-input v-model.trim="client.password" style="width: 150px;" ></a-input>
     </a-form-item>
     <a-form-item label='{{ i18n "additional" }} ID' v-if="inbound.protocol === Protocols.VMESS">
-        <a-input type="number" v-model.number="client.alterId" style="width: 70px;"></a-input>
+        <a-input-number v-model="client.alterId" style="width: 70px;"></a-input-number>
     </a-form-item>
     <a-form-item label="ID" v-if="inbound.protocol === Protocols.VMESS || inbound.protocol === Protocols.VLESS">
         <a-input v-model.trim="client.id" style="width: 300px;"></a-input>
@@ -43,7 +43,7 @@
 				<a-icon type="question-circle" theme="filled"></a-icon>
 			</a-tooltip>
 		</span>
-		<a-input type="number" v-model.number="client.limitIp" min="0" style="width: 70px;" ></a-input>
+		<a-input-number v-model="client.limitIp" min="0" style="width: 70px;"></a-input-number>
 	</a-form-item>
 	<a-form-item v-if="client.email && client.limitIp > 0 && isEdit">
 		<span slot="label">
@@ -82,7 +82,7 @@
     </a-form-item>
     <a-form-item>
         <span slot="label">
-            <span >{{ i18n "pages.inbounds.totalFlow" }}</span>(GB)
+            <span >{{ i18n "pages.inbounds.totalFlow" }}</span> (GB)
             <a-tooltip>
                 <template slot="title">
                     0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
@@ -108,7 +108,7 @@
         <a-switch v-model="clientModal.delayedStart" @click="client._expiryTime=0"></a-switch>
     </a-form-item>
     <a-form-item label='{{ i18n "pages.client.expireDays" }}' v-if="clientModal.delayedStart">
-        <a-input type="number" v-model.number="delayedExpireDays" :min="0"></a-input>
+        <a-input-number v-model="delayedExpireDays" :min="0"></a-input-number>
     </a-form-item>
     <a-form-item v-else>
         <span slot="label">

+ 2 - 2
web/html/xui/form/inbound.html

@@ -25,11 +25,11 @@
         <a-input v-model.trim="inbound.listen"></a-input>
     </a-form-item>
     <a-form-item label='{{ i18n "pages.inbounds.port" }}'>
-        <a-input type="number" v-model.number="inbound.port"></a-input>
+        <a-input-number v-model="inbound.port"></a-input-number>
     </a-form-item>
     <a-form-item>
         <span slot="label">
-            <span >{{ i18n "pages.inbounds.totalFlow" }}</span>(GB)
+            <span >{{ i18n "pages.inbounds.totalFlow" }}</span> (GB)
             <a-tooltip>
                 <template slot="title">
                     0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>

+ 1 - 1
web/html/xui/form/protocol/dokodemo.html

@@ -4,7 +4,7 @@
         <a-input v-model.trim="inbound.settings.address"></a-input>
     </a-form-item>
     <a-form-item label='{{ i18n "pages.inbounds.destinationPort"}}'>
-        <a-input type="number" v-model.number="inbound.settings.port"></a-input>
+        <a-input-number v-model="inbound.settings.port"></a-input-number>
     </a-form-item>
     <a-form-item label='{{ i18n "pages.inbounds.network"}}'>
         <a-select v-model="inbound.settings.network" style="width: 100px;" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">

+ 0 - 1
web/html/xui/form/protocol/socks.html

@@ -1,6 +1,5 @@
 {{define "form/socks"}}
 <a-form layout="inline">
-    <!--    <a-form-item label="Password authentication">-->
     <a-form-item label='{{ i18n "password" }}'>
         <a-switch :checked="inbound.settings.auth === 'password'"
                   @change="checked => inbound.settings.auth = checked ? 'password' : 'noauth'"></a-switch>

+ 3 - 3
web/html/xui/form/protocol/trojan.html

@@ -33,7 +33,7 @@
                     <a-icon type="question-circle" theme="filled"></a-icon>
                 </a-tooltip>
             </span>
-            <a-input type="number" v-model.number="client.limitIp" min="0"  style="width: 70px;" ></a-input>
+            <a-input-number v-model="client.limitIp" min="0"  style="width: 70px;"></a-input-number>
         </a-form-item>
         <a-form-item v-if="inbound.xtls" label="Flow">
             <a-select v-model="client.flow" style="width: 150px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
@@ -43,7 +43,7 @@
         </a-form-item>
         <a-form-item>
             <span slot="label">
-                <span >{{ i18n "pages.inbounds.totalFlow" }}</span>(GB)
+                <span >{{ i18n "pages.inbounds.totalFlow" }}</span> (GB)
                 <a-tooltip>
                     <template slot="title">
                         0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
@@ -113,7 +113,7 @@
             <a-input v-model="fallback.dest"></a-input>
         </a-form-item>
         <a-form-item label="xVer">
-            <a-input type="number" v-model.number="fallback.xver"></a-input>
+            <a-input-number v-model="fallback.xver"></a-input-number>
         </a-form-item>
         <a-divider v-if="inbound.settings.fallbacks.length - 1 === index"/>
     </a-form>

+ 3 - 3
web/html/xui/form/protocol/vless.html

@@ -33,7 +33,7 @@
                     <a-icon type="question-circle" theme="filled"></a-icon>
                 </a-tooltip>
             </span>
-            <a-input type="number" v-model.number="client.limitIp" min="0"  style="width: 70px;" ></a-input>
+            <a-input-number v-model="client.limitIp" min="0"  style="width: 70px;"></a-input-number>
         </a-form-item>
         <a-form-item v-if="inbound.xtls" label="Flow">
             <a-select v-model="inbound.settings.vlesses[index].flow" style="width: 200px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
@@ -49,7 +49,7 @@
         </a-form-item>
         <a-form-item>
             <span slot="label">
-                <span >{{ i18n "pages.inbounds.totalFlow" }}</span>(GB)
+                <span >{{ i18n "pages.inbounds.totalFlow" }}</span> (GB)
                 <a-tooltip>
                     <template slot="title">
                         0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
@@ -119,7 +119,7 @@
             <a-input v-model="fallback.dest"></a-input>
         </a-form-item>
         <a-form-item label="xVer">
-            <a-input type="number" v-model.number="fallback.xver"></a-input>
+            <a-input-number v-model="fallback.xver"></a-input-number>
         </a-form-item>
         <a-divider v-if="inbound.settings.fallbacks.length - 1 === index"/>
     </a-form>

+ 4 - 4
web/html/xui/form/protocol/vmess.html

@@ -15,7 +15,7 @@
             <a-input v-model.trim="client.email" style="width: 150px;"></a-input>
         </a-form-item>
         <a-form-item label='{{ i18n "additional" }} ID'>
-            <a-input type="number" v-model.number="client.alterId" style="width: 70px;"></a-input>
+            <a-input-number v-model="client.alterId" style="width: 70px;"></a-input-number>
         </a-form-item>
         <a-form-item label="ID">
             <a-input v-model.trim="client.id" style="width: 300px;"></a-input>
@@ -36,11 +36,11 @@
                     <a-icon type="question-circle" theme="filled"></a-icon>
                 </a-tooltip>
             </span>
-            <a-input type="number" v-model.number="client.limitIp" min="0" style="width: 70px;"></a-input>
+            <a-input-number v-model="client.limitIp" min="0" style="width: 70px;"></a-input-number>
         </a-form-item>
         <a-form-item>
             <span slot="label">
-                <span >{{ i18n "pages.inbounds.totalFlow" }}</span>(GB)
+                <span >{{ i18n "pages.inbounds.totalFlow" }}</span> (GB)
                 <a-tooltip>
                     <template slot="title">
                         0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
@@ -67,7 +67,7 @@
     </a-collapse-panel>     
 </a-collapse>
 <a-collapse v-else>
-    <a-collapse-panel :header="'{{ i18n "pages.client.clientCount"}} : ' + inbound.settings.vmesses.length">
+    <a-collapse-panel :header="'{{ i18n "pages.client.clientCount" }}: ' + inbound.settings.vmesses.length">
         <table width="100%">
                 <tr class="client-table-header">
                 <th v-for="col in Object.keys(inbound.settings.vmesses[0]).slice(0, 3)">[[ col ]]</th>

+ 14 - 14
web/html/xui/form/stream/stream_kcp.html

@@ -2,37 +2,37 @@
 <a-form layout="inline">
     <a-form-item label='{{ i18n "camouflage" }}'>
         <a-select v-model="inbound.stream.kcp.type" style="width: 280px;">
-            <a-select-option value="none">None(Not Camouflage)</a-select-option>
-            <a-select-option value="srtp">SRTP(Camouflage Video Call)</a-select-option>
-            <a-select-option value="utp">UTP(Camouflage BT Download)</a-select-option>
-            <a-select-option value="wechat-video">Wechat-Video(Camouflage WeChat Video)</a-select-option>
-            <a-select-option value="dtls">DTLS(Camouflage DTLS 1.2 Packages)</a-select-option>
-            <a-select-option value="wireguard">Wireguard(Camouflage Wireguard Packages)</a-select-option>
+            <a-select-option value="none">None (Not Camouflage)</a-select-option>
+            <a-select-option value="srtp">SRTP (Camouflage Video Call)</a-select-option>
+            <a-select-option value="utp">UTP (Camouflage BT Download)</a-select-option>
+            <a-select-option value="wechat-video">Wechat-Video (Camouflage WeChat Video)</a-select-option>
+            <a-select-option value="dtls">DTLS (Camouflage DTLS 1.2 Packages)</a-select-option>
+            <a-select-option value="wireguard">Wireguard (Camouflage Wireguard Packages)</a-select-option>
         </a-select>
     </a-form-item>
     <a-form-item label='{{ i18n "password" }}'>
-        <a-input v-model.number="inbound.stream.kcp.seed"></a-input>
-    </a-form-item>
+        <a-input v-model="inbound.stream.kcp.seed"></a-input>
+    </a-form-item><br />
     <a-form-item label="MTU">
-        <a-input type="number" v-model.number="inbound.stream.kcp.mtu"></a-input>
+        <a-input-number v-model="inbound.stream.kcp.mtu"></a-input-number>
     </a-form-item>
     <a-form-item label="TTI (ms)">
-        <a-input type="number" v-model.number="inbound.stream.kcp.tti"></a-input>
+        <a-input-number v-model="inbound.stream.kcp.tti"></a-input-number>
     </a-form-item>
     <a-form-item label="Uplink Capacity (MB/S)">
-        <a-input type="number" v-model.number="inbound.stream.kcp.upCap"></a-input>
+        <a-input-number v-model="inbound.stream.kcp.upCap"></a-input-number>
     </a-form-item>
     <a-form-item label="Downlink Capacity (MB/S)">
-        <a-input type="number" v-model.number="inbound.stream.kcp.downCap"></a-input>
+        <a-input-number v-model="inbound.stream.kcp.downCap"></a-input-number>
     </a-form-item>
     <a-form-item label="Congestion">
         <a-switch v-model="inbound.stream.kcp.congestion"></a-switch>
     </a-form-item>
     <a-form-item label="Read Buffer Size (MB)">
-        <a-input type="number" v-model.number="inbound.stream.kcp.readBuffer"></a-input>
+        <a-input-number v-model="inbound.stream.kcp.readBuffer"></a-input-number>
     </a-form-item>
     <a-form-item label="Write Buffer Size (MB)">
-        <a-input type="number" v-model.number="inbound.stream.kcp.writeBuffer"></a-input>
+        <a-input-number v-model="inbound.stream.kcp.writeBuffer"></a-input-number>
     </a-form-item>
 </a-form>
 {{end}}

+ 6 - 6
web/html/xui/form/stream/stream_quic.html

@@ -12,12 +12,12 @@
     </a-form-item>
     <a-form-item label='{{ i18n "camouflage" }}'>
         <a-select v-model="inbound.stream.quic.type" style="width: 280px;" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
-            <a-select-option value="none">none(not camouflage)</a-select-option>
-            <a-select-option value="srtp">srtp(camouflage video call)</a-select-option>
-            <a-select-option value="utp">utp(camouflage BT download)</a-select-option>
-            <a-select-option value="wechat-video">wechat-video(camouflage WeChat video)</a-select-option>
-            <a-select-option value="dtls">dtls(camouflage DTLS 1.2 packages)</a-select-option>
-            <a-select-option value="wireguard">wireguard(camouflage wireguard packages)</a-select-option>
+            <a-select-option value="none">none (not camouflage)</a-select-option>
+            <a-select-option value="srtp">srtp (camouflage video call)</a-select-option>
+            <a-select-option value="utp">utp (camouflage BT download)</a-select-option>
+            <a-select-option value="wechat-video">wechat-video (camouflage WeChat video)</a-select-option>
+            <a-select-option value="dtls">dtls (camouflage DTLS 1.2 packages)</a-select-option>
+            <a-select-option value="wireguard">wireguard (camouflage wireguard packages)</a-select-option>
         </a-select>
     </a-form-item>
 </a-form>

+ 1 - 1
web/html/xui/form/stream/stream_tcp.html

@@ -4,7 +4,7 @@
     <a-form-item label="AcceptProxyProtocol">
         <a-switch v-model="inbound.stream.tcp.acceptProxyProtocol"></a-switch>
     </a-form-item>
-    <a-form-item label="HTTP {{ i18n "camouflage" }}">
+    <a-form-item label='HTTP {{ i18n "camouflage" }}'>
         <a-switch
                 :checked="inbound.stream.tcp.type === 'http'"
                 @change="checked => inbound.stream.tcp.type = checked ? 'http' : 'none'">

+ 1 - 1
web/html/xui/form/tls_settings.html

@@ -140,7 +140,7 @@
         </a-switch>
     </a-form-item>
     <a-form-item label="xVer">
-        <a-input type="number" v-model.number="inbound.stream.reality.xver" :min="0" style="width: 60px"></a-input>
+        <a-input-number v-model="inbound.stream.reality.xver" :min="0" style="width: 60px"></a-input-number>
     </a-form-item>
     <a-form-item label="uTLS" >
         <a-select v-model="inbound.stream.reality.settings.fingerprint" 

+ 6 - 5
web/html/xui/inbounds.html

@@ -93,12 +93,13 @@
                                 </a-col>
                                 <a-col :xs="24" :sm="24" :lg="12" style="text-align: right;">
                                     <a-select v-model="refreshInterval"
+                                              style="width: 65px;"
                                               v-if="isRefreshEnabled"
                                               @change="changeRefreshInterval"
                                               :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
                                         <a-select-option v-for="key in [5,10,30,60]" :value="key*1000">[[ key ]]s</a-select-option>
                                     </a-select>
-                                    <a-icon type="sync" :spin="isRefreshEnabled"></a-icon>
+                                    <a-icon type="sync" :spin="isRefreshEnabled" style="margin: 0 5px;"></a-icon>
                                     <a-switch v-model="isRefreshEnabled" @change="toggleRefresh"></a-switch>
                                 </a-col>
                             </a-row>
@@ -155,7 +156,7 @@
                                             <a-icon type="retweet"></a-icon> {{ i18n "pages.inbounds.resetTraffic" }}
                                         </a-menu-item>
                                         <a-menu-item key="clone">
-                                            <a-icon type="block"></a-icon> {{ i18n "pages.inbounds.Clone"}}
+                                            <a-icon type="block"></a-icon> {{ i18n "pages.inbounds.clone"}}
                                         </a-menu-item>
                                         <a-menu-item key="delete">
                                             <span style="color: #FF4D4F">
@@ -478,7 +479,7 @@
             },
 			openCloneInbound(dbInbound) {
                 this.$confirm({
-                    title: '{{ i18n "pages.inbounds.cloneInbound"}}' + dbInbound.remark,
+                    title: '{{ i18n "pages.inbounds.cloneInbound"}} \"' + dbInbound.remark + '\"',
                     content: '{{ i18n "pages.inbounds.cloneInboundContent"}}',
                     okText: '{{ i18n "pages.inbounds.cloneInboundOk"}}',
                     cancelText: '{{ i18n "cancel" }}',
@@ -512,7 +513,7 @@
             openAddInbound() {
                 inModal.show({
                     title: '{{ i18n "pages.inbounds.addInbound"}}',
-                    okText: '{{ i18n "pages.inbounds.addTo"}}',
+                    okText: '{{ i18n "pages.inbounds.create"}}',
                     cancelText: '{{ i18n "close" }}',
                     confirm: async (inbound, dbInbound) => {
                         inModal.loading();
@@ -527,7 +528,7 @@
                 const inbound = dbInbound.toInbound();
                 inModal.show({
                     title: '{{ i18n "pages.inbounds.modifyInbound"}}',
-                    okText: '{{ i18n "pages.inbounds.revise"}}',
+                    okText: '{{ i18n "pages.inbounds.update"}}',
                     cancelText: '{{ i18n "close" }}',
                     inbound: inbound,
                     dbInbound: dbInbound,

+ 1 - 1
web/html/xui/index.html

@@ -382,7 +382,7 @@
                     cancelText: '{{ i18n "cancel"}}',
                     onOk: async () => {
                         versionModal.hide();
-                        this.loading(true, '{{ i18n "pages.index.dontRefreshh"}}');
+                        this.loading(true, '{{ i18n "pages.index.dontRefresh"}}');
                         await HttpUtil.post(`/server/installXray/${version}`);
                         this.loading(false);
                     },

+ 187 - 184
web/html/xui/setting.html → web/html/xui/settings.html

@@ -33,21 +33,22 @@
                 <a-spin :spinning="spinning" :delay="500" tip="loading">
                     <a-space direction="vertical">
                         <a-space direction="horizontal">
-                            <a-button type="primary" :disabled="saveBtnDisable" @click="updateAllSetting">{{ i18n "pages.setting.save" }}</a-button>
-                            <a-button type="danger" :disabled="!saveBtnDisable" @click="restartPanel">{{ i18n "pages.setting.restartPanel" }}</a-button>
+                            <a-button type="primary" :disabled="saveBtnDisable" @click="updateAllSetting">{{ i18n "pages.settings.save" }}</a-button>
+                            <a-button type="danger" :disabled="!saveBtnDisable" @click="restartPanel">{{ i18n "pages.settings.restartPanel" }}</a-button>
                         </a-space>
 
-                        <a-tabs default-active-key="1" :class="siderDrawer.isDarkTheme ? darkClass : ''">
-                            <a-tab-pane key="1" tab='{{ i18n "pages.setting.panelConfig"}}'>
+                        <a-tabs default-active-key="1" :class="siderDrawer.isDarkTheme ? darkClass : ''" style="padding-bottom: 40px;">
+                            <a-tab-pane key="1" tab='{{ i18n "pages.settings.panelSettings"}}'>
                                 <a-list item-layout="horizontal" :style="siderDrawer.isDarkTheme ? 'color: hsla(0,0%,100%,.65);': 'background: white;'">
-                                    <setting-list-item type="text" title='{{ i18n "pages.setting.panelListeningIP"}}' desc='{{ i18n "pages.setting.panelListeningIPDesc"}}' v-model="allSetting.webListen"></setting-list-item>
-                                    <setting-list-item type="number" title='{{ i18n "pages.setting.panelPort"}}' desc='{{ i18n "pages.setting.panelPortDesc"}}' v-model.number="allSetting.webPort"></setting-list-item>
-                                    <setting-list-item type="text" title='{{ i18n "pages.setting.publicKeyPath"}}' desc='{{ i18n "pages.setting.publicKeyPathDesc"}}' v-model="allSetting.webCertFile"></setting-list-item>
-                                    <setting-list-item type="text" title='{{ i18n "pages.setting.privateKeyPath"}}' desc='{{ i18n "pages.setting.privateKeyPathDesc"}}' v-model="allSetting.webKeyFile"></setting-list-item>
-                                    <setting-list-item type="text" title='{{ i18n "pages.setting.panelUrlPath"}}' desc='{{ i18n "pages.setting.panelUrlPathDesc"}}' v-model="allSetting.webBasePath"></setting-list-item>
-                                    <setting-list-item type="number" title='{{ i18n "pages.setting.sessionMaxAge" }}' desc='{{ i18n "pages.setting.sessionMaxAgeDesc" }}'  v-model="allSetting.sessionMaxAge" :min="0"></setting-list-item>
-                                    <setting-list-item type="number" title='{{ i18n "pages.setting.expireTimeDiff" }}' desc='{{ i18n "pages.setting.expireTimeDiffDesc" }}'  v-model="allSetting.expireDiff" :min="0"></setting-list-item>
-                                    <setting-list-item type="number" title='{{ i18n "pages.setting.trafficDiff" }}' desc='{{ i18n "pages.setting.trafficDiffDesc" }}'  v-model="allSetting.trafficDiff" :min="0"></setting-list-item>
+                                    <setting-list-item type="text" title='{{ i18n "pages.settings.panelListeningIP"}}' desc='{{ i18n "pages.settings.panelListeningIPDesc"}}' v-model="allSetting.webListen"></setting-list-item>
+                                    <setting-list-item type="number" title='{{ i18n "pages.settings.panelPort"}}' desc='{{ i18n "pages.settings.panelPortDesc"}}' v-model="allSetting.webPort" :min="0"></setting-list-item>
+                                    <setting-list-item type="text" title='{{ i18n "pages.settings.publicKeyPath"}}' desc='{{ i18n "pages.settings.publicKeyPathDesc"}}' v-model="allSetting.webCertFile"></setting-list-item>
+                                    <setting-list-item type="text" title='{{ i18n "pages.settings.privateKeyPath"}}' desc='{{ i18n "pages.settings.privateKeyPathDesc"}}' v-model="allSetting.webKeyFile"></setting-list-item>
+                                    <setting-list-item type="text" title='{{ i18n "pages.settings.panelUrlPath"}}' desc='{{ i18n "pages.settings.panelUrlPathDesc"}}' v-model="allSetting.webBasePath"></setting-list-item>
+                                    <setting-list-item type="number" title='{{ i18n "pages.settings.sessionMaxAge" }}' desc='{{ i18n "pages.settings.sessionMaxAgeDesc" }}'  v-model="allSetting.sessionMaxAge" :min="0"></setting-list-item>
+                                    <setting-list-item type="number" title='{{ i18n "pages.settings.expireTimeDiff" }}' desc='{{ i18n "pages.settings.expireTimeDiffDesc" }}'  v-model="allSetting.expireDiff" :min="0"></setting-list-item>
+                                    <setting-list-item type="number" title='{{ i18n "pages.settings.trafficDiff" }}' desc='{{ i18n "pages.settings.trafficDiffDesc" }}'  v-model="allSetting.trafficDiff" :min="0"></setting-list-item>
+                                    <setting-list-item type="text" title='{{ i18n "pages.settings.timeZone"}}' desc='{{ i18n "pages.settings.timeZoneDesc"}}' v-model="allSetting.timeLocation"></setting-list-item>
                                     <a-list-item>
                                         <a-row style="padding: 20px">
                                             <a-col :lg="24" :xl="12">
@@ -74,148 +75,153 @@
                                 </a-list>
                             </a-tab-pane>
 
-                            <a-tab-pane key="2" tab='{{ i18n "pages.setting.userSetting"}}'>
-                                <a-form :style="siderDrawer.isDarkTheme ? 'color: hsla(0,0%,100%,.65); padding: 20px;': 'background: white; padding: 20px;'">
-                                    <a-form-item label='{{ i18n "pages.setting.oldUsername"}}'>
-                                        <a-input v-model="user.oldUsername" style="max-width: 300px"></a-input>
-                                    </a-form-item>
-                                    <a-form-item label='{{ i18n "pages.setting.currentPassword"}}'>
-                                        <a-input type="password" v-model="user.oldPassword" style="max-width: 300px"></a-input>
-                                    </a-form-item>
-                                    <a-form-item label='{{ i18n "pages.setting.newUsername"}}'>
-                                        <a-input v-model="user.newUsername" style="max-width: 300px"></a-input>
-                                    </a-form-item>
-                                    <a-form-item label='{{ i18n "pages.setting.newPassword"}}'>
-                                        <a-input type="password" v-model="user.newPassword" style="max-width: 300px"></a-input>
-                                    </a-form-item>
-                                    <a-form-item>
-                                        <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 key="2" tab='{{ i18n "pages.settings.securitySettings"}}' style="padding-top: 10px;">
+                                <a-tabs default-active-key="sec-1" :class="siderDrawer.isDarkTheme ? darkClass : ''">
+                                    <a-tab-pane key="sec-1" tab='{{ i18n "pages.settings.security.admin"}}'>
+                                        <a-form :style="siderDrawer.isDarkTheme ? 'color: hsla(0,0%,100%,.65); padding: 20px;': 'background: white; padding: 20px;'">
+                                            <a-form-item label='{{ i18n "pages.settings.oldUsername"}}'>
+                                                <a-input v-model="user.oldUsername" style="max-width: 300px"></a-input>
+                                            </a-form-item>
+                                            <a-form-item label='{{ i18n "pages.settings.currentPassword"}}'>
+                                                <a-input type="password" v-model="user.oldPassword" style="max-width: 300px"></a-input>
+                                            </a-form-item>
+                                            <a-form-item label='{{ i18n "pages.settings.newUsername"}}'>
+                                                <a-input v-model="user.newUsername" style="max-width: 300px"></a-input>
+                                            </a-form-item>
+                                            <a-form-item label='{{ i18n "pages.settings.newPassword"}}'>
+                                                <a-input type="password" v-model="user.newPassword" style="max-width: 300px"></a-input>
+                                            </a-form-item>
+                                            <a-form-item>
+                                                <a-button type="primary" @click="updateUser">{{ i18n "confirm" }}</a-button>
+                                            </a-form-item>
+                                        </a-form>
+                                    </a-tab-pane>
+                                    <a-tab-pane key="sec-2" tab='{{ i18n "pages.settings.security.secret"}}'>
+                                        <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.settings.security.loginSecurity" }}' description='{{ i18n "pages.settings.security.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.settings.security.secretToken" }}' description='{{ i18n "pages.settings.security.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-tabs>
                             </a-tab-pane>
-                            <a-tab-pane key="3" tab='{{ i18n "pages.setting.xrayConfiguration"}}'>
+
+                            <a-tab-pane key="3" tab='{{ i18n "pages.settings.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>
+                                    <a-divider>{{ i18n "pages.settings.actions"}}</a-divider>
                                     <a-space direction="horizontal" style="padding: 0 20px">
-                                        <a-button type="primary" @click="resetXrayConfigToDefault">{{ i18n "pages.setting.resetDefaultConfig" }}</a-button>
+                                        <a-button type="primary" @click="resetXrayConfigToDefault">{{ i18n "pages.settings.resetDefaultConfig" }}</a-button>
                                     </a-space>
 
-                                    <a-divider>{{ i18n "pages.setting.basicTemplate"}}</a-divider>
-                                    <a-collapse>
-                                        <a-collapse-panel header='{{ i18n "pages.setting.generalConfigs"}}'>
-                                            <a-row :xs="24" :sm="24" :lg="12">
-                                                <h2 style="color: inherit; font-weight: bold; font-size: 18px; padding: 10px 20px; border-bottom: 2px solid;">
-                                                    <a-icon type="warning" style="color: inherit; font-size: 24px;"></a-icon>
-                                                    {{ i18n "pages.setting.generalConfigsDesc" }}
-                                                </h2>
-                                            </a-row>
-                                            <setting-list-item type="switch" title='{{ i18n "pages.setting.xrayConfigTorrent"}}' desc='{{ i18n "pages.setting.xrayConfigTorrentDesc"}}'  v-model="torrentSettings"></setting-list-item>
-                                            <setting-list-item type="switch" title='{{ i18n "pages.setting.xrayConfigPrivateIp"}}' desc='{{ i18n "pages.setting.xrayConfigPrivateIpDesc"}}'  v-model="privateIpSettings"></setting-list-item>
-                                            <setting-list-item type="switch" title='{{ i18n "pages.setting.xrayConfigAds"}}' desc='{{ i18n "pages.setting.xrayConfigAdsDesc"}}'  v-model="AdsSettings"></setting-list-item>
-                                            <setting-list-item type="switch" title='{{ i18n "pages.setting.xrayConfigPorn"}}' desc='{{ i18n "pages.setting.xrayConfigPornDesc"}}'  v-model="PornSettings"></setting-list-item>
-                                        </a-collapse-panel>
-                                        <a-collapse-panel header='{{ i18n "pages.setting.countryConfigs"}}'>
-                                            <a-row :xs="24" :sm="24" :lg="12">
-                                                <h2 style="color: inherit; font-weight: bold; font-size: 18px; padding: 10px 20px; border-bottom: 2px solid;">
-                                                    <a-icon type="warning" style="color: inherit; font-size: 24px;"></a-icon>
-                                                    {{ i18n "pages.setting.countryConfigsDesc" }}
-                                                </h2>
-                                            </a-row>
-                                            <setting-list-item type="switch" title='{{ i18n "pages.setting.xrayConfigIRIp"}}' desc='{{ i18n "pages.setting.xrayConfigIRIpDesc"}}'  v-model="IRIpSettings"></setting-list-item>
-                                            <setting-list-item type="switch" title='{{ i18n "pages.setting.xrayConfigIRDomain"}}' desc='{{ i18n "pages.setting.xrayConfigIRDomainDesc"}}'  v-model="IRDomainSettings"></setting-list-item>
-                                            <setting-list-item type="switch" title='{{ i18n "pages.setting.xrayConfigChinaIp"}}' desc='{{ i18n "pages.setting.xrayConfigChinaIpDesc"}}'  v-model="ChinaIpSettings"></setting-list-item>
-                                            <setting-list-item type="switch" title='{{ i18n "pages.setting.xrayConfigChinaDomain"}}' desc='{{ i18n "pages.setting.xrayConfigChinaDomainDesc"}}'  v-model="ChinaDomainSettings"></setting-list-item>
-                                            <setting-list-item type="switch" title='{{ i18n "pages.setting.xrayConfigRussiaIp"}}' desc='{{ i18n "pages.setting.xrayConfigRussiaIpDesc"}}'  v-model="RussiaIpSettings"></setting-list-item>
-                                            <setting-list-item type="switch" title='{{ i18n "pages.setting.xrayConfigRussiaDomain"}}' desc='{{ i18n "pages.setting.xrayConfigRussiaDomainDesc"}}'  v-model="RussiaDomainSettings"></setting-list-item>
-                                        </a-collapse-panel>
-                                        <a-collapse-panel header='{{ i18n "pages.setting.ipv4Configs"}}'>
-                                            <a-row :xs="24" :sm="24" :lg="12">
-                                                <h2 style="color: inherit; font-weight: bold; font-size: 18px; padding: 10px 20px; border-bottom: 2px solid;">
-                                                    <a-icon type="warning" style="color: inherit; font-size: 24px;"></a-icon>
-                                                    {{ i18n "pages.setting.ipv4ConfigsDesc" }}
-                                                </h2>
-                                            </a-row>
-                                            <setting-list-item type="switch" title='{{ i18n "pages.setting.xrayConfigGoogleIPv4"}}' desc='{{ i18n "pages.setting.xrayConfigGoogleIPv4Desc"}}'  v-model="GoogleIPv4Settings"></setting-list-item>
-                                            <setting-list-item type="switch" title='{{ i18n "pages.setting.xrayConfigNetflixIPv4"}}' desc='{{ i18n "pages.setting.xrayConfigNetflixIPv4Desc"}}'  v-model="NetflixIPv4Settings"></setting-list-item>
-                                        </a-collapse-panel>
-                                        <a-collapse-panel header='{{ i18n "pages.setting.warpConfigs"}}'>
-                                            <a-row :xs="24" :sm="24" :lg="12">
-                                                <h2 style="color: inherit; font-weight: bold; font-size: 18px; padding: 10px 20px; border-bottom: 2px solid;">
-                                                    <a-icon type="warning" style="color: inherit; font-size: 24px;"></a-icon>
-                                                    {{ i18n "pages.setting.warpConfigsDesc" }}
-                                                </h2>
-                                            </a-row>
-                                            <setting-list-item type="switch" title='{{ i18n "pages.setting.xrayConfigGoogleWARP"}}' desc='{{ i18n "pages.setting.xrayConfigGoogleWARPDesc"}}'  v-model="GoogleWARPSettings"></setting-list-item>
-                                            <setting-list-item type="switch" title='{{ i18n "pages.setting.xrayConfigOpenAIWARP"}}' desc='{{ i18n "pages.setting.xrayConfigOpenAIWARPDesc"}}'  v-model="OpenAIWARPSettings"></setting-list-item>
-                                            <setting-list-item type="switch" title='{{ i18n "pages.setting.xrayConfigNetflixWARP"}}' desc='{{ i18n "pages.setting.xrayConfigNetflixWARPDesc"}}'  v-model="NetflixWARPSettings"></setting-list-item>
-                                            <setting-list-item type="switch" title='{{ i18n "pages.setting.xrayConfigSpotifyWARP"}}' desc='{{ i18n "pages.setting.xrayConfigSpotifyWARPDesc"}}'  v-model="SpotifyWARPSettings"></setting-list-item>
-                                        </a-collapse-panel>
-                                    </a-collapse>
-
-                                    <a-divider>{{ i18n "pages.setting.advancedTemplate"}}</a-divider>
-                                    <a-collapse>
-                                        <a-collapse-panel header='{{ i18n "pages.setting.xrayConfigInbounds"}}'>
-                                            <setting-list-item type="textarea" title='{{ i18n "pages.setting.xrayConfigInbounds"}}' desc='{{ i18n "pages.setting.xrayConfigInboundsDesc"}}' v-model="inboundSettings"></setting-list-item>
-                                        </a-collapse-panel>
-                                        <a-collapse-panel header='{{ i18n "pages.setting.xrayConfigOutbounds"}}'>
-                                            <setting-list-item type="textarea" title='{{ i18n "pages.setting.xrayConfigOutbounds"}}' desc='{{ i18n "pages.setting.xrayConfigOutboundsDesc"}}' v-model="outboundSettings"></setting-list-item>
-                                        </a-collapse-panel>
-                                        <a-collapse-panel header='{{ i18n "pages.setting.xrayConfigRoutings"}}'>
-                                            <setting-list-item type="textarea" title='{{ i18n "pages.setting.xrayConfigRoutings"}}' desc='{{ i18n "pages.setting.xrayConfigRoutingsDesc"}}' v-model="routingRuleSettings"></setting-list-item>
-                                        </a-collapse-panel>
-                                    </a-collapse>
+                                    <a-divider>{{ i18n "pages.settings.templates.title"}}</a-divider>
 
-                                    <a-divider>{{ i18n "pages.setting.completeTemplate"}}</a-divider>
-                                    <setting-list-item type="textarea" title='{{ i18n "pages.setting.xrayConfigTemplate"}}' desc='{{ i18n "pages.setting.xrayConfigTemplateDesc"}}' v-model="allSetting.xrayTemplateConfig"></setting-list-item>
+                                    <a-tabs default-active-key="tpl-1" :class="siderDrawer.isDarkTheme ? darkClass : ''" style="padding: 0 20px;">
+                                        <a-tab-pane key="tpl-1" tab='{{ i18n "pages.settings.templates.basicTemplate"}}' style="padding-top: 20px;">
+                                            <a-collapse>
+                                                <a-collapse-panel header='{{ i18n "pages.settings.templates.generalConfigs"}}'>
+                                                    <a-row :xs="24" :sm="24" :lg="12">
+                                                        <h2 style="color: inherit; font-weight: bold; font-size: 18px; padding: 10px 20px; border-bottom: 2px solid;">
+                                                            <a-icon type="warning" style="color: inherit; font-size: 24px;"></a-icon>
+                                                            {{ i18n "pages.settings.templates.generalConfigsDesc" }}
+                                                        </h2>
+                                                    </a-row>
+                                                    <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigTorrent"}}' desc='{{ i18n "pages.settings.templates.xrayConfigTorrentDesc"}}'  v-model="torrentSettings"></setting-list-item>
+                                                    <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigPrivateIp"}}' desc='{{ i18n "pages.settings.templates.xrayConfigPrivateIpDesc"}}'  v-model="privateIpSettings"></setting-list-item>
+                                                    <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigAds"}}' desc='{{ i18n "pages.settings.templates.xrayConfigAdsDesc"}}'  v-model="AdsSettings"></setting-list-item>
+                                                    <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigPorn"}}' desc='{{ i18n "pages.settings.templates.xrayConfigPornDesc"}}'  v-model="PornSettings"></setting-list-item>
+                                                </a-collapse-panel>
+                                                <a-collapse-panel header='{{ i18n "pages.settings.templates.countryConfigs"}}'>
+                                                    <a-row :xs="24" :sm="24" :lg="12">
+                                                        <h2 style="color: inherit; font-weight: bold; font-size: 18px; padding: 10px 20px; border-bottom: 2px solid;">
+                                                            <a-icon type="warning" style="color: inherit; font-size: 24px;"></a-icon>
+                                                            {{ i18n "pages.settings.templates.countryConfigsDesc" }}
+                                                        </h2>
+                                                    </a-row>
+                                                    <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigIRIp"}}' desc='{{ i18n "pages.settings.templates.xrayConfigIRIpDesc"}}'  v-model="IRIpSettings"></setting-list-item>
+                                                    <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigIRDomain"}}' desc='{{ i18n "pages.settings.templates.xrayConfigIRDomainDesc"}}'  v-model="IRDomainSettings"></setting-list-item>
+                                                    <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigChinaIp"}}' desc='{{ i18n "pages.settings.templates.xrayConfigChinaIpDesc"}}'  v-model="ChinaIpSettings"></setting-list-item>
+                                                    <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigChinaDomain"}}' desc='{{ i18n "pages.settings.templates.xrayConfigChinaDomainDesc"}}'  v-model="ChinaDomainSettings"></setting-list-item>
+                                                    <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigRussiaIp"}}' desc='{{ i18n "pages.settings.templates.xrayConfigRussiaIpDesc"}}'  v-model="RussiaIpSettings"></setting-list-item>
+                                                    <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigRussiaDomain"}}' desc='{{ i18n "pages.settings.templates.xrayConfigRussiaDomainDesc"}}'  v-model="RussiaDomainSettings"></setting-list-item>
+                                                </a-collapse-panel>
+                                                <a-collapse-panel header='{{ i18n "pages.settings.templates.ipv4Configs"}}'>
+                                                    <a-row :xs="24" :sm="24" :lg="12">
+                                                        <h2 style="color: inherit; font-weight: bold; font-size: 18px; padding: 10px 20px; border-bottom: 2px solid;">
+                                                            <a-icon type="warning" style="color: inherit; font-size: 24px;"></a-icon>
+                                                            {{ i18n "pages.settings.templates.ipv4ConfigsDesc" }}
+                                                        </h2>
+                                                    </a-row>
+                                                    <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigGoogleIPv4"}}' desc='{{ i18n "pages.settings.templates.xrayConfigGoogleIPv4Desc"}}'  v-model="GoogleIPv4Settings"></setting-list-item>
+                                                    <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigNetflixIPv4"}}' desc='{{ i18n "pages.settings.templates.xrayConfigNetflixIPv4Desc"}}'  v-model="NetflixIPv4Settings"></setting-list-item>
+                                                </a-collapse-panel>
+                                                <a-collapse-panel header='{{ i18n "pages.settings.templates.warpConfigs"}}'>
+                                                    <a-row :xs="24" :sm="24" :lg="12">
+                                                        <h2 style="color: inherit; font-weight: bold; font-size: 18px; padding: 10px 20px; border-bottom: 2px solid;">
+                                                            <a-icon type="warning" style="color: inherit; font-size: 24px;"></a-icon>
+                                                            {{ i18n "pages.settings.templates.warpConfigsDesc" }}
+                                                        </h2>
+                                                    </a-row>
+                                                    <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigGoogleWARP"}}' desc='{{ i18n "pages.settings.templates.xrayConfigGoogleWARPDesc"}}'  v-model="GoogleWARPSettings"></setting-list-item>
+                                                    <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigOpenAIWARP"}}' desc='{{ i18n "pages.settings.templates.xrayConfigOpenAIWARPDesc"}}'  v-model="OpenAIWARPSettings"></setting-list-item>
+                                                    <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigNetflixWARP"}}' desc='{{ i18n "pages.settings.templates.xrayConfigNetflixWARPDesc"}}'  v-model="NetflixWARPSettings"></setting-list-item>
+                                                    <setting-list-item type="switch" title='{{ i18n "pages.settings.templates.xrayConfigSpotifyWARP"}}' desc='{{ i18n "pages.settings.templates.xrayConfigSpotifyWARPDesc"}}'  v-model="SpotifyWARPSettings"></setting-list-item>
+                                                </a-collapse-panel>
+                                            </a-collapse>
+                                        </a-tab-pane>
+                                        <a-tab-pane key="tpl-2" tab='{{ i18n "pages.settings.templates.advancedTemplate"}}' style="padding-top: 20px;">
+                                            <a-collapse>
+                                                <a-collapse-panel header='{{ i18n "pages.settings.templates.xrayConfigInbounds"}}'>
+                                                    <setting-list-item type="textarea" title='{{ i18n "pages.settings.templates.xrayConfigInbounds"}}' desc='{{ i18n "pages.settings.templates.xrayConfigInboundsDesc"}}' v-model="inboundSettings"></setting-list-item>
+                                                </a-collapse-panel>
+                                                <a-collapse-panel header='{{ i18n "pages.settings.templates.xrayConfigOutbounds"}}'>
+                                                    <setting-list-item type="textarea" title='{{ i18n "pages.settings.templates.xrayConfigOutbounds"}}' desc='{{ i18n "pages.settings.templates.xrayConfigOutboundsDesc"}}' v-model="outboundSettings"></setting-list-item>
+                                                </a-collapse-panel>
+                                                <a-collapse-panel header='{{ i18n "pages.settings.templates.xrayConfigRoutings"}}'>
+                                                    <setting-list-item type="textarea" title='{{ i18n "pages.settings.templates.xrayConfigRoutings"}}' desc='{{ i18n "pages.settings.templates.xrayConfigRoutingsDesc"}}' v-model="routingRuleSettings"></setting-list-item>
+                                                </a-collapse-panel>
+                                            </a-collapse>
+                                        </a-tab-pane>
+                                        <a-tab-pane key="tpl-3" tab='{{ i18n "pages.settings.templates.completeTemplate"}}' style="padding-top: 20px;">
+                                            <setting-list-item type="textarea" title='{{ i18n "pages.settings.templates.xrayConfigTemplate"}}' desc='{{ i18n "pages.settings.templates.xrayConfigTemplateDesc"}}' v-model="allSetting.xrayTemplateConfig"></setting-list-item>
+                                        </a-tab-pane>
+                                    </a-tabs>
                                 </a-list>
                             </a-tab-pane>
 
-                            <a-tab-pane key="4" tab='{{ i18n "pages.setting.TGReminder"}}'>
+                            <a-tab-pane key="4" tab='{{ i18n "pages.settings.TGBotSettings"}}'>
                                 <a-list item-layout="horizontal" :style="siderDrawer.isDarkTheme ? 'color: hsla(0,0%,100%,.65);': 'background: white;'">
-                                    <setting-list-item type="switch" title='{{ i18n "pages.setting.telegramBotEnable" }}' desc='{{ i18n "pages.setting.telegramBotEnableDesc" }}' v-model="allSetting.tgBotEnable"></setting-list-item>
-                                    <setting-list-item type="text" title='{{ i18n "pages.setting.telegramToken"}}' desc='{{ i18n "pages.setting.telegramTokenDesc"}}' v-model="allSetting.tgBotToken"></setting-list-item>
-                                    <setting-list-item type="text" title='{{ i18n "pages.setting.telegramChatId"}}' desc='{{ i18n "pages.setting.telegramChatIdDesc"}}' v-model="allSetting.tgBotChatId"></setting-list-item>
-                                    <setting-list-item type="text" title='{{ i18n "pages.setting.telegramNotifyTime"}}' desc='{{ i18n "pages.setting.telegramNotifyTimeDesc"}}' v-model="allSetting.tgRunTime"></setting-list-item>
-                                    <setting-list-item type="switch" title='{{ i18n "pages.setting.tgNotifyBackup" }}' desc='{{ i18n "pages.setting.tgNotifyBackupDesc" }}' v-model="allSetting.tgBotBackup"></setting-list-item>
-                                    <setting-list-item type="number" title='{{ i18n "pages.setting.tgNotifyCpu" }}' desc='{{ i18n "pages.setting.tgNotifyCpuDesc" }}' v-model="allSetting.tgCpu" :min="0" :max="100"></setting-list-item>
-                                </a-list>
-                            </a-tab-pane>
-
-                            <a-tab-pane key="5" tab='{{ i18n "pages.setting.otherSetting"}}'>
-                                <a-list item-layout="horizontal" :style="siderDrawer.isDarkTheme ? 'color: hsla(0,0%,100%,.65);': 'background: white;'">
-                                    <setting-list-item type="text" title='{{ i18n "pages.setting.timeZonee"}}' desc='{{ i18n "pages.setting.timeZoneDesc"}}' v-model="allSetting.timeLocation"></setting-list-item>
+                                    <setting-list-item type="switch" title='{{ i18n "pages.settings.telegramBotEnable" }}' desc='{{ i18n "pages.settings.telegramBotEnableDesc" }}' v-model="allSetting.tgBotEnable"></setting-list-item>
+                                    <setting-list-item type="text" title='{{ i18n "pages.settings.telegramToken"}}' desc='{{ i18n "pages.settings.telegramTokenDesc"}}' v-model="allSetting.tgBotToken"></setting-list-item>
+                                    <setting-list-item type="text" title='{{ i18n "pages.settings.telegramChatId"}}' desc='{{ i18n "pages.settings.telegramChatIdDesc"}}' v-model="allSetting.tgBotChatId"></setting-list-item>
+                                    <setting-list-item type="text" title='{{ i18n "pages.settings.telegramNotifyTime"}}' desc='{{ i18n "pages.settings.telegramNotifyTimeDesc"}}' v-model="allSetting.tgRunTime"></setting-list-item>
+                                    <setting-list-item type="switch" title='{{ i18n "pages.settings.tgNotifyBackup" }}' desc='{{ i18n "pages.settings.tgNotifyBackupDesc" }}' v-model="allSetting.tgBotBackup"></setting-list-item>
+                                    <setting-list-item type="number" title='{{ i18n "pages.settings.tgNotifyCpu" }}' desc='{{ i18n "pages.settings.tgNotifyCpuDesc" }}' v-model="allSetting.tgCpu" :min="0" :max="100"></setting-list-item>
                                 </a-list>
                             </a-tab-pane>
                         </a-tabs>
@@ -294,9 +300,8 @@
                 }
             },
             methods: {
-                loading(spinning = true , obj) {
-                if(obj == null)
-                  this.spinning = spinning;
+                loading(spinning = true, obj) {
+                    if (obj == null) this.spinning = spinning;
                 },
                 async getAllSetting() {
                     this.loading(true);
@@ -329,8 +334,8 @@
                 async restartPanel() {
                     await new Promise(resolve => {
                         this.$confirm({
-                            title: '{{ i18n "pages.setting.restartPanel" }}',
-                            content: '{{ i18n "pages.setting.restartPanelDesc" }}',
+                            title: '{{ i18n "pages.settings.restartPanel" }}',
+                            content: '{{ i18n "pages.settings.restartPanelDesc" }}',
                             okText: '{{ i18n "sure" }}',
                             cancelText: '{{ i18n "cancel" }}',
                             onOk: () => resolve(),
@@ -345,41 +350,39 @@
                         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;
-                    window.location.replace("/logout")
-                }
-                this.loading(false);
-                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);
-                },
-                async toggleToken(value){
-                if(value)
-                  this.getNewSecret();
-                else 
-                  this.user.loginSecret = "";
+                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;
+                        window.location.replace("/logout")
+                    }
+                    this.loading(false);
+                    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);
+                },
+                async toggleToken(value) {
+                    if (value) this.getNewSecret();
+                    else this.user.loginSecret = "";
                 },
                 async resetXrayConfigToDefault() {
                     this.loading(true);

+ 6 - 3
web/service/sub.go

@@ -105,9 +105,10 @@ func (s *SubService) genVmessLink(inbound *model.Inbound, email string) string {
 	if inbound.Protocol != model.VMess {
 		return ""
 	}
+	remark := fmt.Sprintf("%s-%s", inbound.Remark, email)
 	obj := map[string]interface{}{
 		"v":    "2",
-		"ps":   email,
+		"ps":   remark,
 		"add":  s.address,
 		"port": inbound.Port,
 		"type": "none",
@@ -380,7 +381,8 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
 	// Set the new query values on the URL
 	url.RawQuery = q.Encode()
 
-	url.Fragment = email
+	remark := fmt.Sprintf("%s-%s", inbound.Remark, email)
+	url.Fragment = remark
 	return url.String()
 }
 
@@ -558,7 +560,8 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string
 	// Set the new query values on the URL
 	url.RawQuery = q.Encode()
 
-	url.Fragment = email
+	remark := fmt.Sprintf("%s-%s", inbound.Remark, email)
+	url.Fragment = remark
 	return url.String()
 }
 

+ 151 - 145
web/translation/translate.en_US.toml

@@ -22,7 +22,7 @@
 "unlimited" = "Unlimited"
 "none" = "None"
 "qrCode" = "QR Code"
-"info" = "More information"
+"info" = "More Information"
 "edit" = "Edit"
 "delete" = "Delete"
 "reset" = "Reset"
@@ -43,29 +43,29 @@
 "monitor" = "Listening IP"
 "certificate" = "Certificate"
 "fail" = "Fail"
-"success" = " Success"
+"success" = "Success"
 "getVersion" = "Get version"
 "install" = "Install"
 "clients" = "Clients"
 "usage" = "Usage"
-"secretToken" = "Secret token"
+"secretToken" = "Secret Token"
 
 [menu]
 "dashboard" = "System Status"
 "inbounds" = "Inbounds"
-"setting" = "Panel Setting"
+"settings" = "Panel Settings"
 "logout" = "Logout"
 "link" = "Other"
 
 [pages.login]
 "title" = "Login"
-"loginAgain" = "The login time limit has expired, please log in again"
+"loginAgain" = "The login time limit has expired. Please log in again."
 
 [pages.login.toasts]
-"invalidFormData" = "Input Data Format is Invalid"
-"emptyUsername" = "Please Enter Username"
-"emptyPassword" = "Please Enter Password"
-"wrongUsernameOrPassword" = "Invalid username or password"
+"invalidFormData" = "Input data format is invalid."
+"emptyUsername" = "Please enter username."
+"emptyPassword" = "Please enter password."
+"wrongUsernameOrPassword" = "Invalid username or password."
 "successLogin" = "Login"
 
 [pages.index]
@@ -81,21 +81,21 @@
 "operationHours" = "Operation Hours"
 "operationHoursDesc" = "System uptime: time since startup."
 "systemLoad" = "System Load"
-"connectionCount" = "Number of connections"
-"connectionCountDesc" = "Total connections across all network cards"
-"upSpeed" = "Total upload speed for all network cards"
-"downSpeed" = "Total download speed for all network cards"
-"totalSent" = "Total upload traffic of all network cards since system startup"
-"totalReceive" = "Total download data across all network cards since system startup"
-"xraySwitchVersionDialog" = "Switch xray version"
-"xraySwitchVersionDialogDesc" = "Whether to switch the xray version to"
-"dontRefreshh" = "Installation is in progress, please do not refresh this page"
+"connectionCount" = "Number of Connections"
+"connectionCountDesc" = "Total connections across all network cards."
+"upSpeed" = "Total upload speed for all network cards."
+"downSpeed" = "Total download speed for all network cards."
+"totalSent" = "Total upload traffic of all network cards since system startup."
+"totalReceive" = "Total download data across all network cards since system startup."
+"xraySwitchVersionDialog" = "Switch Xray Version"
+"xraySwitchVersionDialogDesc" = "Are you sure you want to switch the Xray version to"
+"dontRefresh" = "Installation is in progress, please do not refresh this page."
 
 [pages.inbounds]
 "title" = "Inbounds"
-"totalDownUp" = "Total uploads/downloads"
-"totalUsage" = "Total usage"
-"inboundCount" = "Number of inbound"
+"totalDownUp" = "Total Uploads/Downloads"
+"totalUsage" = "Total Usage"
+"inboundCount" = "Number of Inbounds"
 "operate" = "Menu"
 "enable" = "Enable"
 "remark" = "Remark"
@@ -104,77 +104,77 @@
 "traffic" = "Traffic"
 "details" = "Details"
 "transportConfig" = "Transport"
-"expireDate" = "Expire date"
-"resetTraffic" = "Reset traffic"
+"expireDate" = "Expire Date"
+"resetTraffic" = "Reset Traffic"
 "addInbound" = "Add Inbound"
 "generalActions" = "General Actions"
-"addTo" = "Create"
-"revise" = "Update"
-"modifyInbound" = "Modify InBound"
+"create" = "Create"
+"update" = "Update"
+"modifyInbound" = "Modify Inbound"
 "deleteInbound" = "Delete Inbound"
 "deleteInboundContent" = "Confirm deletion of inbound?"
 "resetTrafficContent" = "Confirm traffic reset?"
 "copyLink" = "Copy Link"
 "address" = "Address"
 "network" = "Network"
-"destinationPort" = "Destination port"
-"targetAddress" = "Target address"
-"disableInsecureEncryption" = "Disable insecure encryption"
+"destinationPort" = "Destination Port"
+"targetAddress" = "Target Address"
+"disableInsecureEncryption" = "Disable Insecure Encryption"
 "monitorDesc" = "Leave blank by default"
-"meansNoLimit" = "Means no limit"
-"totalFlow" = "Total flow"
-"leaveBlankToNeverExpire" = "Leave blank to set no expiration"
+"meansNoLimit" = "Means No Limit"
+"totalFlow" = "Total Flow"
+"leaveBlankToNeverExpire" = "Leave Blank to Never Expire"
 "noRecommendKeepDefault" = "No special requirements to maintain default settings"
-"certificatePath" = "Certificate file path"
-"certificateContent" = "Certificate file content"
-"publicKeyPath" = "Public key path"
-"publicKeyContent" = "Public key content"
-"keyPath" = "Private Key path"
-"keyContent" = "Private Key content"
+"certificatePath" = "Certificate File Path"
+"certificateContent" = "Certificate File Content"
+"publicKeyPath" = "Public Key Path"
+"publicKeyContent" = "Public Key Content"
+"keyPath" = "Private Key Path"
+"keyContent" = "Private Key Content"
 "clickOnQRcode" = "Click on QR Code to Copy"
 "client" = "Client"
-"export" = "Export links"
-"Clone" = "Clone"
-"cloneInbound" = "Create"
-"cloneInboundContent" = "All settings of this inbound, except for Port, Listening IP, and Clients, will be applied to the clone"
-"cloneInboundOk" = "Creating a clone from"
+"export" = "Export Links"
+"clone" = "Clone"
+"cloneInbound" = "Clone"
+"cloneInboundContent" = "All settings of this inbound, except for Port, Listening IP, and Clients, will be applied to the clone."
+"cloneInboundOk" = "Clone"
 "resetAllTraffic" = "Reset All Inbounds Traffic"
 "resetAllTrafficTitle" = "Reset all inbounds traffic"
-"resetAllTrafficContent" = "Are you sure to reset all inbounds traffic ?"
+"resetAllTrafficContent" = "Are you sure you want to reset all inbounds traffic?"
 "resetAllTrafficOkText" = "Confirm"
 "resetAllTrafficCancelText" = "Cancel"
 "IPLimit" = "IP Limit"
-"IPLimitDesc" = "Disable inbound if the count exceeds the entered value (Enter 0 to disable IP limit)"
+"IPLimitDesc" = "Disable inbound if the count exceeds the entered value (enter 0 to disable IP limit)."
 "resetInboundClientTraffics" = "Reset Clients Traffic"
-"resetInboundClientTrafficTitle" = "Reset all clients traffic"
-"resetInboundClientTrafficContent" = "Are you sure to reset all traffics of this inbound's clients ?"
+"resetInboundClientTrafficTitle" = "Reset all client traffic"
+"resetInboundClientTrafficContent" = "Are you sure you want to reset all traffic for this inbound's clients?"
 "resetAllClientTraffics" = "Reset All Clients Traffic"
 "resetAllClientTrafficTitle" = "Reset all clients traffic"
-"resetAllClientTrafficContent" = "Are you sure to reset all traffics of all clients ?"
-"delDepletedClients" = "Delete depleted clients"
+"resetAllClientTrafficContent" = "Are you sure you want to reset all traffics for all clients?"
+"delDepletedClients" = "Delete Depleted Clients"
 "delDepletedClientsTitle" = "Delete depleted clients"
-"delDepletedClientsContent" = "Are you sure to delete all depleted clients ?"
+"delDepletedClientsContent" = "Are you sure you want to delete all depleted clients?"
 "Email" = "Email"
-"EmailDesc" = "Please provide a unique email address"
+"EmailDesc" = "Please provide a unique email address."
 "IPLimitlog" = "IP Log"
-"IPLimitlogDesc" = "IPs history Log (before enabling inbound after it has been disabled by IP limit, you should clear the log)"
+"IPLimitlogDesc" = "IPs history log (before enabling inbound after it has been disabled by IP limit, you should clear the log)."
 "IPLimitlogclear" = "Clear The Log"
 "setDefaultCert" = "Set cert from panel"
 "XTLSdec" = "Xray core needs to be 1.7.5"
-"Realitydec" = "Xray core needs to be 1.8.0 and above"
+"Realitydec" = "Xray core needs to be 1.8.0 or higher."
 
 [pages.client]
-"add" = "Add client"
-"edit" = "Edit client"
-"submitAdd" = "Add client"
+"add" = "Add Client"
+"edit" = "Edit Client"
+"submitAdd" = "Add Client"
 "submitEdit" = "Save changes"
-"clientCount" = "Number of clients"
-"bulk" = "Add bulk"
+"clientCount" = "Number of Clients"
+"bulk" = "Add Bulk"
 "method" = "Method"
 "first" = "First"
 "last" = "Last"
 "prefix" = "Prefix"
-"postfix" = "postfix"
+"postfix" = "Postfix"
 "delayedStart" = "Start after first use"
 "expireDays" = "Expire days"
 "days" = "day(s)"
@@ -199,113 +199,119 @@
 [pages.inbounds.stream.quic]
 "encryption" = "Encryption"
 
-[pages.setting]
-"title" = "Setting"
+[pages.settings]
+"title" = "Settings"
 "save" = "Save"
-"restartPanel" = "Restart Panel"
-"restartPanelDesc" = "Are you sure you want to restart the panel? Click OK to restart after 3 seconds. If you cannot access the panel after restarting, please go to the server to view the panel log information"
+"restartPanel" = "Restart Panel "
+"restartPanelDesc" = "Are you sure you want to restart the panel? Click OK to restart after 3 seconds. If you cannot access the panel after restarting, please view the panel log information on the server."
 "actions" = "Actions"
-"resetDefaultConfig" = "Reset to default config"
-"panelConfig" = "Panel Configuration"
-"userSetting" = "User Setting"
+"resetDefaultConfig" = "Reset to Default Configuration"
+"panelSettings" = "Panel Settings"
+"securitySettings" = "Security Settings"
 "xrayConfiguration" = "Xray Configuration"
-"TGReminder" = "TG Reminder Related Settings"
-"otherSetting" = "Other Setting"
-"panelListeningIP" = "Panel listening IP"
-"panelListeningIPDesc" = "Leave blank by default to monitor all IPs, restart the panel to take effect"
+"TGBotSettings" = "Telegram Bot Settings"
+"panelListeningIP" = "Panel Listening IP"
+"panelListeningIPDesc" = "Leave blank by default to monitor all IPs. Restart the panel to apply changes."
 "panelPort" = "Panel Port"
-"panelPortDesc" = "Restart the panel to take effect"
-"publicKeyPath" = "Panel certificate public key file path"
-"publicKeyPathDesc" = "Fill in an absolute path starting with '/', restart the panel to take effect"
-"privateKeyPath" = "Panel certificate private key file path"
-"privateKeyPathDesc" = "Fill in an absolute path starting with '/', restart the panel to take effect"
-"panelUrlPath" = "panel url root path"
-"panelUrlPathDesc" = "Must start with '/' and end with '/', restart the panel to take effect"
+"panelPortDesc" = "Restart the panel to apply changes."
+"publicKeyPath" = "Panel Certificate Public Key File Path"
+"publicKeyPathDesc" = "Fill in an absolute path starting with '/'. Restart the panel to apply changes."
+"privateKeyPath" = "Panel Certificate Private Key File Path"
+"privateKeyPathDesc" = "Fill in an absolute path starting with '/'. Restart the panel to apply changes."
+"panelUrlPath" = "Panel URL Root Path"
+"panelUrlPathDesc" = "Must start with '/' and end with '/'. Restart the panel to apply changes."
 "oldUsername" = "Current Username"
 "currentPassword" = "Current Password"
 "newUsername" = "New Username"
 "newPassword" = "New Password"
+"telegramBotEnable" = "Enable Telegram bot"
+"telegramBotEnableDesc" = "Restart the panel to take effect."
+"telegramToken" = "Telegram Token"
+"telegramTokenDesc" = "Restart the panel to take effect."
+"telegramChatId" = "Telegram Admin Chat IDs"
+"telegramChatIdDesc" = "Multiple Chat IDs separated by comma. Restart the panel to apply changes."
+"telegramNotifyTime" = "Telegram bot notification time"
+"telegramNotifyTimeDesc" = "Use Crontab timing format. Restart the panel to apply changes."
+"tgNotifyBackup" = "Database Backup"
+"tgNotifyBackupDesc" = "Include database backup file with report notification. Restart the panel to apply changes."
+"sessionMaxAge" = "Session maximum age"
+"sessionMaxAgeDesc" = "The duration of a login session (unit: minute)"
+"expireTimeDiff" = "Expiration threshold for notification"
+"expireTimeDiffDesc" = "Get notified about account expiration before the threshold (unit: day)"
+"trafficDiff" = "Traffic threshold for notification"
+"trafficDiffDesc" = "Get notified about traffic exhaustion before reaching the threshold (unit: GB)"
+"tgNotifyCpu" = "CPU percentage alert threshold"
+"tgNotifyCpuDesc" = "Receive notification if CPU usage exceeds this threshold (unit: %)"
+"timeZone" = "Time zone"
+"timeZoneDesc" = "Scheduled tasks run according to the time in this time zone. Restart the panel to apply changes."
+
+[pages.settings.templates]
+"title" = "Templates"
 "basicTemplate" = "Basic Template"
-"advancedTemplate" = "Advanced Template parts"
-"completeTemplate" = "Complete Template of Xray configuration"
+"advancedTemplate" = "Advanced Template"
+"completeTemplate" = "Complete Template"
 "generalConfigs" = "General Configs"
-"generalConfigsDesc" = "This options will prevent users from connecting to specific protocols and websites."
+"generalConfigsDesc" = "These options will prevent users from connecting to specific protocols and websites."
 "countryConfigs" = "Country Configs"
-"countryConfigsDesc" = "This options will prevent users from connecting to specific country domains."
+"countryConfigsDesc" = "These options will prevent users from connecting to specific country domains."
 "ipv4Configs" = "IPv4 Configs"
-"ipv4ConfigsDesc" = "This options will be route to target domains only via IPv4."
+"ipv4ConfigsDesc" = "These options will route to target domains only via IPv4."
 "warpConfigs" = "WARP Configs"
-"warpConfigsDesc" = "Caution: Before using this options, Install WARP in socks5 proxy mode on your server by following the steps on the panel's GitHub. WARP will route traffic to websites through Cloudflare servers."
+"warpConfigsDesc" = "Caution: Before using these options, install WARP in socks5 proxy mode on your server by following the steps on the panel's GitHub. WARP will route traffic to websites through Cloudflare servers."
 "xrayConfigTemplate" = "Xray Configuration Template"
-"xrayConfigTemplateDesc" = "Generate the final xray configuration file based on this template, restart the panel to take effect."
-"xrayConfigTorrent" = "Ban bittorrent usage"
-"xrayConfigTorrentDesc" = "Change the configuration template to avoid using bittorrent by users, restart the panel to take effect"
-"xrayConfigPrivateIp" = "Ban private IP ranges to connect"
-"xrayConfigPrivateIpDesc" = "Change the configuration template to avoid connecting with private IP ranges, restart the panel to take effect"
+"xrayConfigTemplateDesc" = "Generate the final Xray configuration file based on this template. Restart the panel to apply changes."
+"xrayConfigTorrent" = "Ban BitTorrent Usage"
+"xrayConfigTorrentDesc" = "Change the configuration template to avoid using BitTorrent by users. Restart the panel to apply changes."
+"xrayConfigPrivateIp" = "Ban Private IP Ranges to Connect"
+"xrayConfigPrivateIpDesc" = "Change the configuration template to avoid connecting with private IP ranges. Restart the panel to apply changes."
 "xrayConfigAds" = "Block Ads"
-"xrayConfigAdsDesc" = "Change the configuration template to block Ads, restart the panel to take effect"
+"xrayConfigAdsDesc" = "Change the configuration template to block ads. Restart the panel to apply changes."
 "xrayConfigPorn" = "Block Porn Websites"
-"xrayConfigPornDesc" = "Change the configuration template to avoid connecting to Porn websites, restart the panel to take effect"
-"xrayConfigIRIp" = "Ban Iran IP ranges to connect"
-"xrayConfigIRIpDesc" = "Change the configuration template to avoid connecting with Iran IP ranges, restart the panel to take effect"
-"xrayConfigIRDomain" = "Ban Iran Domains to connect"
-"xrayConfigIRDomainDesc" = "Change the configuration template to avoid connecting with Iran domains, restart the panel to take effect"
-"xrayConfigChinaIp" = "Ban China IP ranges to connect"
-"xrayConfigChinaIpDesc" = "Change the configuration template to avoid connecting with China IP ranges, restart the panel to take effect"
-"xrayConfigChinaDomain" = "Ban China Domains to connect"
-"xrayConfigChinaDomainDesc" = "Change the configuration template to avoid connecting with China domains, restart the panel to take effect"
-"xrayConfigRussiaIp" = "Ban Russia IP ranges to connect"
-"xrayConfigRussiaIpDesc" = "Change the configuration template to avoid connecting with Russia IP ranges, restart the panel to take effect"
-"xrayConfigRussiaDomain" = "Ban Russia Domains to connect"
-"xrayConfigRussiaDomainDesc" = "Change the configuration template to avoid connecting with Russia domains, restart the panel to take effect"
+"xrayConfigPornDesc" = "Change the configuration template to avoid connecting to porn websites. Restart the panel to apply changes."
+"xrayConfigIRIp" = "Disable connection to Iran IP ranges"
+"xrayConfigIRIpDesc" = "Change the configuration template to avoid connecting with Iran IP ranges. Restart the panel to apply changes."
+"xrayConfigIRDomain" = "Disable connection to Iran domains"
+"xrayConfigIRDomainDesc" = "Change the configuration template to avoid connecting with Iran domains. Restart the panel to apply changes."
+"xrayConfigChinaIp" = "Disable connection to China IP ranges"
+"xrayConfigChinaIpDesc" = "Change the configuration template to avoid connecting with China IP ranges. Restart the panel to apply changes."
+"xrayConfigChinaDomain" = "Disable connection to China domains"
+"xrayConfigChinaDomainDesc" = "Change the configuration template to avoid connecting with China domains. Restart the panel to apply changes."
+"xrayConfigRussiaIp" = "Disable connection to Russia IP ranges"
+"xrayConfigRussiaIpDesc" = "Change the configuration template to avoid connecting with Russia IP ranges. Restart the panel to apply changes."
+"xrayConfigRussiaDomain" = "Disable connection to Russia domains"
+"xrayConfigRussiaDomainDesc" = "Change the configuration template to avoid connecting with Russia domains. Restart the panel to apply changes."
 "xrayConfigGoogleIPv4" = "Use IPv4 for Google"
-"xrayConfigGoogleIPv4Desc" = "Add routing for google to connect with IPv4, restart the panel to take effect"
+"xrayConfigGoogleIPv4Desc" = "Add routing for Google to connect with IPv4. Restart the panel to apply changes."
 "xrayConfigNetflixIPv4" = "Use IPv4 for Netflix"
-"xrayConfigNetflixIPv4Desc" = "Add routing for Netflix to connect with IPv4, restart the panel to take effect"
-"xrayConfigGoogleWARP" = "Route Google to WARP"
-"xrayConfigGoogleWARPDesc" = "Add routing for Google to WARP, restart the panel to take effect"
-"xrayConfigOpenAIWARP" = "Route OpenAI (ChatGPT) to WARP"
-"xrayConfigOpenAIWARPDesc" = "Add routing for OpenAI (ChatGPT) to WARP, restart the panel to take effect"
-"xrayConfigNetflixWARP" = "Route Netflix to WARP"
-"xrayConfigNetflixWARPDesc" = "Add routing for Netflix to WARP, restart the panel to take effect"
-"xrayConfigSpotifyWARP" = "Route Spotify to WARP"
-"xrayConfigSpotifyWARPDesc" = "Add routing for Spotify to WARP, restart the panel to take effect"
-"xrayConfigIRWARP" = "Route Iran Domains to WARP"
-"xrayConfigIRWARPDesc" = "Add routing for Iran Domains to WARP. restart the panel to take effect"
+"xrayConfigNetflixIPv4Desc" = "Add routing for Netflix to connect with IPv4. Restart the panel to apply changes."
+"xrayConfigGoogleWARP" = "Route Google through WARP."
+"xrayConfigGoogleWARPDesc" = "Add routing for Google via WARP. Restart the panel to apply changes."
+"xrayConfigOpenAIWARP" = "Route OpenAI (ChatGPT) through WARP."
+"xrayConfigOpenAIWARPDesc" = "Add routing for OpenAI (ChatGPT) via WARP. Restart the panel to apply changes."
+"xrayConfigNetflixWARP" = "Route Netflix through WARP."
+"xrayConfigNetflixWARPDesc" = "Add routing for Netflix via WARP. Restart the panel to apply changes."
+"xrayConfigSpotifyWARP" = "Route Spotify through WARP."
+"xrayConfigSpotifyWARPDesc" = "Add routing for Spotify via WARP. Restart the panel to apply changes."
+"xrayConfigIRWARP" = "Route Iran domains through WARP."
+"xrayConfigIRWARPDesc" = "Add routing for Iran domains via WARP. Restart the panel to apply changes."
 "xrayConfigInbounds" = "Configuration of Inbounds"
-"xrayConfigInboundsDesc" = "Change the configuration template to accept special clients, restart the panel to take effect"
+"xrayConfigInboundsDesc" = "Change the configuration template to accept specific clients. Restart the panel to apply changes."
 "xrayConfigOutbounds" = "Configuration of Outbounds"
-"xrayConfigOutboundsDesc" = "Change the configuration template to define outgoing ways for this server, restart the panel to take effect"
-"xrayConfigRoutings" = "Configuration of Routing rules"
-"xrayConfigRoutingsDesc" = "Change the configuration template to define Routing rules for this server, restart the panel to take effect"
-"telegramBotEnable" = "Enable telegram bot"
-"telegramBotEnableDesc" = "Restart the panel to take effect"
-"telegramToken" = "Telegram Token"
-"telegramTokenDesc" = "Restart the panel to take effect"
-"telegramChatId" = "Telegram Admin ChatIds"
-"telegramChatIdDesc" = "Multi chatIDs separated by comma. Restart the panel to take effect"
-"telegramNotifyTime" = "Telegram bot notification time"
-"telegramNotifyTimeDesc" = "Using Crontab timing format. Restart the panel to take effect"
-"tgNotifyBackup" = "Database backup"
-"tgNotifyBackupDesc" = "Sending database backup file with report notification. Restart the panel to take effect"
-"sessionMaxAge" = "Session maximum age"
-"sessionMaxAgeDesc" = "The time that you can stay login (unit: minute)"
-"expireTimeDiff" = "Exhaustion time threshold"
-"expireTimeDiffDesc" = "Detect exhaustion before expiration (unit:day)"
-"trafficDiff" = "Exhaustion traffic threshold"
-"trafficDiffDesc" = "Detect exhaustion before finishing traffic (unit:GB)"
-"tgNotifyCpu" = "CPU percentage alert threshold"
-"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"
+"xrayConfigOutboundsDesc" = "Change the configuration template to define outgoing ways for this server. Restart the panel to apply changes."
+"xrayConfigRoutings" = "Configuration of routing rules."
+"xrayConfigRoutingsDesc" = "Change the configuration template to define routing rules for this server. Restart the panel to apply changes."
+
+[pages.settings.security]
+"admin" = "Admin"
+"secret" = "Secret Token"
 "loginSecurity" = "Login security"
-"loginSecurityDesc" = "Toggle additional step in user login page"
+"loginSecurityDesc" = "Enable additional user login security step"
 "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" 
+"secretTokenDesc" = "Please copy and securely store this token in a safe place. This token is required for login and cannot be recovered from the x-ui command tool."
 
-[pages.setting.toasts]
-"modifySetting" = "Modify setting"
-"getSetting" = "Get setting"
-"modifyUser" = "Modify user"
-"originalUserPassIncorrect" = "The original user name or original password is incorrect"
+[pages.settings.toasts]
+"modifySettings" = "Modify Settings "
+"getSettings" = "Get Settings "
+"modifyUser" = "Modify User "
+"originalUserPassIncorrect" = "Incorrect original username or password"
 "userPassMustBeNotEmpty" = "New username and new password cannot be empty"

+ 44 - 38
web/translation/translate.fa_IR.toml

@@ -52,8 +52,8 @@
 
 [menu]
 "dashboard" = "وضعیت سیستم"
-"inbounds" = "سرویس ها" 
-"setting" = "تنظیمات پنل"
+"inbounds" = "سرویس ها"
+"settings" = "تنظیمات پنل"
 "logout" = "خروج"
 "link" = "دیگر"
 
@@ -89,7 +89,7 @@
 "totalReceive" = "جمع کل ترافیک دانلود مصرفی"
 "xraySwitchVersionDialog" = "تغییر ورژن"
 "xraySwitchVersionDialogDesc" = "آیا از تغییر ورژن مطمئن هستین"
-"dontRefreshh" = "در حال نصب ، لطفا رفرش نکنید "
+"dontRefresh" = "در حال نصب ، لطفا رفرش نکنید "
 
 [pages.inbounds]
 "title" = "کاربران"
@@ -108,8 +108,8 @@
 "resetTraffic" = "ریست ترافیک"
 "addInbound" = "اضافه کردن سرویس"
 "generalActions" = "عملیات کلی"
-"addTo" = "اضافه کردن"
-"revise" = "ویرایش"
+"create" = "اضافه کردن"
+"update" = "ویرایش"
 "modifyInbound" = "ویرایش سرویس"
 "deleteInbound" = "حذف سرویس"
 "deleteInboundContent" = "آیا مطمئن به حذف سرویس هستید ؟"
@@ -134,7 +134,7 @@
 "clickOnQRcode" = "برای کپی بر روی کد تصویری کلیک کنید"
 "client" = "کاربر"
 "export" = "استخراج لینکها"
-"Clone" = "شبیه سازی"
+"clone" = "شبیه سازی"
 "cloneInbound" = "ایجاد"
 "cloneInboundContent" = "همه موارد این ورودی بجز پورت ، ای پی و کلاینت ها شبیه سازی خواهند شد"
 "cloneInboundOk" = "ساختن شبیه ساز"
@@ -197,18 +197,17 @@
 [pages.inbounds.stream.quic]
 "encryption" = "رمزنگاری"
 
-[pages.setting]
+[pages.settings]
 "title" = "تنظیمات"
 "save" = "ذخیره"
 "restartPanel" = "ریستارت پنل"
 "restartPanelDesc" = "آیا مطمئن هستید که می خواهید پنل را دوباره راه اندازی کنید؟ برای راه اندازی مجدد روی OK کلیک کنید. اگر بعد از 3 ثانیه نمی توانید به پنل دسترسی پیدا کنید، لطفاً برای مشاهده اطلاعات گزارش پانل به سرور برگردید"
 "actions" = "عملیات ها"
 "resetDefaultConfig" = "برگشت به تنظیمات پیشفرض"
-"panelConfig" = "تنظیمات پنل"
-"userSetting" = "تنظیمات مدیر"
+"panelSettings" = "تنظیمات پنل"
+"securitySettings" = "تنظیمات امنیتی"
 "xrayConfiguration" = "تنظیمات Xray"
-"TGReminder" = "تنظیمات ربات تلگرام"
-"otherSetting" = "دیگر تنظیمات"
+"TGBotSettings" = "تنظیمات ربات تلگرام"
 "panelListeningIP" = "محدودیت آی پی پنل"
 "panelListeningIPDesc" = "برای استفاده از تمام IP ها به طور پیش فرض خالی بگذارید. پنل را مجدداً راه اندازی کنید تا اعمال شود"
 "panelPort" = "پورت پنل"
@@ -223,9 +222,32 @@
 "currentPassword" = "رمز عبور فعلی"
 "newUsername" = "نام کاربری جدید"
 "newPassword" = "رمز عبور جدید"
-"basicTemplate" = "بخش پایه"
-"advancedTemplate" = "بخش های پیشرفته الگو"
-"completeTemplate" = "الگوی کامل تنظیمات ایکس ری"
+"telegramBotEnable" = "فعالسازی ربات تلگرام"
+"telegramBotEnableDesc" = "پنل را مجدداً راه اندازی کنید تا اعمال شود"
+"telegramToken" = "توکن تلگرام"
+"telegramTokenDesc" = "پنل را مجدداً راه اندازی کنید تا اعمال شود"
+"telegramChatId" = "آی دی تلگرام مدیریت"
+"telegramChatIdDesc" = "با استفاده از کاما میتونید چند آی دی را از هم جدا کنید. پنل را مجدداً راه اندازی کنید تا اعمال شود"
+"telegramNotifyTime" = "مدت زمان نوتیفیکیشن ربات تلگرام"
+"telegramNotifyTimeDesc" = "از فرمت زمان بندی لینوکس استفاده کنید . پنل را مجدداً راه اندازی کنید تا اعمال شود"
+"tgNotifyBackup" = "پشتیبان گیری از پایگاه داده"
+"tgNotifyBackupDesc" = "ارسال کپی فایل پایگاه داده به همراه گزارش دوره ای"
+"sessionMaxAge" = "بیشینه زمان جلسه وب"
+"sessionMaxAgeDesc" = "بیشینه زمانی که میتوانید لاگین بمانید (واحد: دقیقه)"
+"expireTimeDiff" = "آستانه زمان باقی مانده"
+"expireTimeDiffDesc" = "فاصله زمانی هشدار تا رسیدن به زمان انقضا (واحد: روز)"
+"trafficDiff" = "آستانه ترافیک باقی مانده"
+"trafficDiffDesc" = "فاصله زمانی هشدار تا رسیدن به اتمام ترافیک (واحد: گیگابایت)"
+"tgNotifyCpu" = "آستانه هشدار درصد پردازنده"
+"tgNotifyCpuDesc" = "این ربات تلگرام در صورت استفاده پردازنده بیشتر از این درصد برای شما پیام ارسال می کند.(واحد: درصد)"
+"timeZone" = "منظقه زمانی"
+"timeZoneDesc" = "وظایف برنامه ریزی شده بر اساس این منطقه زمانی اجرا می شوند. پنل را مجدداً راه اندازی می کند تا اعمال شود"
+
+[pages.settings.templates]
+"title" = "الگوها"
+"basicTemplate" = "بخش الگو پایه"
+"advancedTemplate" = "بخش الگو پیشرفته"
+"completeTemplate" = "بخش الگو کامل"
 "generalConfigs" = "تنظیمات عمومی"
 "generalConfigsDesc" = "این گزینه ها از اتصال کاربران به پروتکل ها و وب سایت های خاص جلوگیری می کند."
 "countryConfigs" = "تنظیمات برای کشورها"
@@ -276,34 +298,18 @@
 "xrayConfigOutboundsDesc" = "میتوانید الگوی تنظیمات را برای خروجی اینترنت تنظیم نمایید.  پنل را مجدداً راه اندازی کنید تا اعمال شود"
 "xrayConfigRoutings" = "تنظیمات قوانین مسیریابی"
 "xrayConfigRoutingsDesc" = "میتوانید الگوی تنظیمات را برای مسیریابی تنظیم نمایید.  پنل را مجدداً راه اندازی کنید تا اعمال شود"
-"telegramBotEnable" = "فعالسازی ربات تلگرام"
-"telegramBotEnableDesc" = "پنل را مجدداً راه اندازی کنید تا اعمال شود"
-"telegramToken" = "توکن تلگرام"
-"telegramTokenDesc" = "پنل را مجدداً راه اندازی کنید تا اعمال شود"
-"telegramChatId" = "آی دی تلگرام مدیریت"
-"telegramChatIdDesc" = "با استفاده از کاما میتونید چند آی دی را از هم جدا کنید. پنل را مجدداً راه اندازی کنید تا اعمال شود"
-"telegramNotifyTime" = "مدت زمان نوتیفیکیشن ربات تلگرام"
-"telegramNotifyTimeDesc" = "از فرمت زمان بندی لینوکس استفاده کنید . پنل را مجدداً راه اندازی کنید تا اعمال شود"
-"tgNotifyBackup" = "پشتیبان گیری از پایگاه داده"
-"tgNotifyBackupDesc" = "ارسال کپی فایل پایگاه داده به همراه گزارش دوره ای"
-"sessionMaxAge" = "بیشینه زمان جلسه وب"
-"sessionMaxAgeDesc" = "بیشینه زمانی که میتوانید لاگین بمانید (واحد: دقیقه)"
-"expireTimeDiff" = "آستانه زمان باقی مانده"
-"expireTimeDiffDesc" = "فاصله زمانی هشدار تا رسیدن به زمان انقضا (واحد: روز)"
-"trafficDiff" = "آستانه ترافیک باقی مانده"
-"trafficDiffDesc" = "فاصله زمانی هشدار تا رسیدن به اتمام ترافیک (واحد: گیگابایت)"
-"tgNotifyCpu" = "آستانه هشدار درصد پردازنده"
-"tgNotifyCpuDesc" = "این ربات تلگرام در صورت استفاده پردازنده بیشتر از این درصد برای شما پیام ارسال می کند.(واحد: درصد)"
-"timeZonee" = "منظقه زمانی"
-"timeZoneDesc" = "وظایف برنامه ریزی شده بر اساس این منطقه زمانی اجرا می شوند. پنل را مجدداً راه اندازی می کند تا اعمال شود"
+
+[pages.settings.security]
+"admin" = "مدیر"
+"secret" = "توکن امنیتی"
 "loginSecurity" = "لاگین ایمن"
 "loginSecurityDesc" = "افزودن یک مرحله دیگر به فرآیند لاگین"
 "secretToken" = "توکن امنیتی"
-"secretTokenDesc" = "این کد امنیتی را نزد خود در این جای امن نگه داری، بدون این کد امکان ورود به پنل را نخواهید داشت. امکان بازیابی آن وجود ندارد!" 
+"secretTokenDesc" = "این کد امنیتی را نزد خود در این جای امن نگه داری، بدون این کد امکان ورود به پنل را نخواهید داشت. امکان بازیابی آن وجود ندارد!"
 
-[pages.setting.toasts]
-"modifySetting" = "ویرایش تنظیمات"
-"getSetting" = "دریافت تنظیمات"
+[pages.settings.toasts]
+"modifySettings" = "ویرایش تنظیمات"
+"getSettings" = "دریافت تنظیمات"
 "modifyUser" = "ویرایش کاربر"
 "originalUserPassIncorrect" = "نام کاربری و رمز عبور فعلی اشتباه می باشد ."
 "userPassMustBeNotEmpty" = "نام کاربری و رمز عبور جدید نمیتواند خالی باشد ."

+ 39 - 33
web/translation/translate.zh_Hans.toml

@@ -53,7 +53,7 @@
 [menu]
 "dashboard" = "系统状态"
 "inbounds" = "入站列表"
-"setting" = "面板设置"
+"settings" = "面板设置"
 "logout" = "退出登录"
 "link" = "其他"
 
@@ -89,7 +89,7 @@
 "totalReceive" = "系统启动以来所有网卡的总下载流量"
 "xraySwitchVersionDialog" = "切换 xray 版本"
 "xraySwitchVersionDialogDesc" = "是否切换 xray 版本至"
-"dontRefreshh" = "安装中,请不要刷新此页面"
+"dontRefresh" = "安装中,请不要刷新此页面"
 
 [pages.inbounds]
 "title" = "入站列表"
@@ -108,8 +108,8 @@
 "resetTraffic" = "重置流量"
 "addInbound" = "添加入"
 "generalActions" = "通用操作"
-"addTo" = "添加"
-"revise" = "修改"
+"create" = "添加"
+"update" = "修改"
 "modifyInbound" = "修改入站"
 "deleteInbound" = "删除入站"
 "deleteInboundContent" = "确定要删除入站吗?"
@@ -134,7 +134,7 @@
 "clickOnQRcode" = "点击二维码复制"
 "client" = "客户"
 "export" = "导出链接"
-"Clone" = "克隆"
+"clone" = "克隆"
 "cloneInbound" = "创造"
 "cloneInboundContent" = "此入站的所有项目除 Port、Listening IP、Clients 将应用于克隆"
 "cloneInboundOk" = "从创建克隆"
@@ -197,18 +197,17 @@
 [pages.inbounds.stream.quic]
 "encryption" = "加密"
 
-[pages.setting]
+[pages.settings]
 "title" = "设置"
 "save" = "保存配置"
 "restartPanel" = "重启面板"
 "restartPanelDesc" = "确定要重启面板吗?点击确定将于 3 秒后重启,若重启后无法访问面板,请前往服务器查看面板日志信息"
 "actions" = "动作"
 "resetDefaultConfig" = "重置为默认配置"
-"panelConfig" = "面板配置"
-"userSetting" = "用户设置"
+"panelSettings" = "面板配置"
+"securitySettings" = "安全设定"
 "xrayConfiguration" = "xray 相关设置"
-"TGReminder" = "TG提醒相关设置"
-"otherSetting" = "其他设置"
+"TGBotSettings" = "TG提醒相关设置"
 "panelListeningIP" = "面板监听 IP"
 "panelListeningIPDesc" = "默认留空监听所有 IP,重启面板生效"
 "panelPort" = "面板监听端口"
@@ -223,6 +222,29 @@
 "currentPassword" = "原密码"
 "newUsername" = "新用户名"
 "newPassword" = "新密码"
+"telegramBotEnable" = "启用电报机器人"
+"telegramBotEnableDesc" = "重启面板生效"
+"telegramToken" = "电报机器人TOKEN"
+"telegramTokenDesc" = "重启面板生效"
+"telegramChatId" = "以逗号分隔的多个 chatID 重启面板生效"
+"telegramChatIdDesc" = "重启面板生效"
+"telegramNotifyTime" = "电报机器人通知时间"
+"telegramNotifyTimeDesc" = "采用Crontab定时格式,重启面板生效"
+"tgNotifyBackup" = "数据库备份"
+"tgNotifyBackupDesc" = "正在发送数据库备份文件和报告通知。重启面板生效"
+"sessionMaxAge" = "会话最大年龄"
+"sessionMaxAgeDesc" = "您可以保持登录状态的时间(单位:分钟)"
+"expireTimeDiff" = "耗尽时间阈值"
+"expireTimeDiffDesc" = "到期前检测耗尽(单位:天)"
+"trafficDiff" = "耗尽流量阈值"
+"trafficDiffDesc" = "完成流量前检测耗尽(单位:GB)"
+"tgNotifyCpu" = "CPU 百分比警报阈值"
+"tgNotifyCpuDesc" = "如果 CPU 使用率超过此百分比(单位:%),此 talegram bot 将向您发送通知"
+"timeZone" = "时区"
+"timeZoneDesc" = "定时任务按照该时区的时间运行,重启面板生效"
+
+[pages.settings.templates]
+"title" = "模板"
 "basicTemplate" = "基本模板"
 "advancedTemplate" = "高级模板部件"
 "completeTemplate" = "Xray 配置的完整模板"
@@ -276,34 +298,18 @@
 "xrayConfigOutboundsDesc" = "更改配置模板定义此服务器的传出方式,重启面板生效"
 "xrayConfigRoutings" = "路由规则配置"
 "xrayConfigRoutingsDesc" = "更改配置模板为该服务器定义路由规则,重启面板生效"
-"telegramBotEnable" = "启用电报机器人"
-"telegramBotEnableDesc" = "重启面板生效"
-"telegramToken" = "电报机器人TOKEN"
-"telegramTokenDesc" = "重启面板生效"
-"telegramChatId" = "以逗号分隔的多个 chatID 重启面板生效"
-"telegramChatIdDesc" = "重启面板生效"
-"telegramNotifyTime" = "电报机器人通知时间"
-"telegramNotifyTimeDesc" = "采用Crontab定时格式,重启面板生效"
-"tgNotifyBackup" = "数据库备份"
-"tgNotifyBackupDesc" = "正在发送数据库备份文件和报告通知。重启面板生效"
-"sessionMaxAge" = "会话最大年龄"
-"sessionMaxAgeDesc" = "您可以保持登录状态的时间(单位:分钟)"
-"expireTimeDiff" = "耗尽时间阈值"
-"expireTimeDiffDesc" = "到期前检测耗尽(单位:天)"
-"trafficDiff" = "耗尽流量阈值"
-"trafficDiffDesc" = "完成流量前检测耗尽(单位:GB)"
-"tgNotifyCpu" = "CPU 百分比警报阈值"
-"tgNotifyCpuDesc" = "如果 CPU 使用率超过此百分比(单位:%),此 talegram bot 将向您发送通知"
-"timeZonee" = "时区"
-"timeZoneDesc" = "定时任务按照该时区的时间运行,重启面板生效"
+
+[pages.settings.security]
+"admin" = "行政"
+"secret" = "秘密令牌"
 "loginSecurity" = "登录安全"
 "loginSecurityDesc" = "在用户登录页面中切换附加步骤"
 "secretToken" = "秘密令牌"
 "secretTokenDesc" = "复制此秘密令牌并将其保存在安全的地方;没有这个你将无法登录。这也无法从 x-ui 命令工具中恢复" 
 
-[pages.setting.toasts]
-"modifySetting" = "修改设置"
-"getSetting" = "获取设置"
+[pages.settings.toasts]
+"modifySettings" = "修改设置"
+"getSettings" = "获取设置"
 "modifyUser" = "修改用户"
 "originalUserPassIncorrect" = "原用户名或原密码错误"
 "userPassMustBeNotEmpty" = "新用户名和新密码不能为空"