mirror of
https://github.com/alireza0/x-ui.git
synced 2026-03-14 05:23:09 +00:00
Merge pull request #26 from foroughi1380/main
Add support for Multi-Language
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -10,3 +10,4 @@ x-ui-*.tar.gz
|
||||
main
|
||||
release/
|
||||
access.log
|
||||
.cache
|
||||
|
||||
79
web/assets/js/langs.js
Normal file
79
web/assets/js/langs.js
Normal file
@@ -0,0 +1,79 @@
|
||||
supportLangs = [
|
||||
{
|
||||
name : "English",
|
||||
value : "en-US",
|
||||
icon : "🇺🇸"
|
||||
},
|
||||
{
|
||||
name : "汉语",
|
||||
value : "zh-Hans",
|
||||
icon : "🇨🇳"
|
||||
},
|
||||
]
|
||||
|
||||
function getLang(){
|
||||
let lang = getCookie('lang')
|
||||
|
||||
if (! lang){
|
||||
if (window.navigator){
|
||||
lang = window.navigator.language || window.navigator.userLanguage;
|
||||
|
||||
if (isSupportLang(lang)){
|
||||
setCookie('lang' , lang , 150)
|
||||
}else{
|
||||
setCookie('lang' , 'en-US' , 150)
|
||||
window.location.reload();
|
||||
}
|
||||
}else{
|
||||
setCookie('lang' , 'en-US' , 150)
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
|
||||
return lang;
|
||||
}
|
||||
|
||||
function setLang(lang){
|
||||
|
||||
if (!isSupportLang(lang)){
|
||||
lang = 'en-US';
|
||||
}
|
||||
|
||||
setCookie('lang' , lang , 150)
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
function isSupportLang(lang){
|
||||
for (l of supportLangs){
|
||||
if (l.value === lang){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function getCookie(cname) {
|
||||
let name = cname + "=";
|
||||
let decodedCookie = decodeURIComponent(document.cookie);
|
||||
let ca = decodedCookie.split(';');
|
||||
for(let i = 0; i <ca.length; i++) {
|
||||
let c = ca[i];
|
||||
while (c.charAt(0) == ' ') {
|
||||
c = c.substring(1);
|
||||
}
|
||||
if (c.indexOf(name) == 0) {
|
||||
return c.substring(name.length, c.length);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function setCookie(cname, cvalue, exdays) {
|
||||
const d = new Date();
|
||||
d.setTime(d.getTime() + (exdays*24*60*60*1000));
|
||||
let expires = "expires="+ d.toUTCString();
|
||||
document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
|
||||
}
|
||||
@@ -33,13 +33,13 @@ function safeBase64(str) {
|
||||
|
||||
function formatSecond(second) {
|
||||
if (second < 60) {
|
||||
return second.toFixed(0) + ' 秒';
|
||||
return second.toFixed(0) + ' s';
|
||||
} else if (second < 3600) {
|
||||
return (second / 60).toFixed(0) + ' 分钟';
|
||||
return (second / 60).toFixed(0) + ' m';
|
||||
} else if (second < 3600 * 24) {
|
||||
return (second / 3600).toFixed(0) + ' 小时';
|
||||
return (second / 3600).toFixed(0) + ' h';
|
||||
} else {
|
||||
return (second / 3600 / 24).toFixed(0) + ' 天';
|
||||
return (second / 3600 / 24).toFixed(0) + ' d';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ type BaseController struct {
|
||||
func (a *BaseController) checkLogin(c *gin.Context) {
|
||||
if !session.IsLogin(c) {
|
||||
if isAjax(c) {
|
||||
pureJsonMsg(c, false, "登录时效已过,请重新登录")
|
||||
pureJsonMsg(c, false, I18n(c , "pages.login.loginAgain"))
|
||||
} else {
|
||||
c.Redirect(http.StatusTemporaryRedirect, c.GetString("base_path"))
|
||||
}
|
||||
@@ -21,3 +21,13 @@ func (a *BaseController) checkLogin(c *gin.Context) {
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func I18n(c *gin.Context , name string, data ...string) string{
|
||||
anyfunc, _ := c.Get("I18n")
|
||||
i18n, _ := anyfunc.(func(key string, params ...string) (string, error))
|
||||
|
||||
message, _ := i18n(name)
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ func (a *InboundController) getInbounds(c *gin.Context) {
|
||||
user := session.GetLoginUser(c)
|
||||
inbounds, err := a.inboundService.GetInbounds(user.Id)
|
||||
if err != nil {
|
||||
jsonMsg(c, "获取", err)
|
||||
jsonMsg(c, I18n(c , "pages.inbounds.toasts.obtain"), err)
|
||||
return
|
||||
}
|
||||
jsonObj(c, inbounds, nil)
|
||||
@@ -64,7 +64,7 @@ func (a *InboundController) addInbound(c *gin.Context) {
|
||||
inbound := &model.Inbound{}
|
||||
err := c.ShouldBind(inbound)
|
||||
if err != nil {
|
||||
jsonMsg(c, "添加", err)
|
||||
jsonMsg(c, I18n(c , "pages.inbounds.addTo"), err)
|
||||
return
|
||||
}
|
||||
user := session.GetLoginUser(c)
|
||||
@@ -72,7 +72,7 @@ func (a *InboundController) addInbound(c *gin.Context) {
|
||||
inbound.Enable = true
|
||||
inbound.Tag = fmt.Sprintf("inbound-%v", inbound.Port)
|
||||
err = a.inboundService.AddInbound(inbound)
|
||||
jsonMsg(c, "添加", err)
|
||||
jsonMsg(c, I18n(c , "pages.inbounds.addTo"), err)
|
||||
if err == nil {
|
||||
a.xrayService.SetToNeedRestart()
|
||||
}
|
||||
@@ -81,11 +81,11 @@ func (a *InboundController) addInbound(c *gin.Context) {
|
||||
func (a *InboundController) delInbound(c *gin.Context) {
|
||||
id, err := strconv.Atoi(c.Param("id"))
|
||||
if err != nil {
|
||||
jsonMsg(c, "删除", err)
|
||||
jsonMsg(c, I18n(c , "delete"), err)
|
||||
return
|
||||
}
|
||||
err = a.inboundService.DelInbound(id)
|
||||
jsonMsg(c, "删除", err)
|
||||
jsonMsg(c, I18n(c , "delete"), err)
|
||||
if err == nil {
|
||||
a.xrayService.SetToNeedRestart()
|
||||
}
|
||||
@@ -94,7 +94,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, "修改", err)
|
||||
jsonMsg(c, I18n(c , "pages.inbounds.revise"), err)
|
||||
return
|
||||
}
|
||||
inbound := &model.Inbound{
|
||||
@@ -102,11 +102,11 @@ func (a *InboundController) updateInbound(c *gin.Context) {
|
||||
}
|
||||
err = c.ShouldBind(inbound)
|
||||
if err != nil {
|
||||
jsonMsg(c, "修改", err)
|
||||
jsonMsg(c, I18n(c , "pages.inbounds.revise"), err)
|
||||
return
|
||||
}
|
||||
err = a.inboundService.UpdateInbound(inbound)
|
||||
jsonMsg(c, "修改", err)
|
||||
jsonMsg(c, I18n(c , "pages.inbounds.revise"), err)
|
||||
if err == nil {
|
||||
a.xrayService.SetToNeedRestart()
|
||||
}
|
||||
|
||||
@@ -39,22 +39,22 @@ func (a *IndexController) index(c *gin.Context) {
|
||||
c.Redirect(http.StatusTemporaryRedirect, "xui/")
|
||||
return
|
||||
}
|
||||
html(c, "login.html", "登录", nil)
|
||||
html(c, "login.html", "pages.login.title", nil)
|
||||
}
|
||||
|
||||
func (a *IndexController) login(c *gin.Context) {
|
||||
var form LoginForm
|
||||
err := c.ShouldBind(&form)
|
||||
if err != nil {
|
||||
pureJsonMsg(c, false, "数据格式错误")
|
||||
pureJsonMsg(c, false, I18n(c , "pages.login.toasts.invalidFormData"))
|
||||
return
|
||||
}
|
||||
if form.Username == "" {
|
||||
pureJsonMsg(c, false, "请输入用户名")
|
||||
pureJsonMsg(c, false, I18n(c, "pages.login.toasts.emptyUsername"))
|
||||
return
|
||||
}
|
||||
if form.Password == "" {
|
||||
pureJsonMsg(c, false, "请输入密码")
|
||||
pureJsonMsg(c, false, I18n(c , "pages.login.toasts.emptyPassword"))
|
||||
return
|
||||
}
|
||||
user := a.userService.CheckUser(form.Username, form.Password)
|
||||
@@ -62,7 +62,7 @@ func (a *IndexController) login(c *gin.Context) {
|
||||
if user == nil {
|
||||
job.NewStatsNotifyJob().UserLoginNotify(form.Username, getRemoteIp(c), timeStr, 0)
|
||||
logger.Infof("wrong username or password: \"%s\" \"%s\"", form.Username, form.Password)
|
||||
pureJsonMsg(c, false, "用户名或密码错误")
|
||||
pureJsonMsg(c, false, I18n(c , "pages.login.toasts.wrongUsernameOrPassword"))
|
||||
return
|
||||
} else {
|
||||
logger.Infof("%s login success,Ip Address:%s\n", form.Username, getRemoteIp(c))
|
||||
@@ -71,7 +71,7 @@ func (a *IndexController) login(c *gin.Context) {
|
||||
|
||||
err = session.SetLoginUser(c, user)
|
||||
logger.Info("user", user.Id, "login success")
|
||||
jsonMsg(c, "登录", err)
|
||||
jsonMsg(c, I18n(c , "pages.login.toasts.successLogin"), err)
|
||||
}
|
||||
|
||||
func (a *IndexController) logout(c *gin.Context) {
|
||||
|
||||
@@ -68,7 +68,7 @@ func (a *ServerController) getXrayVersion(c *gin.Context) {
|
||||
|
||||
versions, err := a.serverService.GetXrayVersions()
|
||||
if err != nil {
|
||||
jsonMsg(c, "获取版本", err)
|
||||
jsonMsg(c, I18n(c , "getVersion"), err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -81,5 +81,5 @@ func (a *ServerController) getXrayVersion(c *gin.Context) {
|
||||
func (a *ServerController) installXray(c *gin.Context) {
|
||||
version := c.Param("version")
|
||||
err := a.serverService.UpdateXray(version)
|
||||
jsonMsg(c, "安装 xray", err)
|
||||
jsonMsg(c, I18n(c , "install") + " xray", err)
|
||||
}
|
||||
|
||||
@@ -40,7 +40,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, "获取设置", err)
|
||||
jsonMsg(c, I18n(c , "pages.setting.toasts.getSetting"), err)
|
||||
return
|
||||
}
|
||||
jsonObj(c, allSetting, nil)
|
||||
@@ -50,27 +50,27 @@ func (a *SettingController) updateSetting(c *gin.Context) {
|
||||
allSetting := &entity.AllSetting{}
|
||||
err := c.ShouldBind(allSetting)
|
||||
if err != nil {
|
||||
jsonMsg(c, "修改设置", err)
|
||||
jsonMsg(c, I18n(c , "pages.setting.toasts.modifySetting"), err)
|
||||
return
|
||||
}
|
||||
err = a.settingService.UpdateAllSetting(allSetting)
|
||||
jsonMsg(c, "修改设置", err)
|
||||
jsonMsg(c, I18n(c , "pages.setting.toasts.modifySetting"), err)
|
||||
}
|
||||
|
||||
func (a *SettingController) updateUser(c *gin.Context) {
|
||||
form := &updateUserForm{}
|
||||
err := c.ShouldBind(form)
|
||||
if err != nil {
|
||||
jsonMsg(c, "修改用户", err)
|
||||
jsonMsg(c, I18n(c , "pages.setting.toasts.modifySetting"), err)
|
||||
return
|
||||
}
|
||||
user := session.GetLoginUser(c)
|
||||
if user.Username != form.OldUsername || user.Password != form.OldPassword {
|
||||
jsonMsg(c, "修改用户", errors.New("原用户名或原密码错误"))
|
||||
jsonMsg(c, I18n(c , "pages.setting.toasts.modifyUser"), errors.New(I18n(c , "pages.setting.toasts.originalUserPassIncorrect")))
|
||||
return
|
||||
}
|
||||
if form.NewUsername == "" || form.NewPassword == "" {
|
||||
jsonMsg(c, "修改用户", errors.New("新用户名和新密码不能为空"))
|
||||
jsonMsg(c,I18n(c , "pages.setting.toasts.modifyUser"), errors.New(I18n(c , "pages.setting.toasts.userPassMustBeNotEmpty")))
|
||||
return
|
||||
}
|
||||
err = a.userService.UpdateUser(user.Id, form.NewUsername, form.NewPassword)
|
||||
@@ -79,10 +79,10 @@ func (a *SettingController) updateUser(c *gin.Context) {
|
||||
user.Password = form.NewPassword
|
||||
session.SetLoginUser(c, user)
|
||||
}
|
||||
jsonMsg(c, "修改用户", err)
|
||||
jsonMsg(c, I18n(c , "pages.setting.toasts.modifyUser"), err)
|
||||
}
|
||||
|
||||
func (a *SettingController) restartPanel(c *gin.Context) {
|
||||
err := a.panelService.RestartPanel(time.Second * 3)
|
||||
jsonMsg(c, "重启面板", err)
|
||||
jsonMsg(c, I18n(c , "pages.setting.restartPanel"), err)
|
||||
}
|
||||
|
||||
@@ -46,12 +46,12 @@ func jsonMsgObj(c *gin.Context, msg string, obj interface{}, err error) {
|
||||
if err == nil {
|
||||
m.Success = true
|
||||
if msg != "" {
|
||||
m.Msg = msg + "成功"
|
||||
m.Msg = msg + I18n(c , "success")
|
||||
}
|
||||
} else {
|
||||
m.Success = false
|
||||
m.Msg = msg + "失败: " + err.Error()
|
||||
logger.Warning(msg+"失败: ", err)
|
||||
m.Msg = msg + I18n(c , "fail") + ": " + err.Error()
|
||||
logger.Warning(msg + I18n(c , "fail") + ": ", err)
|
||||
}
|
||||
c.JSON(http.StatusOK, m)
|
||||
}
|
||||
|
||||
@@ -30,13 +30,13 @@ func (a *XUIController) initRouter(g *gin.RouterGroup) {
|
||||
}
|
||||
|
||||
func (a *XUIController) index(c *gin.Context) {
|
||||
html(c, "index.html", "系统状态", nil)
|
||||
html(c, "index.html", "pages.index.title", nil)
|
||||
}
|
||||
|
||||
func (a *XUIController) inbounds(c *gin.Context) {
|
||||
html(c, "inbounds.html", "入站列表", nil)
|
||||
html(c, "inbounds.html", "pages.inbounds.title", nil)
|
||||
}
|
||||
|
||||
func (a *XUIController) setting(c *gin.Context) {
|
||||
html(c, "setting.html", "设置", nil)
|
||||
html(c, "setting.html", "pages.setting.title", nil)
|
||||
}
|
||||
|
||||
@@ -12,6 +12,6 @@
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<title>{{.title}}</title>
|
||||
<title>{{ i18n .title}}</title>
|
||||
</head>
|
||||
{{end}}
|
||||
@@ -14,6 +14,7 @@
|
||||
<script src="{{ .base_path }}assets/js/util/utils.js?{{ .cur_ver }}"></script>
|
||||
<script src="{{ .base_path }}assets/js/model/xray.js?{{ .cur_ver }}"></script>
|
||||
<script src="{{ .base_path }}assets/js/model/models.js?{{ .cur_ver }}"></script>
|
||||
<script src="{{ .base_path }}assets/js/langs.js"></script>
|
||||
<script>
|
||||
const basePath = '{{ .base_path }}';
|
||||
axios.defaults.baseURL = basePath;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{{define "promptModal"}}
|
||||
<a-modal id="prompt-modal" v-model="promptModal.visible" :title="promptModal.title"
|
||||
:closable="true" @ok="promptModal.ok" :mask-closable="false"
|
||||
:ok-text="promptModal.okText" cancel-text="取消">
|
||||
:ok-text="promptModal.okText" cancel-text='{{ i18n "cancel" }}'>
|
||||
<a-input id="prompt-modal-input" :type="promptModal.type"
|
||||
v-model="promptModal.value"
|
||||
:autosize="{minRows: 10, maxRows: 20}"
|
||||
@@ -15,7 +15,7 @@
|
||||
title: '',
|
||||
type: '',
|
||||
value: '',
|
||||
okText: '确定',
|
||||
okText: '{{ i18n "sure"}}',
|
||||
visible: false,
|
||||
keyEnter(e) {
|
||||
if (this.type !== 'textarea') {
|
||||
@@ -38,7 +38,7 @@
|
||||
title='',
|
||||
type='text',
|
||||
value='',
|
||||
okText='确定',
|
||||
okText='{{ i18n "sure"}}',
|
||||
confirm=() => {},
|
||||
}) {
|
||||
this.title = title;
|
||||
|
||||
@@ -26,6 +26,12 @@
|
||||
padding-left: 50px;
|
||||
}
|
||||
|
||||
.selectLang{
|
||||
display: flex;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
</style>
|
||||
<body>
|
||||
<a-layout id="app" v-cloak>
|
||||
@@ -33,7 +39,7 @@
|
||||
<a-layout-content>
|
||||
<a-row type="flex" justify="center">
|
||||
<a-col :xs="22" :sm="20" :md="16" :lg="12" :xl="8">
|
||||
<h1>{{ .title }}</h1>
|
||||
<h1>{{ i18n "pages.login.title" }}</h1>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row type="flex" justify="center">
|
||||
@@ -53,6 +59,29 @@
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-button block @click="login" :loading="loading">{{ i18n "login" }}</a-button>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
|
||||
<a-row justify="center" class="selectLang">
|
||||
<a-col :span="4"><span>Language : </span></a-col>
|
||||
|
||||
<a-col :span="6">
|
||||
<a-select
|
||||
ref="selectLang"
|
||||
v-model="lang"
|
||||
@change="setLang(lang)"
|
||||
>
|
||||
<a-select-option :value="l.value" label="China" v-for="l in supportLangs" >
|
||||
<span role="img" aria-label="l.name" v-text="l.icon"></span>
|
||||
<span v-text="l.name"></span>
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-col>
|
||||
|
||||
</a-row>
|
||||
|
||||
|
||||
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-col>
|
||||
@@ -73,6 +102,10 @@
|
||||
data: {
|
||||
loading: false,
|
||||
user: new User(),
|
||||
lang : ""
|
||||
},
|
||||
created(){
|
||||
this.lang = getLang();
|
||||
},
|
||||
methods: {
|
||||
async login() {
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{{define "menuItems"}}
|
||||
<a-menu-item key="{{ .base_path }}xui/">
|
||||
<a-icon type="dashboard"></a-icon>
|
||||
<span>系统状态</span>
|
||||
<span>{{ i18n "menu.dashboard"}}</span>
|
||||
</a-menu-item>
|
||||
<a-menu-item key="{{ .base_path }}xui/inbounds">
|
||||
<a-icon type="user"></a-icon>
|
||||
<span>入站列表</span>
|
||||
<span>{{ i18n "menu.inbounds"}}</span>
|
||||
</a-menu-item>
|
||||
<a-menu-item key="{{ .base_path }}xui/setting">
|
||||
<a-icon type="setting"></a-icon>
|
||||
<span>面板设置</span>
|
||||
<span>{{ i18n "menu.setting"}}</span>
|
||||
</a-menu-item>
|
||||
<!--<a-menu-item key="{{ .base_path }}xui/clients">-->
|
||||
<!-- <a-icon type="laptop"></a-icon>-->
|
||||
@@ -18,7 +18,7 @@
|
||||
<a-sub-menu>
|
||||
<template slot="title">
|
||||
<a-icon type="link"></a-icon>
|
||||
<span>其他</span>
|
||||
<span>{{ i18n "menu.link"}}</span>
|
||||
</template>
|
||||
<a-menu-item key="https://github.com/vaxilu/x-ui/">
|
||||
<a-icon type="github"></a-icon>
|
||||
@@ -27,7 +27,7 @@
|
||||
</a-sub-menu>
|
||||
<a-menu-item key="{{ .base_path }}logout">
|
||||
<a-icon type="logout"></a-icon>
|
||||
<span>退出登录</span>
|
||||
<span>{{ i18n "menu.logout"}}</span>
|
||||
</a-menu-item>
|
||||
{{end}}
|
||||
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
{{define "inboundInfoStream"}}
|
||||
<p>传输: <a-tag color="green">[[ inbound.network ]]</a-tag></p>
|
||||
<p>{{ i18n "transmission" }}: <a-tag color="green">[[ inbound.network ]]</a-tag></p>
|
||||
|
||||
<template v-if="inbound.isTcp || inbound.isWs || inbound.isH2">
|
||||
<p v-if="inbound.host">host: <a-tag color="green">[[ inbound.host ]]</a-tag></p>
|
||||
<p v-else>host: <a-tag color="orange">无</a-tag></p>
|
||||
<p v-else>{{ i18n "host" }}: <a-tag color="orange">{{ i18n "none" }}</a-tag></p>
|
||||
|
||||
<p v-if="inbound.path">path: <a-tag color="green">[[ inbound.path ]]</a-tag></p>
|
||||
<p v-else>path: <a-tag color="orange">无</a-tag></p>
|
||||
<p v-else>{{ i18n "path" }}: <a-tag color="orange">{{ i18n "none" }}</a-tag></p>
|
||||
</template>
|
||||
|
||||
<template v-if="inbound.isQuic">
|
||||
<p>quic 加密: <a-tag color="green">[[ inbound.quicSecurity ]]</a-tag></p>
|
||||
<p>quic 密码: <a-tag color="green">[[ inbound.quicKey ]]</a-tag></p>
|
||||
<p>quic 伪装: <a-tag color="green">[[ inbound.quicType ]]</a-tag></p>
|
||||
<p>quic {{ i18n "encryption" }}: <a-tag color="green">[[ inbound.quicSecurity ]]</a-tag></p>
|
||||
<p>quic {{ i18n "password" }}: <a-tag color="green">[[ inbound.quicKey ]]</a-tag></p>
|
||||
<p>quic {{ i18n "camouflage" }}: <a-tag color="green">[[ inbound.quicType ]]</a-tag></p>
|
||||
</template>
|
||||
|
||||
<template v-if="inbound.isKcp">
|
||||
<p>kcp 加密: <a-tag color="green">[[ inbound.kcpType ]]</a-tag></p>
|
||||
<p>kcp 密码: <a-tag color="green">[[ inbound.kcpSeed ]]</a-tag></p>
|
||||
<p>kcp {{ i18n "encryption" }}: <a-tag color="green">[[ inbound.kcpType ]]</a-tag></p>
|
||||
<p>kcp {{ i18n "password" }}: <a-tag color="green">[[ inbound.kcpSeed ]]</a-tag></p>
|
||||
</template>
|
||||
|
||||
<template v-if="inbound.isGrpc">
|
||||
@@ -25,26 +25,26 @@
|
||||
</template>
|
||||
|
||||
<template v-if="inbound.tls || inbound.xtls">
|
||||
<p v-if="inbound.tls">tls: <a-tag color="green">开启</a-tag></p>
|
||||
<p v-if="inbound.xtls">xtls: <a-tag color="green">开启</a-tag></p>
|
||||
<p v-if="inbound.tls">tls: <a-tag color="green">{{ i18n "turnOn" }}</a-tag></p>
|
||||
<p v-if="inbound.xtls">xtls: <a-tag color="green">{{ i18n "turnOn" }}</a-tag></p>
|
||||
</template>
|
||||
<template v-else>
|
||||
<p>tls: <a-tag color="red">关闭</a-tag></p>
|
||||
<p>tls: <a-tag color="red">{{ i18n "closure" }}</a-tag></p>
|
||||
</template>
|
||||
<p v-if="inbound.tls">
|
||||
tls域名: <a-tag :color="inbound.serverName ? 'green' : 'orange'">[[ inbound.serverName ? inbound.serverName : "无" ]]</a-tag>
|
||||
tls {{ i18n "domainName" }}: <a-tag :color="inbound.serverName ? 'green' : 'orange'">[[ inbound.serverName ? inbound.serverName : '{{ i18n "none" }}' ]]</a-tag>
|
||||
</p>
|
||||
<p v-if="inbound.xtls">
|
||||
xtls域名: <a-tag :color="inbound.serverName ? 'green' : 'orange'">[[ inbound.serverName ? inbound.serverName : "无" ]]</a-tag>
|
||||
xtls {{ i18n "domainName" }}: <a-tag :color="inbound.serverName ? 'green' : 'orange'">[[ inbound.serverName ? inbound.serverName : '{{ i18n "none" }}' ]]</a-tag>
|
||||
</p>
|
||||
{{end}}
|
||||
|
||||
|
||||
{{define "component/inboundInfoComponent"}}
|
||||
<div>
|
||||
<p>协议: <a-tag color="green">[[ dbInbound.protocol ]]</a-tag></p>
|
||||
<p>地址: <a-tag color="blue">[[ dbInbound.address ]]</a-tag></p>
|
||||
<p>端口: <a-tag color="green">[[ dbInbound.port ]]</a-tag></p>
|
||||
<p>{{ i18n "protocol"}}: <a-tag color="green">[[ dbInbound.protocol ]]</a-tag></p>
|
||||
<p>{{ i18n "pages.inbounds.address"}}: <a-tag color="blue">[[ dbInbound.address ]]</a-tag></p>
|
||||
<p>{{ i18n "pages.inbounds.port"}}: <a-tag color="green">[[ dbInbound.port ]]</a-tag></p>
|
||||
|
||||
<template v-if="dbInbound.isVMess">
|
||||
<p>uuid: <a-tag color="green">[[ inbound.uuid ]]</a-tag></p>
|
||||
@@ -57,22 +57,22 @@
|
||||
</template>
|
||||
|
||||
<template v-if="dbInbound.isTrojan">
|
||||
<p>密码: <a-tag color="green">[[ inbound.password ]]</a-tag></p>
|
||||
<p>{{ i18n "password"}}: <a-tag color="green">[[ inbound.password ]]</a-tag></p>
|
||||
</template>
|
||||
|
||||
<template v-if="dbInbound.isSS">
|
||||
<p>加密: <a-tag color="green">[[ inbound.method ]]</a-tag></p>
|
||||
<p>密码: <a-tag color="green">[[ inbound.password ]]</a-tag></p>
|
||||
<p>{{ i18n "encryption"}}: <a-tag color="green">[[ inbound.method ]]</a-tag></p>
|
||||
<p>{{ i18n "password"}}: <a-tag color="green">[[ inbound.password ]]</a-tag></p>
|
||||
</template>
|
||||
|
||||
<template v-if="dbInbound.isSocks">
|
||||
<p>用户名: <a-tag color="green">[[ inbound.username ]]</a-tag></p>
|
||||
<p>密码: <a-tag color="green">[[ inbound.password ]]</a-tag></p>
|
||||
<p>{{ i18n "username"}}: <a-tag color="green">[[ inbound.username ]]</a-tag></p>
|
||||
<p>{{ i18n "password"}}: <a-tag color="green">[[ inbound.password ]]</a-tag></p>
|
||||
</template>
|
||||
|
||||
<template v-if="dbInbound.isHTTP">
|
||||
<p>用户名: <a-tag color="green">[[ inbound.username ]]</a-tag></p>
|
||||
<p>密码: <a-tag color="green">[[ inbound.password ]]</a-tag></p>
|
||||
<p>{{ i18n "username"}}: <a-tag color="green">[[ inbound.username ]]</a-tag></p>
|
||||
<p>{{ i18n "password"}}: <a-tag color="green">[[ inbound.password ]]</a-tag></p>
|
||||
</template>
|
||||
|
||||
<template v-if="dbInbound.isVMess || dbInbound.isVLess || dbInbound.isTrojan || dbInbound.isSS">
|
||||
|
||||
@@ -14,25 +14,25 @@
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<span slot="label">
|
||||
监听 IP
|
||||
{{ i18n "monitor" }}
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
默认留空即可
|
||||
<span>{{ i18n "pages.inbounds.monitorDesc" }}</span>
|
||||
</template>
|
||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
||||
</a-tooltip>
|
||||
</span>
|
||||
<a-input v-model.trim="inbound.listen"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="端口">
|
||||
<a-form-item label='{{ i18n "pages.inbounds.port" }}'>
|
||||
<a-input type="number" v-model.number="inbound.port"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<span slot="label">
|
||||
总流量(GB)
|
||||
<span >{{ i18n "pages.inbounds.totalFlow" }}</span>(GB)
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
0 表示不限制
|
||||
0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
|
||||
</template>
|
||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
||||
</a-tooltip>
|
||||
@@ -41,10 +41,10 @@
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<span slot="label">
|
||||
到期时间
|
||||
<span >{{ i18n "pages.inbounds.expireDate" }}</span>
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
留空则永不到期
|
||||
<span>{{ i18n "pages.inbounds.leaveBlankToNeverExpire" }}</span>
|
||||
</template>
|
||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
||||
</a-tooltip>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{{define "form/dokodemo"}}
|
||||
<a-form layout="inline">
|
||||
<a-form-item label="目标地址">
|
||||
<a-form-item label='{{ i18n "pages.inbounds.targetAddress"}}'>
|
||||
<a-input v-model.trim="inbound.settings.address"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="目标端口">
|
||||
<a-form-item label='{{ i18n "pages.inbounds.destinationPort"}}'>
|
||||
<a-input type="number" v-model.number="inbound.settings.port"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="网络">
|
||||
<a-form-item label='{{ i18n "pages.inbounds.network"}}'>
|
||||
<a-select v-model="inbound.settings.network" style="width: 100px;">
|
||||
<a-select-option value="tcp,udp">tcp+udp</a-select-option>
|
||||
<a-select-option value="tcp">tcp</a-select-option>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{{define "form/http"}}
|
||||
<a-form layout="inline">
|
||||
<a-form-item label="用户名">
|
||||
<a-form-item label='{{ i18n "username"}}'>
|
||||
<a-input v-model.trim="inbound.settings.accounts[0].user"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="密码">
|
||||
<a-form-item label='{{ i18n "password" }}'>
|
||||
<a-input v-model.trim="inbound.settings.accounts[0].pass"></a-input>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
{{define "form/shadowsocks"}}
|
||||
<a-form layout="inline">
|
||||
<a-form-item label="加密">
|
||||
<a-form-item label='{{ i18n "encryption" }}'>
|
||||
<a-select v-model="inbound.settings.method" style="width: 165px;">
|
||||
<a-select-option v-for="method in SSMethods" :value="method">[[ method ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="密码">
|
||||
<a-form-item label='{{ i18n "password" }}'>
|
||||
<a-input v-model.trim="inbound.settings.password"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="网络">
|
||||
<a-form-item label='{{ i18n "pages.inbounds.network" }}'>
|
||||
<a-select v-model="inbound.settings.network" style="width: 100px;">
|
||||
<a-select-option value="tcp,udp">tcp+udp</a-select-option>
|
||||
<a-select-option value="tcp">tcp</a-select-option>
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
{{define "form/socks"}}
|
||||
<a-form layout="inline">
|
||||
<a-form-item label="密码认证">
|
||||
<!-- <a-form-item label="密码认证">-->
|
||||
<a-form-item label='{{ i18n "password" }}'>
|
||||
<a-switch :checked="inbound.settings.auth === 'password'"
|
||||
@change="checked => inbound.settings.auth = checked ? 'password' : 'noauth'"></a-switch>
|
||||
</a-form-item>
|
||||
<template v-if="inbound.settings.auth === 'password'">
|
||||
<a-form-item label="用户名">
|
||||
<a-form-item label='{{ i18n "username" }}'>
|
||||
<a-input v-model.trim="inbound.settings.accounts[0].user"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="密码">
|
||||
<a-form-item label='{{ i18n "password" }}'>
|
||||
<a-input v-model.trim="inbound.settings.accounts[0].pass"></a-input>
|
||||
</a-form-item>
|
||||
</template>
|
||||
<a-form-item label="启用 udp">
|
||||
<a-form-item label='{{ i18n "pages.inbounds.enable" }} udp'>
|
||||
<a-switch v-model="inbound.settings.udp"></a-switch>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="inbound.settings.udp"
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
{{define "form/trojan"}}
|
||||
<a-form layout="inline">
|
||||
<a-form-item label="密码">
|
||||
<a-form-item label='{{ i18n "password" }}'>
|
||||
<a-input v-model.trim="inbound.settings.clients[0].password"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="inbound.xtls" label="flow">
|
||||
<a-select v-model="inbound.settings.clients[0].flow" style="width: 150px">
|
||||
<a-select-option value="">无</a-select-option>
|
||||
<a-select-option value="" v-text='{{ i18n "none" }}'></a-select-option>
|
||||
<a-select-option v-for="key in FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
</a-form-item>
|
||||
<a-form-item v-if="inbound.xtls" label="flow">
|
||||
<a-select v-model="inbound.settings.vlesses[0].flow" style="width: 150px">
|
||||
<a-select-option value="">无</a-select-option>
|
||||
<a-select-option value="" v-text='{{ i18n "none" }}'></a-select-option>
|
||||
<a-select-option v-for="key in FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
|
||||
@@ -38,10 +38,10 @@
|
||||
<a-form-item label="id">
|
||||
<a-input v-model.trim="inbound.settings.vmesses[0].id"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="额外 ID">
|
||||
<a-form-item label='{{ i18n "additional" }} ID'>
|
||||
<a-input type="number" v-model.number="inbound.settings.vmesses[0].alterId"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="禁用不安全加密">
|
||||
<a-form-item label='{{ i18n "pages.inbounds.disableInsecureEncryption" }}'>
|
||||
<a-switch v-model.number="inbound.settings.disableInsecure"></a-switch>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
sniffing
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
没有特殊需求保持默认即可
|
||||
<span >{{ i18n "pages.inbounds.noRecommendKeepDefault" }}</span>
|
||||
</template>
|
||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
||||
</a-tooltip>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{{define "form/streamSettings"}}
|
||||
<!-- select stream network -->
|
||||
<a-form layout="inline">
|
||||
<a-form-item label="传输">
|
||||
<a-form-item label='{{ i18n "transmission" }}'>
|
||||
<a-select v-model="inbound.stream.network" @change="streamNetworkChange">
|
||||
<a-select-option value="tcp">tcp</a-select-option>
|
||||
<a-select-option value="kcp">kcp</a-select-option>
|
||||
|
||||
@@ -13,33 +13,33 @@
|
||||
<!-- tls settings -->
|
||||
<a-form v-if="inbound.tls || inbound.xtls"
|
||||
layout="inline">
|
||||
<a-form-item label="域名">
|
||||
<a-form-item label='{{ i18n "domainName" }}'>
|
||||
<a-input v-model.trim="inbound.stream.tls.server"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="alpn" placeholder="http/1.1,h2">
|
||||
<a-input v-model.trim="inbound.stream.tls.alpn"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="证书">
|
||||
<a-form-item label='{{ i18n "certificate" }}'>
|
||||
<a-radio-group v-model="inbound.stream.tls.certs[0].useFile"
|
||||
button-style="solid">
|
||||
<a-radio-button :value="true">certificate file path</a-radio-button>
|
||||
<a-radio-button :value="false">certificate file content</a-radio-button>
|
||||
<a-radio-button :value="true">{{ i18n "pages.inbounds.certificatePath" }}</a-radio-button>
|
||||
<a-radio-button :value="false">{{ i18n "pages.inbounds.certificateContent" }}</a-radio-button>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<template v-if="inbound.stream.tls.certs[0].useFile">
|
||||
<a-form-item label="公钥文件路径">
|
||||
<a-form-item label='{{ i18n "pages.inbounds.publicKeyPath" }}'>
|
||||
<a-input v-model.trim="inbound.stream.tls.certs[0].certFile"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="密钥文件路径">
|
||||
<a-form-item label='{{ i18n "pages.inbounds.keyPath" }}'>
|
||||
<a-input v-model.trim="inbound.stream.tls.certs[0].keyFile"></a-input>
|
||||
</a-form-item>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-form-item label="公钥内容">
|
||||
<a-form-item label='{{ i18n "pages.inbounds.publicKeyContent" }}'>
|
||||
<a-input type="textarea" :rows="2"
|
||||
v-model="inbound.stream.tls.certs[0].cert"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="密钥内容">
|
||||
<a-form-item label='{{ i18n "pages.inbounds.keyContent" }}'>
|
||||
<a-input type="textarea" :rows="2"
|
||||
v-model="inbound.stream.tls.certs[0].key"></a-input>
|
||||
</a-form-item>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{{define "inboundInfoModal"}}
|
||||
{{template "component/inboundInfo"}}
|
||||
<a-modal id="inbound-info-modal" v-model="infoModal.visible" title="详细信息" @ok="infoModal.ok"
|
||||
<a-modal id="inbound-info-modal" v-model="infoModal.visible" title='{{ i18n "pages.inbounds.details"}}' @ok="infoModal.ok"
|
||||
:closable="true" :mask-closable="true"
|
||||
ok-text="复制链接" cancel-text='{{ i18n "close" }}' :ok-button-props="infoModal.okBtnPros">
|
||||
ok-text='{{ i18n "pages.inbounds.copyLink"}}' cancel-text='{{ i18n "close" }}' :ok-button-props="infoModal.okBtnPros">
|
||||
<inbound-info :db-inbound="dbInbound" :inbound="inbound"></inbound-info>
|
||||
</a-modal>
|
||||
<script>
|
||||
@@ -34,7 +34,7 @@
|
||||
this.clipboard = new ClipboardJS(`#${this.okBtnPros.attrs.id}`, {
|
||||
text: () => this.dbInbound.genLink(),
|
||||
});
|
||||
this.clipboard.on('success', () => app.$message.success('复制成功'));
|
||||
this.clipboard.on('success', () => app.$message.success('{{ i18n "copySuccess" }}'));
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
title: '',
|
||||
visible: false,
|
||||
confirmLoading: false,
|
||||
okText: '确定',
|
||||
okText: '{{ i18n "sure" }}',
|
||||
isEdit: false,
|
||||
confirm: null,
|
||||
inbound: new Inbound(),
|
||||
@@ -19,7 +19,7 @@
|
||||
ok() {
|
||||
ObjectUtil.execute(inModal.confirm, inModal.inbound, inModal.dbInbound);
|
||||
},
|
||||
show({ title='', okText='确定', inbound=null, dbInbound=null, confirm=(inbound, dbInbound)=>{}, isEdit=false }) {
|
||||
show({ title='', okText='{{ i18n "sure" }}', inbound=null, dbInbound=null, confirm=(inbound, dbInbound)=>{}, isEdit=false }) {
|
||||
this.title = title;
|
||||
this.okText = okText;
|
||||
if (inbound) {
|
||||
|
||||
@@ -27,15 +27,15 @@
|
||||
<a-card hoverable style="margin-bottom: 20px;">
|
||||
<a-row>
|
||||
<a-col :xs="24" :sm="24" :lg="12">
|
||||
总上传 / 下载:
|
||||
{{ i18n "pages.inbounds.totalDownUp" }}:
|
||||
<a-tag color="green">[[ sizeFormat(total.up) ]] / [[ sizeFormat(total.down) ]]</a-tag>
|
||||
</a-col>
|
||||
<a-col :xs="24" :sm="24" :lg="12">
|
||||
总用量:
|
||||
{{ i18n "pages.inbounds.totalUsage" }}:
|
||||
<a-tag color="green">[[ sizeFormat(total.up + total.down) ]]</a-tag>
|
||||
</a-col>
|
||||
<a-col :xs="24" :sm="24" :lg="12">
|
||||
入站数量:
|
||||
{{ i18n "pages.inbounds.inboundCount" }}:
|
||||
<a-tag color="green">[[ dbInbounds.length ]]</a-tag>
|
||||
</a-col>
|
||||
</a-row>
|
||||
@@ -55,20 +55,22 @@
|
||||
@change="() => getDBInbounds()">
|
||||
<template slot="action" slot-scope="text, dbInbound">
|
||||
<a-dropdown :trigger="['click']">
|
||||
<a @click="e => e.preventDefault()">操作</a>
|
||||
<a @click="e => e.preventDefault()">{{ i18n "pages.inbounds.operate" }}</a>
|
||||
<a-menu slot="overlay" @click="a => clickAction(a, dbInbound)">
|
||||
<a-menu-item v-if="dbInbound.hasLink()" key="qrcode">
|
||||
<a-icon type="qrcode"></a-icon>二维码
|
||||
<a-icon type="qrcode"></a-icon>
|
||||
{{ i18n "qrCode" }}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="edit">
|
||||
<a-icon type="edit"></a-icon>编辑
|
||||
<a-icon type="edit"></a-icon>
|
||||
{{ i18n "edit" }}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="resetTraffic">
|
||||
<a-icon type="retweet"></a-icon>重置流量
|
||||
<a-icon type="retweet"></a-icon> {{ i18n "pages.inbounds.resetTraffic" }}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="delete">
|
||||
<span style="color: #FF4D4F">
|
||||
<a-icon type="delete"></a-icon>删除
|
||||
<a-icon type="delete"></a-icon> {{ i18n "delete"}}
|
||||
</span>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
@@ -83,10 +85,10 @@
|
||||
<a-tag v-if="dbInbound.up + dbInbound.down < dbInbound.total" color="cyan">[[ sizeFormat(dbInbound.total) ]]</a-tag>
|
||||
<a-tag v-else color="red">[[ sizeFormat(dbInbound.total) ]]</a-tag>
|
||||
</template>
|
||||
<a-tag v-else color="green">无限制</a-tag>
|
||||
<a-tag v-else color="green">{{ i18n "unlimited" }}</a-tag>
|
||||
</template>
|
||||
<template slot="settings" slot-scope="text, dbInbound">
|
||||
<a-button type="link" @click="showInfo(dbInbound)">查看</a-button>
|
||||
<a-button type="link" @click="showInfo(dbInbound)">{{ i18n "check" }}</a-button>
|
||||
</template>
|
||||
<template slot="stream" slot-scope="text, dbInbound, index">
|
||||
<template v-if="dbInbound.isVMess || dbInbound.isVLess || dbInbound.isTrojan || dbInbound.isSS">
|
||||
@@ -94,7 +96,7 @@
|
||||
<a-tag v-if="inbounds[index].stream.isTls" color="blue">tls</a-tag>
|
||||
<a-tag v-if="inbounds[index].stream.isXTls" color="blue">xtls</a-tag>
|
||||
</template>
|
||||
<template v-else>无</template>
|
||||
<template v-else>{{ i18n "none" }}</template>
|
||||
</template>
|
||||
<template slot="enable" slot-scope="text, dbInbound">
|
||||
<a-switch v-model="dbInbound.enable" @change="switchEnable(dbInbound)"></a-switch>
|
||||
@@ -108,7 +110,7 @@
|
||||
[[ DateUtil.formatMillis(dbInbound.expiryTime) ]]
|
||||
</a-tag>
|
||||
</template>
|
||||
<a-tag v-else color="green">无限期</a-tag>
|
||||
<a-tag v-else color="green">{{ i18n "indefinitely" }}</a-tag>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-card>
|
||||
@@ -121,52 +123,52 @@
|
||||
<script>
|
||||
|
||||
const columns = [{
|
||||
title: "操作",
|
||||
title: '{{ i18n "pages.inbounds.operate" }}',
|
||||
align: 'center',
|
||||
width: 30,
|
||||
scopedSlots: { customRender: 'action' },
|
||||
}, {
|
||||
title: "启用",
|
||||
title: '{{ i18n "pages.inbounds.enable" }}',
|
||||
align: 'center',
|
||||
width: 40,
|
||||
scopedSlots: { customRender: 'enable' },
|
||||
}, {
|
||||
title: "id",
|
||||
title: "Id",
|
||||
align: 'center',
|
||||
dataIndex: "id",
|
||||
width: 30,
|
||||
}, {
|
||||
title: "备注",
|
||||
title: '{{ i18n "pages.inbounds.remark" }}',
|
||||
align: 'center',
|
||||
width: 100,
|
||||
dataIndex: "remark",
|
||||
}, {
|
||||
title: "协议",
|
||||
title: '{{ i18n "pages.inbounds.protocol" }}',
|
||||
align: 'center',
|
||||
width: 60,
|
||||
scopedSlots: { customRender: 'protocol' },
|
||||
}, {
|
||||
title: "端口",
|
||||
title: '{{ i18n "pages.inbounds.port" }}',
|
||||
align: 'center',
|
||||
dataIndex: "port",
|
||||
width: 60,
|
||||
}, {
|
||||
title: "流量↑|↓",
|
||||
title: '{{ i18n "pages.inbounds.traffic" }}↑|↓',
|
||||
align: 'center',
|
||||
width: 150,
|
||||
scopedSlots: { customRender: 'traffic' },
|
||||
}, {
|
||||
title: "详细信息",
|
||||
title: '{{ i18n "pages.inbounds.details" }}',
|
||||
align: 'center',
|
||||
width: 40,
|
||||
scopedSlots: { customRender: 'settings' },
|
||||
}, {
|
||||
title: "传输配置",
|
||||
title: '{{ i18n "pages.inbounds.transportConfig" }}',
|
||||
align: 'center',
|
||||
width: 60,
|
||||
scopedSlots: { customRender: 'stream' },
|
||||
}, {
|
||||
title: "到期时间",
|
||||
title: '{{ i18n "pages.inbounds.expireDate" }}',
|
||||
align: 'center',
|
||||
width: 80,
|
||||
scopedSlots: { customRender: 'expiryTime' },
|
||||
@@ -234,8 +236,9 @@
|
||||
},
|
||||
openAddInbound() {
|
||||
inModal.show({
|
||||
title: '添加入站',
|
||||
okText: '添加',
|
||||
title: '{{ i18n "pages.inbounds.addInbound"}}',
|
||||
okText: '{{ i18n "pages.inbounds.addTo"}}',
|
||||
cancelText: '{{ i18n "close" }}',
|
||||
confirm: async (inbound, dbInbound) => {
|
||||
inModal.loading();
|
||||
await this.addInbound(inbound, dbInbound);
|
||||
@@ -247,8 +250,9 @@
|
||||
openEditInbound(dbInbound) {
|
||||
const inbound = dbInbound.toInbound();
|
||||
inModal.show({
|
||||
title: '修改入站',
|
||||
okText: '修改',
|
||||
title: '{{ i18n "pages.inbounds.modifyInbound"}}',
|
||||
okText: '{{ i18n "pages.inbounds.revise"}}',
|
||||
cancelText: '{{ i18n "close" }}',
|
||||
inbound: inbound,
|
||||
dbInbound: dbInbound,
|
||||
confirm: async (inbound, dbInbound) => {
|
||||
@@ -297,10 +301,10 @@
|
||||
},
|
||||
resetTraffic(dbInbound) {
|
||||
this.$confirm({
|
||||
title: '重置流量',
|
||||
content: '确定要重置流量吗?',
|
||||
okText: '重置',
|
||||
cancelText: '取消',
|
||||
title: '{{ i18n "pages.inbounds.resetTraffic"}}',
|
||||
content: '{{ i18n "pages.inbounds.resetTrafficContent"}}',
|
||||
okText: '{{ i18n "reset"}}',
|
||||
cancelText: '{{ i18n "cancel"}}',
|
||||
onOk: () => {
|
||||
const inbound = dbInbound.toInbound();
|
||||
dbInbound.up = 0;
|
||||
@@ -311,16 +315,16 @@
|
||||
},
|
||||
delInbound(dbInbound) {
|
||||
this.$confirm({
|
||||
title: '删除入站',
|
||||
content: '确定要删除入站吗?',
|
||||
okText: '删除',
|
||||
cancelText: '取消',
|
||||
title: '{{ i18n "pages.inbounds.deleteInbound"}}',
|
||||
content: '{{ i18n "pages.inbounds.deleteInboundContent"}}',
|
||||
okText: '{{ i18n "delete"}}',
|
||||
cancelText: '{{ i18n "cancel"}}',
|
||||
onOk: () => this.submit('/xui/inbound/del/' + dbInbound.id),
|
||||
});
|
||||
},
|
||||
showQrcode(dbInbound) {
|
||||
const link = dbInbound.genLink();
|
||||
qrModal.show('二维码', link);
|
||||
qrModal.show('{{ i18n "qrCode"}}', link);
|
||||
},
|
||||
showInfo(dbInbound) {
|
||||
infoModal.show(dbInbound);
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
:stroke-color="status.mem.color"
|
||||
:percent="status.mem.percent"></a-progress>
|
||||
<div>
|
||||
内存: [[ sizeFormat(status.mem.current) ]] / [[ sizeFormat(status.mem.total) ]]
|
||||
{{ i18n "pages.index.memory"}}: [[ sizeFormat(status.mem.current) ]] / [[ sizeFormat(status.mem.total) ]]
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
@@ -55,7 +55,7 @@
|
||||
:stroke-color="status.disk.color"
|
||||
:percent="status.disk.percent"></a-progress>
|
||||
<div>
|
||||
硬盘: [[ sizeFormat(status.disk.current) ]] / [[ sizeFormat(status.disk.total) ]]
|
||||
{{ i18n "pages.index.hard"}}: [[ sizeFormat(status.disk.current) ]] / [[ sizeFormat(status.disk.total) ]]
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
@@ -68,7 +68,7 @@
|
||||
<a-row>
|
||||
<a-col :sm="24" :md="12">
|
||||
<a-card hoverable>
|
||||
xray 状态:
|
||||
{{ i18n "pages.index.xrayStatus" }}:
|
||||
<a-tag :color="status.xray.color">[[ status.xray.state ]]</a-tag>
|
||||
<a-tooltip v-if="status.xray.state === State.Error">
|
||||
<template slot="title">
|
||||
@@ -77,16 +77,16 @@
|
||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
||||
</a-tooltip>
|
||||
<a-tag color="green" @click="openSelectV2rayVersion">[[ status.xray.version ]]</a-tag>
|
||||
<a-tag color="blue" @click="openSelectV2rayVersion">切换版本</a-tag>
|
||||
<a-tag color="blue" @click="openSelectV2rayVersion">{{ i18n "pages.index.xraySwitch"}}</a-tag>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :sm="24" :md="12">
|
||||
<a-card hoverable>
|
||||
运行时间:
|
||||
{{ i18n "pages.index.operationHours" }}:
|
||||
<a-tag color="#87d068">[[ formatSecond(status.uptime) ]]</a-tag>
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
系统自启动以来的运行时间
|
||||
{{ i18n "pages.index.operationHoursDesc" }}
|
||||
</template>
|
||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
||||
</a-tooltip>
|
||||
@@ -94,15 +94,15 @@
|
||||
</a-col>
|
||||
<a-col :sm="24" :md="12">
|
||||
<a-card hoverable>
|
||||
系统负载: [[ status.loads[0] ]] | [[ status.loads[1] ]] | [[ status.loads[2] ]]
|
||||
{{ i18n "pages.index.systemLoad" }}: [[ status.loads[0] ]] | [[ status.loads[1] ]] | [[ status.loads[2] ]]
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :sm="24" :md="12">
|
||||
<a-card hoverable>
|
||||
tcp / udp 连接数: [[ status.tcpCount ]] / [[ status.udpCount ]]
|
||||
tcp / udp {{ i18n "pages.index.connectionCount" }}: [[ status.tcpCount ]] / [[ status.udpCount ]]
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
所有网卡的总连接数
|
||||
{{ i18n "pages.index.connectionCountDesc" }}
|
||||
</template>
|
||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
||||
</a-tooltip>
|
||||
@@ -116,7 +116,7 @@
|
||||
[[ sizeFormat(status.netIO.up) ]] / S
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
所有网卡的总上传速度
|
||||
{{ i18n "pages.index.upSpeed" }}
|
||||
</template>
|
||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
||||
</a-tooltip>
|
||||
@@ -126,7 +126,7 @@
|
||||
[[ sizeFormat(status.netIO.down) ]] / S
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
所有网卡的总下载速度
|
||||
{{ i18n "pages.index.downSpeed" }}
|
||||
</template>
|
||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
||||
</a-tooltip>
|
||||
@@ -142,7 +142,7 @@
|
||||
[[ sizeFormat(status.netTraffic.sent) ]]
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
系统启动以来所有网卡的总上传流量
|
||||
{{ i18n "pages.index.totalSent" }}
|
||||
</template>
|
||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
||||
</a-tooltip>
|
||||
@@ -152,7 +152,7 @@
|
||||
[[ sizeFormat(status.netTraffic.recv) ]]
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
系统启动以来所有网卡的总下载流量
|
||||
{{ i18n "pages.index.totalReceive" }}
|
||||
</template>
|
||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
||||
</a-tooltip>
|
||||
@@ -164,11 +164,11 @@
|
||||
</transition>
|
||||
</a-layout-content>
|
||||
</a-layout>
|
||||
<a-modal id="version-modal" v-model="versionModal.visible" title="切换版本"
|
||||
<a-modal id="version-modal" v-model="versionModal.visible" title='{{ i18n "pages.index.xraySwitch" }}'
|
||||
:closable="true" @ok="() => versionModal.visible = false"
|
||||
ok-text="确定" cancel-text="取消">
|
||||
<h2>点击你想切换的版本</h2>
|
||||
<h2>请谨慎选择,旧版本可能配置不兼容</h2>
|
||||
ok-text='{{ i18n "confirm" }}' cancel-text='{{ i18n "cancel"}}'>
|
||||
<h2>{{ i18n "pages.index.xraySwitchClick"}}</h2>
|
||||
<h2>{{ i18n "pages.index.xraySwitchClickDesk"}}</h2>
|
||||
<template v-for="version, index in versionModal.versions">
|
||||
<a-tag :color="index % 2 == 0 ? 'blue' : 'green'"
|
||||
style="margin: 10px" @click="switchV2rayVersion(version)">
|
||||
@@ -277,10 +277,10 @@
|
||||
status: new Status(),
|
||||
versionModal,
|
||||
spinning: false,
|
||||
loadingTip: '加载中',
|
||||
loadingTip: '{{ i18n "loading"}}',
|
||||
},
|
||||
methods: {
|
||||
loading(spinning, tip = '加载中') {
|
||||
loading(spinning, tip = '{{ i18n "loading"}}') {
|
||||
this.spinning = spinning;
|
||||
this.loadingTip = tip;
|
||||
},
|
||||
@@ -304,13 +304,13 @@
|
||||
},
|
||||
switchV2rayVersion(version) {
|
||||
this.$confirm({
|
||||
title: '切换 xray 版本',
|
||||
content: '是否切换 xray 版本至' + ` ${version}?`,
|
||||
okText: '确定',
|
||||
cancelText: '取消',
|
||||
title: '{{ i18n "pages.index.xraySwitchVersionDialog"}}',
|
||||
content: '{{ i18n "pages.index.xraySwitchVersionDialogDesc"}}' + ` ${version}?`,
|
||||
okText: '{{ i18n "confirm"}}',
|
||||
cancelText: '{{ i18n "cancel"}}',
|
||||
onOk: async () => {
|
||||
versionModal.hide();
|
||||
this.loading(true, '安装中,请不要刷新此页面');
|
||||
this.loading(true, '{{ i18n "pages.index.dontRefreshh"}}');
|
||||
await HttpUtil.post(`/server/installXray/${version}`);
|
||||
this.loading(false);
|
||||
},
|
||||
|
||||
@@ -32,56 +32,82 @@
|
||||
<a-spin :spinning="spinning" :delay="500" tip="loading">
|
||||
<a-space direction="vertical">
|
||||
<a-space direction="horizontal">
|
||||
<a-button type="primary" :disabled="saveBtnDisable" @click="updateAllSetting">保存配置</a-button>
|
||||
<a-button type="danger" :disabled="!saveBtnDisable" @click="restartPanel">重启面板</a-button>
|
||||
<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-space>
|
||||
<a-tabs default-active-key="1">
|
||||
<a-tab-pane key="1" tab="面板配置">
|
||||
<a-tab-pane key="1" tab='{{ i18n "pages.setting.panelConfig"}}'>
|
||||
|
||||
<a-list item-layout="horizontal" style="background: white">
|
||||
<setting-list-item type="text" title="面板监听 IP" desc="默认留空监听所有 IP,重启面板生效" v-model="allSetting.webListen"></setting-list-item>
|
||||
<setting-list-item type="number" title="面板监听端口" desc="重启面板生效" v-model.number="allSetting.webPort"></setting-list-item>
|
||||
<setting-list-item type="text" title="面板证书公钥文件路径" desc="填写一个 '/' 开头的绝对路径,重启面板生效" v-model="allSetting.webCertFile"></setting-list-item>
|
||||
<setting-list-item type="text" title="面板证书密钥文件路径" desc="填写一个 '/' 开头的绝对路径,重启面板生效" v-model="allSetting.webKeyFile"></setting-list-item>
|
||||
<setting-list-item type="text" title="面板 url 根路径" desc="必须以 '/' 开头,以 '/' 结尾,重启面板生效" v-model="allSetting.webBasePath"></setting-list-item>
|
||||
<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>
|
||||
<a-list-item>
|
||||
<a-row style="padding: 20px">
|
||||
<a-col :lg="24" :xl="12">
|
||||
<a-list-item-meta title="Language"/>
|
||||
</a-col>
|
||||
|
||||
<a-col :lg="24" :xl="12">
|
||||
<temlate>
|
||||
<a-select
|
||||
ref="selectLang"
|
||||
v-model="lang"
|
||||
@change="setLang(lang)"
|
||||
style="width: 100%"
|
||||
>
|
||||
<a-select-option :value="l.value" label="China" v-for="l in supportLangs" >
|
||||
<span role="img" aria-label="l.name" v-text="l.icon"></span>
|
||||
<span v-text="l.name"></span>
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</temlate>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
</a-list-item>
|
||||
</a-list>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="2" tab="用户设置">
|
||||
<a-tab-pane key="2" tab='{{ i18n "pages.setting.userSetting"}}'>
|
||||
<a-form style="background: white; padding: 20px">
|
||||
<a-form-item label="原用户名">
|
||||
<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="原密码">
|
||||
<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="新用户名">
|
||||
<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="新密码">
|
||||
<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">修改</a-button>
|
||||
<!-- <a-button type="primary" @click="updateUser">修改</a-button>-->
|
||||
<a-button type="primary" @click="updateUser">{{ i18n "confirm" }}</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="3" tab="xray 相关设置">
|
||||
<a-tab-pane key="3" tab='{{ i18n "pages.setting.xrayConfiguration"}}'>
|
||||
<a-list item-layout="horizontal" style="background: white">
|
||||
<setting-list-item type="textarea" title="xray 配置模版" desc="以该模版为基础生成最终的 xray 配置文件,重启面板生效" v-model="allSetting.xrayTemplateConfig"></setting-list-item>
|
||||
<setting-list-item type="textarea" title='{{ i18n "pages.setting.xrayConfigTemplate"}}' desc='{{ i18n "pages.setting.xrayConfigTemplateDesc"}}' v-model="allSetting.xrayTemplateConfig"></setting-list-item>
|
||||
</a-list>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="4" tab="TG提醒相关设置">
|
||||
<a-tab-pane key="4" tab='{{ i18n "pages.setting.TGReminder"}}'>
|
||||
<a-list item-layout="horizontal" style="background: white">
|
||||
<setting-list-item type="switch" title="启用电报机器人" desc="重启面板生效" v-model="allSetting.tgBotEnable"></setting-list-item>
|
||||
<setting-list-item type="text" title="电报机器人TOKEN" desc="重启面板生效" v-model="allSetting.tgBotToken"></setting-list-item>
|
||||
<setting-list-item type="number" title="电报机器人ChatId" desc="重启面板生效" v-model.number="allSetting.tgBotChatId"></setting-list-item>
|
||||
<setting-list-item type="text" title="电报机器人通知时间" desc="采用Crontab定时格式,重启面板生效" v-model="allSetting.tgRunTime"></setting-list-item>
|
||||
<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="number" title='{{ i18n "pages.setting.telegramChatId"}}' desc='{{ i18n "pages.setting.telegramChatIdDesc"}}' v-model.number="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>
|
||||
</a-list>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="5" tab="其他设置">
|
||||
<a-tab-pane key="5" tab='{{ i18n "pages.setting.otherSetting"}}'>
|
||||
<a-list item-layout="horizontal" style="background: white">
|
||||
<setting-list-item type="text" title="时区" desc="定时任务按照该时区的时间运行,重启面板生效" v-model="allSetting.timeLocation"></setting-list-item>
|
||||
<setting-list-item type="text" title='{{ i18n "pages.setting.timeZonee"}}' desc='{{ i18n "pages.setting.timeZoneDesc"}}' v-model="allSetting.timeLocation"></setting-list-item>
|
||||
</a-list>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
@@ -104,6 +130,7 @@
|
||||
allSetting: new AllSetting(),
|
||||
saveBtnDisable: true,
|
||||
user: {},
|
||||
lang : getLang()
|
||||
},
|
||||
methods: {
|
||||
loading(spinning = true) {
|
||||
@@ -138,10 +165,10 @@
|
||||
async restartPanel() {
|
||||
await new Promise(resolve => {
|
||||
this.$confirm({
|
||||
title: '重启面板',
|
||||
content: '确定要重启面板吗?点击确定将于 3 秒后重启,若重启后无法访问面板,请前往服务器查看面板日志信息',
|
||||
okText: '确定',
|
||||
cancelText: '取消',
|
||||
title: '{{ i18n "pages.setting.restartPanel" }}',
|
||||
content: '{{ i18n "pages.setting.restartPanelDesc" }}',
|
||||
okText: '{{ i18n "sure" }}',
|
||||
cancelText: '{{ i18n "cancel" }}',
|
||||
onOk: () => resolve(),
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,4 +9,164 @@
|
||||
"download" = "download"
|
||||
"remark" = "remark"
|
||||
"enable" = "enable"
|
||||
"protocol" = "protocol"
|
||||
"protocol" = "protocol"
|
||||
|
||||
"loading" = "Loading"
|
||||
"second" = "second"
|
||||
"minute" = "minute"
|
||||
"hour" = "hour"
|
||||
"day" = "day"
|
||||
"check" = "check"
|
||||
"indefinitely" = "indefinitely"
|
||||
"unlimited" = "unlimited"
|
||||
"none" = "none"
|
||||
"qrCode" = "QR Code"
|
||||
"edit" = "edit"
|
||||
"delete" = "delete"
|
||||
"reset" = "reset"
|
||||
"copySuccess" = "Copy successfully"
|
||||
"sure" = "Sure"
|
||||
"encryption" = "encryption"
|
||||
"transmission" = "transmission"
|
||||
"host" = "host"
|
||||
"path" = "path"
|
||||
"camouflage" = "camouflage"
|
||||
"turnOn" = "turn on"
|
||||
"closure" = "closure"
|
||||
"domainName" = "domain name"
|
||||
"additional" = "additional"
|
||||
"monitor" = "monitor"
|
||||
"certificate" = "certificat"
|
||||
"fail" = "fail"
|
||||
"success" = "success"
|
||||
"getVersion" = "get version"
|
||||
"install" = "install"
|
||||
|
||||
[menu]
|
||||
"dashboard" = "System Status"
|
||||
"inbounds" = "Inbounds"
|
||||
"setting" = "Panel Setting"
|
||||
"logout" = "LogOut"
|
||||
"link" = "Other"
|
||||
|
||||
[pages.login]
|
||||
"title" = "Login"
|
||||
"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"
|
||||
"successLogin" = "Login"
|
||||
|
||||
|
||||
[pages.index]
|
||||
"title" = "system status"
|
||||
"memory" = "memory"
|
||||
"hard" = "hard disk"
|
||||
"xrayStatus" = "xray Status"
|
||||
"xraySwitch" = "Switch Version"
|
||||
"xraySwitchClick" = "Click on the version you want to switch"
|
||||
"xraySwitchClickDesk" = "Please choose carefully, older versions may have incompatible configurations"
|
||||
"operationHours" = "Operation Hours"
|
||||
"operationHoursDesc" = "The running time of the system since it was started"
|
||||
"systemLoad" = "System Load"
|
||||
"connectionCount" = "Connection Count"
|
||||
"connectionCountDesc" = "The total number of connections for 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 traffic of 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"
|
||||
|
||||
|
||||
[pages.inbounds]
|
||||
"title" = "Inbounds"
|
||||
"totalDownUp" = "Total uploads/downloads"
|
||||
"totalUsage" = "Total usage"
|
||||
"inboundCount" = "Number of inbound"
|
||||
"operate" = "operate"
|
||||
"enable" = "enable"
|
||||
"remark" = "remark"
|
||||
"protocol" = "protocol"
|
||||
"port" = "port"
|
||||
"traffic" = "traffic"
|
||||
"details" = "details"
|
||||
"transportConfig" = "transport config"
|
||||
"expireDate" = "expire date"
|
||||
"resetTraffic" = "reset traffic"
|
||||
"addInbound" = "addInbound"
|
||||
"addTo" = "Add To"
|
||||
"revise" = "Revise"
|
||||
"modifyInbound" = "Modify InBound"
|
||||
"deleteInbound" = "Delete Inbound"
|
||||
"deleteInboundContent" = "Are you sure you want to delete inbound?"
|
||||
"resetTrafficContent" = "Are you sure you want to reset traffic?"
|
||||
"copyLink" = "Copy Link"
|
||||
"address" = "address"
|
||||
"network" = "network"
|
||||
"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 never expire"
|
||||
"noRecommendKeepDefault" = "There are no special requirements to keep the default"
|
||||
"certificatePath" = "certificate file path"
|
||||
"certificateContent" = "certificate file content"
|
||||
"publicKeyPath" = "public key file path"
|
||||
"publicKeyContent" = "public key content"
|
||||
"keyPath" = "key file path"
|
||||
"keyContent" = "key content"
|
||||
|
||||
[pages.inbounds.toasts]
|
||||
"obtain" = "Obtain"
|
||||
|
||||
|
||||
[pages.setting]
|
||||
"title" = "Setting"
|
||||
"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"
|
||||
"panelConfig" = "Panel Configuration"
|
||||
"userSetting" = "User Setting"
|
||||
"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"
|
||||
"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 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"
|
||||
"oldUsername" = "Current Username"
|
||||
"currentPassword" = "Current Password"
|
||||
"newUsername" = "New Username"
|
||||
"newPassword" = "New Password"
|
||||
"xrayConfigTemplate" = "xray Configuration Template"
|
||||
"xrayConfigTemplateDesc" = "Generate the final xray configuration file based on this template, 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 ChatId"
|
||||
"telegramChatIdDesc" = "Restart the panel to take effect"
|
||||
"telegramNotifyTime" = "Telegram bot notification time"
|
||||
"telegramNotifyTimeDesc" = "Using Crontab timing format, restart the panel to take effect"
|
||||
"timeZonee" = "Time Zone"
|
||||
"timeZoneDesc" = "The scheduled task runs according to the time in the time zone, and restarts the panel to take effect"
|
||||
|
||||
[pages.setting.toasts]
|
||||
"modifySetting" = "modify setting"
|
||||
"getSetting" = "get setting"
|
||||
"modifyUser" = "modify user"
|
||||
"originalUserPassIncorrect" = "The original user name or original password is incorrect"
|
||||
"userPassMustBeNotEmpty" = "New username and new password cannot be empty"
|
||||
@@ -9,4 +9,165 @@
|
||||
"download" = "下载"
|
||||
"remark" = "备注"
|
||||
"enable" = "启用"
|
||||
"protocol" = "协议"
|
||||
"protocol" = "协议"
|
||||
|
||||
"loading" = "加载中"
|
||||
"second" = "秒"
|
||||
"minute" = "分钟"
|
||||
"hour" = "小时"
|
||||
"day" = "天"
|
||||
"check" = "查看"
|
||||
"indefinitely" = "无限期"
|
||||
"unlimited" = "无限制"
|
||||
"none" = "无"
|
||||
"qrCode" = "二维码"
|
||||
"edit" = "编辑"
|
||||
"delete" = "删除"
|
||||
"reset" = "重置"
|
||||
"copySuccess" = "复制成功"
|
||||
"sure" = "确定"
|
||||
"encryption" = "加密"
|
||||
"transmission" = "传输"
|
||||
"host" = "主持人"
|
||||
"path" = "小路"
|
||||
"camouflage" = "伪装"
|
||||
"turnOn" = "开启"
|
||||
"closure" = "关闭"
|
||||
"domainName" = "域名"
|
||||
"additional" = "额外"
|
||||
"monitor" = "监听"
|
||||
"certificate" = "证书"
|
||||
"fail" = "失败"
|
||||
"success" = "成功"
|
||||
"getVersion" = "获取版本"
|
||||
"install" = "安装"
|
||||
|
||||
[menu]
|
||||
"dashboard" = "系统状态"
|
||||
"inbounds" = "入站列表"
|
||||
"setting" = "面板设置"
|
||||
"logout" = "退出登录"
|
||||
"link" = "其他"
|
||||
|
||||
[pages.login]
|
||||
"title" = "登录"
|
||||
"loginAgain" = "登录时效已过,请重新登录"
|
||||
|
||||
[pages.login.toasts]
|
||||
"invalidFormData" = "数据格式错误"
|
||||
"emptyUsername" = "请输入用户名"
|
||||
"emptyPassword" = "请输入密码"
|
||||
"wrongUsernameOrPassword" = "用户名或密码错误"
|
||||
"successLogin" = "登录"
|
||||
|
||||
[pages.index]
|
||||
"title" = "系统状态"
|
||||
"memory" = "内存"
|
||||
"hard" = "硬盘"
|
||||
"xrayStatus" = "xray 状态"
|
||||
"xraySwitch" = "切换版本"
|
||||
"xraySwitchClick" = "点击你想切换的版本"
|
||||
"xraySwitchClickDesk" = "请谨慎选择,旧版本可能配置不兼容"
|
||||
"operationHours" = "运行时间"
|
||||
"operationHoursDesc" = "系统自启动以来的运行时间"
|
||||
"systemLoad" = "系统负载"
|
||||
"connectionCount" = "连接数"
|
||||
"connectionCountDesc" = "所有网卡的总连接数"
|
||||
"upSpeed" = "所有网卡的总上传速度"
|
||||
"downSpeed" = "所有网卡的总下载速度"
|
||||
"totalSent" = "系统启动以来所有网卡的总上传流量"
|
||||
"totalReceive" = "系统启动以来所有网卡的总下载流量"
|
||||
"xraySwitchVersionDialog" = "切换 xray 版本"
|
||||
"xraySwitchVersionDialogDesc" = "是否切换 xray 版本至"
|
||||
"dontRefreshh" = "安装中,请不要刷新此页面"
|
||||
|
||||
|
||||
[pages.inbounds]
|
||||
"title" = "入站列表"
|
||||
"totalDownUp" = "总上传 / 下载"
|
||||
"totalUsage" = "总用量"
|
||||
"inboundCount" = "入站数量"
|
||||
"operate" = "操作"
|
||||
"enable" = "启用"
|
||||
"remark" = "备注"
|
||||
"protocol" = "协议"
|
||||
"port" = "端口"
|
||||
"traffic" = "流量"
|
||||
"details" = "详细信息"
|
||||
"transportConfig" = "传输配置"
|
||||
"expireDate" = "到期时间"
|
||||
"resetTraffic" = "重置流量"
|
||||
"addInbound" = "添加入"
|
||||
"addTo" = "添加"
|
||||
"revise" = "修改"
|
||||
"modifyInbound" = "修改入站"
|
||||
"deleteInbound" = "删除入站"
|
||||
"deleteInboundContent" = "确定要删除入站吗?"
|
||||
"resetTrafficContent" = "确定要重置流量吗?"
|
||||
"copyLink" = "复制链接"
|
||||
"address" = "地址"
|
||||
"network" = "网络"
|
||||
"destinationPort" = "目标端口"
|
||||
"targetAddress" = "目标地址"
|
||||
"disableInsecureEncryption" = "禁用不安全加密"
|
||||
"monitorDesc" = "默认留空即可"
|
||||
"meansNoLimit" = "表示不限制"
|
||||
"totalFlow" = "总流量"
|
||||
"leaveBlankToNeverExpire" = "留空则永不到期"
|
||||
"noRecommendKeepDefault" = "没有特殊需求保持默认即可"
|
||||
"certificatePath" = "证书文件路径"
|
||||
"certificateContent" = "证书文件内容"
|
||||
"publicKeyPath" = "公钥文件路径"
|
||||
"publicKeyContent" = "公钥内容"
|
||||
"keyPath" = "密钥文件路径"
|
||||
"keyContent" = "密钥内容"
|
||||
|
||||
[pages.inbounds.toasts]
|
||||
"obtain" = "获取"
|
||||
|
||||
|
||||
|
||||
[pages.setting]
|
||||
"title" = "设置"
|
||||
"save" = "保存配置"
|
||||
"restartPanel" = "重启面板"
|
||||
"restartPanelDesc" = "确定要重启面板吗?点击确定将于 3 秒后重启,若重启后无法访问面板,请前往服务器查看面板日志信息"
|
||||
"panelConfig" = "面板配置"
|
||||
"userSetting" = "用户设置"
|
||||
"xrayConfiguration" = "xray 相关设置"
|
||||
"TGReminder" = "TG提醒相关设置"
|
||||
"otherSetting" = "其他设置"
|
||||
"panelListeningIP" = "面板监听 IP"
|
||||
"panelListeningIPDesc" = "默认留空监听所有 IP,重启面板生效"
|
||||
"panelPort" = "面板监听端口"
|
||||
"panelPortDesc" = "重启面板生效"
|
||||
"publicKeyPath" = "面板证书公钥文件路径"
|
||||
"publicKeyPathDesc" = "填写一个 '/' 开头的绝对路径,重启面板生效"
|
||||
"privateKeyPath" = "面板证书密钥文件路径"
|
||||
"privateKeyPathDesc" = "填写一个 '/' 开头的绝对路径,重启面板生效"
|
||||
"panelUrlPath" = "面板 url 根路径"
|
||||
"panelUrlPathDesc" = "必须以 '/' 开头,以 '/' 结尾,重启面板生效"
|
||||
"oldUsername" = "原用户名"
|
||||
"currentPassword" = "原密码"
|
||||
"newUsername" = "新用户名"
|
||||
"newPassword" = "新密码"
|
||||
"xrayConfigTemplate" = "xray 配置模版"
|
||||
"xrayConfigTemplateDesc" = "以该模版为基础生成最终的 xray 配置文件,重启面板生效"
|
||||
"telegramBotEnable" = "启用电报机器人"
|
||||
"telegramBotEnableDesc" = "重启面板生效"
|
||||
"telegramToken" = "电报机器人TOKEN"
|
||||
"telegramTokenDesc" = "重启面板生效"
|
||||
"telegramChatId" = "电报机器人ChatId"
|
||||
"telegramChatIdDesc" = "重启面板生效"
|
||||
"telegramNotifyTime" = "电报机器人通知时间"
|
||||
"telegramNotifyTimeDesc" = "采用Crontab定时格式,重启面板生效"
|
||||
"timeZonee" = "时区"
|
||||
"timeZoneDesc" = "定时任务按照该时区的时间运行,重启面板生效"
|
||||
|
||||
[pages.setting.toasts]
|
||||
"modifySetting" = "修改设置"
|
||||
"getSetting" = "获取设置"
|
||||
"modifyUser" = "修改用户"
|
||||
"originalUserPassIncorrect" = "原用户名或原密码错误"
|
||||
"userPassMustBeNotEmpty" = "新用户名和新密码不能为空"
|
||||
|
||||
|
||||
@@ -9,4 +9,30 @@
|
||||
"download" = "下載"
|
||||
"remark" = "備註"
|
||||
"enable" = "啟用"
|
||||
"protocol" = "協議"
|
||||
"protocol" = "協議"
|
||||
|
||||
[menu]
|
||||
"dashboard" = "系统状态"
|
||||
"inbounds" = "入站列表"
|
||||
"setting" = "面板设置"
|
||||
"logout" = "退出登录"
|
||||
"link" = "其他"
|
||||
|
||||
[pages.login]
|
||||
"title" = "登錄"
|
||||
|
||||
[pages.login.toasts]
|
||||
"invalidFormData" = "数据格式错误"
|
||||
"emptyUsername" = "请输入用户名"
|
||||
"emptyPassword" = "请输入密码"
|
||||
"wrongUsernameOrPassword" = "用户名或密码错误"
|
||||
"successLogin" = "登录"
|
||||
|
||||
[pages.index]
|
||||
"title" = "系统状态"
|
||||
|
||||
[pages.inbounds]
|
||||
"title" = "入站列表"
|
||||
|
||||
[pages.setting]
|
||||
"title" = "设置"
|
||||
18
web/web.go
18
web/web.go
@@ -254,7 +254,7 @@ func (s *Server) initI18n(engine *gin.Engine) error {
|
||||
|
||||
var localizer *i18n.Localizer
|
||||
|
||||
engine.FuncMap["i18n"] = func(key string, params ...string) (string, error) {
|
||||
I18n := func(key string, params ...string) (string, error) {
|
||||
names := findI18nParamNames(key)
|
||||
if len(names) != len(params) {
|
||||
return "", common.NewError("find names:", names, "---------- params:", params, "---------- num not equal")
|
||||
@@ -269,10 +269,22 @@ func (s *Server) initI18n(engine *gin.Engine) error {
|
||||
})
|
||||
}
|
||||
|
||||
engine.FuncMap["i18n"] = I18n;
|
||||
|
||||
engine.Use(func(c *gin.Context) {
|
||||
accept := c.GetHeader("Accept-Language")
|
||||
localizer = i18n.NewLocalizer(bundle, accept)
|
||||
//accept := c.GetHeader("Accept-Language")
|
||||
|
||||
var lang string
|
||||
|
||||
if cookie, err := c.Request.Cookie("lang"); err == nil {
|
||||
lang = cookie.Value
|
||||
} else {
|
||||
lang = c.GetHeader("Accept-Language")
|
||||
}
|
||||
|
||||
localizer = i18n.NewLocalizer(bundle, lang)
|
||||
c.Set("localizer", localizer)
|
||||
c.Set("I18n" , I18n)
|
||||
c.Next()
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user