Merge branch 'main' into 3x-ui
This commit is contained in:
@@ -9,6 +9,9 @@ class DBInbound {
|
||||
this.remark = "";
|
||||
this.enable = true;
|
||||
this.expiryTime = 0;
|
||||
|
||||
// 新增:入站级设备限制(0 表示不限制)
|
||||
this.deviceLimit = 0;
|
||||
|
||||
this.listen = "";
|
||||
this.port = 0;
|
||||
@@ -145,4 +148,4 @@ class DBInbound {
|
||||
const inbound = this.toInbound();
|
||||
return inbound.genInboundLinks(this.remark, remarkModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -731,8 +731,8 @@ class RealityStreamSettings extends XrayCommonClass {
|
||||
constructor(
|
||||
show = false,
|
||||
xver = 0,
|
||||
dest = 'google.com:443',
|
||||
serverNames = 'google.com,www.google.com',
|
||||
dest = 'tesla.com:443',
|
||||
serverNames = 'tesla.com,www.tesla.com',
|
||||
privateKey = '',
|
||||
minClientVer = '',
|
||||
maxClientVer = '',
|
||||
|
||||
@@ -22,7 +22,7 @@ class AllSetting {
|
||||
this.tgBotBackup = false;
|
||||
this.tgBotLoginNotify = true;
|
||||
this.tgCpu = 80;
|
||||
this.tgLang = "en-US";
|
||||
this.tgLang = "zh-CN";
|
||||
this.twoFactorEnable = false;
|
||||
this.twoFactorToken = "";
|
||||
this.xrayTemplateConfig = "";
|
||||
@@ -58,4 +58,4 @@ class AllSetting {
|
||||
equals(other) {
|
||||
return ObjectUtil.equals(this, other);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -817,11 +817,11 @@ class LanguageManager {
|
||||
if (LanguageManager.isSupportLanguage(lang)) {
|
||||
CookieManager.setCookie("lang", lang, 150);
|
||||
} else {
|
||||
CookieManager.setCookie("lang", "en-US", 150);
|
||||
CookieManager.setCookie("lang", "zh-CN", 150);
|
||||
window.location.reload();
|
||||
}
|
||||
} else {
|
||||
CookieManager.setCookie("lang", "en-US", 150);
|
||||
CookieManager.setCookie("lang", "zh-CN", 150);
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
@@ -831,7 +831,7 @@ class LanguageManager {
|
||||
|
||||
static setLanguage(language) {
|
||||
if (!LanguageManager.isSupportLanguage(language)) {
|
||||
language = "en-US";
|
||||
language = "zh-CN";
|
||||
}
|
||||
|
||||
CookieManager.setCookie("lang", language, 150);
|
||||
@@ -884,4 +884,4 @@ class FileManager {
|
||||
|
||||
link.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ func (a *XUIController) initRouter(g *gin.RouterGroup) {
|
||||
g.GET("/inbounds", a.inbounds)
|
||||
g.GET("/settings", a.settings)
|
||||
g.GET("/xray", a.xraySettings)
|
||||
g.GET("/navigation", a.navigation)
|
||||
|
||||
a.inboundController = NewInboundController(g)
|
||||
a.settingController = NewSettingController(g)
|
||||
@@ -47,3 +48,7 @@ func (a *XUIController) settings(c *gin.Context) {
|
||||
func (a *XUIController) xraySettings(c *gin.Context) {
|
||||
html(c, "xray.html", "pages.xray.title", nil)
|
||||
}
|
||||
|
||||
func (a *XUIController) navigation(c *gin.Context) {
|
||||
html(c, "navigation.html", "pages.navigation.title", nil)
|
||||
}
|
||||
|
||||
@@ -64,6 +64,11 @@
|
||||
icon: 'tool',
|
||||
title: '{{ i18n "menu.xray"}}'
|
||||
},
|
||||
{
|
||||
key: '{{ .base_path }}panel/navigation',
|
||||
icon: 'link',
|
||||
title: '{{ i18n "menu.navigation"}}'
|
||||
},
|
||||
{
|
||||
key: '{{ .base_path }}logout/',
|
||||
icon: 'logout',
|
||||
@@ -100,4 +105,4 @@
|
||||
template: `{{template "component/sidebar/content"}}`,
|
||||
});
|
||||
</script>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
@@ -1,19 +1,24 @@
|
||||
{{define "form/inbound"}}
|
||||
<!-- base -->
|
||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||
<!-- 开关:启用/禁用 -->
|
||||
<a-form-item label='{{ i18n "enable" }}'>
|
||||
<a-switch v-model="dbInbound.enable"></a-switch>
|
||||
</a-form-item>
|
||||
|
||||
<!-- 备注 -->
|
||||
<a-form-item label='{{ i18n "remark" }}'>
|
||||
<a-input v-model.trim="dbInbound.remark"></a-input>
|
||||
</a-form-item>
|
||||
|
||||
<!-- 协议选择 -->
|
||||
<a-form-item label='{{ i18n "protocol" }}'>
|
||||
<a-select v-model="inbound.protocol" :disabled="isEdit" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="p in Protocols" :key="p" :value="p">[[ p ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
|
||||
<!-- 监听地址 -->
|
||||
<a-form-item>
|
||||
<template slot="label">
|
||||
<a-tooltip>
|
||||
@@ -27,23 +32,60 @@
|
||||
<a-input v-model.trim="inbound.listen"></a-input>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label='{{ i18n "pages.inbounds.port" }}'>
|
||||
<a-input-number v-model.number="inbound.port" :min="1" :max="65531"></a-input-number>
|
||||
</a-form-item>
|
||||
<!-- 端口 + 总流量 在同一行 -->
|
||||
<a-row :gutter="16">
|
||||
<!-- 左侧:端口 -->
|
||||
<a-col :span="12">
|
||||
<a-form-item>
|
||||
<template slot="label">
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
端口范围:1 - 65531(请尽量使用高位端口)
|
||||
</template>
|
||||
{{ i18n "pages.inbounds.port" }}
|
||||
<a-icon type="question-circle"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input-number v-model.number="inbound.port" :min="1" :max="65531" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
|
||||
<!-- 右侧:总流量 -->
|
||||
<a-col :span="12">
|
||||
<a-form-item>
|
||||
<template slot="label">
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
|
||||
</template>
|
||||
{{ i18n "pages.inbounds.totalFlow" }}
|
||||
<a-icon type="question-circle"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input-number v-model.number="dbInbound.totalGB" :min="0" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<!-- 设备限制 单独占一行 -->
|
||||
<a-form-item>
|
||||
<template slot="label">
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
|
||||
0 表示不限制(留空也表示不限制)
|
||||
</template>
|
||||
{{ i18n "pages.inbounds.totalFlow" }}
|
||||
设备限制
|
||||
<a-icon type="question-circle"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input-number v-model.number="dbInbound.totalGB" :min="0"></a-input-number>
|
||||
<a-input-number
|
||||
v-model.number="dbInbound.deviceLimit"
|
||||
:min="0"
|
||||
style="width: 100%"
|
||||
placeholder="0 = 不限制" />
|
||||
</a-form-item>
|
||||
|
||||
<!-- 到期时间 -->
|
||||
<a-form-item>
|
||||
<template slot="label">
|
||||
<a-tooltip>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<a-textarea v-model="inbound.stream.reality.privateKey"></a-textarea>
|
||||
</a-form-item>
|
||||
<a-form-item label=" ">
|
||||
<a-button type="primary" icon="import" @click="getNewX25519Cert">Get New Cert</a-button>
|
||||
<a-button type="primary" icon="import" @click="getNewX25519Cert">Get New Cert(随机获取新证书)</a-button>
|
||||
</a-form-item>
|
||||
<a-form-item label="mldsa65 Seed">
|
||||
<a-textarea v-model="inbound.stream.reality.mldsa65Seed"></a-textarea>
|
||||
@@ -57,7 +57,7 @@
|
||||
<a-textarea v-model="inbound.stream.reality.settings.mldsa65Verify"></a-textarea>
|
||||
</a-form-item>
|
||||
<a-form-item label=" ">
|
||||
<a-button type="primary" icon="import" @click="getNewmldsa65">Get New Seed</a-button>
|
||||
<a-button type="primary" icon="import" @click="getNewmldsa65">Get New Seed(获取Mldsa65数值)</a-button>
|
||||
</a-form-item>
|
||||
</template>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
+14
-4
@@ -701,7 +701,7 @@
|
||||
}, {
|
||||
title: '{{ i18n "pages.inbounds.remark" }}',
|
||||
align: 'center',
|
||||
width: 60,
|
||||
width: 50,
|
||||
dataIndex: "remark",
|
||||
}, {
|
||||
title: '{{ i18n "pages.inbounds.port" }}',
|
||||
@@ -716,13 +716,19 @@
|
||||
}, {
|
||||
title: '{{ i18n "clients" }}',
|
||||
align: 'left',
|
||||
width: 50,
|
||||
width: 40,
|
||||
scopedSlots: { customRender: 'clients' },
|
||||
}, {
|
||||
title: '{{ i18n "pages.inbounds.traffic" }}',
|
||||
align: 'center',
|
||||
width: 60,
|
||||
width: 50,
|
||||
scopedSlots: { customRender: 'traffic' },
|
||||
}, {
|
||||
title: '{{ i18n "pages.inbounds.deviceLimit" }}',
|
||||
dataIndex: "deviceLimit",
|
||||
align: "center",
|
||||
width: 40,
|
||||
customRender: (text) => (text > 0 ? text : i18n("pages.inbounds.unlimited")),
|
||||
}, {
|
||||
title: '{{ i18n "pages.inbounds.expireDate" }}',
|
||||
align: 'center',
|
||||
@@ -1115,6 +1121,8 @@
|
||||
remark: dbInbound.remark,
|
||||
enable: dbInbound.enable,
|
||||
expiryTime: dbInbound.expiryTime,
|
||||
// 新增这一行
|
||||
deviceLimit: dbInbound.deviceLimit,
|
||||
|
||||
listen: inbound.listen,
|
||||
port: inbound.port,
|
||||
@@ -1139,6 +1147,8 @@
|
||||
remark: dbInbound.remark,
|
||||
enable: dbInbound.enable,
|
||||
expiryTime: dbInbound.expiryTime,
|
||||
// 新增这一行
|
||||
deviceLimit: dbInbound.deviceLimit,
|
||||
|
||||
listen: inbound.listen,
|
||||
port: inbound.port,
|
||||
@@ -1632,4 +1642,4 @@
|
||||
},
|
||||
});
|
||||
</script>
|
||||
{{ template "page/body_end" .}}
|
||||
{{ template "page/body_end" .}}
|
||||
|
||||
+9
-7
@@ -219,20 +219,20 @@
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :sm="24" :lg="12">
|
||||
<a-card title='3X-UI' hoverable>
|
||||
<a rel="noopener" href="https://github.com/MHSanaei/3x-ui/releases" target="_blank">
|
||||
<a-card title='〔3X-UI〕中文优化版' hoverable>
|
||||
<a rel="noopener" href="https://github.com/xeefei/3x-ui/releases" target="_blank">
|
||||
<a-tag color="green">
|
||||
<span>v{{ .cur_ver }}</span>
|
||||
</a-tag>
|
||||
</a>
|
||||
<a rel="noopener" href="https://t.me/XrayUI" target="_blank">
|
||||
<a rel="noopener" href="https://t.me/is_Chat_Bot" target="_blank">
|
||||
<a-tag color="green">
|
||||
<span>@XrayUI</span>
|
||||
<span>TG私聊交流</span>
|
||||
</a-tag>
|
||||
</a>
|
||||
<a rel="noopener" href="https://github.com/MHSanaei/3x-ui/wiki" target="_blank">
|
||||
<a rel="noopener" href="https://t.me/XUI_CN" target="_blank">
|
||||
<a-tag color="purple">
|
||||
<span>{{ i18n "pages.index.documentation" }}</span>
|
||||
<span>〔3X-UI〕中文交流群</span>
|
||||
</a-tag>
|
||||
</a>
|
||||
</a-card>
|
||||
@@ -253,6 +253,8 @@
|
||||
</template>
|
||||
</a-tooltip>
|
||||
</a-tag>
|
||||
<a rel="noopener" href="https://ping.pe" target="_blank"><a-tag color="green">端口检测</a-tag></a>
|
||||
<a rel="noopener" href="https://www.speedtest.net" target="_blank"><a-tag color="green">网络测速</a-tag></a>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :sm="24" :lg="12">
|
||||
@@ -911,4 +913,4 @@ ${dateTime}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
{{ template "page/body_end" .}}
|
||||
{{ template "page/body_end" .}}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{{define "modals/ruleModal"}}
|
||||
<a-modal id="rule-modal" v-model="ruleModal.visible" :title="ruleModal.title" @ok="ruleModal.ok" :confirm-loading="ruleModal.confirmLoading" :closable="true" :mask-closable="false" :ok-text="ruleModal.okText" cancel-text='{{ i18n "close" }}' :class="themeSwitcher.currentTheme">
|
||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-form-item label='Domain Matcher'>
|
||||
<a-form-item label='{{ i18n "pages.xray.rules.DomainMatcher" }}'>
|
||||
<a-select v-model="ruleModal.rule.domainMatcher" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="dm in ['','hybrid','linear']" :value="dm">[[ dm ]]</a-select-option>
|
||||
</a-select>
|
||||
@@ -11,7 +11,7 @@
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
<span>{{ i18n "pages.xray.rules.useComma" }}</span>
|
||||
</template> Source IPs <a-icon type="question-circle"></a-icon>
|
||||
</template> {{ i18n "pages.xray.rules.SourceIPs" }} <a-icon type="question-circle"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input v-model.trim="ruleModal.rule.source"></a-input>
|
||||
@@ -21,22 +21,22 @@
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
<span>{{ i18n "pages.xray.rules.useComma" }}</span>
|
||||
</template> Source Port <a-icon type="question-circle"></a-icon>
|
||||
</template> {{ i18n "pages.xray.rules.SourcePort" }} <a-icon type="question-circle"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input v-model.trim="ruleModal.rule.sourcePort"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='Network'>
|
||||
<a-form-item label='{{ i18n "pages.xray.rules.Network" }}'>
|
||||
<a-select v-model="ruleModal.rule.network" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="x in ['','TCP','UDP','TCP,UDP']" :value="x">[[ x ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label='Protocol'>
|
||||
<a-form-item label='{{ i18n "pages.xray.rules.Protocol" }}'>
|
||||
<a-select v-model="ruleModal.rule.protocol" mode="multiple" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="x in ['http','tls','bittorrent','quic']" :value="x">[[ x ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label='Attributes'>
|
||||
<a-form-item label='{{ i18n "pages.xray.rules.Attributes" }}'>
|
||||
<a-button icon="plus" size="small" :style="{ marginLeft: '10px' }" @click="ruleModal.rule.attrs.push(['', ''])"></a-button>
|
||||
</a-form-item>
|
||||
<a-form-item :wrapper-col="{span: 24}">
|
||||
@@ -64,7 +64,7 @@
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
<span>{{ i18n "pages.xray.rules.useComma" }}</span>
|
||||
</template> Domain <a-icon type="question-circle"></a-icon>
|
||||
</template> {{ i18n "pages.xray.rules.Domain" }} <a-icon type="question-circle"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input v-model.trim="ruleModal.rule.domain"></a-input>
|
||||
@@ -74,7 +74,7 @@
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
<span>{{ i18n "pages.xray.rules.useComma" }}</span>
|
||||
</template> User <a-icon type="question-circle"></a-icon>
|
||||
</template> {{ i18n "pages.xray.rules.User" }} <a-icon type="question-circle"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input v-model.trim="ruleModal.rule.user"></a-input>
|
||||
@@ -84,17 +84,17 @@
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
<span>{{ i18n "pages.xray.rules.useComma" }}</span>
|
||||
</template> Port <a-icon type="question-circle"></a-icon>
|
||||
</template> {{ i18n "pages.xray.rules.Port" }} <a-icon type="question-circle"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input v-model.trim="ruleModal.rule.port"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='Inbound Tags'>
|
||||
<a-form-item label='{{ i18n "pages.xray.rules.InboundTag" }}'>
|
||||
<a-select v-model="ruleModal.rule.inboundTag" mode="multiple" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="tag in ruleModal.inboundTags" :value="tag">[[ tag ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label='Outbound Tag'>
|
||||
<a-form-item label='{{ i18n "pages.xray.rules.OutboundTag" }}'>
|
||||
<a-select v-model="ruleModal.rule.outboundTag" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="tag in ruleModal.outboundTags" :value="tag">[[ tag ]]</a-select-option>
|
||||
</a-select>
|
||||
@@ -104,7 +104,7 @@
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
<span>{{ i18n "pages.xray.balancer.balancerDesc" }}</span>
|
||||
</template> Balancer Tag <a-icon type="question-circle"></a-icon>
|
||||
</template> {{ i18n "pages.xray.rules.BalancerTag" }} <a-icon type="question-circle"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-select v-model="ruleModal.rule.balancerTag" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>实用导航&技巧</title>
|
||||
<style>
|
||||
body {
|
||||
background-color: #151F31;
|
||||
color: white;
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
}
|
||||
.sidebar {
|
||||
width: 150px;
|
||||
background-color: #151F31;
|
||||
padding: 20px;
|
||||
}
|
||||
.sidebar h2 {
|
||||
color: #fff;
|
||||
margin-top: 0;
|
||||
}
|
||||
.sidebar button {
|
||||
background-color: #2C3E56;
|
||||
color: #008000; /* 绿色字体 */
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
margin-bottom: 10px;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
width: 100%;
|
||||
border-radius: 15px; /* 增加圆角幅度 */
|
||||
font-weight: bold; /* 加粗字体 */
|
||||
}
|
||||
.sidebar button:hover {
|
||||
background-color: #4A90E2;
|
||||
}
|
||||
.sidebar .friend-links {
|
||||
margin-top: auto;
|
||||
text-align: center;
|
||||
margin-bottom: 12px; /* 调整友情链接下边距 */
|
||||
}
|
||||
.sidebar .friend-links a {
|
||||
color: #0080FF; /* 蓝色字体 */
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.separator {
|
||||
width: 15px;
|
||||
background-color: #34495e;
|
||||
}
|
||||
.content {
|
||||
flex: 1;
|
||||
padding: 20px;
|
||||
padding-top: 0; /* 移除顶部空白 */
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="sidebar">
|
||||
<button onclick="history.back()">返回上一步</button>
|
||||
<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
|
||||
<div class="friend-links">
|
||||
<a href="https://chat.openai.com/">ChatGPT</a>
|
||||
<a href="https://github.com/xeefei/3x-ui">项目地址</a>
|
||||
<a href="https://t.me/XUI_CN">3X-UI交流群</a>
|
||||
<a href="https://www.youtube.com/results?search_query=4k%E6%B5%8B%E9%80%9F">油管4K测速</a>
|
||||
<a href="https://whatismyipaddress.com/">我的IP查询</a>
|
||||
<a href="https://translate.google.com/?hl=zh-CN">Google翻译</a>
|
||||
<a href="https://xtls.github.io/">Project X</a>
|
||||
<a href="https://t.me/is_Haotian_Bot">预留&占位</a>
|
||||
<a href="https://t.me/is_Chat_Bot">联系作者</a>
|
||||
<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
|
||||
<button onclick="window.scrollTo(0, 0)">回滚至顶部</button>
|
||||
<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
|
||||
<button onclick="history.back()">返回到面板</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="separator"></div>
|
||||
<div class="content">
|
||||
<h3>一、【3x-ui】中文交流群:<a href="https://t.me/XUI_CN">https://t.me/XUI_CN</a></h3>
|
||||
<h3> 【3x-ui】详细安装流程步骤:<a href="https://xeefei.blogspot.com/2025/07/3x-ui.html">https://xeefei.blogspot.com/2025/07/3x-ui.html</a></h3>
|
||||
|
||||
<h3>二、判断VPS服务器的IP是否【送中】?</h3>
|
||||
<p>***点击打开:<a href="https://music.youtube.com/">https://music.youtube.com/</a>,能正常打开访问,就代表【没送中】,反之就是送中了。</p>
|
||||
<p>***如果送中了如何解决去【拉回来】?</p>
|
||||
<p>1:关闭/取消登录了谷歌账户的APP定位权限/授权;2:将常用的一个谷歌账号的位置记录功能打开;3:在电脑上打开Chrome/谷歌浏览器,登录开了位置记录功能的谷歌账号,安装Location Guard拓展插件<a href="https://chrome.google.com/webstore/detail/location-guard/cfohepagpmnodfdmjliccbbigdkfcgia">https://chrome.google.com/webstore/detail/location-guard/cfohepagpmnodfdmjliccbbigdkfcgia</a>(也可在其他支持此插件的浏览器使用);4:打开Location Guard插件,选择Fixed Location,并在给出的地图上单击,即可标记上你想要IP所处的国家/地区
|
||||
Google IP定位错误,使用Location Guard修改;5:转换到Options选项,Default level默认设置为Use fixed location;6:打开谷歌地图google.com/maps,点击右下角定位授权图标,使google maps获取当前“我的GPS位置”
|
||||
Google IP定位错误,使用Location Guard修改GPS位置地址;7:谷歌搜索my ip,即可看到谷歌IP定位到了刚才地图上标记的位置;8:在此网页向谷歌报告IP问题:<a href="https://support.google.com/websearch/workflow/9308722">https://support.google.com/websearch/workflow/9308722</a></p>
|
||||
|
||||
<h3>三、在自己的VPS服务器部署【订阅转换】功能</h3>
|
||||
<p>如何把vless/vmess等协议转换成Clash/Surge等软件支持的格式?
|
||||
1、进入脚本输入x-ui命令调取面板,选择第【24】选项安装订阅转换模块,
|
||||
2、等待安装【订阅转换】成功之后,访问地址:你的IP:18080(端口号)进行转换,
|
||||
3、因为在转换过程中需要调取后端API,所以请确保端口25500是打开放行的,
|
||||
4、在得到【转换链接】之后,只要你的VPS服务器25500端口是能ping通的,就能导入Clash/Surge等软件成功下载配置,
|
||||
5、此功能集成到3x-ui面板中,是为了保证安全,通过调取24选项把【订阅转换】功能部署在自己的VPS中,不会造成链接泄露。</p>
|
||||
|
||||
<h3>四、如何保护自己的IP不被墙被封?</h3>
|
||||
<p>1、使用的代理协议要安全,加密是必备,推荐使用vless+reality+vision协议组合,
|
||||
2、因为有时节点会共享,在不同的地区,多个省份之间不要共同连接同一个IP,
|
||||
3、连接同一个IP就算了,不要同一个端口,不要同IP+同端口到处漫游,要分开,
|
||||
4、同一台VPS,不要在一天内一直大流量去下载东西使用,不要流量过高要切换,
|
||||
5、创建【入站协议】的时候,尽量用【高位端口】,比如40000--65000之间的端口号。
|
||||
提醒:为什么在特殊时期,比如:两会,春节等被封得最严重最惨?
|
||||
尼玛同一个IP+同一个端口号,多个省份去漫游,跟开飞机场一样!不封你,封谁的IP和端口?
|
||||
总结:不要多终端/多省份/多个朋友/共同使用同一个IP和端口号!使用3x-ui多创建几个【入站】,
|
||||
多做几条备用,各用各的!各行其道才比较安全!GFW的思维模式是干掉机场,机场的特征个人用户不要去沾染,自然IP就保护好了。</p>
|
||||
|
||||
<h3>五、检测IP纯净度的方法:</h3>
|
||||
<p>网址:<a href="https://scamalytics.com/">https://scamalytics.com/</a>,输入IP进行检测,看【欺诈分数】,分数越高IP越脏。</p>
|
||||
|
||||
<h3>六、常见的翻墙软件/工具:</h3>
|
||||
<ol>
|
||||
<p>1、Windows系统v2rayN:<a href="https://github.com/2dust/v2rayN">https://github.com/2dust/v2rayN</a></p>
|
||||
<p>2、安卓手机版【v2rayNG】:<a href="https://github.com/2dust/v2rayNG">https://github.com/2dust/v2rayNG</a></p>
|
||||
<p>3、苹果手机IOS【小火箭】:<a href="https://apple02.com/">https://apple02.com/</a></p>
|
||||
<p>4、苹果MacOS电脑【Clash Verge】:<a href="https://github.com/clash-verge-rev/clash-verge-rev/releases">https://github.com/clash-verge-rev/clash-verge-rev/releases</a>或v2rayN:<a href="https://github.com/2dust/v2rayN">https://github.com/2dust/v2rayN</a></p>
|
||||
</ol>
|
||||
|
||||
<h3>七、查看节点【指定端口】的网络连接数/命令:</h3>
|
||||
<p>netstat -ntu | grep :节点端口 | grep ESTABLISHED | awk '{print $5}'</p>
|
||||
|
||||
<h3>八、用3x-ui如何实现【自己偷自己】?</h3>
|
||||
<p>其实很简单,只要你为面板设置了证书,
|
||||
开启了HTTPS登录,就可以将3x-ui自身作为web server,
|
||||
无需Nginx等,这里给一个示例:
|
||||
其中目标网站(Dest)请填写面板监听端口,
|
||||
可选域名(SNI)填写面板登录域名,
|
||||
如果您使用其他web server(如nginx)等,
|
||||
将目标网站改为对应监听端口也可。
|
||||
需要说明的是,如果您处于白名单地区,自己“偷”自己并不适合你;其次,可选域名一项实际上可以填写任意SNI,只要客户端保持一致即可,不过并不推荐这样做。</p>
|
||||
|
||||
<h3>九、【接码】网站:</h3>
|
||||
<p>网址:<a href="https://sms-activate.org/cn">https://sms-activate.org/cn</a>,直接注册账号购买。</p>
|
||||
|
||||
<h3>十、一些MJJ经常逛的网站和群组:</h3>
|
||||
<ol>
|
||||
<p>1、NodeSeek论坛:<a href="https://www.nodeseek.com/">https://www.nodeseek.com/</a></p>
|
||||
<p>2、V2EX论坛:<a href="https://www.v2ex.com/">https://www.v2ex.com/</a></p>
|
||||
<p>3、搬瓦工TG群:<a href="https://t.me/BWHOfficial">https://t.me/BWHOfficial</a></p>
|
||||
<p>4、Xray的官方群:<a href="https://t.me/projectXray">https://t.me/projectXray</a></p>
|
||||
<p>5、Dmit交流群:<a href="https://t.me/DmitChat">https://t.me/DmitChat</a></p>
|
||||
<p>6、白丝云用户群:<a href="https://t.me/+VHZLKELTQyzPNgOV">https://t.me/+VHZLKELTQyzPNgOV</a></p>
|
||||
<p>7、NameSilo域名注册:<a href="https://www.namesilo.com/">https://www.namesilo.com/</a></p>
|
||||
</ol>
|
||||
|
||||
<h3>十一、若此项目对你有帮助,你正想购买VPS的话,可以走一下我的AFF:</h3>
|
||||
<ol>
|
||||
<p>1、搬瓦工GIA线路:<a href="https://bandwagonhost.com/aff.php?aff=75015">https://bandwagonhost.com/aff.php?aff=75015</a></p>
|
||||
<p>2、Dmit高端GIA:<a href="https://www.dmit.io/aff.php?aff=9326">https://www.dmit.io/aff.php?aff=9326</a></p>
|
||||
<p>3、白丝云【4837】:<a href="https://cloudsilk.io/aff.php?aff=706">https://cloudsilk.io/aff.php?aff=706</a></p>
|
||||
</ol>
|
||||
|
||||
<h3>十二、项目〔声明和注意〕</h3>
|
||||
<ol>
|
||||
<p>1、声明: 此项目仅供个人学习、交流使用,请遵守当地法律法规,勿用于非法用途;请勿用于生产环境;</a></p>
|
||||
<p>2、注意: 在使用此项目和〔教程〕过程中,若因违反以上声明使用规则而产生的一切后果由使用者自负。</a></p>
|
||||
</ol>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,4 +1,10 @@
|
||||
{{define "settings/panel/general"}}
|
||||
<style>
|
||||
.red-placeholder input::-webkit-input-placeholder { color: red !important; }
|
||||
.red-placeholder input::-moz-placeholder { color: red !important; }
|
||||
.red-placeholder input:-ms-input-placeholder { color: red !important; }
|
||||
.red-placeholder input::placeholder { color: red !important; }
|
||||
</style>
|
||||
<a-collapse default-active-key="1">
|
||||
<a-collapse-panel key="1" header='{{ i18n "pages.xray.generalConfigs"}}'>
|
||||
<a-setting-list-item paddings="small">
|
||||
@@ -97,14 +103,14 @@
|
||||
<template #title>{{ i18n "pages.settings.publicKeyPath"}}</template>
|
||||
<template #description>{{ i18n "pages.settings.publicKeyPathDesc"}}</template>
|
||||
<template #control>
|
||||
<a-input type="text" v-model="allSetting.webCertFile"></a-input>
|
||||
<a-input type="text" v-model="allSetting.webCertFile" placeholder="/root/cert/域名/fullchain.pem" class="red-placeholder"></a-input>
|
||||
</template>
|
||||
</a-setting-list-item>
|
||||
<a-setting-list-item paddings="small">
|
||||
<template #title>{{ i18n "pages.settings.privateKeyPath"}}</template>
|
||||
<template #description>{{ i18n "pages.settings.privateKeyPathDesc"}}</template>
|
||||
<template #control>
|
||||
<a-input type="text" v-model="allSetting.webKeyFile"></a-input>
|
||||
<a-input type="text" v-model="allSetting.webKeyFile" placeholder="/root/cert/域名/privkey.pem" class="red-placeholder"></a-input>
|
||||
</template>
|
||||
</a-setting-list-item>
|
||||
</a-collapse-panel>
|
||||
@@ -146,4 +152,4 @@
|
||||
</a-setting-list-item>
|
||||
</a-collapse-panel>
|
||||
</a-collapse>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
@@ -10,13 +10,330 @@ import (
|
||||
"regexp"
|
||||
"sort"
|
||||
"time"
|
||||
"sync"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
|
||||
"x-ui/database"
|
||||
"x-ui/database/model"
|
||||
"x-ui/logger"
|
||||
"x-ui/xray"
|
||||
"x-ui/web/service"
|
||||
)
|
||||
|
||||
// =================================================================
|
||||
// 中文注释: 以下是用于实现设备限制功能的核心代码
|
||||
// =================================================================
|
||||
|
||||
// ActiveClientIPs 中文注释: 用于在内存中跟踪每个用户的活跃IP (TTL机制)
|
||||
// 结构: map[用户email] -> map[IP地址] -> 最后活跃时间
|
||||
var ActiveClientIPs = make(map[string]map[string]time.Time)
|
||||
var activeClientsLock sync.RWMutex
|
||||
|
||||
// ClientStatus 中文注释: 用于跟踪每个用户的状态(是否因为设备超限而被禁用)
|
||||
// 结构: map[用户email] -> 是否被禁用(true/false)
|
||||
var ClientStatus = make(map[string]bool)
|
||||
var clientStatusLock sync.RWMutex
|
||||
|
||||
// CheckDeviceLimitJob 中文注释: 这是我们的设备限制任务的结构体
|
||||
type CheckDeviceLimitJob struct {
|
||||
inboundService service.InboundService
|
||||
xrayService *service.XrayService
|
||||
// 中文注释: 新增 xrayApi 字段,用于持有 Xray API 客户端实例
|
||||
xrayApi xray.XrayAPI
|
||||
// lastPosition 中文注释: 用于记录上次读取 access.log 的位置,避免重复读取
|
||||
lastPosition int64
|
||||
}
|
||||
|
||||
// RandomUUID 中文注释: 新增一个辅助函数,用于生成一个随机的 UUID
|
||||
func RandomUUID() string {
|
||||
uuid := make([]byte, 16)
|
||||
rand.Read(uuid)
|
||||
uuid[6] = (uuid[6] & 0x0f) | 0x40
|
||||
uuid[8] = (uuid[8] & 0x3f) | 0x80
|
||||
return hex.EncodeToString(uuid[0:4]) + "-" + hex.EncodeToString(uuid[4:6]) + "-" + hex.EncodeToString(uuid[6:8]) + "-" + hex.EncodeToString(uuid[8:10]) + "-" + hex.EncodeToString(uuid[10:16])
|
||||
}
|
||||
|
||||
// NewCheckDeviceLimitJob 中文注释: 创建一个新的任务实例
|
||||
func NewCheckDeviceLimitJob(xrayService *service.XrayService) *CheckDeviceLimitJob {
|
||||
return &CheckDeviceLimitJob{
|
||||
xrayService: xrayService,
|
||||
// 中文注释: 初始化 xrayApi 字段
|
||||
xrayApi: xray.XrayAPI{},
|
||||
}
|
||||
}
|
||||
|
||||
// Run 中文注释: 定时任务的主函数,每次定时器触发时执行
|
||||
func (j *CheckDeviceLimitJob) Run() {
|
||||
// 中文注释: 检查 xray 是否正在运行,如果xray没运行,则无需执行此任务
|
||||
if !j.xrayService.IsXrayRunning() {
|
||||
return
|
||||
}
|
||||
|
||||
// 1. 清理过期的IP
|
||||
j.cleanupExpiredIPs()
|
||||
|
||||
// 2. 解析新的日志并更新IP列表
|
||||
j.parseAccessLog()
|
||||
|
||||
// 3. 检查所有用户的设备限制状态
|
||||
j.checkAllClientsLimit()
|
||||
}
|
||||
|
||||
// cleanupExpiredIPs 中文注释: 清理长时间不活跃的IP
|
||||
func (j *CheckDeviceLimitJob) cleanupExpiredIPs() {
|
||||
activeClientsLock.Lock()
|
||||
defer activeClientsLock.Unlock()
|
||||
|
||||
now := time.Now()
|
||||
// 中文注释: 活跃判断窗口(TTL): 近3分钟内出现过就算“活跃”
|
||||
const activeTTL = 3 * time.Minute
|
||||
for email, ips := range ActiveClientIPs {
|
||||
for ip, lastSeen := range ips {
|
||||
// 中文注释: 如果一个IP超过3分钟没有新的连接日志,我们就认为它已经下线
|
||||
if now.Sub(lastSeen) > activeTTL {
|
||||
delete(ActiveClientIPs[email], ip)
|
||||
}
|
||||
}
|
||||
// 中文注释: 如果一个用户的所有IP都下线了,就从大Map中移除这个用户,节省内存
|
||||
if len(ActiveClientIPs[email]) == 0 {
|
||||
delete(ActiveClientIPs, email)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// parseAccessLog 中文注释: 解析 xray access log 来获取最新的用户IP信息
|
||||
func (j *CheckDeviceLimitJob) parseAccessLog() {
|
||||
logPath, err := xray.GetAccessLogPath()
|
||||
if err != nil || logPath == "none" || logPath == "" {
|
||||
return
|
||||
}
|
||||
|
||||
file, err := os.Open(logPath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// 中文注释: 移动到上次读取结束的位置,实现增量读取
|
||||
file.Seek(j.lastPosition, 0)
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
|
||||
// 中文注释: 使用正则表达式从日志行中提取 email 和 IP
|
||||
emailRegex := regexp.MustCompile(`email: ([^ ]+)`)
|
||||
ipRegex := regexp.MustCompile(`from (?:tcp:|udp:)?\[?([0-9a-fA-F\.:]+)\]?:\d+ accepted`)
|
||||
|
||||
activeClientsLock.Lock()
|
||||
defer activeClientsLock.Unlock()
|
||||
|
||||
now := time.Now()
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
|
||||
emailMatch := emailRegex.FindStringSubmatch(line)
|
||||
ipMatch := ipRegex.FindStringSubmatch(line)
|
||||
|
||||
if len(emailMatch) > 1 && len(ipMatch) > 1 {
|
||||
email := emailMatch[1]
|
||||
ip := ipMatch[1]
|
||||
|
||||
if ip == "127.0.0.1" || ip == "::1" {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, ok := ActiveClientIPs[email]; !ok {
|
||||
ActiveClientIPs[email] = make(map[string]time.Time)
|
||||
}
|
||||
ActiveClientIPs[email][ip] = now
|
||||
}
|
||||
}
|
||||
|
||||
currentPosition, err := file.Seek(0, os.SEEK_END)
|
||||
if err == nil {
|
||||
if currentPosition < j.lastPosition {
|
||||
j.lastPosition = 0
|
||||
} else {
|
||||
j.lastPosition = currentPosition
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// checkAllClientsLimit 中文注释: 核心功能,检查所有用户,对超限的执行封禁,对恢复的执行解封
|
||||
func (j *CheckDeviceLimitJob) checkAllClientsLimit() {
|
||||
db := database.GetDB()
|
||||
var inbounds []*model.Inbound
|
||||
// 中文注释: 这里仅查询启用了设备限制(device_limit > 0)并且自身是开启状态的入站规则
|
||||
db.Where("device_limit > 0 AND enable = ?", true).Find(&inbounds)
|
||||
|
||||
if len(inbounds) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// 中文注释: 获取 API 端口。如果端口为0 (说明Xray未完全启动或有问题),则直接返回
|
||||
apiPort := j.xrayService.GetApiPort()
|
||||
if apiPort == 0 {
|
||||
return
|
||||
}
|
||||
// 中文注释: 使用获取到的端口号初始化 API 客户端
|
||||
j.xrayApi.Init(apiPort)
|
||||
defer j.xrayApi.Close()
|
||||
|
||||
// 中文注释: 优化 - 在一次循环中同时获取 tag 和 protocol
|
||||
inboundInfoMap := make(map[int]struct {
|
||||
Limit int
|
||||
Tag string
|
||||
Protocol model.Protocol
|
||||
})
|
||||
for _, inbound := range inbounds {
|
||||
inboundInfoMap[inbound.Id] = struct {
|
||||
Limit int
|
||||
Tag string
|
||||
Protocol model.Protocol
|
||||
}{Limit: inbound.DeviceLimit, Tag: inbound.Tag, Protocol: inbound.Protocol}
|
||||
}
|
||||
|
||||
activeClientsLock.RLock()
|
||||
clientStatusLock.Lock()
|
||||
defer activeClientsLock.RUnlock()
|
||||
defer clientStatusLock.Unlock()
|
||||
|
||||
// 第一步: 处理当前在线的用户
|
||||
for email, ips := range ActiveClientIPs {
|
||||
traffic, err := j.inboundService.GetClientTrafficByEmail(email)
|
||||
if err != nil || traffic == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
info, ok := inboundInfoMap[traffic.InboundId]
|
||||
if !ok || info.Limit <= 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
isBanned := ClientStatus[email]
|
||||
activeIPCount := len(ips)
|
||||
|
||||
// 调用封禁函数
|
||||
if activeIPCount > info.Limit && !isBanned {
|
||||
// 中文注释: 调用封禁函数时,传入当前的IP数用于记录日志
|
||||
j.banUser(email, activeIPCount, &info)
|
||||
}
|
||||
|
||||
// 调用解封函数
|
||||
if activeIPCount <= info.Limit && isBanned {
|
||||
// 中文注释: 调用解封函数时,传入当前的IP数用于记录日志
|
||||
j.unbanUser(email, activeIPCount, &info)
|
||||
}
|
||||
}
|
||||
|
||||
// 第二步: 专门处理那些“已被封禁”但“已不在线”的用户,为他们解封
|
||||
for email, isBanned := range ClientStatus {
|
||||
if !isBanned {
|
||||
continue
|
||||
}
|
||||
if _, online := ActiveClientIPs[email]; !online {
|
||||
traffic, err := j.inboundService.GetClientTrafficByEmail(email)
|
||||
if err != nil || traffic == nil {
|
||||
continue
|
||||
}
|
||||
info, ok := inboundInfoMap[traffic.InboundId]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
logger.Infof("已封禁用户 %s 已完全下线,执行解封操作。", email)
|
||||
|
||||
// 调用解封函数,这种情况下:活跃IP数为0,我们直接传入0用于记录日志
|
||||
j.unbanUser(email, 0, &info)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// banUser 中文注释: 封装的封禁用户函数;IP数量超限,且用户当前未被封禁 -> 执行封禁 (UUID 替换)
|
||||
func (j *CheckDeviceLimitJob) banUser(email string, activeIPCount int, info *struct {
|
||||
Limit int
|
||||
Tag string
|
||||
Protocol model.Protocol
|
||||
}) {
|
||||
// =================================================================
|
||||
// 这一行代码是整个解封逻辑的灵魂!
|
||||
// GetClientByEmail 函数会去查询您的数据库 (x-ui.db),
|
||||
// 找到 `inbounds` 表,解析其中的 `settings` 字段,并从中去,
|
||||
// 读取出您最初设置的、最原始、最正确的用户信息(包括最原始的UUID),
|
||||
// 然后把它赋值给 `client` 这个变量;此时,`client` 变量就持有了那个“老链接”的正确原始 UUID。
|
||||
// =================================================================
|
||||
_, client, err := j.inboundService.GetClientByEmail(email)
|
||||
if err != nil || client == nil {
|
||||
return
|
||||
}
|
||||
logger.Infof("〔设备限制〕超限:用户 %s. 限制: %d, 当前活跃: %d. 执行封禁掐网。", email, info.Limit, activeIPCount)
|
||||
|
||||
// 中文注释: 步骤一:先从 Xray-Core 中删除该用户。
|
||||
j.xrayApi.RemoveUser(info.Tag, email)
|
||||
|
||||
// =================================================================
|
||||
// 中文注释: 增加 5000 毫秒延时,解决竞态条件问题
|
||||
time.Sleep(5000 * time.Millisecond)
|
||||
// =================================================================
|
||||
|
||||
// 中文注释: 创建一个带有随机UUID/Password的临时客户端配置用于“封禁”
|
||||
tempClient := *client
|
||||
|
||||
// 适用于 VMess/VLESS
|
||||
if tempClient.ID != "" { tempClient.ID = RandomUUID() }
|
||||
|
||||
// 适用于 Trojan/Shadowsocks/Socks
|
||||
if tempClient.Password != "" { tempClient.Password = RandomUUID() }
|
||||
|
||||
var clientMap map[string]interface{}
|
||||
clientJson, _ := json.Marshal(tempClient)
|
||||
json.Unmarshal(clientJson, &clientMap)
|
||||
|
||||
// 中文注释: 步骤二:将这个带有错误UUID/Password的临时用户添加回去。
|
||||
// 客户端持有的还是旧的UUID,自然就无法通过验证,从而达到了“封禁”的效果。
|
||||
err = j.xrayApi.AddUser(string(info.Protocol), info.Tag, clientMap)
|
||||
if err != nil {
|
||||
logger.Warningf("通过API封禁用户 %s 失败: %v", email, err)
|
||||
} else {
|
||||
// 中文注释: 封禁成功后,在内存中标记该用户为“已封禁”状态。
|
||||
ClientStatus[email] = true
|
||||
}
|
||||
}
|
||||
|
||||
// unbanUser 中文注释: 封装的解封用户函数;IP数量已恢复正常,但用户处于封禁状态 -> 执行解封 (恢复原始 UUID)
|
||||
func (j *CheckDeviceLimitJob) unbanUser(email string, activeIPCount int, info *struct {
|
||||
Limit int
|
||||
Tag string
|
||||
Protocol model.Protocol
|
||||
}) {
|
||||
_, client, err := j.inboundService.GetClientByEmail(email)
|
||||
if err != nil || client == nil {
|
||||
return
|
||||
}
|
||||
logger.Infof("〔设备数量〕已恢复:用户 %s. 限制: %d, 当前活跃: %d. 执行解封/恢复用户。", email, info.Limit, activeIPCount)
|
||||
|
||||
// 中文注释: 步骤一:先从 Xray-Core 中删除用于“封禁”的那个临时用户。
|
||||
j.xrayApi.RemoveUser(info.Tag, email)
|
||||
|
||||
// =================================================================
|
||||
// 中文注释: 同样增加 5000 毫秒延时,确保解封操作的稳定性
|
||||
time.Sleep(5000 * time.Millisecond)
|
||||
// =================================================================
|
||||
|
||||
var clientMap map[string]interface{}
|
||||
clientJson, _ := json.Marshal(client)
|
||||
json.Unmarshal(clientJson, &clientMap)
|
||||
|
||||
// 中文注释: 步骤二:将数据库中原始的、正确的用户信息重新添加回 Xray-Core,从而实现“解封”。
|
||||
err = j.xrayApi.AddUser(string(info.Protocol), info.Tag, clientMap)
|
||||
if err != nil {
|
||||
logger.Warningf("通过API恢复用户 %s 失败: %v", email, err)
|
||||
} else {
|
||||
// 中文注释: 解封成功后,从内存中移除该用户的“已封禁”状态标记。
|
||||
delete(ClientStatus, email)
|
||||
}
|
||||
}
|
||||
|
||||
type CheckClientIpJob struct {
|
||||
lastClear int64
|
||||
disAllowedIps []string
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"log": {
|
||||
"access": "none",
|
||||
"dnsLog": false,
|
||||
"error": "",
|
||||
"loglevel": "warning",
|
||||
"access": "./access.log",
|
||||
"dnsLog": true,
|
||||
"error": "./error.log",
|
||||
"loglevel": "debug",
|
||||
"maskAddress": ""
|
||||
},
|
||||
"api": {
|
||||
|
||||
@@ -397,6 +397,8 @@ func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound,
|
||||
oldInbound.Remark = inbound.Remark
|
||||
oldInbound.Enable = inbound.Enable
|
||||
oldInbound.ExpiryTime = inbound.ExpiryTime
|
||||
// 中文注释:确保在更新数据时,将前端传来的 deviceLimit 值赋给从数据库中读出的旧对象。
|
||||
oldInbound.DeviceLimit = inbound.DeviceLimit
|
||||
oldInbound.Listen = inbound.Listen
|
||||
oldInbound.Port = inbound.Port
|
||||
oldInbound.Protocol = inbound.Protocol
|
||||
|
||||
@@ -47,7 +47,7 @@ var defaultValueMap = map[string]string{
|
||||
"tgBotBackup": "false",
|
||||
"tgBotLoginNotify": "true",
|
||||
"tgCpu": "80",
|
||||
"tgLang": "en-US",
|
||||
"tgLang": "zh-CN",
|
||||
"twoFactorEnable": "false",
|
||||
"twoFactorToken": "",
|
||||
"subEnable": "false",
|
||||
|
||||
@@ -26,10 +26,24 @@ type XrayService struct {
|
||||
xrayAPI xray.XrayAPI
|
||||
}
|
||||
|
||||
// IsXrayRunning 检查 Xray 是否正在运行
|
||||
func (s *XrayService) IsXrayRunning() bool {
|
||||
return p != nil && p.IsRunning()
|
||||
}
|
||||
|
||||
// 中文注释:
|
||||
// 新增 GetApiPort 函数。
|
||||
// 这个函数的作用是安全地返回当前 Xray 进程正在监听的 API 端口号。
|
||||
// 如果 Xray 没有运行 (p == nil),则返回 0。
|
||||
// 我们的后台任务将调用这个函数来获取端口号。
|
||||
func (s *XrayService) GetApiPort() int {
|
||||
if p == nil {
|
||||
return 0
|
||||
}
|
||||
return p.GetAPIPort()
|
||||
}
|
||||
|
||||
|
||||
func (s *XrayService) GetXrayErr() error {
|
||||
if p == nil {
|
||||
return nil
|
||||
|
||||
@@ -81,6 +81,7 @@
|
||||
"xray" = "Xray Configs"
|
||||
"logout" = "Log Out"
|
||||
"link" = "Manage"
|
||||
"navigation" = "navigation"
|
||||
|
||||
[pages.login]
|
||||
"hello" = "Hello"
|
||||
@@ -451,6 +452,9 @@
|
||||
"statsOutboundDownlink" = "Outbound Download Statistics"
|
||||
"statsOutboundDownlinkDesc" = "Enables the statistics collection for downstream traffic of all outbound proxies."
|
||||
|
||||
[pages.navigation]
|
||||
"title" = "navigation"
|
||||
|
||||
[pages.xray.rules]
|
||||
"first" = "First"
|
||||
"last" = "Last"
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
"edit" = "编辑"
|
||||
"delete" = "删除"
|
||||
"reset" = "重置"
|
||||
"noData" = "无数据。"
|
||||
"noData" = "无数据"
|
||||
"copySuccess" = "复制成功"
|
||||
"sure" = "确定"
|
||||
"encryption" = "加密"
|
||||
@@ -54,17 +54,18 @@
|
||||
"install" = "安装"
|
||||
"clients" = "客户端"
|
||||
"usage" = "使用情况"
|
||||
"secretToken" = "安全密钥"
|
||||
"twoFactorCode" = "代码"
|
||||
"remained" = "剩余"
|
||||
"security" = "安全"
|
||||
"secAlertTitle" = "安全警报"
|
||||
"secAlertSsl" = "此连接不安全。在激活 TLS 进行数据保护之前,请勿输入敏感信息。"
|
||||
"secAlertConf" = "某些设置易受攻击。建议加强安全协议以防止潜在漏洞。"
|
||||
"secAlertSSL" = "面板缺少安全连接。请安装 TLS 证书以保护数据安全。"
|
||||
"secAlertPanelPort" = "面板默认端口存在安全风险。请配置随机端口或特定端口。"
|
||||
"secAlertPanelURI" = "面板默认 URI 路径不安全。请配置复杂的 URI 路径。"
|
||||
"secAlertSubURI" = "订阅默认 URI 路径不安全。请配置复杂的 URI 路径。"
|
||||
"secAlertSubJsonURI" = "订阅 JSON 默认 URI 路径不安全。请配置复杂的 URI 路径。"
|
||||
"secAlertSsl" = "此连接不安全!在激活 TLS 进行数据保护之前,请勿输入敏感信息。"
|
||||
"secAlertConf" = "某些设置易受攻击!建议加强安全协议以防止潜在漏洞。"
|
||||
"secAlertSSL" = "面板缺少安全连接!请安装 TLS 证书以保护数据安全。"
|
||||
"secAlertPanelPort" = "面板默认端口存在安全风险!请配置随机端口或特定端口。"
|
||||
"secAlertPanelURI" = "面板默认 URI 路径不安全!请配置复杂的 URI 路径。"
|
||||
"secAlertSubURI" = "订阅默认 URI 路径不安全!请配置复杂的 URI 路径。"
|
||||
"secAlertSubJsonURI" = "订阅 JSON 默认 URI 路径不安全!请配置复杂的 URI 路径。"
|
||||
"emptyDnsDesc" = "未添加DNS服务器。"
|
||||
"emptyFakeDnsDesc" = "未添加Fake DNS服务器。"
|
||||
"emptyBalancersDesc" = "未添加负载均衡器。"
|
||||
@@ -74,13 +75,14 @@
|
||||
[menu]
|
||||
"theme" = "主题"
|
||||
"dark" = "暗色"
|
||||
"ultraDark" = "超暗色"
|
||||
"ultraDark" = "黑色"
|
||||
"dashboard" = "系统状态"
|
||||
"inbounds" = "入站列表"
|
||||
"settings" = "面板设置"
|
||||
"xray" = "Xray 设置"
|
||||
"xray" = "Xray设置"
|
||||
"logout" = "退出登录"
|
||||
"link" = "管理"
|
||||
"navigation" = "实用导航"
|
||||
|
||||
[pages.login]
|
||||
"hello" = "你好"
|
||||
@@ -102,6 +104,7 @@
|
||||
"swap" = "交换分区"
|
||||
"storage" = "存储"
|
||||
"memory" = "内存"
|
||||
"hard" = "磁盘"
|
||||
"threads" = "线程"
|
||||
"xrayStatus" = "Xray"
|
||||
"stopXray" = "停止"
|
||||
@@ -138,7 +141,7 @@
|
||||
"dontRefresh" = "安装中,请勿刷新此页面"
|
||||
"logs" = "日志"
|
||||
"config" = "配置"
|
||||
"backup" = "备份"
|
||||
"backup" = "备份和恢复"
|
||||
"backupTitle" = "备份和恢复数据库"
|
||||
"exportDatabase" = "备份"
|
||||
"exportDatabaseDesc" = "点击下载包含当前数据库备份的 .db 文件到您的设备。"
|
||||
@@ -172,6 +175,8 @@
|
||||
"generalActions" = "通用操作"
|
||||
"autoRefresh" = "自动刷新"
|
||||
"autoRefreshInterval" = "间隔"
|
||||
"create" = "添加"
|
||||
"update" = "修改"
|
||||
"modifyInbound" = "修改入站"
|
||||
"deleteInbound" = "删除入站"
|
||||
"deleteInboundContent" = "确定要删除入站吗?"
|
||||
@@ -193,7 +198,7 @@
|
||||
"publicKey" = "公钥"
|
||||
"privatekey" = "私钥"
|
||||
"clickOnQRcode" = "点击二维码复制"
|
||||
"client" = "客户"
|
||||
"client" = "客户/用户"
|
||||
"export" = "导出链接"
|
||||
"clone" = "克隆"
|
||||
"cloneInbound" = "克隆"
|
||||
@@ -212,14 +217,14 @@
|
||||
"delDepletedClientsTitle" = "删除流量耗尽的客户端"
|
||||
"delDepletedClientsContent" = "确定要删除所有流量耗尽的客户端吗?"
|
||||
"email" = "电子邮件"
|
||||
"emailDesc" = "电子邮件必须完全唯一"
|
||||
"emailDesc" = "电子邮件必须确保唯一"
|
||||
"IPLimit" = "IP 限制"
|
||||
"IPLimitDesc" = "如果数量超过设置值,则禁用入站流量。(0 = 禁用)"
|
||||
"IPLimitlog" = "IP 日志"
|
||||
"IPLimitlogDesc" = "IP 历史日志(要启用被禁用的入站流量,请清除日志)"
|
||||
"IPLimitlogclear" = "清除日志"
|
||||
"setDefaultCert" = "从面板设置证书"
|
||||
"telegramDesc" = "请提供Telegram聊天ID。(在机器人中使用'/id'命令)或(@userinfobot"
|
||||
"telegramDesc" = "请提供Telegram聊天ID。(在机器人中使用'/id'命令或跟@userinfobot机器人对话获取)"
|
||||
"subscriptionDesc" = "要找到你的订阅 URL,请导航到“详细信息”。此外,你可以为多个客户端使用相同的名称。"
|
||||
"info" = "信息"
|
||||
"same" = "相同"
|
||||
@@ -227,6 +232,12 @@
|
||||
"exportInbound" = "导出入站规则"
|
||||
"import"="导入"
|
||||
"importInbound" = "导入入站规则"
|
||||
"reviewTitle"="请确认以下配置信息"
|
||||
"neverExpire"="永不过期"
|
||||
"reviewHint"="如需修改请返回上一步"
|
||||
"unlimited"="无限制"
|
||||
"deviceLimit"="设备限制"
|
||||
|
||||
|
||||
[pages.client]
|
||||
"add" = "添加客户端"
|
||||
@@ -241,7 +252,7 @@
|
||||
"prefix" = "前缀"
|
||||
"postfix" = "后缀"
|
||||
"delayedStart" = "首次使用后开始"
|
||||
"expireDays" = "期间"
|
||||
"expireDays" = "到期天数/期间"
|
||||
"days" = "天"
|
||||
"renew" = "自动续订"
|
||||
"renewDesc" = "到期后自动续订。(0 = 禁用)(单位: 天)"
|
||||
@@ -291,7 +302,7 @@
|
||||
"resetDefaultConfig" = "重置为默认配置"
|
||||
"panelSettings" = "常规"
|
||||
"securitySettings" = "安全设定"
|
||||
"TGBotSettings" = "Telegram 机器人配置"
|
||||
"TGBotSettings" = "Telegram机器人配置"
|
||||
"panelListeningIP" = "面板监听 IP"
|
||||
"panelListeningIPDesc" = "默认留空监听所有 IP"
|
||||
"panelListeningDomain" = "面板监听域名"
|
||||
@@ -299,10 +310,12 @@
|
||||
"panelPort" = "面板监听端口"
|
||||
"panelPortDesc" = "重启面板生效"
|
||||
"publicKeyPath" = "面板证书公钥文件路径"
|
||||
"publicKeyPathDesc" = "填写一个 '/' 开头的绝对路径"
|
||||
"DefaultpublicKeyPath" = "/root/.acme.sh/域名_ecc/域名.cer"
|
||||
"publicKeyPathDesc" = "填写一个 '/' 开头的绝对路径,〔acme方式〕请自行在填入时修改域名"
|
||||
"privateKeyPath" = "面板证书密钥文件路径"
|
||||
"privateKeyPathDesc" = "填写一个 '/' 开头的绝对路径"
|
||||
"panelUrlPath" = "面板 url 根路径"
|
||||
"DefaultprivateKeyPath" = "/root/.acme.sh/域名_ecc/域名.key"
|
||||
"privateKeyPathDesc" = "填写一个 '/' 开头的绝对路径,〔acme方式〕请自行在填入时修改域名"
|
||||
"panelUrlPath" = "面板登录访问路径"
|
||||
"panelUrlPathDesc" = "必须以 '/' 开头,以 '/' 结尾"
|
||||
"pageSize" = "分页大小"
|
||||
"pageSizeDesc" = "定义入站表的页面大小。设置 0 表示禁用"
|
||||
@@ -318,7 +331,7 @@
|
||||
"telegramBotEnable" = "启用 Telegram 机器人"
|
||||
"telegramBotEnableDesc" = "启用 Telegram 机器人功能"
|
||||
"telegramToken" = "Telegram 机器人令牌(token)"
|
||||
"telegramTokenDesc" = "从 '@BotFather' 获取的 Telegram 机器人令牌"
|
||||
"telegramTokenDesc" = "跟 '@BotFather' 对话获取的 Telegram 机器人令牌"
|
||||
"telegramProxy" = "SOCKS5 Proxy"
|
||||
"telegramProxyDesc" = "启用 SOCKS5 代理连接到 Telegram(根据指南调整设置)"
|
||||
"telegramAPIServer" = "Telegram API Server"
|
||||
@@ -451,13 +464,16 @@
|
||||
"statsOutboundDownlink" = "出站下载统计"
|
||||
"statsOutboundDownlinkDesc" = "启用所有出站代理的下行流量统计收集。"
|
||||
|
||||
[pages.navigation]
|
||||
"title" = "实用导航"
|
||||
|
||||
[pages.xray.rules]
|
||||
"first" = "置顶"
|
||||
"last" = "置底"
|
||||
"up" = "向上"
|
||||
"down" = "向下"
|
||||
"source" = "来源"
|
||||
"dest" = "目的地址"
|
||||
"dest" = "目标地址"
|
||||
"inbound" = "入站"
|
||||
"outbound" = "出站"
|
||||
"balancer" = "负载均衡"
|
||||
@@ -465,6 +481,18 @@
|
||||
"add" = "添加规则"
|
||||
"edit" = "编辑规则"
|
||||
"useComma" = "逗号分隔的项目"
|
||||
"DomainMatcher" = "域匹配类型"
|
||||
"SourceIPs" = "源IP"
|
||||
"SourcePort" = "源端口"
|
||||
"Network" = "网络类型"
|
||||
"Protocol" = "传输协议"
|
||||
"Attributes" = "属性"
|
||||
"Domain" = "域地址"
|
||||
"User" = "用户"
|
||||
"Port" = "端口"
|
||||
"InboundTag" = "入站 Tag"
|
||||
"OutboundTag" = "出站 Tag"
|
||||
"BalancerTag" = "负载均衡 Tag"
|
||||
|
||||
[pages.xray.outbound]
|
||||
"addOutbound" = "添加出站"
|
||||
@@ -565,27 +593,26 @@
|
||||
[tgbot]
|
||||
"keyboardClosed" = "❌ 自定义键盘已关闭!"
|
||||
"noResult" = "❗ 没有结果!"
|
||||
"noQuery" = "❌ 未找到查询!请再次使用该命令!"
|
||||
"noQuery" = "❌ 未找到查询!请重新使用命令!"
|
||||
"wentWrong" = "❌ 出了点问题!"
|
||||
"noIpRecord" = "❗ 没有IP记录!"
|
||||
"noInbounds" = "❗ 未找到入站!"
|
||||
"unlimited" = "♾ 无限(重置)"
|
||||
"noIpRecord" = "❗ 没有 IP 记录!"
|
||||
"noInbounds" = "❗ 没有找到入站连接!"
|
||||
"unlimited" = "♾ 无限制"
|
||||
"add" = "添加"
|
||||
"month" = "月"
|
||||
"months" = "月"
|
||||
"day" = "天"
|
||||
"days" = "天"
|
||||
"hours" = "小时"
|
||||
"minutes" = "分钟"
|
||||
"unknown" = "未知"
|
||||
"inbounds" = "入站"
|
||||
"inbounds" = "入站连接"
|
||||
"clients" = "客户端"
|
||||
"offline" = "🔴 离线"
|
||||
"online" = "🟢 在线"
|
||||
|
||||
[tgbot.commands]
|
||||
"unknown" = "❗ 未知命令"
|
||||
"pleaseChoose" = "👇 请选择:\r\n"
|
||||
"pleaseChoose" = "👇请〔按照需求〕选择下方按钮 :\r\n"
|
||||
"help" = "🤖 欢迎使用本机器人!它旨在为您提供来自服务器的特定数据,并允许您进行必要的修改。\r\n\r\n"
|
||||
"start" = "👋 你好,<i>{{ .Firstname }}</i>。\r\n"
|
||||
"welcome" = "🤖 欢迎来到 <b>{{ .Hostname }}</b> 管理机器人。\r\n"
|
||||
@@ -612,7 +639,7 @@
|
||||
"report" = "🕰 定时报告:{{ .RunTime }}\r\n"
|
||||
"datetime" = "⏰ 日期时间:{{ .DateTime }}\r\n"
|
||||
"hostname" = "💻 主机名:{{ .Hostname }}\r\n"
|
||||
"version" = "🚀 X-UI 版本:{{ .Version }}\r\n"
|
||||
"version" = "🚀 3X-UI 版本:{{ .Version }}\r\n"
|
||||
"xrayVersion" = "📡 Xray 版本: {{ .XrayVersion }}\r\n"
|
||||
"ipv6" = "🌐 IPv6:{{ .IPv6 }}\r\n"
|
||||
"ipv4" = "🌐 IPv4:{{ .IPv4 }}\r\n"
|
||||
@@ -635,7 +662,7 @@
|
||||
"active" = "💡 激活:{{ .Enable }}\r\n"
|
||||
"enabled" = "🚨 已启用:{{ .Enable }}\r\n"
|
||||
"online" = "🌐 连接状态:{{ .Status }}\r\n"
|
||||
"email" = "📧 邮箱:{{ .Email }}\r\n"
|
||||
"email" = "📧 邮箱(用户):{{ .Email }}\r\n"
|
||||
"upload" = "🔼 上传↑:{{ .Upload }}\r\n"
|
||||
"download" = "🔽 下载↓:{{ .Download }}\r\n"
|
||||
"total" = "📊 总计:{{ .UpDown }} / {{ .Total }}\r\n"
|
||||
@@ -678,12 +705,12 @@
|
||||
"confirmRemoveTGUser" = "✅ 确认移除 Telegram 用户?"
|
||||
"confirmToggle" = "✅ 确认启用/禁用用户?"
|
||||
"dbBackup" = "获取数据库备份"
|
||||
"serverUsage" = "服务器使用情况"
|
||||
"serverUsage" = "服务器状态"
|
||||
"getInbounds" = "获取入站信息"
|
||||
"depleteSoon" = "即将耗尽"
|
||||
"clientUsage" = "获取使用情况"
|
||||
"onlines" = "在线客户端"
|
||||
"commands" = "命令"
|
||||
"commands" = "常用命令"
|
||||
"refresh" = "🔄 刷新"
|
||||
"clearIPs" = "❌ 清除 IP"
|
||||
"removeTGUser" = "❌ 移除 Telegram 用户"
|
||||
@@ -695,7 +722,7 @@
|
||||
"ipLimit" = "🔢 IP 限制"
|
||||
"setTGUser" = "👤 设置 Telegram 用户"
|
||||
"toggle" = "🔘 启用/禁用"
|
||||
"custom" = "🔢 风俗"
|
||||
"custom" = "🔢 自定义输入"
|
||||
"confirmNumber" = "✅ 确认: {{ .Num }}"
|
||||
"confirmNumberAdd" = "✅ 确认添加:{{ .Num }}"
|
||||
"limitTraffic" = "🚧 流量限制"
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
"username" = "使用者名稱"
|
||||
"username" = "用戶名"
|
||||
"password" = "密碼"
|
||||
"login" = "登入"
|
||||
"confirm" = "確定"
|
||||
"cancel" = "取消"
|
||||
"close" = "關閉"
|
||||
"create" = "建立"
|
||||
"create" = "創建"
|
||||
"update" = "更新"
|
||||
"copy" = "複製"
|
||||
"copied" = "已複製"
|
||||
@@ -27,8 +27,8 @@
|
||||
"info" = "更多資訊"
|
||||
"edit" = "編輯"
|
||||
"delete" = "刪除"
|
||||
"reset" = "重置"
|
||||
"noData" = "無數據。"
|
||||
"reset" = "重設"
|
||||
"noData" = "無資料"
|
||||
"copySuccess" = "複製成功"
|
||||
"sure" = "確定"
|
||||
"encryption" = "加密"
|
||||
@@ -43,10 +43,10 @@
|
||||
"depleted" = "耗盡"
|
||||
"depletingSoon" = "即將耗盡"
|
||||
"offline" = "離線"
|
||||
"online" = "線上"
|
||||
"domainName" = "域名"
|
||||
"online" = "在線"
|
||||
"domainName" = "網域名稱"
|
||||
"monitor" = "監聽"
|
||||
"certificate" = "憑證"
|
||||
"certificate" = "數位憑證"
|
||||
"fail" = "失敗"
|
||||
"comment" = "評論"
|
||||
"success" = "成功"
|
||||
@@ -54,33 +54,35 @@
|
||||
"install" = "安裝"
|
||||
"clients" = "客戶端"
|
||||
"usage" = "使用情況"
|
||||
"secretToken" = "安全金鑰"
|
||||
"twoFactorCode" = "代碼"
|
||||
"remained" = "剩餘"
|
||||
"security" = "安全"
|
||||
"secAlertTitle" = "安全警報"
|
||||
"secAlertSsl" = "此連線不安全。在啟用 TLS 進行資料保護之前,請勿輸入敏感資訊。"
|
||||
"secAlertConf" = "某些設定易受攻擊。建議加強安全協議以防止潛在漏洞。"
|
||||
"secAlertSSL" = "面板缺少安全連線。請安裝 TLS 證書以保護資料安全。"
|
||||
"secAlertPanelPort" = "面板預設埠存在安全風險。請配置隨機埠或特定埠。"
|
||||
"secAlertPanelURI" = "面板預設 URI 路徑不安全。請配置複雜的 URI 路徑。"
|
||||
"secAlertSubURI" = "訂閱預設 URI 路徑不安全。請配置複雜的 URI 路徑。"
|
||||
"secAlertSubJsonURI" = "訂閱 JSON 預設 URI 路徑不安全。請配置複雜的 URI 路徑。"
|
||||
"emptyDnsDesc" = "未添加DNS伺服器。"
|
||||
"emptyFakeDnsDesc" = "未添加Fake DNS伺服器。"
|
||||
"emptyBalancersDesc" = "未添加負載平衡器。"
|
||||
"emptyReverseDesc" = "未添加反向代理。"
|
||||
"somethingWentWrong" = "發生錯誤"
|
||||
"secAlertSsl" = "此連線不安全!在啟用 TLS 進行資料保護之前,請勿輸入敏感資訊。"
|
||||
"secAlertConf" = "某些設定易受攻擊!建議加強安全協議以防止潛在漏洞。"
|
||||
"secAlertSSL" = "面板缺少安全連線!請安裝 TLS 憑證以保護資料安全。"
|
||||
"secAlertPanelPort" = "面板預設連接埠存在安全風險!請設定隨機連接埠或特定連接埠。"
|
||||
"secAlertPanelURI" = "面板預設 URI 路徑不安全!請設定複雜的 URI 路徑。"
|
||||
"secAlertSubURI" = "訂閱預設 URI 路徑不安全!請設定複雜的 URI 路徑。"
|
||||
"secAlertSubJsonURI" = "訂閱 JSON 預設 URI 路徑不安全!請設定複雜的 URI 路徑。"
|
||||
"emptyDnsDesc" = "未新增 DNS 伺服器。"
|
||||
"emptyFakeDnsDesc" = "未新增 Fake DNS 伺服器。"
|
||||
"emptyBalancersDesc" = "未新增負載平衡器。"
|
||||
"emptyReverseDesc" = "未新增反向代理。"
|
||||
"somethingWentWrong" = "出了一點問題"
|
||||
|
||||
[menu]
|
||||
"theme" = "主題"
|
||||
"dark" = "深色"
|
||||
"ultraDark" = "超深色"
|
||||
"dark" = "暗色"
|
||||
"ultraDark" = "黑色"
|
||||
"dashboard" = "系統狀態"
|
||||
"inbounds" = "入站列表"
|
||||
"settings" = "面板設定"
|
||||
"xray" = "Xray 設定"
|
||||
"logout" = "退出登入"
|
||||
"logout" = "登出"
|
||||
"link" = "管理"
|
||||
"navigation" = "實用導覽"
|
||||
|
||||
[pages.login]
|
||||
"hello" = "你好"
|
||||
@@ -89,9 +91,9 @@
|
||||
|
||||
[pages.login.toasts]
|
||||
"invalidFormData" = "資料格式錯誤"
|
||||
"emptyUsername" = "請輸入使用者名稱"
|
||||
"emptyUsername" = "請輸入用戶名"
|
||||
"emptyPassword" = "請輸入密碼"
|
||||
"wrongUsernameOrPassword" = "用戶名、密碼或雙重驗證碼無效。"
|
||||
"wrongUsernameOrPassword" = "用戶名、密碼或雙重驗證碼無效。"
|
||||
"successLogin" = "您已成功登入您的帳戶。"
|
||||
|
||||
[pages.index]
|
||||
@@ -99,56 +101,57 @@
|
||||
"cpu" = "CPU"
|
||||
"logicalProcessors" = "邏輯處理器"
|
||||
"frequency" = "頻率"
|
||||
"swap" = "交換空間"
|
||||
"swap" = "交換分區"
|
||||
"storage" = "儲存"
|
||||
"memory" = "記憶體"
|
||||
"hard" = "磁碟"
|
||||
"threads" = "執行緒"
|
||||
"xrayStatus" = "Xray"
|
||||
"stopXray" = "停止"
|
||||
"restartXray" = "重啟"
|
||||
"xraySwitch" = "版本"
|
||||
"xraySwitchClick" = "選擇你要切換到的版本"
|
||||
"xraySwitchClickDesk" = "請謹慎選擇,因為較舊版本可能與當前配置不相容"
|
||||
"xraySwitchClick" = "選擇您要切換到的版本"
|
||||
"xraySwitchClickDesk" = "請謹慎選擇,因為較舊版本可能與目前設定不相容"
|
||||
"xrayStatusUnknown" = "未知"
|
||||
"xrayStatusRunning" = "運行中"
|
||||
"xrayStatusStop" = "停止"
|
||||
"xrayStatusError" = "錯誤"
|
||||
"xrayErrorPopoverTitle" = "執行Xray時發生錯誤"
|
||||
"xrayErrorPopoverTitle" = "執行 Xray 時發生錯誤"
|
||||
"operationHours" = "系統正常執行時間"
|
||||
"systemLoad" = "系統負載"
|
||||
"systemLoadDesc" = "過去 1、5 和 15 分鐘的系統平均負載"
|
||||
"connectionCount" = "連線數"
|
||||
"ipAddresses" = "IP地址"
|
||||
"toggleIpVisibility" = "切換IP可見性"
|
||||
"ipAddresses" = "IP 位址"
|
||||
"toggleIpVisibility" = "切換 IP 可見性"
|
||||
"overallSpeed" = "整體速度"
|
||||
"upload" = "上傳"
|
||||
"download" = "下載"
|
||||
"totalData" = "總數據"
|
||||
"sent" = "已發送"
|
||||
"sent" = "已傳送"
|
||||
"received" = "已接收"
|
||||
"documentation" = "文件"
|
||||
"xraySwitchVersionDialog" = "您確定要變更Xray版本嗎?"
|
||||
"xraySwitchVersionDialogDesc" = "這將會把Xray版本變更為#version#。"
|
||||
"xraySwitchVersionDialog" = "您確定要變更 Xray 版本嗎?"
|
||||
"xraySwitchVersionDialogDesc" = "這將把 Xray 版本變更為 #version#。"
|
||||
"xraySwitchVersionPopover" = "Xray 更新成功"
|
||||
"geofileUpdateDialog" = "您確定要更新地理檔案嗎?"
|
||||
"geofileUpdateDialogDesc" = "這將更新 #filename# 檔案。"
|
||||
"geofilesUpdateDialogDesc" = "這將更新所有文件。"
|
||||
"geofilesUpdateDialogDesc" = "這將更新所有檔案。"
|
||||
"geofilesUpdateAll" = "全部更新"
|
||||
"geofileUpdatePopover" = "地理檔案更新成功"
|
||||
"dontRefresh" = "安裝中,請勿重新整理此頁面"
|
||||
"logs" = "日誌"
|
||||
"config" = "配置"
|
||||
"backup" = "備份和恢復"
|
||||
"backupTitle" = "備份和恢復資料庫"
|
||||
"config" = "設定"
|
||||
"backup" = "備份與還原"
|
||||
"backupTitle" = "備份與還原資料庫"
|
||||
"exportDatabase" = "備份"
|
||||
"exportDatabaseDesc" = "點擊下載包含當前資料庫備份的 .db 文件到您的設備。"
|
||||
"importDatabase" = "恢復"
|
||||
"importDatabaseDesc" = "點擊選擇並上傳設備中的 .db 文件以從備份恢復資料庫。"
|
||||
"exportDatabaseDesc" = "點擊下載包含目前資料庫備份的 .db 檔案到您的裝置。"
|
||||
"importDatabase" = "還原"
|
||||
"importDatabaseDesc" = "點擊選擇並上傳裝置中的 .db 檔案以從備份還原資料庫。"
|
||||
"importDatabaseSuccess" = "資料庫匯入成功"
|
||||
"importDatabaseError" = "匯入資料庫時發生錯誤"
|
||||
"readDatabaseError" = "讀取資料庫時發生錯誤"
|
||||
"getDatabaseError" = "檢索資料庫時發生錯誤"
|
||||
"getConfigError" = "檢索設定檔時發生錯誤"
|
||||
"getDatabaseError" = "擷取資料庫時發生錯誤"
|
||||
"getConfigError" = "擷取設定檔時發生錯誤"
|
||||
|
||||
[pages.inbounds]
|
||||
"title" = "入站列表"
|
||||
@@ -159,30 +162,32 @@
|
||||
"enable" = "啟用"
|
||||
"remark" = "備註"
|
||||
"protocol" = "協議"
|
||||
"port" = "埠"
|
||||
"portMap" = "埠映射"
|
||||
"port" = "連接埠"
|
||||
"portMap" = "連接埠映射"
|
||||
"traffic" = "流量"
|
||||
"details" = "詳細資訊"
|
||||
"transportConfig" = "傳輸配置"
|
||||
"transportConfig" = "傳輸設定"
|
||||
"expireDate" = "到期時間"
|
||||
"createdAt" = "建立時間"
|
||||
"updatedAt" = "更新時間"
|
||||
"resetTraffic" = "重置流量"
|
||||
"addInbound" = "新增入站"
|
||||
"generalActions" = "通用操作"
|
||||
"autoRefresh" = "自動刷新"
|
||||
"autoRefresh" = "自動重新整理"
|
||||
"autoRefreshInterval" = "間隔"
|
||||
"create" = "新增"
|
||||
"update" = "修改"
|
||||
"modifyInbound" = "修改入站"
|
||||
"deleteInbound" = "刪除入站"
|
||||
"deleteInboundContent" = "確定要刪除入站嗎?"
|
||||
"deleteClient" = "刪除客戶端"
|
||||
"deleteClientContent" = "確定要刪除客戶端嗎?"
|
||||
"resetTrafficContent" = "確定要重置流量嗎?"
|
||||
"resetTrafficContent" = "確定要重設流量嗎?"
|
||||
"copyLink" = "複製連結"
|
||||
"address" = "地址"
|
||||
"address" = "位址"
|
||||
"network" = "網路"
|
||||
"destinationPort" = "目標埠"
|
||||
"targetAddress" = "目標地址"
|
||||
"destinationPort" = "目標連接埠"
|
||||
"targetAddress" = "目標位址"
|
||||
"monitorDesc" = "留空表示監聽所有 IP"
|
||||
"meansNoLimit" = "= 無限制(單位:GB)"
|
||||
"totalFlow" = "總流量"
|
||||
@@ -192,41 +197,46 @@
|
||||
"certificateContent" = "檔案內容"
|
||||
"publicKey" = "公鑰"
|
||||
"privatekey" = "私鑰"
|
||||
"clickOnQRcode" = "點選二維碼複製"
|
||||
"client" = "客戶"
|
||||
"clickOnQRcode" = "點擊二維碼複製"
|
||||
"client" = "客戶/用戶"
|
||||
"export" = "匯出連結"
|
||||
"clone" = "複製"
|
||||
"cloneInbound" = "複製"
|
||||
"cloneInboundContent" = "此入站規則除埠(Port)、監聽 IP(Listening IP)和客戶端(Clients)以外的所有配置都將應用於克隆"
|
||||
"cloneInboundOk" = "建立克隆"
|
||||
"resetAllTraffic" = "重置所有入站流量"
|
||||
"resetAllTrafficTitle" = "重置所有入站流量"
|
||||
"resetAllTrafficContent" = "確定要重置所有入站流量嗎?"
|
||||
"resetInboundClientTraffics" = "重置客戶端流量"
|
||||
"resetInboundClientTrafficTitle" = "重置所有客戶端流量"
|
||||
"resetInboundClientTrafficContent" = "確定要重置此入站客戶端的所有流量嗎?"
|
||||
"resetAllClientTraffics" = "重置所有客戶端流量"
|
||||
"resetAllClientTrafficTitle" = "重置所有客戶端流量"
|
||||
"resetAllClientTrafficContent" = "確定要重置所有客戶端的所有流量嗎?"
|
||||
"clone" = "克隆"
|
||||
"cloneInbound" = "克隆"
|
||||
"cloneInboundContent" = "此入站規則除連接埠(Port)、監聽 IP(Listening IP)和客戶端(Clients)以外的所有設定都將應用於克隆"
|
||||
"cloneInboundOk" = "創建克隆"
|
||||
"resetAllTraffic" = "重設所有入站流量"
|
||||
"resetAllTrafficTitle" = "重設所有入站流量"
|
||||
"resetAllTrafficContent" = "確定要重設所有入站流量嗎?"
|
||||
"resetInboundClientTraffics" = "重設客戶端流量"
|
||||
"resetInboundClientTrafficTitle" = "重設所有客戶端流量"
|
||||
"resetInboundClientTrafficContent" = "確定要重設此入站客戶端的所有流量嗎?"
|
||||
"resetAllClientTraffics" = "重設所有客戶端流量"
|
||||
"resetAllClientTrafficTitle" = "重設所有客戶端流量"
|
||||
"resetAllClientTrafficContent" = "確定要重設所有客戶端的所有流量嗎?"
|
||||
"delDepletedClients" = "刪除流量耗盡的客戶端"
|
||||
"delDepletedClientsTitle" = "刪除流量耗盡的客戶端"
|
||||
"delDepletedClientsContent" = "確定要刪除所有流量耗盡的客戶端嗎?"
|
||||
"email" = "電子郵件"
|
||||
"emailDesc" = "電子郵件必須完全唯一"
|
||||
"emailDesc" = "電子郵件必須確保唯一"
|
||||
"IPLimit" = "IP 限制"
|
||||
"IPLimitDesc" = "如果數量超過設定值,則禁用入站流量。(0 = 禁用)"
|
||||
"IPLimitDesc" = "如果數量超過設定值,則停用入站流量。(0 = 停用)"
|
||||
"IPLimitlog" = "IP 日誌"
|
||||
"IPLimitlogDesc" = "IP 歷史日誌(要啟用被禁用的入站流量,請清除日誌)"
|
||||
"IPLimitlogDesc" = "IP 歷史日誌(要啟用被停用的入站流量,請清除日誌)"
|
||||
"IPLimitlogclear" = "清除日誌"
|
||||
"setDefaultCert" = "從面板設定證書"
|
||||
"telegramDesc" = "請提供Telegram聊天ID。(在機器人中使用'/id'命令)或(@userinfobot"
|
||||
"subscriptionDesc" = "要找到你的訂閱 URL,請導航到“詳細資訊”。此外,你可以為多個客戶端使用相同的名稱。"
|
||||
"setDefaultCert" = "從面板設定憑證"
|
||||
"telegramDesc" = "請提供 Telegram 聊天 ID。(在機器人中使用 '/id' 指令或跟 @userinfobot 機器人對話獲取)"
|
||||
"subscriptionDesc" = "要找到您的訂閱 URL,請導覽至「詳細資訊」。此外,您可以為多個客戶端使用相同的名稱。"
|
||||
"info" = "資訊"
|
||||
"same" = "相同"
|
||||
"inboundData" = "入站資料"
|
||||
"exportInbound" = "匯出入站規則"
|
||||
"import"="匯入"
|
||||
"import" = "匯入"
|
||||
"importInbound" = "匯入入站規則"
|
||||
"reviewTitle" = "請確認以下設定資訊"
|
||||
"neverExpire" = "永不過期"
|
||||
"reviewHint" = "如需修改請返回上一步"
|
||||
"unlimited" = "無限制"
|
||||
"deviceLimit" = "裝置限制"
|
||||
|
||||
[pages.client]
|
||||
"add" = "新增客戶端"
|
||||
@@ -234,40 +244,40 @@
|
||||
"submitAdd" = "新增客戶端"
|
||||
"submitEdit" = "儲存修改"
|
||||
"clientCount" = "客戶端數量"
|
||||
"bulk" = "批量建立"
|
||||
"bulk" = "批次創建"
|
||||
"method" = "方法"
|
||||
"first" = "置頂"
|
||||
"last" = "置底"
|
||||
"prefix" = "字首"
|
||||
"postfix" = "字尾"
|
||||
"prefix" = "前綴"
|
||||
"postfix" = "後綴"
|
||||
"delayedStart" = "首次使用後開始"
|
||||
"expireDays" = "期間"
|
||||
"expireDays" = "到期天數/期間"
|
||||
"days" = "天"
|
||||
"renew" = "自動續訂"
|
||||
"renewDesc" = "到期後自動續訂。(0 = 禁用)(單位: 天)"
|
||||
"renew" = "自動續約"
|
||||
"renewDesc" = "到期後自動續約。(0 = 停用)(單位: 天)"
|
||||
|
||||
[pages.inbounds.toasts]
|
||||
"obtain" = "獲取"
|
||||
"updateSuccess" = "更新成功"
|
||||
"logCleanSuccess" = "日誌已清除"
|
||||
"inboundsUpdateSuccess" = "入站連接已成功更新"
|
||||
"inboundUpdateSuccess" = "入站連接已成功更新"
|
||||
"inboundCreateSuccess" = "入站連接已成功建立"
|
||||
"inboundDeleteSuccess" = "入站連接已成功刪除"
|
||||
"inboundsUpdateSuccess" = "入站連線已成功更新"
|
||||
"inboundUpdateSuccess" = "入站連線已成功更新"
|
||||
"inboundCreateSuccess" = "入站連線已成功創建"
|
||||
"inboundDeleteSuccess" = "入站連線已成功刪除"
|
||||
"inboundClientAddSuccess" = "已新增入站客戶端"
|
||||
"inboundClientDeleteSuccess" = "入站客戶端已刪除"
|
||||
"inboundClientUpdateSuccess" = "入站客戶端已更新"
|
||||
"delDepletedClientsSuccess" = "所有耗盡客戶端已刪除"
|
||||
"resetAllClientTrafficSuccess" = "客戶端所有流量已重置"
|
||||
"resetAllTrafficSuccess" = "所有流量已重置"
|
||||
"resetInboundClientTrafficSuccess" = "流量已重置"
|
||||
"trafficGetError" = "取得流量資料時發生錯誤"
|
||||
"getNewX25519CertError" = "取得X25519憑證時發生錯誤。"
|
||||
"getNewmldsa65Error" = "取得mldsa65憑證時發生錯誤。"
|
||||
"resetAllClientTrafficSuccess" = "客戶端所有流量已重設"
|
||||
"resetAllTrafficSuccess" = "所有流量已重設"
|
||||
"resetInboundClientTrafficSuccess" = "流量已重設"
|
||||
"trafficGetError" = "獲取流量資料時發生錯誤"
|
||||
"getNewX25519CertError" = "獲取 X25519 憑證時發生錯誤。"
|
||||
"getNewmldsa65Error" = "獲取 mldsa65 憑證時發生錯誤。"
|
||||
|
||||
[pages.inbounds.stream.general]
|
||||
"request" = "請求"
|
||||
"response" = "響應"
|
||||
"response" = "回應"
|
||||
"name" = "名稱"
|
||||
"value" = "值"
|
||||
|
||||
@@ -277,60 +287,62 @@
|
||||
"path" = "路徑"
|
||||
"status" = "狀態"
|
||||
"statusDescription" = "狀態說明"
|
||||
"requestHeader" = "請求頭"
|
||||
"responseHeader" = "響應頭"
|
||||
"requestHeader" = "請求標頭"
|
||||
"responseHeader" = "回應標頭"
|
||||
|
||||
[pages.settings]
|
||||
"title" = "面板設定"
|
||||
"save" = "儲存"
|
||||
"infoDesc" = "此處的所有更改都需要儲存並重啟面板才能生效"
|
||||
"infoDesc" = "此處的所有變更都需要儲存並重啟面板才能生效"
|
||||
"restartPanel" = "重啟面板"
|
||||
"restartPanelDesc" = "確定要重啟面板嗎?若重啟後無法訪問面板,請前往伺服器檢視面板日誌資訊"
|
||||
"restartPanelSuccess" = "面板已成功重新啟動"
|
||||
"restartPanelDesc" = "確定要重啟面板嗎?若重啟後無法存取面板,請前往伺服器查看面板日誌資訊"
|
||||
"restartPanelSuccess" = "面板已成功重啟"
|
||||
"actions" = "操作"
|
||||
"resetDefaultConfig" = "重置為預設配置"
|
||||
"panelSettings" = "常規"
|
||||
"resetDefaultConfig" = "重設為預設設定"
|
||||
"panelSettings" = "一般"
|
||||
"securitySettings" = "安全設定"
|
||||
"TGBotSettings" = "Telegram 機器人配置"
|
||||
"TGBotSettings" = "Telegram 機器人設定"
|
||||
"panelListeningIP" = "面板監聽 IP"
|
||||
"panelListeningIPDesc" = "預設留空監聽所有 IP"
|
||||
"panelListeningDomain" = "面板監聽域名"
|
||||
"panelListeningDomainDesc" = "預設情況下留空以監視所有域名和 IP 地址"
|
||||
"panelPort" = "面板監聽埠"
|
||||
"panelListeningDomain" = "面板監聽網域"
|
||||
"panelListeningDomainDesc" = "預設情況下留空以監視所有網域和 IP 位址"
|
||||
"panelPort" = "面板監聽連接埠"
|
||||
"panelPortDesc" = "重啟面板生效"
|
||||
"publicKeyPath" = "面板證書公鑰檔案路徑"
|
||||
"publicKeyPathDesc" = "填寫一個 '/' 開頭的絕對路徑"
|
||||
"privateKeyPath" = "面板證書金鑰檔案路徑"
|
||||
"privateKeyPathDesc" = "填寫一個 '/' 開頭的絕對路徑"
|
||||
"panelUrlPath" = "面板 url 根路徑"
|
||||
"publicKeyPath" = "面板憑證公鑰檔案路徑"
|
||||
"DefaultpublicKeyPath" = "/root/.acme.sh/網域_ecc/網域.cer"
|
||||
"publicKeyPathDesc" = "填寫一個 '/' 開頭的絕對路徑,〔acme 方式〕請自行在填入時修改網域"
|
||||
"privateKeyPath" = "面板憑證金鑰檔案路徑"
|
||||
"DefaultprivateKeyPath" = "/root/.acme.sh/網域_ecc/網域.key"
|
||||
"privateKeyPathDesc" = "填寫一個 '/' 開頭的絕對路徑,〔acme 方式〕請自行在填入時修改網域"
|
||||
"panelUrlPath" = "面板登入存取路徑"
|
||||
"panelUrlPathDesc" = "必須以 '/' 開頭,以 '/' 結尾"
|
||||
"pageSize" = "分頁大小"
|
||||
"pageSizeDesc" = "定義入站表的頁面大小。設定 0 表示禁用"
|
||||
"remarkModel" = "備註模型和分隔符"
|
||||
"pageSizeDesc" = "定義入站列表的頁面大小。設定 0 表示停用"
|
||||
"remarkModel" = "備註模型與分隔符"
|
||||
"datepicker" = "日期選擇器"
|
||||
"datepickerPlaceholder" = "選擇日期"
|
||||
"datepickerDescription" = "選擇器日曆類型指定到期日期"
|
||||
"sampleRemark" = "備註示例"
|
||||
"oldUsername" = "原使用者名稱"
|
||||
"sampleRemark" = "備註範例"
|
||||
"oldUsername" = "原用戶名"
|
||||
"currentPassword" = "原密碼"
|
||||
"newUsername" = "新使用者名稱"
|
||||
"newUsername" = "新用戶名"
|
||||
"newPassword" = "新密碼"
|
||||
"telegramBotEnable" = "啟用 Telegram 機器人"
|
||||
"telegramBotEnableDesc" = "啟用 Telegram 機器人功能"
|
||||
"telegramToken" = "Telegram 機器人令牌(token)"
|
||||
"telegramTokenDesc" = "從 '@BotFather' 獲取的 Telegram 機器人令牌"
|
||||
"telegramToken" = "Telegram 機器人權杖(token)"
|
||||
"telegramTokenDesc" = "跟 '@BotFather' 對話獲取的 Telegram 機器人權杖"
|
||||
"telegramProxy" = "SOCKS5 Proxy"
|
||||
"telegramProxyDesc" = "啟用 SOCKS5 代理連線到 Telegram(根據指南調整設定)"
|
||||
"telegramAPIServer" = "Telegram API Server"
|
||||
"telegramAPIServerDesc" = "要使用的 Telegram API 伺服器。留空以使用預設伺服器。"
|
||||
"telegramChatId" = "管理員聊天 ID"
|
||||
"telegramChatIdDesc" = "Telegram 管理員聊天 ID (多個以逗號分隔)(可通過 @userinfobot 獲取,或在機器人中使用 '/id' 命令獲取)"
|
||||
"telegramChatIdDesc" = "Telegram 管理員聊天 ID (多個以逗號分隔)(可透過 @userinfobot 獲取,或在機器人中使用 '/id' 指令獲取)"
|
||||
"telegramNotifyTime" = "通知時間"
|
||||
"telegramNotifyTimeDesc" = "設定週期性的 Telegram 機器人通知時間(使用 crontab 時間格式)"
|
||||
"tgNotifyBackup" = "資料庫備份"
|
||||
"tgNotifyBackupDesc" = "傳送帶有報告的資料庫備份檔案"
|
||||
"tgNotifyLogin" = "登入通知"
|
||||
"tgNotifyLoginDesc" = "當有人試圖登入你的面板時顯示使用者名稱、IP 地址和時間"
|
||||
"tgNotifyLoginDesc" = "當有人試圖登入您的面板時顯示用戶名、IP 位址和時間"
|
||||
"sessionMaxAge" = "會話時長"
|
||||
"sessionMaxAgeDesc" = "保持登入狀態的時長(單位:分鐘)"
|
||||
"expireTimeDiff" = "到期通知閾值"
|
||||
@@ -345,19 +357,19 @@
|
||||
"subEnable" = "啟用訂閱服務"
|
||||
"subEnableDesc" = "啟用訂閱服務功能"
|
||||
"subTitle" = "訂閱標題"
|
||||
"subTitleDesc" = "在VPN客戶端中顯示的標題"
|
||||
"subTitleDesc" = "在 VPN 客戶端中顯示的標題"
|
||||
"subListen" = "監聽 IP"
|
||||
"subListenDesc" = "訂閱服務監聽的 IP 地址(留空表示監聽所有 IP)"
|
||||
"subPort" = "監聽埠"
|
||||
"subPortDesc" = "訂閱服務監聽的埠號(必須是未使用的埠)"
|
||||
"subListenDesc" = "訂閱服務監聽的 IP 位址(留空表示監聽所有 IP)"
|
||||
"subPort" = "監聽連接埠"
|
||||
"subPortDesc" = "訂閱服務監聽的連接埠號(必須是未使用的連接埠)"
|
||||
"subCertPath" = "公鑰路徑"
|
||||
"subCertPathDesc" = "訂閱服務使用的公鑰檔案路徑(以 '/' 開頭)"
|
||||
"subKeyPath" = "私鑰路徑"
|
||||
"subKeyPathDesc" = "訂閱服務使用的私鑰檔案路徑(以 '/' 開頭)"
|
||||
"subPath" = "URI 路徑"
|
||||
"subPathDesc" = "訂閱服務使用的 URI 路徑(以 '/' 開頭,以 '/' 結尾)"
|
||||
"subDomain" = "監聽域名"
|
||||
"subDomainDesc" = "訂閱服務監聽的域名(留空表示監聽所有域名和 IP)"
|
||||
"subDomain" = "監聽網域"
|
||||
"subDomainDesc" = "訂閱服務監聽的網域(留空表示監聽所有網域和 IP)"
|
||||
"subUpdates" = "更新間隔"
|
||||
"subUpdatesDesc" = "客戶端應用中訂閱 URL 的更新間隔(單位:小時)"
|
||||
"subEncrypt" = "編碼"
|
||||
@@ -366,12 +378,12 @@
|
||||
"subShowInfoDesc" = "客戶端應用中將顯示剩餘流量和日期資訊"
|
||||
"subURI" = "反向代理 URI"
|
||||
"subURIDesc" = "用於代理後面的訂閱 URL 的 URI 路徑"
|
||||
"externalTrafficInformEnable" = "外部交通通知"
|
||||
"externalTrafficInformEnable" = "外部流量通知"
|
||||
"externalTrafficInformEnableDesc" = "每次流量更新時通知外部 API"
|
||||
"externalTrafficInformURI" = "外部流量通知 URI"
|
||||
"externalTrafficInformURIDesc" = "流量更新將會傳送到此 URI"
|
||||
"externalTrafficInformURIDesc" = "流量更新將傳送到此 URI"
|
||||
"fragment" = "分片"
|
||||
"fragmentDesc" = "啟用 TLS hello 資料包分片"
|
||||
"fragmentDesc" = "啟用 TLS hello 封包分片"
|
||||
"fragmentSett" = "設定"
|
||||
"noisesDesc" = "啟用 Noises."
|
||||
"noisesSett" = "Noises 設定"
|
||||
@@ -379,68 +391,68 @@
|
||||
"muxDesc" = "在已建立的資料流內傳輸多個獨立的資料流"
|
||||
"muxSett" = "複用器設定"
|
||||
"direct" = "直接連線"
|
||||
"directDesc" = "直接與特定國家的域或IP範圍建立連線"
|
||||
"directDesc" = "直接與特定國家的網域或 IP 範圍建立連線"
|
||||
"notifications" = "通知"
|
||||
"certs" = "證書"
|
||||
"certs" = "憑證"
|
||||
"externalTraffic" = "外部流量"
|
||||
"dateAndTime" = "日期和時間"
|
||||
"proxyAndServer" = "代理和伺服器"
|
||||
"dateAndTime" = "日期與時間"
|
||||
"proxyAndServer" = "代理與伺服器"
|
||||
"intervals" = "間隔"
|
||||
"information" = "資訊"
|
||||
"language" = "語言"
|
||||
"telegramBotLanguage" = "Telegram 機器人語言"
|
||||
|
||||
[pages.xray]
|
||||
"title" = "Xray 配置"
|
||||
"title" = "Xray 設定"
|
||||
"save" = "儲存"
|
||||
"restart" = "重新啟動 Xray"
|
||||
"restartSuccess" = "Xray 已成功重新啟動"
|
||||
"stopSuccess" = "Xray 已成功停止"
|
||||
"restartError" = "重新啟動Xray時發生錯誤。"
|
||||
"stopError" = "停止Xray時發生錯誤。"
|
||||
"basicTemplate" = "基礎配置"
|
||||
"advancedTemplate" = "高階配置"
|
||||
"generalConfigs" = "常規配置"
|
||||
"generalConfigsDesc" = "這些選項將決定常規配置"
|
||||
"restartError" = "重啟 Xray 時發生錯誤。"
|
||||
"stopError" = "停止 Xray 時發生錯誤。"
|
||||
"basicTemplate" = "基礎設定"
|
||||
"advancedTemplate" = "進階設定"
|
||||
"generalConfigs" = "一般設定"
|
||||
"generalConfigsDesc" = "這些選項將決定一般設定"
|
||||
"logConfigs" = "日誌"
|
||||
"logConfigsDesc" = "日誌可能會影響伺服器的效能,建議僅在需要時啟用"
|
||||
"blockConfigsDesc" = "這些選項將阻止使用者連線到特定協議和網站"
|
||||
"basicRouting" = "基本路由"
|
||||
"blockConnectionsConfigsDesc" = "這些選項將根據特定的請求國家阻止流量。"
|
||||
"directConnectionsConfigsDesc" = "直接連線確保特定的流量不會通過其他伺服器路由。"
|
||||
"blockips" = "阻止IP"
|
||||
"blockdomains" = "阻止域名"
|
||||
"directips" = "直接IP"
|
||||
"directdomains" = "直接域名"
|
||||
"directConnectionsConfigsDesc" = "直接連線確保特定的流量不會透過其他伺服器路由。"
|
||||
"blockips" = "阻止 IP"
|
||||
"blockdomains" = "阻止網域"
|
||||
"directips" = "直接 IP"
|
||||
"directdomains" = "直接網域"
|
||||
"ipv4Routing" = "IPv4 路由"
|
||||
"ipv4RoutingDesc" = "此選項將僅通過 IPv4 路由到目標域"
|
||||
"ipv4RoutingDesc" = "此選項將僅透過 IPv4 路由到目標網域"
|
||||
"warpRouting" = "WARP 路由"
|
||||
"warpRoutingDesc" = "注意:在使用這些選項之前,請按照面板 GitHub 上的步驟在你的伺服器上以 socks5 代理模式安裝 WARP。WARP 將通過 Cloudflare 伺服器將流量路由到網站。"
|
||||
"Template" = "高階 Xray 配置模板"
|
||||
"TemplateDesc" = "最終的 Xray 配置檔案將基於此模板生成"
|
||||
"warpRoutingDesc" = "注意:在使用這些選項之前,請按照面板 GitHub 上的步驟在您的伺服器上以 socks5 代理模式安裝 WARP。WARP 將透過 Cloudflare 伺服器將流量路由到網站。"
|
||||
"Template" = "進階 Xray 設定模板"
|
||||
"TemplateDesc" = "最終的 Xray 設定檔將基於此模板產生"
|
||||
"FreedomStrategy" = "Freedom 協議策略"
|
||||
"FreedomStrategyDesc" = "設定 Freedom 協議中網路的輸出策略"
|
||||
"RoutingStrategy" = "配置路由域策略"
|
||||
"RoutingStrategy" = "設定路由網域策略"
|
||||
"RoutingStrategyDesc" = "設定 DNS 解析的整體路由策略"
|
||||
"Torrent" = "遮蔽 BitTorrent 協議"
|
||||
"Torrent" = "封鎖 BitTorrent 協議"
|
||||
"Inbounds" = "入站規則"
|
||||
"InboundsDesc" = "接受來自特定客戶端的流量"
|
||||
"Outbounds" = "出站規則"
|
||||
"Balancers" = "負載均衡"
|
||||
"Balancers" = "負載平衡"
|
||||
"OutboundsDesc" = "設定出站流量傳出方式"
|
||||
"Routings" = "路由規則"
|
||||
"RoutingsDesc" = "每條規則的優先順序都很重要"
|
||||
"RoutingsDesc" = "每條規則的優先級都很重要"
|
||||
"completeTemplate" = "全部"
|
||||
"logLevel" = "日誌級別"
|
||||
"logLevelDesc" = "錯誤日誌的日誌級別,用於指示需要記錄的資訊"
|
||||
"accessLog" = "訪問日誌"
|
||||
"accessLogDesc" = "訪問日誌的檔案路徑。特殊值 'none' 禁用訪問日誌"
|
||||
"accessLog" = "存取日誌"
|
||||
"accessLogDesc" = "存取日誌的檔案路徑。特殊值 'none' 停用存取日誌"
|
||||
"errorLog" = "錯誤日誌"
|
||||
"errorLogDesc" = "錯誤日誌的檔案路徑。特殊值 'none' 禁用錯誤日誌"
|
||||
"errorLogDesc" = "錯誤日誌的檔案路徑。特殊值 'none' 停用錯誤日誌"
|
||||
"dnsLog" = "DNS 日誌"
|
||||
"dnsLogDesc" = "是否啟用 DNS 查詢日誌"
|
||||
"maskAddress" = "隱藏地址"
|
||||
"maskAddressDesc" = "IP 地址掩碼,啟用時會自動替換日誌中出現的 IP 地址。"
|
||||
"maskAddress" = "隱藏位址"
|
||||
"maskAddressDesc" = "IP 位址遮罩,啟用時會自動替換日誌中出現的 IP 位址。"
|
||||
"statistics" = "統計"
|
||||
"statsInboundUplink" = "入站上傳統計"
|
||||
"statsInboundUplinkDesc" = "啟用所有入站代理的上行流量統計收集。"
|
||||
@@ -451,20 +463,35 @@
|
||||
"statsOutboundDownlink" = "出站下載統計"
|
||||
"statsOutboundDownlinkDesc" = "啟用所有出站代理的下行流量統計收集。"
|
||||
|
||||
[pages.navigation]
|
||||
"title" = "實用導覽"
|
||||
|
||||
[pages.xray.rules]
|
||||
"first" = "置頂"
|
||||
"last" = "置底"
|
||||
"up" = "向上"
|
||||
"down" = "向下"
|
||||
"source" = "來源"
|
||||
"dest" = "目的地址"
|
||||
"dest" = "目標位址"
|
||||
"inbound" = "入站"
|
||||
"outbound" = "出站"
|
||||
"balancer" = "負載均衡"
|
||||
"balancer" = "負載平衡"
|
||||
"info" = "資訊"
|
||||
"add" = "新增規則"
|
||||
"edit" = "編輯規則"
|
||||
"useComma" = "逗號分隔的項目"
|
||||
"DomainMatcher" = "網域匹配類型"
|
||||
"SourceIPs" = "來源 IP"
|
||||
"SourcePort" = "來源連接埠"
|
||||
"Network" = "網路類型"
|
||||
"Protocol" = "傳輸協議"
|
||||
"Attributes" = "屬性"
|
||||
"Domain" = "網域"
|
||||
"User" = "使用者"
|
||||
"Port" = "連接埠"
|
||||
"InboundTag" = "入站 Tag"
|
||||
"OutboundTag" = "出站 Tag"
|
||||
"BalancerTag" = "負載平衡 Tag"
|
||||
|
||||
[pages.xray.outbound]
|
||||
"addOutbound" = "新增出站"
|
||||
@@ -473,9 +500,9 @@
|
||||
"editReverse" = "編輯反向"
|
||||
"tag" = "標籤"
|
||||
"tagDesc" = "唯一標籤"
|
||||
"address" = "地址"
|
||||
"address" = "位址"
|
||||
"reverse" = "反向"
|
||||
"domain" = "域名"
|
||||
"domain" = "網域"
|
||||
"type" = "類型"
|
||||
"bridge" = "Bridge"
|
||||
"portal" = "Portal"
|
||||
@@ -487,8 +514,8 @@
|
||||
"sendThrough" = "傳送通過"
|
||||
|
||||
[pages.xray.balancer]
|
||||
"addBalancer" = "新增負載均衡"
|
||||
"editBalancer" = "編輯負載均衡"
|
||||
"addBalancer" = "新增負載平衡"
|
||||
"editBalancer" = "編輯負載平衡"
|
||||
"balancerStrategy" = "策略"
|
||||
"balancerSelectors" = "選擇器"
|
||||
"tag" = "標籤"
|
||||
@@ -501,103 +528,102 @@
|
||||
"allowedIPs" = "允許的 IP"
|
||||
"endpoint" = "端點"
|
||||
"psk" = "共享金鑰"
|
||||
"domainStrategy" = "域策略"
|
||||
"domainStrategy" = "網域策略"
|
||||
|
||||
[pages.xray.dns]
|
||||
"enable" = "啟用 DNS"
|
||||
"enableDesc" = "啟用內建 DNS 伺服器"
|
||||
"tag" = "DNS 入站標籤"
|
||||
"tagDesc" = "此標籤將在路由規則中可用作入站標籤"
|
||||
"clientIp" = "客戶端IP"
|
||||
"clientIpDesc" = "用於在DNS查詢期間通知伺服器指定的IP位置"
|
||||
"disableCache" = "禁用快取"
|
||||
"disableCacheDesc" = "禁用DNS快取"
|
||||
"disableFallback" = "禁用回退"
|
||||
"disableFallbackDesc" = "禁用回退DNS查詢"
|
||||
"disableFallbackIfMatch" = "匹配時禁用回退"
|
||||
"disableFallbackIfMatchDesc" = "當DNS伺服器的匹配域名列表命中時,禁用回退DNS查詢"
|
||||
"clientIp" = "客戶端 IP"
|
||||
"clientIpDesc" = "用於在 DNS 查詢期間通知伺服器指定的 IP 位置"
|
||||
"disableCache" = "停用快取"
|
||||
"disableCacheDesc" = "停用 DNS 快取"
|
||||
"disableFallback" = "停用回退"
|
||||
"disableFallbackDesc" = "停用回退 DNS 查詢"
|
||||
"disableFallbackIfMatch" = "匹配時停用回退"
|
||||
"disableFallbackIfMatchDesc" = "當 DNS 伺服器的匹配網域列表命中時,停用回退 DNS 查詢"
|
||||
"strategy" = "查詢策略"
|
||||
"strategyDesc" = "解析域名的總體策略"
|
||||
"strategyDesc" = "解析網域的總體策略"
|
||||
"add" = "新增伺服器"
|
||||
"edit" = "編輯伺服器"
|
||||
"domains" = "域"
|
||||
"domains" = "網域"
|
||||
"expectIPs" = "預期 IP"
|
||||
"unexpectIPs" = "意外IP"
|
||||
"useSystemHosts" = "使用系統Hosts"
|
||||
"useSystemHostsDesc" = "使用已安裝系統的hosts檔案"
|
||||
"usePreset" = "使用範本"
|
||||
"dnsPresetTitle" = "DNS範本"
|
||||
"unexpectIPs" = "意外 IP"
|
||||
"useSystemHosts" = "使用系統 Hosts"
|
||||
"useSystemHostsDesc" = "使用已安裝系統的 hosts 檔案"
|
||||
"usePreset" = "使用模板"
|
||||
"dnsPresetTitle" = "DNS 模板"
|
||||
"dnsPresetFamily" = "家庭"
|
||||
|
||||
[pages.xray.fakedns]
|
||||
"add" = "新增假 DNS"
|
||||
"edit" = "編輯假 DNS"
|
||||
"ipPool" = "IP 池子網"
|
||||
"ipPool" = "IP 池子網路"
|
||||
"poolSize" = "池大小"
|
||||
|
||||
[pages.settings.security]
|
||||
"admin" = "管理員憑證"
|
||||
"twoFactor" = "雙重驗證"
|
||||
"twoFactorEnable" = "啟用2FA"
|
||||
"twoFactorEnableDesc" = "增加額外的驗證層以提高安全性。"
|
||||
"twoFactor" = "雙重驗證"
|
||||
"twoFactorEnable" = "啟用 2FA"
|
||||
"twoFactorEnableDesc" = "增加額外的驗證層以提高安全性。"
|
||||
"twoFactorModalSetTitle" = "啟用雙重認證"
|
||||
"twoFactorModalDeleteTitle" = "停用雙重認證"
|
||||
"twoFactorModalSteps" = "要設定雙重認證,請執行以下步驟:"
|
||||
"twoFactorModalFirstStep" = "1. 在認證應用程式中掃描此QR碼,或複製QR碼附近的令牌並貼到應用程式中"
|
||||
"twoFactorModalFirstStep" = "1. 在認證應用程式中掃描此 QR 碼,或複製 QR 碼附近的權杖並貼到應用程式中"
|
||||
"twoFactorModalSecondStep" = "2. 輸入應用程式中的驗證碼"
|
||||
"twoFactorModalRemoveStep" = "輸入應用程式中的驗證碼以移除雙重認證。"
|
||||
"twoFactorModalChangeCredentialsTitle" = "更改憑證"
|
||||
"twoFactorModalChangeCredentialsStep" = "輸入應用程式中的代碼以更改管理員憑證。"
|
||||
"twoFactorModalSetSuccess" = "雙重身份驗證已成功建立"
|
||||
"twoFactorModalDeleteSuccess" = "雙重身份驗證已成功刪除"
|
||||
"twoFactorModalChangeCredentialsTitle" = "變更憑證"
|
||||
"twoFactorModalChangeCredentialsStep" = "輸入應用程式中的代碼以變更管理員憑證。"
|
||||
"twoFactorModalSetSuccess" = "雙因素認證已成功建立"
|
||||
"twoFactorModalDeleteSuccess" = "雙因素認證已成功刪除"
|
||||
"twoFactorModalError" = "驗證碼錯誤"
|
||||
|
||||
[pages.settings.toasts]
|
||||
"modifySettings" = "參數已更改。"
|
||||
"getSettings" = "取得參數時發生錯誤"
|
||||
"modifySettings" = "參數已變更。"
|
||||
"getSettings" = "獲取參數時發生錯誤"
|
||||
"modifyUserError" = "變更管理員憑證時發生錯誤。"
|
||||
"modifyUser" = "您已成功變更管理員憑證。"
|
||||
"originalUserPassIncorrect" = "原使用者名稱或原密碼錯誤"
|
||||
"userPassMustBeNotEmpty" = "新使用者名稱和新密碼不能為空"
|
||||
"getOutboundTrafficError" = "取得出站流量錯誤"
|
||||
"originalUserPassIncorrect" = "原用戶名或原密碼錯誤"
|
||||
"userPassMustBeNotEmpty" = "新用戶名和新密碼不能為空"
|
||||
"getOutboundTrafficError" = "獲取出站流量錯誤"
|
||||
"resetOutboundTrafficError" = "重設出站流量錯誤"
|
||||
|
||||
[tgbot]
|
||||
"keyboardClosed" = "❌ 自定義鍵盤已關閉!"
|
||||
"keyboardClosed" = "❌ 自訂鍵盤已關閉!"
|
||||
"noResult" = "❗ 沒有結果!"
|
||||
"noQuery" = "❌ 未找到查詢!請再次使用該命令!"
|
||||
"wentWrong" = "❌ 出了點問題!"
|
||||
"noIpRecord" = "❗ 沒有IP記錄!"
|
||||
"noInbounds" = "❗ 未找到入站!"
|
||||
"unlimited" = "♾ 無限(重置)"
|
||||
"add" = "添加"
|
||||
"noQuery" = "❌ 找不到查詢!請重新使用指令!"
|
||||
"wentWrong" = "❌ 出了一點問題!"
|
||||
"noIpRecord" = "❗ 沒有 IP 記錄!"
|
||||
"noInbounds" = "❗ 找不到入站連線!"
|
||||
"unlimited" = "♾ 無限制"
|
||||
"add" = "新增"
|
||||
"month" = "月"
|
||||
"months" = "月"
|
||||
"day" = "天"
|
||||
"days" = "天"
|
||||
"hours" = "小時"
|
||||
"minutes" = "分鐘"
|
||||
"unknown" = "未知"
|
||||
"inbounds" = "入站"
|
||||
"inbounds" = "入站連線"
|
||||
"clients" = "客戶端"
|
||||
"offline" = "🔴 離線"
|
||||
"online" = "🟢 在線"
|
||||
|
||||
[tgbot.commands]
|
||||
"unknown" = "❗ 未知命令"
|
||||
"pleaseChoose" = "👇 請選擇:\r\n"
|
||||
"unknown" = "❗ 未知指令"
|
||||
"pleaseChoose" = "👇請〔按照需求〕選擇下方按鈕 :\r\n"
|
||||
"help" = "🤖 歡迎使用本機器人!它旨在為您提供來自伺服器的特定資料,並允許您進行必要的修改。\r\n\r\n"
|
||||
"start" = "👋 你好,<i>{{ .Firstname }}</i>。\r\n"
|
||||
"welcome" = "🤖 歡迎來到 <b>{{ .Hostname }}</b> 管理機器人。\r\n"
|
||||
"status" = "✅ 機器人正常執行!"
|
||||
"status" = "✅ 機器人正常運作!"
|
||||
"usage" = "❗ 請輸入要搜尋的文字!"
|
||||
"getID" = "🆔 您的 ID 為:<code>{{ .ID }}</code>"
|
||||
"helpAdminCommands" = "要重新啟動 Xray Core:\r\n<code>/restart</code>\r\n\r\n要搜尋客戶電子郵件:\r\n<code>/usage [電子郵件]</code>\r\n\r\n要搜尋入站(帶有客戶統計資料):\r\n<code>/inbound [備註]</code>\r\n\r\nTelegram聊天ID:\r\n<code>/id</code>"
|
||||
"helpClientCommands" = "要搜尋統計資料,請使用以下命令:\r\n<code>/usage [電子郵件]</code>\r\n\r\nTelegram聊天ID:\r\n<code>/id</code>"
|
||||
"helpAdminCommands" = "要重新啟動 Xray Core:\r\n<code>/restart</code>\r\n\r\n要搜尋客戶電子郵件:\r\n<code>/usage [電子郵件]</code>\r\n\r\n要搜尋入站(帶有客戶統計資料):\r\n<code>/inbound [備註]</code>\r\n\r\nTelegram 聊天 ID:\r\n<code>/id</code>"
|
||||
"helpClientCommands" = "要搜尋統計資料,請使用以下指令:\r\n<code>/usage [電子郵件]</code>\r\n\r\nTelegram 聊天 ID:\r\n<code>/id</code>"
|
||||
"restartUsage" = "\r\n\r\n<code>/restart</code>"
|
||||
"restartSuccess" = "✅ 操作成功!"
|
||||
"restartSuccess" = "✅ 操作成功!"
|
||||
"restartFailed" = "❗ 操作錯誤。\r\n\r\n<code>錯誤: {{ .Error }}</code>."
|
||||
"xrayNotRunning" = "❗ Xray Core 未運行。"
|
||||
"xrayNotRunning" = "❗ Xray Core 未運作。"
|
||||
"startDesc" = "顯示主選單"
|
||||
"helpDesc" = "機器人幫助"
|
||||
"statusDesc" = "檢查機器人狀態"
|
||||
@@ -606,44 +632,44 @@
|
||||
[tgbot.messages]
|
||||
"cpuThreshold" = "🔴 CPU 使用率為 {{ .Percent }}%,超過閾值 {{ .Threshold }}%"
|
||||
"selectUserFailed" = "❌ 使用者選擇錯誤!"
|
||||
"userSaved" = "✅ 電報使用者已儲存。"
|
||||
"userSaved" = "✅ Telegram 用戶已儲存。"
|
||||
"loginSuccess" = "✅ 成功登入到面板。\r\n"
|
||||
"loginFailed" = "❗️ 面板登入失敗。\r\n"
|
||||
"report" = "🕰 定時報告:{{ .RunTime }}\r\n"
|
||||
"datetime" = "⏰ 日期時間:{{ .DateTime }}\r\n"
|
||||
"hostname" = "💻 主機名:{{ .Hostname }}\r\n"
|
||||
"version" = "🚀 X-UI 版本:{{ .Version }}\r\n"
|
||||
"xrayVersion" = "📡 Xray 版本: {{ .XrayVersion }}\r\n"
|
||||
"hostname" = "💻 主機名稱:{{ .Hostname }}\r\n"
|
||||
"version" = "🚀 3X-UI 版本:{{ .Version }}\r\n"
|
||||
"xrayVersion" = "📡 Xray 版本:{{ .XrayVersion }}\r\n"
|
||||
"ipv6" = "🌐 IPv6:{{ .IPv6 }}\r\n"
|
||||
"ipv4" = "🌐 IPv4:{{ .IPv4 }}\r\n"
|
||||
"ip" = "🌐 IP:{{ .IP }}\r\n"
|
||||
"ips" = "🔢 IP 地址:\r\n{{ .IPs }}\r\n"
|
||||
"serverUpTime" = "⏳ 伺服器執行時間:{{ .UpTime }} {{ .Unit }}\r\n"
|
||||
"ips" = "🔢 IP 位址:\r\n{{ .IPs }}\r\n"
|
||||
"serverUpTime" = "⏳ 伺服器運作時間:{{ .UpTime }} {{ .Unit }}\r\n"
|
||||
"serverLoad" = "📈 伺服器負載:{{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n"
|
||||
"serverMemory" = "📋 伺服器記憶體:{{ .Current }}/{{ .Total }}\r\n"
|
||||
"tcpCount" = "🔹 TCP 連線數:{{ .Count }}\r\n"
|
||||
"udpCount" = "🔸 UDP 連線數:{{ .Count }}\r\n"
|
||||
"traffic" = "🚦 流量:{{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n"
|
||||
"xrayStatus" = "ℹ️ Xray 狀態:{{ .State }}\r\n"
|
||||
"username" = "👤 使用者名稱:{{ .Username }}\r\n"
|
||||
"password" = "👤 密碼: {{ .Password }}\r\n"
|
||||
"username" = "👤 用戶名:{{ .Username }}\r\n"
|
||||
"password" = "👤 密碼:{{ .Password }}\r\n"
|
||||
"time" = "⏰ 時間:{{ .Time }}\r\n"
|
||||
"inbound" = "📍 入站:{{ .Remark }}\r\n"
|
||||
"port" = "🔌 埠:{{ .Port }}\r\n"
|
||||
"port" = "🔌 連接埠:{{ .Port }}\r\n"
|
||||
"expire" = "📅 過期日期:{{ .Time }}\r\n"
|
||||
"expireIn" = "📅 剩餘時間:{{ .Time }}\r\n"
|
||||
"active" = "💡 啟用:{{ .Enable }}\r\n"
|
||||
"enabled" = "🚨 已啟用:{{ .Enable }}\r\n"
|
||||
"online" = "🌐 連線狀態:{{ .Status }}\r\n"
|
||||
"email" = "📧 郵箱:{{ .Email }}\r\n"
|
||||
"email" = "📧 電子郵件(用戶):{{ .Email }}\r\n"
|
||||
"upload" = "🔼 上傳↑:{{ .Upload }}\r\n"
|
||||
"download" = "🔽 下載↓:{{ .Download }}\r\n"
|
||||
"total" = "📊 總計:{{ .UpDown }} / {{ .Total }}\r\n"
|
||||
"TGUser" = "👤 電報使用者:{{ .TelegramID }}\r\n"
|
||||
"TGUser" = "👤 Telegram 用戶:{{ .TelegramID }}\r\n"
|
||||
"exhaustedMsg" = "🚨 耗盡的 {{ .Type }}:\r\n"
|
||||
"exhaustedCount" = "🚨 耗盡的 {{ .Type }} 數量:\r\n"
|
||||
"onlinesCount" = "🌐 線上客戶:{{ .Count }}\r\n"
|
||||
"disabled" = "🛑 禁用:{{ .Disabled }}\r\n"
|
||||
"onlinesCount" = "🌐 在線客戶:{{ .Count }}\r\n"
|
||||
"disabled" = "🛑 停用:{{ .Disabled }}\r\n"
|
||||
"depleteSoon" = "🔜 即將耗盡:{{ .Deplete }}\r\n\r\n"
|
||||
"backupTime" = "🗄 備份時間:{{ .Time }}\r\n"
|
||||
"refreshedOn" = "\r\n📋🔄 重新整理時間:{{ .Time }}\r\n\r\n"
|
||||
@@ -653,64 +679,64 @@
|
||||
"received_password" = "🔑📥 密碼已更新。"
|
||||
"received_email" = "📧📥 電子郵件已更新。"
|
||||
"received_comment" = "💬📥 評論已更新。"
|
||||
"id_prompt" = "🔑 預設 ID: {{ .ClientId }}\n\n請輸入您的 ID。"
|
||||
"pass_prompt" = "🔑 預設密碼: {{ .ClientPassword }}\n\n請輸入您的密碼。"
|
||||
"email_prompt" = "📧 預設電子郵件: {{ .ClientEmail }}\n\n請輸入您的電子郵件。"
|
||||
"comment_prompt" = "💬 預設評論: {{ .ClientComment }}\n\n請輸入您的評論。"
|
||||
"inbound_client_data_id" = "🔄 入站: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 電子郵件: {{ .ClientEmail }}\n📊 流量: {{ .ClientTraffic }}\n📅 到期日: {{ .ClientExp }}\n🌐 IP 限制: {{ .IpLimit }}\n💬 備註: {{ .ClientComment }}\n\n你現在可以將客戶加入入站了!"
|
||||
"inbound_client_data_pass" = "🔄 入站: {{ .InboundRemark }}\n\n🔑 密碼: {{ .ClientPass }}\n📧 電子郵件: {{ .ClientEmail }}\n📊 流量: {{ .ClientTraffic }}\n📅 到期日: {{ .ClientExp }}\n🌐 IP 限制: {{ .IpLimit }}\n💬 備註: {{ .ClientComment }}\n\n你現在可以將客戶加入入站了!"
|
||||
"cancel" = "❌ 程序已取消!\n\n您可以隨時使用 /start 重新開始。 🔄"
|
||||
"error_add_client" = "⚠️ 錯誤:\n\n {{ .error }}"
|
||||
"id_prompt" = "🔑 預設 ID:{{ .ClientId }}\n\n請輸入您的 ID。"
|
||||
"pass_prompt" = "🔑 預設密碼:{{ .ClientPassword }}\n\n請輸入您的密碼。"
|
||||
"email_prompt" = "📧 預設電子郵件:{{ .ClientEmail }}\n\n請輸入您的電子郵件。"
|
||||
"comment_prompt" = "💬 預設評論:{{ .ClientComment }}\n\n請輸入您的評論。"
|
||||
"inbound_client_data_id" = "🔄 入站:{{ .InboundRemark }}\n\n🔑 ID:{{ .ClientId }}\n📧 電子郵件:{{ .ClientEmail }}\n📊 流量:{{ .ClientTraffic }}\n📅 到期日期:{{ .ClientExp }}\n🌐 IP 限制:{{ .IpLimit }}\n💬 備註:{{ .ClientComment }}\n\n您現在可以將客戶新增到入站了!"
|
||||
"inbound_client_data_pass" = "🔄 入站:{{ .InboundRemark }}\n\n🔑 密碼:{{ .ClientPass }}\n📧 電子郵件:{{ .ClientEmail }}\n📊 流量:{{ .ClientTraffic }}\n📅 到期日期:{{ .ClientExp }}\n🌐 IP 限制:{{ .IpLimit }}\n💬 備註:{{ .ClientComment }}\n\n您現在可以將客戶新增到入站了!"
|
||||
"cancel" = "❌ 流程已取消!\n\n您可以隨時使用 /start 重新開始。 🔄"
|
||||
"error_add_client" = "⚠️ 錯誤:\n\n {{ .error }}"
|
||||
"using_default_value" = "好的,我會使用預設值。 😊"
|
||||
"incorrect_input" ="您的輸入無效。\n短語應連續輸入,不能有空格。\n正確示例: aaaaaa\n錯誤示例: aaa aaa 🚫"
|
||||
"AreYouSure" = "你確定嗎?🤔"
|
||||
"SuccessResetTraffic" = "📧 電子郵件: {{ .ClientEmail }}\n🏁 結果: ✅ 成功"
|
||||
"FailedResetTraffic" = "📧 電子郵件: {{ .ClientEmail }}\n🏁 結果: ❌ 失敗 \n\n🛠️ 錯誤: [ {{ .ErrorMessage }} ]"
|
||||
"FinishProcess" = "🔚 所有客戶的流量重置已完成。"
|
||||
"incorrect_input" ="您的輸入無效。\n片語應連續輸入,不能有空格。\n正確範例:aaaaaa\n錯誤範例:aaa aaa 🚫"
|
||||
"AreYouSure" = "您確定嗎?🤔"
|
||||
"SuccessResetTraffic" = "📧 電子郵件:{{ .ClientEmail }}\n🏁 結果:✅ 成功"
|
||||
"FailedResetTraffic" = "📧 電子郵件:{{ .ClientEmail }}\n🏁 結果:❌ 失敗 \n\n🛠️ 錯誤:[ {{ .ErrorMessage }} ]"
|
||||
"FinishProcess" = "🔚 所有客戶的流量重設已完成。"
|
||||
|
||||
[tgbot.buttons]
|
||||
"closeKeyboard" = "❌ 關閉鍵盤"
|
||||
"cancel" = "❌ 取消"
|
||||
"cancelReset" = "❌ 取消重置"
|
||||
"cancelReset" = "❌ 取消重設"
|
||||
"cancelIpLimit" = "❌ 取消 IP 限制"
|
||||
"confirmResetTraffic" = "✅ 確認重置流量?"
|
||||
"confirmResetTraffic" = "✅ 確認重設流量?"
|
||||
"confirmClearIps" = "✅ 確認清除 IP?"
|
||||
"confirmRemoveTGUser" = "✅ 確認移除 Telegram 使用者?"
|
||||
"confirmToggle" = "✅ 確認啟用/禁用使用者?"
|
||||
"confirmRemoveTGUser" = "✅ 確認移除 Telegram 用戶?"
|
||||
"confirmToggle" = "✅ 確認啟用/停用用戶?"
|
||||
"dbBackup" = "獲取資料庫備份"
|
||||
"serverUsage" = "伺服器使用情況"
|
||||
"serverUsage" = "伺服器狀態"
|
||||
"getInbounds" = "獲取入站資訊"
|
||||
"depleteSoon" = "即將耗盡"
|
||||
"clientUsage" = "獲取使用情況"
|
||||
"onlines" = "線上客戶端"
|
||||
"commands" = "命令"
|
||||
"onlines" = "在線客戶端"
|
||||
"commands" = "常用指令"
|
||||
"refresh" = "🔄 重新整理"
|
||||
"clearIPs" = "❌ 清除 IP"
|
||||
"removeTGUser" = "❌ 移除 Telegram 使用者"
|
||||
"selectTGUser" = "👤 選擇 Telegram 使用者"
|
||||
"selectOneTGUser" = "👤 選擇一個 Telegram 使用者:"
|
||||
"resetTraffic" = "📈 重置流量"
|
||||
"resetExpire" = "📅 更改到期日期"
|
||||
"removeTGUser" = "❌ 移除 Telegram 用戶"
|
||||
"selectTGUser" = "👤 選擇 Telegram 用戶"
|
||||
"selectOneTGUser" = "👤 選擇一個 Telegram 用戶:"
|
||||
"resetTraffic" = "📈 重設流量"
|
||||
"resetExpire" = "📅 變更到期日期"
|
||||
"ipLog" = "🔢 IP 日誌"
|
||||
"ipLimit" = "🔢 IP 限制"
|
||||
"setTGUser" = "👤 設定 Telegram 使用者"
|
||||
"toggle" = "🔘 啟用/禁用"
|
||||
"custom" = "🔢 風俗"
|
||||
"confirmNumber" = "✅ 確認: {{ .Num }}"
|
||||
"setTGUser" = "👤 設定 Telegram 用戶"
|
||||
"toggle" = "🔘 啟用/停用"
|
||||
"custom" = "🔢 自訂輸入"
|
||||
"confirmNumber" = "✅ 確認:{{ .Num }}"
|
||||
"confirmNumberAdd" = "✅ 確認新增:{{ .Num }}"
|
||||
"limitTraffic" = "🚧 流量限制"
|
||||
"getBanLogs" = "禁止日誌"
|
||||
"getBanLogs" = "封鎖日誌"
|
||||
"allClients" = "所有客戶"
|
||||
"addClient" = "新增客戶"
|
||||
"submitDisable" = "以停用方式送出 ☑️"
|
||||
"submitEnable" = "以啟用方式送出 ✅"
|
||||
"use_default" = "🏷️ 使用預設值"
|
||||
"submitDisable" = "提交為停用 ☑️"
|
||||
"submitEnable" = "提交為啟用 ✅"
|
||||
"use_default" = "🏷️ 使用預設"
|
||||
"change_id" = "⚙️🔑 ID"
|
||||
"change_password" = "⚙️🔑 密碼"
|
||||
"change_email" = "⚙️📧 電子郵件"
|
||||
"change_comment" = "⚙️💬 評論"
|
||||
"ResetAllTraffics" = "重設所有流量"
|
||||
"SortedTrafficUsageReport" = "排序過的流量使用報告"
|
||||
"SortedTrafficUsageReport" = "排序的流量使用報告"
|
||||
|
||||
[tgbot.answers]
|
||||
"successfulOperation" = "✅ 成功!"
|
||||
@@ -720,17 +746,17 @@
|
||||
"canceled" = "❌ {{ .Email }}:操作已取消。"
|
||||
"clientRefreshSuccess" = "✅ {{ .Email }}:客戶端重新整理成功。"
|
||||
"IpRefreshSuccess" = "✅ {{ .Email }}:IP 重新整理成功。"
|
||||
"TGIdRefreshSuccess" = "✅ {{ .Email }}:客戶端的 Telegram 使用者重新整理成功。"
|
||||
"resetTrafficSuccess" = "✅ {{ .Email }}:流量已重置成功。"
|
||||
"setTrafficLimitSuccess" = "✅ {{ .Email }}: 流量限制儲存成功。"
|
||||
"expireResetSuccess" = "✅ {{ .Email }}:過期天數已重置成功。"
|
||||
"TGIdRefreshSuccess" = "✅ {{ .Email }}:客戶端的 Telegram 用戶重新整理成功。"
|
||||
"resetTrafficSuccess" = "✅ {{ .Email }}:流量已重設成功。"
|
||||
"setTrafficLimitSuccess" = "✅ {{ .Email }}:流量限制儲存成功。"
|
||||
"expireResetSuccess" = "✅ {{ .Email }}:過期天數已重設成功。"
|
||||
"resetIpSuccess" = "✅ {{ .Email }}:成功儲存 IP 限制數量為 {{ .Count }}。"
|
||||
"clearIpSuccess" = "✅ {{ .Email }}:IP 已成功清除。"
|
||||
"getIpLog" = "✅ {{ .Email }}:獲取 IP 日誌。"
|
||||
"getUserInfo" = "✅ {{ .Email }}:獲取 Telegram 使用者資訊。"
|
||||
"removedTGUserSuccess" = "✅ {{ .Email }}:Telegram 使用者已成功移除。"
|
||||
"getUserInfo" = "✅ {{ .Email }}:獲取 Telegram 用戶資訊。"
|
||||
"removedTGUserSuccess" = "✅ {{ .Email }}:Telegram 用戶已成功移除。"
|
||||
"enableSuccess" = "✅ {{ .Email }}:已成功啟用。"
|
||||
"disableSuccess" = "✅ {{ .Email }}:已成功禁用。"
|
||||
"askToAddUserId" = "未找到您的配置!\r\n請向管理員詢問,在您的配置中使用您的 Telegram 使用者 ChatID。\r\n\r\n您的使用者 ChatID:<code>{{ .TgUserID }}</code>"
|
||||
"disableSuccess" = "✅ {{ .Email }}:已成功停用。"
|
||||
"askToAddUserId" = "找不到您的設定!\r\n請向管理員詢問,在您的設定中使用您的 Telegram 用戶 ChatID。\r\n\r\n您的用戶 ChatID:<code>{{ .TgUserID }}</code>"
|
||||
"chooseClient" = "為入站 {{ .Inbound }} 選擇一個客戶"
|
||||
"chooseInbound" = "選擇一個入站"
|
||||
|
||||
+99
-19
@@ -42,6 +42,9 @@ var i18nFS embed.FS
|
||||
|
||||
var startTime = time.Now()
|
||||
|
||||
// 预定义 IPv4 私网和回环网段
|
||||
var privateIPv4Nets []*net.IPNet
|
||||
|
||||
type wrapAssetsFS struct {
|
||||
embed.FS
|
||||
}
|
||||
@@ -125,28 +128,39 @@ func (s *Server) getHtmlFiles() ([]string, error) {
|
||||
}
|
||||
|
||||
func (s *Server) getHtmlTemplate(funcMap template.FuncMap) (*template.Template, error) {
|
||||
t := template.New("").Funcs(funcMap)
|
||||
err := fs.WalkDir(htmlFS, "html", func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 这里用 htmlFS(//go:embed html/*)而不是“templates”
|
||||
t := template.New("").Funcs(funcMap)
|
||||
|
||||
if d.IsDir() {
|
||||
newT, err := t.ParseFS(htmlFS, path+"/*.html")
|
||||
if err != nil {
|
||||
// ignore
|
||||
return nil
|
||||
}
|
||||
t = newT
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return t, nil
|
||||
// 递归遍历 embed 的 html 目录,解析所有 .html 模板
|
||||
err := fs.WalkDir(htmlFS, "html", func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if d.IsDir() {
|
||||
return nil
|
||||
}
|
||||
if !strings.HasSuffix(path, ".html") {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 读出模板内容
|
||||
b, err := htmlFS.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 去掉前缀“html/”,让 {{template "form/inbound"}} 这种名字能被正确找到
|
||||
name := strings.TrimPrefix(path, "html/")
|
||||
_, err = t.New(name).Parse(string(b))
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
|
||||
func (s *Server) initRouter() (*gin.Engine, error) {
|
||||
if config.IsDebug() {
|
||||
gin.SetMode(gin.DebugMode)
|
||||
@@ -331,6 +345,12 @@ func (s *Server) Start() (err error) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if certFile == "" || keyFile == "" {
|
||||
// 如果没有证书,强制检查 listen 是否内部 IP,否则回退到本地
|
||||
if !isInternalIP(listen) {
|
||||
listen = fallbackToLocalhost(listen)
|
||||
}
|
||||
}
|
||||
listenAddr := net.JoinHostPort(listen, strconv.Itoa(port))
|
||||
listener, err := net.Listen("tcp", listenAddr)
|
||||
if err != nil {
|
||||
@@ -400,3 +420,63 @@ func (s *Server) GetCtx() context.Context {
|
||||
func (s *Server) GetCron() *cron.Cron {
|
||||
return s.cron
|
||||
}
|
||||
|
||||
// isInternalIP 判断是否为私网或回环IP(支持IPv4和IPv6)
|
||||
func isInternalIP(ipStr string) bool {
|
||||
ip := net.ParseIP(ipStr)
|
||||
if ip == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
// IPv4 判断是否在私网/回环网段内
|
||||
for _, privateNet := range privateIPv4Nets {
|
||||
if privateNet.Contains(ip4) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IPv6 判断回环或链路本地地址
|
||||
if ip.IsLoopback() || ip.IsLinkLocalUnicast() {
|
||||
return true
|
||||
}
|
||||
|
||||
// 判断 IPv6 fc00::/7 私网地址段
|
||||
if ip[0]&0xfe == 0xfc {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// fallbackToLocalhost 根据传入地址返回对应的本地回环地址
|
||||
func fallbackToLocalhost(listen string) string {
|
||||
ip := net.ParseIP(listen)
|
||||
if ip == nil {
|
||||
// 无法解析则默认回退 IPv4 回环
|
||||
return "127.0.0.1"
|
||||
}
|
||||
if ip.To4() != nil {
|
||||
// IPv4 回退 IPv4 回环
|
||||
return "127.0.0.1"
|
||||
}
|
||||
// IPv6 回退 IPv6 回环
|
||||
return "::1"
|
||||
}
|
||||
|
||||
func init() {
|
||||
for _, cidr := range []string{
|
||||
"10.0.0.0/8", // A类私网
|
||||
"172.16.0.0/12", // B类私网
|
||||
"192.168.0.0/16", // C类私网
|
||||
"100.64.0.0/10", // CGNAT地址段
|
||||
"127.0.0.0/8", // 回环
|
||||
} {
|
||||
_, netw, err := net.ParseCIDR(cidr)
|
||||
if err == nil {
|
||||
privateIPv4Nets = append(privateIPv4Nets, netw)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user