mirror of
https://github.com/alireza0/x-ui.git
synced 2026-03-15 05:36:09 +00:00
Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7995310aa7 | ||
|
|
c277fd29fd | ||
|
|
6249528a58 | ||
|
|
83c26ad81d | ||
|
|
9b5379c0e7 | ||
|
|
8041950c29 | ||
|
|
541170c3c2 | ||
|
|
47ec105d1f | ||
|
|
525327c4ac | ||
|
|
0267a1b32a | ||
|
|
83df4ae7cf | ||
|
|
24465aeb43 | ||
|
|
ee2bbffc8f | ||
|
|
294a3f46a0 | ||
|
|
44a1104bff | ||
|
|
fbc4ba1ba0 | ||
|
|
b829ebca2b | ||
|
|
288c0b982a | ||
|
|
8919099594 | ||
|
|
3ee7761ab0 | ||
|
|
346a82b203 | ||
|
|
45e2d66f4b | ||
|
|
36ce3e0130 | ||
|
|
a04f2306bc | ||
|
|
fdc3b15efe | ||
|
|
2fb5168918 | ||
|
|
1fcf3d68f1 | ||
|
|
de654fdbc4 | ||
|
|
e32c723d68 | ||
|
|
41089df567 | ||
|
|
a703f70302 | ||
|
|
3c922d8673 | ||
|
|
78c3912002 | ||
|
|
0b8f5f7f67 | ||
|
|
ca3d45ddb4 | ||
|
|
fff74d7ea7 | ||
|
|
b3393e402b | ||
|
|
30d7376463 | ||
|
|
07f507dc1f | ||
|
|
49aafa5657 |
@@ -1 +1 @@
|
||||
1.7.0
|
||||
1.7.1
|
||||
10
go.mod
10
go.mod
@@ -7,17 +7,17 @@ require (
|
||||
github.com/gin-gonic/gin v1.9.1
|
||||
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1
|
||||
github.com/goccy/go-json v0.10.2
|
||||
github.com/nicksnyder/go-i18n/v2 v2.3.0
|
||||
github.com/nicksnyder/go-i18n/v2 v2.4.0
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
|
||||
github.com/pelletier/go-toml/v2 v2.1.1
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/shirou/gopsutil/v3 v3.23.12
|
||||
github.com/shirou/gopsutil/v3 v3.24.1
|
||||
github.com/xtls/xray-core v1.8.7
|
||||
go.uber.org/atomic v1.11.0
|
||||
golang.org/x/text v0.14.0
|
||||
google.golang.org/grpc v1.60.1
|
||||
gorm.io/driver/sqlite v1.5.4
|
||||
gorm.io/gorm v1.25.5
|
||||
google.golang.org/grpc v1.61.1
|
||||
gorm.io/driver/sqlite v1.5.5
|
||||
gorm.io/gorm v1.25.7
|
||||
)
|
||||
|
||||
require (
|
||||
|
||||
21
go.sum
21
go.sum
@@ -187,8 +187,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
|
||||
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.3.0 h1:2NPsCsNFCVd7i+Su0xYsBrIhS3bE2XMv5gNTft2O+PQ=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.3.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.4.0 h1:3IcvPOAvnCKwNm0TB0dLDTuawWEj+ax/RERNC+diLMM=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.4.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4=
|
||||
github.com/onsi/ginkgo/v2 v2.13.2 h1:Bi2gGVkfn6gQcjNjZJVO8Gf0FHzMPf2phUei9tejVMs=
|
||||
github.com/onsi/ginkgo/v2 v2.13.2/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM=
|
||||
github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg=
|
||||
@@ -237,8 +237,8 @@ github.com/sagernet/sing-shadowsocks v0.2.6/go.mod h1:j2YZBIpWIuElPFL/5sJAj470bc
|
||||
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb h1:XfLJSPIOUX+osiMraVgIrMR27uMXnRJWGm1+GL8/63U=
|
||||
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4=
|
||||
github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM=
|
||||
github.com/shirou/gopsutil/v3 v3.24.1 h1:R3t6ondCEvmARp3wxODhXMTLC/klMa87h2PHUw5m7QI=
|
||||
github.com/shirou/gopsutil/v3 v3.24.1/go.mod h1:UU7a2MSBQa+kW1uuDq8DeEBS8kmrnQwsv2b5O513rwU=
|
||||
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
||||
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
|
||||
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
|
||||
@@ -378,7 +378,6 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
@@ -423,8 +422,8 @@ google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmE
|
||||
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU=
|
||||
google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM=
|
||||
google.golang.org/grpc v1.61.1 h1:kLAiWrZs7YeDM6MumDe7m3y4aM6wacLzM1Y/wiLP9XY=
|
||||
google.golang.org/grpc v1.61.1/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
@@ -446,10 +445,10 @@ gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/sqlite v1.5.4 h1:IqXwXi8M/ZlPzH/947tn5uik3aYQslP9BVveoax0nV0=
|
||||
gorm.io/driver/sqlite v1.5.4/go.mod h1:qxAuCol+2r6PannQDpOP1FP6ag3mKi4esLnB/jHed+4=
|
||||
gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls=
|
||||
gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||
gorm.io/driver/sqlite v1.5.5 h1:7MDMtUZhV065SilG62E0MquljeArQZNfJnjd9i9gx3E=
|
||||
gorm.io/driver/sqlite v1.5.5/go.mod h1:6NgQ7sQWAIFsPrJJl1lSNSu2TABh0ZZ/zm5fosATavE=
|
||||
gorm.io/gorm v1.25.7 h1:VsD6acwRjz2zFxGO50gPO6AkNs7KKnvfzUjHQhZDz/A=
|
||||
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
|
||||
gvisor.dev/gvisor v0.0.0-20231104011432-48a6d7d5bd0b h1:yqkg3pTifuKukuWanp8spDsL4irJkHF5WI0J47hU87o=
|
||||
gvisor.dev/gvisor v0.0.0-20231104011432-48a6d7d5bd0b/go.mod h1:10sU+Uh5KKNv1+2x2A0Gvzt8FjD3ASIhorV3YsauXhk=
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"x-ui/database/model"
|
||||
"x-ui/logger"
|
||||
"x-ui/util/common"
|
||||
"x-ui/util/random"
|
||||
"x-ui/web/service"
|
||||
"x-ui/xray"
|
||||
|
||||
@@ -382,25 +383,21 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
|
||||
if realitySetting != nil {
|
||||
if sniValue, ok := searchKey(realitySetting, "serverNames"); ok {
|
||||
sNames, _ := sniValue.([]interface{})
|
||||
params["sni"], _ = sNames[0].(string)
|
||||
params["sni"] = sNames[random.Num(len(sNames))].(string)
|
||||
}
|
||||
if pbkValue, ok := searchKey(realitySettings, "publicKey"); ok {
|
||||
params["pbk"], _ = pbkValue.(string)
|
||||
}
|
||||
if sidValue, ok := searchKey(realitySetting, "shortIds"); ok {
|
||||
shortIds, _ := sidValue.([]interface{})
|
||||
params["sid"], _ = shortIds[0].(string)
|
||||
params["sid"] = shortIds[random.Num(len(shortIds))].(string)
|
||||
}
|
||||
if fpValue, ok := searchKey(realitySettings, "fingerprint"); ok {
|
||||
if fp, ok := fpValue.(string); ok && len(fp) > 0 {
|
||||
params["fp"] = fp
|
||||
}
|
||||
}
|
||||
if spxValue, ok := searchKey(realitySettings, "spiderX"); ok {
|
||||
if spx, ok := spxValue.(string); ok && len(spx) > 0 {
|
||||
params["spx"] = spx
|
||||
}
|
||||
}
|
||||
params["spx"] = "/" + random.Seq(15)
|
||||
}
|
||||
|
||||
if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 {
|
||||
@@ -563,25 +560,21 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string
|
||||
if realitySetting != nil {
|
||||
if sniValue, ok := searchKey(realitySetting, "serverNames"); ok {
|
||||
sNames, _ := sniValue.([]interface{})
|
||||
params["sni"], _ = sNames[0].(string)
|
||||
params["sni"] = sNames[random.Num(len(sNames))].(string)
|
||||
}
|
||||
if pbkValue, ok := searchKey(realitySettings, "publicKey"); ok {
|
||||
params["pbk"], _ = pbkValue.(string)
|
||||
}
|
||||
if sidValue, ok := searchKey(realitySetting, "shortIds"); ok {
|
||||
shortIds, _ := sidValue.([]interface{})
|
||||
params["sid"], _ = shortIds[0].(string)
|
||||
params["sid"] = shortIds[random.Num(len(shortIds))].(string)
|
||||
}
|
||||
if fpValue, ok := searchKey(realitySettings, "fingerprint"); ok {
|
||||
if fp, ok := fpValue.(string); ok && len(fp) > 0 {
|
||||
params["fp"] = fp
|
||||
}
|
||||
}
|
||||
if spxValue, ok := searchKey(realitySettings, "spiderX"); ok {
|
||||
if spx, ok := spxValue.(string); ok && len(spx) > 0 {
|
||||
params["spx"] = spx
|
||||
}
|
||||
}
|
||||
params["spx"] = "/" + random.Seq(15)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,3 +41,7 @@ func Seq(n int) string {
|
||||
}
|
||||
return string(runes)
|
||||
}
|
||||
|
||||
func Num(n int) int {
|
||||
return rand.Intn(n)
|
||||
}
|
||||
|
||||
@@ -1049,3 +1049,29 @@ li.ant-select-dropdown-menu-item:empty:after {
|
||||
.ant-input-number {
|
||||
overflow: clip;
|
||||
}
|
||||
|
||||
.ant-modal-body,
|
||||
.ant-collapse-content>.ant-collapse-content-box {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.dark .ant-dropdown-menu-item:hover,
|
||||
.dark .ant-dropdown-menu-submenu-title:hover,
|
||||
.dark .ant-select-dropdown-menu-item-active:not(.ant-select-dropdown-menu-item-disabled),
|
||||
.dark .ant-select-dropdown-menu-item:hover:not(.ant-select-dropdown-menu-item-disabled) {
|
||||
background-color: #313f5a;
|
||||
}
|
||||
|
||||
.ant-select-dropdown,
|
||||
.ant-popover-inner {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.ant-popover-inner-content {
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.ant-input-group-addon:not(:first-child):not(:last-child), .ant-input-group-wrap:not(:first-child):not(:last-child), .ant-input-group>.ant-input:not(:first-child):not(:last-child) {
|
||||
border-radius: 0rem 1rem 1rem 0rem;
|
||||
}
|
||||
@@ -418,7 +418,7 @@ class Outbound extends CommonClass {
|
||||
}
|
||||
|
||||
canEnableTls() {
|
||||
if (![Protocols.VMess, Protocols.VLESS, Protocols.Trojan].includes(this.protocol)) return false;
|
||||
if (![Protocols.VMess, Protocols.VLESS, Protocols.Trojan, Protocols.Shadowsocks].includes(this.protocol)) return false;
|
||||
return ["tcp", "ws", "http", "quic", "grpc"].includes(this.stream.network);
|
||||
}
|
||||
|
||||
@@ -860,13 +860,13 @@ Outbound.SocksSettings = class extends CommonClass {
|
||||
}
|
||||
|
||||
static fromJson(json={}) {
|
||||
servers = json.servers;
|
||||
let servers = json.servers;
|
||||
if(ObjectUtil.isArrEmpty(servers)) servers=[{users: [{}]}];
|
||||
return new Outbound.SocksSettings(
|
||||
servers[0].address,
|
||||
servers[0].port,
|
||||
ObjectUtil.isArrEmpty(servers[0].users) ? '' : servers[0].users[0].user,
|
||||
ObjectUtil.isArrEmpty(servers[0].pass) ? '' : servers[0].users[0].pass,
|
||||
ObjectUtil.isArrEmpty(servers[0].users) ? '' : servers[0].users[0].pass,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -890,13 +890,13 @@ Outbound.HttpSettings = class extends CommonClass {
|
||||
}
|
||||
|
||||
static fromJson(json={}) {
|
||||
servers = json.servers;
|
||||
let servers = json.servers;
|
||||
if(ObjectUtil.isArrEmpty(servers)) servers=[{users: [{}]}];
|
||||
return new Outbound.HttpSettings(
|
||||
servers[0].address,
|
||||
servers[0].port,
|
||||
ObjectUtil.isArrEmpty(servers[0].users) ? '' : servers[0].users[0].user,
|
||||
ObjectUtil.isArrEmpty(servers[0].pass) ? '' : servers[0].users[0].pass,
|
||||
ObjectUtil.isArrEmpty(servers[0].users) ? '' : servers[0].users[0].pass,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -912,7 +912,7 @@ Outbound.HttpSettings = class extends CommonClass {
|
||||
};
|
||||
Outbound.WireguardSettings = class extends CommonClass {
|
||||
constructor(
|
||||
mtu=1420, secretKey=Wireguard.generateKeypair().privateKey,
|
||||
mtu=1420, secretKey='',
|
||||
address='', workers=2, domainStrategy='', reserved='',
|
||||
peers=[new Outbound.WireguardSettings.Peer()], kernelMode=false) {
|
||||
super();
|
||||
|
||||
@@ -1347,6 +1347,28 @@ class Inbound extends XrayCommonClass {
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
getWireguardLink(address, port, remark, peerId) {
|
||||
let txt = `[Interface]\n`
|
||||
txt += `PrivateKey = ${this.settings.peers[peerId].privateKey}\n`
|
||||
txt += `Address = ${this.settings.peers[peerId].allowedIPs[0]}\n`
|
||||
txt += `DNS = 1.1.1.1, 9.9.9.9\n`
|
||||
if (this.settings.mtu) {
|
||||
txt += `MTU = ${this.settings.mtu}\n`
|
||||
}
|
||||
txt += `\n# ${remark}\n`
|
||||
txt += `[Peer]\n`
|
||||
txt += `PublicKey = ${this.settings.pubKey}\n`
|
||||
txt += `AllowedIPs = 0.0.0.0/0, ::/0\n`
|
||||
txt += `Endpoint = ${address}:${port}`
|
||||
if (this.settings.peers[peerId].psk) {
|
||||
txt += `\nPresharedKey = ${this.settings.peers[peerId].psk}`
|
||||
}
|
||||
if (this.settings.peers[peerId].keepAlive) {
|
||||
txt += `\nPersistentKeepalive = ${this.settings.peers[peerId].keepAlive}\n`
|
||||
}
|
||||
return txt;
|
||||
}
|
||||
|
||||
genLink(address='', port=this.port, forceTls='same', remark='', client) {
|
||||
switch (this.protocol) {
|
||||
case Protocols.VMESS:
|
||||
@@ -1370,7 +1392,7 @@ class Inbound extends XrayCommonClass {
|
||||
const orderChars = remarkModel.slice(1);
|
||||
let orders = {
|
||||
'i': remark,
|
||||
'e': client ? client.email : '',
|
||||
'e': email,
|
||||
'o': '',
|
||||
};
|
||||
if(ObjectUtil.isArrEmpty(this.stream.externalProxy)){
|
||||
@@ -1393,6 +1415,7 @@ class Inbound extends XrayCommonClass {
|
||||
}
|
||||
|
||||
genInboundLinks(remark = '', remarkModel = '-ieo') {
|
||||
let addr = !ObjectUtil.isEmpty(this.listen) && this.listen !== "0.0.0.0" ? this.listen : location.hostname;
|
||||
if(this.clients){
|
||||
let links = [];
|
||||
this.clients.forEach((client) => {
|
||||
@@ -1402,7 +1425,14 @@ class Inbound extends XrayCommonClass {
|
||||
});
|
||||
return links.join('\r\n');
|
||||
} else {
|
||||
if(this.protocol == Protocols.SHADOWSOCKS && !this.isSSMultiUser) return this.genSSLink(this.listen, this.port, 'same', remark);
|
||||
if(this.protocol == Protocols.SHADOWSOCKS && !this.isSSMultiUser) return this.genSSLink(addr, this.port, 'same', remark);
|
||||
if(this.protocol == Protocols.WIREGUARD) {
|
||||
let links = [];
|
||||
this.settings.peers.forEach((p,index) => {
|
||||
links.push(this.getWireguardLink(addr,this.port,remark + remarkModel.charAt(0) + (index+1),index));
|
||||
});
|
||||
return links.join('\r\n');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
}
|
||||
@@ -2098,9 +2128,13 @@ Inbound.WireguardSettings = class extends XrayCommonClass {
|
||||
};
|
||||
|
||||
Inbound.WireguardSettings.Peer = class extends XrayCommonClass {
|
||||
constructor(publicKey='', psk='', allowedIPs=['0.0.0.0/0','::/0'], keepAlive=0) {
|
||||
constructor(privateKey, publicKey, psk='', allowedIPs=['10.0.0.0/24'], keepAlive=0) {
|
||||
super();
|
||||
this.privateKey = privateKey
|
||||
this.publicKey = publicKey;
|
||||
if (!this.publicKey){
|
||||
[this.publicKey, this.privateKey] = Object.values(Wireguard.generateKeypair())
|
||||
}
|
||||
this.psk = psk;
|
||||
this.allowedIPs = allowedIPs;
|
||||
this.keepAlive = keepAlive;
|
||||
@@ -2108,6 +2142,7 @@ Inbound.WireguardSettings.Peer = class extends XrayCommonClass {
|
||||
|
||||
static fromJson(json={}){
|
||||
return new Inbound.WireguardSettings.Peer(
|
||||
json.privateKey,
|
||||
json.publicKey,
|
||||
json.preSharedKey,
|
||||
json.allowedIPs,
|
||||
@@ -2117,6 +2152,7 @@ Inbound.WireguardSettings.Peer = class extends XrayCommonClass {
|
||||
|
||||
toJson() {
|
||||
return {
|
||||
privateKey: this.privateKey,
|
||||
publicKey: this.publicKey,
|
||||
preSharedKey: this.psk.length>0 ? this.psk : undefined,
|
||||
allowedIPs: this.allowedIPs,
|
||||
|
||||
@@ -136,9 +136,9 @@ class RandomUtil {
|
||||
}
|
||||
|
||||
static randomShortId() {
|
||||
let shortIds = ['','','',''];
|
||||
for (var ii = 0; ii < 4; ii++) {
|
||||
for (var jj = 0; jj < this.randomInt(8); jj++){
|
||||
let shortIds = new Array(24).fill('');
|
||||
for (var ii = 0; ii < 24; ii++) {
|
||||
for (var jj = 0; jj < this.randomInt(16); jj++){
|
||||
let randomNum = this.randomInt(256);
|
||||
shortIds[ii] += ('0' + randomNum.toString(16)).slice(-2)
|
||||
}
|
||||
|
||||
@@ -6,12 +6,16 @@
|
||||
<a-tag color="green" style="margin-bottom: 10px;display: block;text-align: center;" >{{ i18n "pages.inbounds.clickOnQRcode" }}</a-tag>
|
||||
<template v-if="app.subSettings.enable && qrModal.subId">
|
||||
<a-divider>Subscription</a-divider>
|
||||
<canvas @click="copyToClipboard('qrCode-sub',genSubLink(qrModal.client.subId))" id="qrCode-sub" style="width: 100%; height: 100%;"></canvas>
|
||||
<canvas @click="copyToClipboard('qrCode-sub',genSubLink(qrModal.client.subId))"
|
||||
id="qrCode-sub"
|
||||
style="width: 100%; height: 100%; display: flex; border-radius: 1rem;"></canvas>
|
||||
</template>
|
||||
<a-divider>{{ i18n "pages.inbounds.client" }}</a-divider>
|
||||
<template v-for="(row, index) in qrModal.qrcodes">
|
||||
<a-tag color="blue" style="margin: 10px 0; display: block; text-align: center;">[[ row.remark ]]</a-tag>
|
||||
<canvas @click="copyToClipboard('qrCode-'+index, row.link)" :id="'qrCode-'+index" style="width: 100%; height: 100%;"></canvas>
|
||||
<canvas @click="copyToClipboard('qrCode-'+index, row.link)"
|
||||
:id="'qrCode-'+index"
|
||||
style="width: 100%; height: 100%; display: flex; border-radius: 1rem;"></canvas>
|
||||
</template>
|
||||
</a-modal>
|
||||
|
||||
@@ -32,12 +36,21 @@
|
||||
this.client = client;
|
||||
this.subId = '';
|
||||
this.qrcodes = [];
|
||||
this.inbound.genAllLinks(this.dbInbound.remark, app.remarkModel, client).forEach(l => {
|
||||
this.qrcodes.push({
|
||||
remark: l.remark,
|
||||
link: l.link
|
||||
if (this.inbound.protocol == Protocols.WIREGUARD){
|
||||
this.inbound.genInboundLinks(dbInbound.remark).split('\r\n').forEach((l,index) =>{
|
||||
this.qrcodes.push({
|
||||
remark: "Peer " + (index+1),
|
||||
link: l
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.inbound.genAllLinks(this.dbInbound.remark, app.remarkModel, client).forEach(l => {
|
||||
this.qrcodes.push({
|
||||
remark: l.remark,
|
||||
link: l.link
|
||||
});
|
||||
});
|
||||
}
|
||||
this.visible = true;
|
||||
},
|
||||
close: function () {
|
||||
|
||||
@@ -97,7 +97,8 @@
|
||||
<a-col :xs="22" :sm="20" :md="14" :lg="10" :xl="8" :xxl="6" id="login" style="margin: 3rem 0;">
|
||||
<a-row type="flex" justify="center">
|
||||
<a-col>
|
||||
<h1 class="title">{{ i18n "pages.login.title" }}</h1>
|
||||
<h1 class="title" style="margin-bottom: 5px; font-size: 24px;">X-UI</h1>
|
||||
<h2 class="title" style="text-align: center;">{{ i18n "pages.login.title" }}</h2>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row type="flex" justify="center">
|
||||
@@ -117,8 +118,8 @@
|
||||
<a-form-item>
|
||||
<a-row justify="center" class="centered">
|
||||
<a-button type="primary" :loading="loading" @click="login" :icon="loading ? 'poweroff' : undefined"
|
||||
:style="loading ? { width: '50px' } : { display: 'inline-block', width: '100%' }">
|
||||
[[ loading ? '' : '{{ i18n "login" }}' ]]
|
||||
:style="{ fontWeight: 'bold', width: loading ? '50px' : '100%', display: 'inline-block' }">
|
||||
[[ loading ? '' : '{{ i18n "login" }}' ]]
|
||||
</a-button>
|
||||
</a-row>
|
||||
</a-form-item>
|
||||
|
||||
@@ -61,13 +61,13 @@
|
||||
<template slot="label">
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
|
||||
<span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
|
||||
</template>
|
||||
{{ i18n "pages.inbounds.totalFlow" }} (GB)
|
||||
{{ i18n "pages.inbounds.totalFlow" }}
|
||||
<a-icon type="question-circle"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input-number v-model="clientsBulkModal.totalGB" :min="0"></a-input-number>
|
||||
<a-input-number v-model="clientsBulkModal.totalGB" :min="0"></a-input-number> GB
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.client.delayedStart" }}'>
|
||||
<a-switch v-model="clientsBulkModal.delayedStart" @click="clientsBulkModal.expiryTime=0"></a-switch>
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
{{define "menuItems"}}
|
||||
<a-menu-item key="{{ .base_path }}xui/">
|
||||
<a-icon type="dashboard"></a-icon>
|
||||
<span>{{ i18n "menu.dashboard"}}</span>
|
||||
<span><strong>{{ i18n "menu.dashboard"}}</strong></span>
|
||||
</a-menu-item>
|
||||
<a-menu-item key="{{ .base_path }}xui/inbounds">
|
||||
<a-icon type="user"></a-icon>
|
||||
<span>{{ i18n "menu.inbounds"}}</span>
|
||||
<span><strong>{{ i18n "menu.inbounds"}}</strong></span>
|
||||
</a-menu-item>
|
||||
<a-menu-item key="{{ .base_path }}xui/settings">
|
||||
<a-icon type="setting"></a-icon>
|
||||
<span>{{ i18n "menu.settings"}}</span>
|
||||
<span><strong>{{ i18n "menu.settings"}}</strong></span>
|
||||
</a-menu-item>
|
||||
<a-menu-item key="{{ .base_path }}xui/xray">
|
||||
<a-icon type="tool"></a-icon>
|
||||
<span>{{ i18n "menu.xray"}}</span>
|
||||
<span><strong>{{ i18n "menu.xray"}}</strong></span>
|
||||
</a-menu-item>
|
||||
<a-menu-item key="{{ .base_path }}logout">
|
||||
<a-icon type="logout"></a-icon>
|
||||
<span>{{ i18n "menu.logout"}}</span>
|
||||
<span><strong>{{ i18n "menu.logout"}}</strong></span>
|
||||
</a-menu-item>
|
||||
{{end}}
|
||||
|
||||
|
||||
@@ -73,13 +73,13 @@
|
||||
<template slot="label">
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
|
||||
<span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
|
||||
</template>
|
||||
{{ i18n "pages.inbounds.totalFlow" }} (GB)
|
||||
{{ i18n "pages.inbounds.totalFlow" }}
|
||||
<a-icon type="question-circle"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input-number v-model="client._totalGB" :min="0"></a-input-number>
|
||||
<a-input-number v-model="client._totalGB" :min="0"></a-input-number> GB
|
||||
</a-form-item>
|
||||
<a-form-item v-if="isEdit && clientStats" label='{{ i18n "usage" }}'>
|
||||
<a-tag :color="clientUsageColor(clientStats, app.trafficDiff)">
|
||||
|
||||
@@ -35,13 +35,13 @@
|
||||
<template slot="label">
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
|
||||
<span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
|
||||
</template>
|
||||
{{ i18n "pages.inbounds.totalFlow" }} (GB)
|
||||
{{ i18n "pages.inbounds.totalFlow" }}
|
||||
<a-icon type="question-circle"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input-number v-model="dbInbound.totalGB" :min="0"></a-input-number>
|
||||
<a-input-number v-model="dbInbound.totalGB" :min="0"></a-input-number> GB
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item>
|
||||
|
||||
@@ -193,17 +193,23 @@
|
||||
<a-input v-model.trim="outbound.settings.pass"></a-input>
|
||||
</a-form-item>
|
||||
</template>
|
||||
<!-- trojan/shadowsocks -->
|
||||
<template v-if="[Protocols.Trojan, Protocols.Shadowsocks].includes(outbound.protocol)">
|
||||
<a-form-item label='{{ i18n "password" }}'>
|
||||
<a-input v-model.trim="outbound.settings.password"></a-input>
|
||||
</a-form-item>
|
||||
</template>
|
||||
<!-- shadowsocks -->
|
||||
<template v-if="outbound.protocol === Protocols.Shadowsocks">
|
||||
<a-form-item label='{{ i18n "encryption" }}'>
|
||||
<a-select v-model="outbound.settings.method" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="(method,method_name) in SSMethods" :value="method">[[ method_name ]]</a-select-option>
|
||||
<a-select-option v-for="(method, method_name) in SSMethods" :value="method">[[ method_name ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label='UDP over TCP'>
|
||||
<a-switch v-model="outbound.settings.uot"></a-switch>
|
||||
</a-form-item>
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<!-- stream settings -->
|
||||
@@ -365,7 +371,7 @@
|
||||
|
||||
<!-- reality settings -->
|
||||
<template v-if="outbound.stream.isReality">
|
||||
<a-form-item label='{{ i18n "domainName" }}'>
|
||||
<a-form-item label="SNI">
|
||||
<a-input v-model.trim="outbound.stream.reality.serverName"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="uTLS">
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
<span>{{ i18n "reset" }}</span>
|
||||
</template>
|
||||
{{ i18n "password" }}
|
||||
<a-icon @click="client.password = RandomUtil.randomShadowsocksPassword()" type="sync"></a-icon>
|
||||
<a-icon @click="inbound.settings.password = RandomUtil.randomShadowsocksPassword()" type="sync"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input v-model.trim="inbound.settings.password"></a-input>
|
||||
|
||||
@@ -32,10 +32,34 @@
|
||||
<a-icon v-if="inbound.settings.peers.length>1" type="delete" @click="() => inbound.settings.delPeer(index)"
|
||||
style="color: rgb(255, 77, 79);cursor: pointer;"/>
|
||||
</a-divider>
|
||||
<a-form-item label='{{ i18n "pages.xray.wireguard.publicKey" }}'>
|
||||
<a-form-item>
|
||||
<template slot="label">
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
<span>{{ i18n "reset" }}</span>
|
||||
</template>
|
||||
{{ i18n "pages.xray.wireguard.secretKey" }}
|
||||
<a-icon @click="[peer.publicKey, peer.privateKey] = Object.values(Wireguard.generateKeypair())"type="sync"> </a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input v-model.trim="peer.privateKey"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<template slot="label">
|
||||
{{ i18n "pages.xray.wireguard.publicKey" }}
|
||||
</template>
|
||||
<a-input v-model.trim="peer.publicKey"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.xray.wireguard.psk" }}'>
|
||||
<a-form-item>
|
||||
<template slot="label">
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
<span>{{ i18n "reset" }}</span>
|
||||
</template>
|
||||
{{ i18n "pages.xray.wireguard.psk" }}
|
||||
<a-icon @click="peer.psk = Wireguard.keyToBase64(Wireguard.generatePresharedKey())"type="sync"> </a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input v-model.trim="peer.psk"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
|
||||
@@ -4,57 +4,60 @@
|
||||
<a-form-item label="PROXY Protocol" v-if="inbound.canEnableTls()">
|
||||
<a-switch v-model="inbound.stream.tcp.acceptProxyProtocol"></a-switch>
|
||||
</a-form-item>
|
||||
<a-form-item label="HTTP {{ i18n "camouflage" }}">
|
||||
<a-switch
|
||||
:checked="inbound.stream.tcp.type === 'http'"
|
||||
<a-form-item label='HTTP {{ i18n "camouflage" }}'>
|
||||
<a-switch :checked="inbound.stream.tcp.type === 'http'"
|
||||
@change="checked => inbound.stream.tcp.type = checked ? 'http' : 'none'">
|
||||
</a-switch>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<a-form v-if="inbound.stream.tcp.type === 'http'" :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-form v-if="inbound.stream.tcp.type === 'http'" :colon="false" :label-col="{ md: {span:8} }"
|
||||
:wrapper-col="{ md: {span:14} }">
|
||||
<!-- tcp request -->
|
||||
<a-divider style="margin:0;">{{ i18n "pages.inbounds.stream.general.requestHeader" }}</a-divider>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.requestVersion" }}'>
|
||||
<a-divider style="margin:0;">{{ i18n "pages.inbounds.stream.general.request" }}</a-divider>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.version" }}'>
|
||||
<a-input v-model.trim="inbound.stream.tcp.request.version"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.requestMethod" }}'>
|
||||
<a-input v-model.trim="inbound.stream.tcp.request.method"></a-input>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.method" }}'>
|
||||
<a-input v-model.trim="inbound.stream.tcp.request.method"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<template slot="label">{{ i18n "pages.inbounds.stream.tcp.requestPath" }}
|
||||
<template slot="label">{{ i18n "pages.inbounds.stream.tcp.path" }}
|
||||
<a-button size="small" @click="inbound.stream.tcp.request.addPath('/')">+</a-button>
|
||||
</template>
|
||||
<template v-for="(path, index) in inbound.stream.tcp.request.path">
|
||||
<a-input v-model.trim="inbound.stream.tcp.request.path[index]">
|
||||
<a-button size="small" slot="addonAfter"
|
||||
@click="inbound.stream.tcp.request.removePath(index)"
|
||||
v-if="inbound.stream.tcp.request.path.length>1">-</a-button>
|
||||
<a-button size="small" slot="addonAfter" @click="inbound.stream.tcp.request.removePath(index)"
|
||||
v-if="inbound.stream.tcp.request.path.length>1">-</a-button>
|
||||
</a-input>
|
||||
</template>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.general.requestHeader" }}'>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.requestHeader" }}'>
|
||||
<a-button size="small" @click="inbound.stream.tcp.request.addHeader('', '')">+</a-button>
|
||||
</a-form-item>
|
||||
<a-form-item :wrapper-col="{span:24}">
|
||||
<a-input-group compact v-for="(header, index) in inbound.stream.tcp.request.headers">
|
||||
<a-input style="width: 50%" v-model.trim="header.name" placeholder='{{ i18n "pages.inbounds.stream.general.name" }}'>
|
||||
<a-input style="width: 50%" v-model.trim="header.name"
|
||||
placeholder='{{ i18n "pages.inbounds.stream.general.name" }}'>
|
||||
<template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
|
||||
</a-input>
|
||||
<a-input style="width: 50%" v-model.trim="header.value" placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
||||
<a-button slot="addonAfter" size="small" @click="inbound.stream.tcp.request.removeHeader(index)">-</a-button>
|
||||
<a-input style="width: 50%" v-model.trim="header.value"
|
||||
placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
||||
<a-button slot="addonAfter" size="small"
|
||||
@click="inbound.stream.tcp.request.removeHeader(index)">-</a-button>
|
||||
</a-input>
|
||||
</a-input-group>
|
||||
</a-form-item>
|
||||
|
||||
<!-- tcp response -->
|
||||
<a-divider style="margin:0;">{{ i18n "pages.inbounds.stream.tcp.responseHeader" }}</a-divider>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.responseVersion" }}'>
|
||||
<a-divider style="margin:0;">{{ i18n "pages.inbounds.stream.general.response" }}</a-divider>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.version" }}'>
|
||||
<a-input v-model.trim="inbound.stream.tcp.response.version"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.responseStatus" }}'>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.status" }}'>
|
||||
<a-input v-model.trim="inbound.stream.tcp.response.status"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.responseStatusDescription" }}'>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.statusDescription" }}'>
|
||||
<a-input v-model.trim="inbound.stream.tcp.response.reason"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.responseHeader" }}'>
|
||||
@@ -63,11 +66,12 @@
|
||||
</a-form-item>
|
||||
<a-form-item :wrapper-col="{span:24}">
|
||||
<a-input-group compact v-for="(header, index) in inbound.stream.tcp.response.headers">
|
||||
<a-input style="width: 50%" v-model.trim="header.name" placeholder='{{ i18n "pages.inbounds.stream.general.name" }}'>
|
||||
<a-input style="width: 50%" v-model.trim="header.name"
|
||||
placeholder='{{ i18n "pages.inbounds.stream.general.name" }}'>
|
||||
<template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
|
||||
</a-input>
|
||||
<a-input style="width: 50%" v-model.trim="header.value"
|
||||
placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
||||
placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
||||
<template slot="addonAfter">
|
||||
<a-button size="small" @click="inbound.stream.tcp.response.removeHeader(index)">-</a-button>
|
||||
</template>
|
||||
@@ -75,4 +79,4 @@
|
||||
</a-input-group>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
{{end}}
|
||||
{{end}}
|
||||
@@ -6,7 +6,7 @@
|
||||
<a-form-item label='{{ i18n "path" }}'>
|
||||
<a-input v-model.trim="inbound.stream.ws.path"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.general.requestHeader" }}'>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.requestHeader" }}'>
|
||||
<a-button size="small" @click="inbound.stream.ws.addHeader()">+</a-button>
|
||||
</a-form-item>
|
||||
<a-form-item :wrapper-col="{span:24}">
|
||||
|
||||
@@ -279,24 +279,50 @@
|
||||
</tr>
|
||||
<template v-for="(peer, index) in inbound.settings.peers">
|
||||
<tr>
|
||||
<td colspan="2"><a-tag>Peer [[ index + 1 ]]</a-tag></td>
|
||||
<td colspan="2"><a-divider>Peer [[ index + 1 ]]</a-divider></td>
|
||||
</tr>
|
||||
<tr class="client-table-odd-row">
|
||||
<td>{{ i18n "pages.xray.wireguard.secretKey" }}</td>
|
||||
<td>[[ peer.privateKey ]]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ i18n "pages.xray.wireguard.publicKey" }}</td>
|
||||
<td>[[ peer.publicKey ]]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<tr class="client-table-odd-row">
|
||||
<td>{{ i18n "pages.xray.wireguard.psk" }}</td>
|
||||
<td>[[ peer.psk ]]</td>
|
||||
</tr>
|
||||
<tr class="client-table-odd-row">
|
||||
<tr>
|
||||
<td>{{ i18n "pages.xray.wireguard.allowedIPs" }}</td>
|
||||
<td>[[ peer.allowedIPs.join(",") ]]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<tr class="client-table-odd-row">
|
||||
<td>Keep Alive</td>
|
||||
<td>[[ peer.keepAlive ]]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<a-row>
|
||||
<a-col :span="22" style="overflow-wrap: anywhere;">
|
||||
<a-tag color="blue">Config</a-tag>
|
||||
<div
|
||||
v-html="infoModal.links[index].replaceAll(`\n`,`<br />`)"
|
||||
style="border-radius: 1rem; padding: 0.5rem;"
|
||||
class="client-table-odd-row"></div>
|
||||
</a-col>
|
||||
<a-col :span="2" style="text-align: right;">
|
||||
<a-tooltip title='{{ i18n "copy" }}'>
|
||||
<button class="ant-btn ant-btn-primary"
|
||||
:id="'copy-url-link-'+index"
|
||||
@click="copyToClipboard('copy-url-link-'+index, infoModal.links[index])">
|
||||
<a-icon type="snippets"></a-icon>
|
||||
</button>
|
||||
</a-tooltip>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</template>
|
||||
</template>
|
||||
@@ -323,7 +349,11 @@
|
||||
this.clientSettings = this.inbound.clients ? this.inbound.clients[index] : null;
|
||||
this.isExpired = this.inbound.clients ? this.inbound.isExpiry(index): this.dbInbound.isExpiry;
|
||||
this.clientStats = this.inbound.clients ? this.dbInbound.clientStats.find(row => row.email === this.clientSettings.email) : [];
|
||||
this.links = this.inbound.genAllLinks(this.dbInbound.remark, app.remarkModel, this.clientSettings);
|
||||
if (this.inbound.protocol == Protocols.WIREGUARD){
|
||||
this.links = this.inbound.genInboundLinks(dbInbound.remark).split('\r\n')
|
||||
} else {
|
||||
this.links = this.inbound.genAllLinks(this.dbInbound.remark, app.remarkModel, this.clientSettings);
|
||||
}
|
||||
if (this.clientSettings) {
|
||||
if (this.clientSettings.subId) {
|
||||
this.subLink = this.genSubLink(this.clientSettings.subId);
|
||||
|
||||
@@ -68,15 +68,15 @@
|
||||
<a-card hoverable>
|
||||
<a-row>
|
||||
<a-col :xs="24" :sm="24" :lg="12">
|
||||
{{ i18n "pages.inbounds.totalDownUp" }}:
|
||||
<strong>{{ i18n "pages.inbounds.totalDownUp" }}:</strong>
|
||||
<a-tag color="blue">[[ sizeFormat(total.up) ]] / [[ sizeFormat(total.down) ]]</a-tag>
|
||||
</a-col>
|
||||
<a-col :xs="24" :sm="24" :lg="12">
|
||||
{{ i18n "pages.inbounds.totalUsage" }}:
|
||||
<strong>{{ i18n "pages.inbounds.totalUsage" }}:</strong>
|
||||
<a-tag color="blue">[[ sizeFormat(total.up + total.down) ]]</a-tag>
|
||||
</a-col>
|
||||
<a-col :xs="24" :sm="24" :lg="12">
|
||||
{{ i18n "pages.inbounds.inboundCount" }}:
|
||||
<strong>{{ i18n "pages.inbounds.inboundCount" }}:</strong>
|
||||
<a-tag color="blue">[[ dbInbounds.length ]]</a-tag>
|
||||
</a-col>
|
||||
<a-col :xs="24" :sm="24" :lg="12">
|
||||
@@ -84,7 +84,7 @@
|
||||
<div>
|
||||
<a-back-top :target="() => document.getElementById('content-layout')" visibility-height="200">
|
||||
</a-back-top>
|
||||
{{ i18n "clients" }}:
|
||||
<strong>{{ i18n "clients" }}:</strong>
|
||||
<a-tag color="blue">[[ total.clients ]]</a-tag>
|
||||
<a-popover title='{{ i18n "disabled" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
||||
<template slot="content">
|
||||
@@ -137,6 +137,10 @@
|
||||
<a-icon type="export"></a-icon>
|
||||
{{ i18n "pages.inbounds.export" }}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="subs">
|
||||
<a-icon type="export"></a-icon>
|
||||
{{ i18n "pages.inbounds.export" }} - {{ i18n "pages.settings.subSettings" }}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="resetInbounds">
|
||||
<a-icon type="reload"></a-icon>
|
||||
{{ i18n "pages.inbounds.resetAllTraffic" }}
|
||||
@@ -145,7 +149,7 @@
|
||||
<a-icon type="file-done"></a-icon>
|
||||
{{ i18n "pages.inbounds.resetAllClientTraffics" }}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="delDepletedClients">
|
||||
<a-menu-item key="delDepletedClients" style="color: #FF4D4F;">
|
||||
<a-icon type="rest"></a-icon>
|
||||
{{ i18n "pages.inbounds.delDepletedClients" }}
|
||||
</a-menu-item>
|
||||
@@ -199,7 +203,7 @@
|
||||
<a-icon type="edit"></a-icon>
|
||||
{{ i18n "edit" }}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="qrcode" v-if="dbInbound.isSS && !dbInbound.toInbound().isSSMultiUser">
|
||||
<a-menu-item key="qrcode" v-if="(dbInbound.isSS && !dbInbound.toInbound().isSSMultiUser) || dbInbound.isWireguard">
|
||||
<a-icon type="qrcode"></a-icon>
|
||||
{{ i18n "qrCode" }}
|
||||
</a-menu-item>
|
||||
@@ -220,7 +224,11 @@
|
||||
<a-icon type="export"></a-icon>
|
||||
{{ i18n "pages.inbounds.export"}}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="delDepletedClients">
|
||||
<a-menu-item key="subs">
|
||||
<a-icon type="export"></a-icon>
|
||||
{{ i18n "pages.inbounds.export"}} - {{ i18n "pages.settings.subSettings" }}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="delDepletedClients" style="color: #FF4D4F;">
|
||||
<a-icon type="rest"></a-icon>
|
||||
{{ i18n "pages.inbounds.delDepletedClients" }}
|
||||
</a-menu-item>
|
||||
@@ -236,7 +244,7 @@
|
||||
</a-menu-item>
|
||||
<a-menu-item key="clipboard">
|
||||
<a-icon type="copy"></a-icon>
|
||||
{{ i18n "pages.inbounds.copyToClipboard" }}
|
||||
{{ i18n "pages.inbounds.exportInbound" }}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="clone">
|
||||
<a-icon type="block"></a-icon> {{ i18n "pages.inbounds.clone"}}
|
||||
@@ -247,7 +255,7 @@
|
||||
</span>
|
||||
</a-menu-item>
|
||||
<a-menu-item v-if="isMobile">
|
||||
<a-switch size="small" v-model="dbInbound.enable" @change="switchEnable(dbInbound.id)"></a-switch>
|
||||
<a-switch size="small" v-model="dbInbound.enable" @change="switchEnable(dbInbound.id,dbInbound.enable)"></a-switch>
|
||||
{{ i18n "pages.inbounds.enable" }}
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
@@ -314,7 +322,7 @@
|
||||
</a-popover>
|
||||
</template>
|
||||
<template slot="enable" slot-scope="text, dbInbound">
|
||||
<a-switch v-model="dbInbound.enable" @change="switchEnable(dbInbound.id)"></a-switch>
|
||||
<a-switch v-model="dbInbound.enable" @change="switchEnable(dbInbound.id,dbInbound.enable)"></a-switch>
|
||||
</template>
|
||||
<template slot="expiryTime" slot-scope="text, dbInbound">
|
||||
<a-popover v-if="dbInbound.expiryTime > 0" :overlay-class-name="themeSwitcher.currentTheme">
|
||||
@@ -727,6 +735,9 @@
|
||||
case "export":
|
||||
this.exportAllLinks();
|
||||
break;
|
||||
case "subs":
|
||||
this.exportAllSubs();
|
||||
break;
|
||||
case "resetInbounds":
|
||||
this.resetAllTraffic();
|
||||
break;
|
||||
@@ -758,6 +769,9 @@
|
||||
case "export":
|
||||
this.inboundLinks(dbInbound.id);
|
||||
break;
|
||||
case "subs":
|
||||
this.exportSubs(dbInbound.id);
|
||||
break;
|
||||
case "clipboard":
|
||||
this.copyToClipboard(dbInbound.id);
|
||||
break;
|
||||
@@ -1030,9 +1044,9 @@
|
||||
newDbInbound = this.checkFallback(dbInbound);
|
||||
infoModal.show(newDbInbound, index);
|
||||
},
|
||||
switchEnable(dbInboundId) {
|
||||
switchEnable(dbInboundId,state) {
|
||||
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
||||
dbInbound.enable = !dbInbound.enable;
|
||||
dbInbound.enable = state;
|
||||
this.submit(`/xui/inbound/update/${dbInboundId}`, dbInbound);
|
||||
},
|
||||
async switchEnableClient(dbInboundId, client) {
|
||||
@@ -1182,6 +1196,22 @@
|
||||
newDbInbound = this.checkFallback(dbInbound);
|
||||
txtModal.show('{{ i18n "pages.inbounds.export"}}', newDbInbound.genInboundLinks(this.remarkModel), newDbInbound.remark);
|
||||
},
|
||||
exportSubs(dbInboundId) {
|
||||
const dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
||||
const clients = this.getInboundClients(dbInbound);
|
||||
let subLinks = []
|
||||
if (clients != null){
|
||||
clients.forEach(c => {
|
||||
if (c.subId && c.subId.length>0){
|
||||
subLinks.push(this.subSettings.subURI + c.subId + "?name=" + c.subId)
|
||||
}
|
||||
})
|
||||
}
|
||||
txtModal.show(
|
||||
'{{ i18n "pages.inbounds.export"}} - {{ i18n "pages.settings.subSettings" }}',
|
||||
[...new Set(subLinks)].join('\n'),
|
||||
dbInbound.remark + "-Subs");
|
||||
},
|
||||
importInbound() {
|
||||
promptModal.open({
|
||||
title: '{{ i18n "pages.inbounds.importInbound" }}',
|
||||
@@ -1194,7 +1224,24 @@
|
||||
},
|
||||
});
|
||||
},
|
||||
exportAllLinks() {
|
||||
exportAllSubs() {
|
||||
let subLinks = []
|
||||
for (const dbInbound of this.dbInbounds) {
|
||||
const clients = this.getInboundClients(dbInbound);
|
||||
if (clients != null){
|
||||
clients.forEach(c => {
|
||||
if (c.subId && c.subId.length>0){
|
||||
subLinks.push(this.subSettings.subURI + c.subId + "?name=" + c.subId)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
txtModal.show(
|
||||
'{{ i18n "pages.inbounds.export"}} - {{ i18n "pages.settings.subSettings" }}',
|
||||
[...new Set(subLinks)].join('\r\n'),
|
||||
'All-Inbounds-Subs');
|
||||
},
|
||||
exportAllLinks() {
|
||||
let copyText = [];
|
||||
for (const dbInbound of this.dbInbounds) {
|
||||
copyText.push(dbInbound.genInboundLinks(this.remarkModel));
|
||||
|
||||
@@ -44,14 +44,14 @@
|
||||
<a-progress type="dashboard" status="normal"
|
||||
:stroke-color="status.cpu.color"
|
||||
:percent="status.cpu.percent"></a-progress>
|
||||
<div>CPU: [[ cpuCoreFormat(status.cpuCount) ]]</div>
|
||||
<div><strong>CPU:</strong> [[ cpuCoreFormat(status.cpuCount) ]]</div>
|
||||
</a-col>
|
||||
<a-col :span="12" style="text-align: center">
|
||||
<a-progress type="dashboard" status="normal"
|
||||
:stroke-color="status.mem.color"
|
||||
:percent="status.mem.percent"></a-progress>
|
||||
<div>
|
||||
{{ i18n "pages.index.memory"}}: [[ sizeFormat(status.mem.current) ]] / [[ sizeFormat(status.mem.total) ]]
|
||||
<strong>{{ i18n "pages.index.memory"}}:</strong> [[ sizeFormat(status.mem.current) ]] / [[ sizeFormat(status.mem.total) ]]
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
@@ -63,7 +63,7 @@
|
||||
:stroke-color="status.swap.color"
|
||||
:percent="status.swap.percent"></a-progress>
|
||||
<div>
|
||||
Swap: [[ sizeFormat(status.swap.current) ]] / [[ sizeFormat(status.swap.total) ]]
|
||||
<strong>Swap:</strong> [[ sizeFormat(status.swap.current) ]] / [[ sizeFormat(status.swap.total) ]]
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="12" style="text-align: center">
|
||||
@@ -71,7 +71,7 @@
|
||||
:stroke-color="status.disk.color"
|
||||
:percent="status.disk.percent"></a-progress>
|
||||
<div>
|
||||
{{ i18n "pages.index.hard"}}: [[ sizeFormat(status.disk.current) ]] / [[ sizeFormat(status.disk.total) ]]
|
||||
<strong>{{ i18n "pages.index.hard"}}:</strong> [[ sizeFormat(status.disk.current) ]] / [[ sizeFormat(status.disk.total) ]]
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
@@ -84,32 +84,45 @@
|
||||
<a-row>
|
||||
<a-col :sm="24" :md="12">
|
||||
<a-card hoverable>
|
||||
X-UI <a href="https://github.com/alireza0/x-ui/releases" target="_blank"><a-tag color="blue">{{ .cur_ver }}</a-tag></a>
|
||||
Xray
|
||||
<a-tooltip title='{{ i18n "pages.index.xraySwitch" }}'>
|
||||
<a-tag color="blue" style="cursor: pointer;" @click="openSelectV2rayVersion">[[ status.xray.version ]]</a-tag>
|
||||
<strong>{{ i18n "pages.index.machineInfo" }}:</strong>
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
{{ i18n "pages.index.hostname" }}
|
||||
</template>
|
||||
<a-tag color="blue" style="margin-right: 3px;">[[ status.hostInfo.hostname ]]</a-tag>
|
||||
</a-tooltip>
|
||||
<template v-if="status.hostInfo.ipv4">
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
IPv4:<br>[[ status.hostInfo.ipv4 ]]<br>IPv6:<br>[[ status.hostInfo.ipv6 ]]
|
||||
</template>
|
||||
<a-tag color="blue" style="margin-right: 3px;">IPv4/v6</a-tag>
|
||||
</a-tooltip>
|
||||
<a href="https://github.com/alireza0/x-ui/releases" target="_blank"><a-tag color="purple">X-UI {{ .cur_ver }}</a-tag></a>
|
||||
</template>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :sm="24" :md="12">
|
||||
<a-card hoverable>
|
||||
{{ i18n "pages.index.operationHours" }}:
|
||||
Xray
|
||||
<a-tag color="blue">[[ formatSecond(status.appStats.uptime) ]]</a-tag>
|
||||
OS
|
||||
<strong>{{ i18n "pages.index.operationHours" }}:</strong>
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
{{ i18n "pages.index.xrayoperationHoursDesc" }}
|
||||
</template>
|
||||
<a-tag color="blue" style="margin-right: 3px;">Xray [[ formatSecond(status.appStats.uptime) ]]</a-tag>
|
||||
</a-tooltip>
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
{{ i18n "pages.index.operationHoursDesc" }}
|
||||
</template>
|
||||
<a-icon type="question-circle"></a-icon>
|
||||
<a-tag color="blue">OS [[ formatSecond(status.uptime) ]]</a-tag>
|
||||
</a-tooltip>
|
||||
<a-tag color="blue">[[ formatSecond(status.uptime) ]]</a-tag>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :sm="24" :md="12">
|
||||
<a-card hoverable>
|
||||
{{ i18n "pages.index.xrayStatus" }}:
|
||||
<a-tag :color="status.xray.color">[[ status.xray.state ]]</a-tag>
|
||||
<strong>{{ i18n "pages.index.xrayStatus" }}:</strong>
|
||||
<a-tag :color="status.xray.color" style="margin-right: 3px;"><strong>[[ status.xray.state ]]</strong></a-tag>
|
||||
<a-popover v-if="status.xray.state === State.Error"
|
||||
:overlay-class-name="themeSwitcher.currentTheme">
|
||||
<span slot="title" style="font-size: 12pt">An error occurred while running Xray
|
||||
@@ -118,73 +131,64 @@
|
||||
<template slot="content">
|
||||
<p style="max-width: 400px" v-for="line in status.xray.errorMsg.split('\n')">[[ line ]]</p>
|
||||
</template>
|
||||
<a-icon type="question-circle"></a-icon>
|
||||
<a-icon type="exclamation-circle"></a-icon>
|
||||
</a-popover>
|
||||
<a-tag color="purple" style="cursor: pointer;" @click="stopXrayService">{{ i18n "pages.index.stopXray" }}</a-tag>
|
||||
<a-tag color="purple" style="cursor: pointer;" @click="restartXrayService">{{ i18n "pages.index.restartXray" }}</a-tag>
|
||||
<a-tag color="purple" style="cursor: pointer; margin-right: 3px;" @click="stopXrayService">{{ i18n "pages.index.stopXray" }}</a-tag>
|
||||
<a-tag color="purple" style="cursor: pointer; margin-right: 3px;" @click="restartXrayService">{{ i18n "pages.index.restartXray" }}</a-tag>
|
||||
<a-tooltip title='{{ i18n "pages.index.xraySwitch" }}'>
|
||||
<a-tag color="purple" style="cursor: pointer;" @click="openSelectV2rayVersion">[[ status.xray.version ]]</a-tag>
|
||||
</a-tooltip>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :sm="24" :md="12">
|
||||
<a-card hoverable>
|
||||
{{ i18n "menu.link" }}:
|
||||
<a-tag color="purple" style="cursor: pointer;" @click="openLogs()">{{ i18n "pages.index.logs" }}</a-tag>
|
||||
<a-tag color="purple" style="cursor: pointer;" @click="openConfig">{{ i18n "pages.index.config" }}</a-tag>
|
||||
<strong>{{ i18n "menu.link" }}:</strong>
|
||||
<a-tag color="purple" style="cursor: pointer; margin-right: 3px;" @click="openLogs()">{{ i18n "pages.index.logs" }}</a-tag>
|
||||
<a-tag color="purple" style="cursor: pointer; margin-right: 3px;" @click="openConfig">{{ i18n "pages.index.config" }}</a-tag>
|
||||
<a-tag color="purple" style="cursor: pointer;" @click="openBackup">{{ i18n "pages.index.backup" }}</a-tag>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :sm="24" :md="12">
|
||||
<a-card hoverable>
|
||||
{{ i18n "pages.index.systemLoad" }}: [[ status.loads[0] ]] | [[ status.loads[1] ]] | [[ status.loads[2] ]]
|
||||
<strong>{{ i18n "pages.index.systemLoad" }}:</strong>
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
{{ i18n "pages.index.systemLoadDesc" }}
|
||||
</template>
|
||||
<a-tag color="blue">[[ status.loads[0] ]] | [[ status.loads[1] ]] | [[ status.loads[2] ]]</a-tag>
|
||||
</a-tooltip>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :sm="24" :md="12">
|
||||
<a-card hoverable>
|
||||
{{ i18n "usage"}}:
|
||||
RAM: [[ sizeFormat(status.appStats.mem) ]] -
|
||||
Threads: [[ status.appStats.threads ]]
|
||||
</a-tooltip>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :sm="24" :md="12">
|
||||
<a-card hoverable>
|
||||
Host: [[ status.hostInfo.hostname ]] -
|
||||
<template v-if="status.hostInfo.ipv4">IPv4:
|
||||
<strong>{{ i18n "usage" }}:</strong>
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
[[ status.hostInfo.ipv4 ]]
|
||||
</template>
|
||||
<a-icon type="question-circle"></a-icon>
|
||||
<a-tag color="blue" style="margin-right: 3px;">RAM [[ sizeFormat(status.appStats.mem) ]]</a-tag>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-if="status.hostInfo.ipv6">IPv6:
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
[[ status.hostInfo.ipv6 ]]
|
||||
</template>
|
||||
<a-icon type="question-circle"></a-icon>
|
||||
<a-tag color="blue">Threads [[ status.appStats.threads ]]</a-tag>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :sm="24" :md="12">
|
||||
<a-card hoverable>
|
||||
<a-row>
|
||||
<a-col :span="12">
|
||||
TCP: [[ status.tcpCount ]]
|
||||
<a-icon type="swap"></a-icon>
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
{{ i18n "pages.index.connectionTcpCountDesc" }}
|
||||
</template>
|
||||
<a-icon type="question-circle"></a-icon>
|
||||
<strong>TCP:</Strong> [[ status.tcpCount ]]
|
||||
</a-tooltip>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
UDP: [[ status.udpCount ]]
|
||||
<a-icon type="swap"></a-icon>
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
{{ i18n "pages.index.connectionUdpCountDesc" }}
|
||||
</template>
|
||||
<a-icon type="question-circle"></a-icon>
|
||||
<strong>UDP:</strong> [[ status.udpCount ]]
|
||||
</a-tooltip>
|
||||
</a-col>
|
||||
</a-row>
|
||||
@@ -195,22 +199,20 @@
|
||||
<a-row>
|
||||
<a-col :span="12">
|
||||
<a-icon type="arrow-up"></a-icon>
|
||||
[[ sizeFormat(status.netIO.up) ]]/s
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
{{ i18n "pages.index.upSpeed" }}
|
||||
</template>
|
||||
<a-icon type="question-circle"></a-icon>
|
||||
<strong>UL:</strong> [[ sizeFormat(status.netIO.up) ]]/s
|
||||
</a-tooltip>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-icon type="arrow-down"></a-icon>
|
||||
[[ sizeFormat(status.netIO.down) ]]/s
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
{{ i18n "pages.index.downSpeed" }}
|
||||
</template>
|
||||
<a-icon type="question-circle"></a-icon>
|
||||
<strong>DL:</strong> [[ sizeFormat(status.netIO.down) ]]/s
|
||||
</a-tooltip>
|
||||
</a-col>
|
||||
</a-row>
|
||||
@@ -221,22 +223,20 @@
|
||||
<a-row>
|
||||
<a-col :span="12">
|
||||
<a-icon type="cloud-upload"></a-icon>
|
||||
[[ sizeFormat(status.netTraffic.sent) ]]
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
{{ i18n "pages.index.totalSent" }}
|
||||
</template>
|
||||
<a-icon type="question-circle"></a-icon>
|
||||
<strong>Out:</strong> [[ sizeFormat(status.netTraffic.sent) ]]
|
||||
</a-tooltip>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-icon type="cloud-download"></a-icon>
|
||||
[[ sizeFormat(status.netTraffic.recv) ]]
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
{{ i18n "pages.index.totalReceive" }}
|
||||
</template>
|
||||
<a-icon type="question-circle"></a-icon>
|
||||
<strong>In:</strong> [[ sizeFormat(status.netTraffic.recv) ]]
|
||||
</a-tooltip>
|
||||
</a-col>
|
||||
</a-row>
|
||||
@@ -302,7 +302,7 @@
|
||||
</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<div class="ant-input" style="height: auto; max-height: 500px; overflow: auto;" v-html="logModal.logs"></div>
|
||||
<div class="ant-input" style="height: auto; max-height: 500px; overflow: auto;" v-html="logModal.formattedLogs"></div>
|
||||
</a-modal>
|
||||
|
||||
<a-modal id="backup-modal" v-model="backupModal.visible" :title="backupModal.title"
|
||||
@@ -330,9 +330,9 @@
|
||||
{{template "textModal"}}
|
||||
<script>
|
||||
const State = {
|
||||
Running: "running",
|
||||
Stop: "stop",
|
||||
Error: "error",
|
||||
Running: "Running",
|
||||
Stop: "Stop",
|
||||
Error: "Error",
|
||||
}
|
||||
Object.freeze(State);
|
||||
|
||||
@@ -400,7 +400,7 @@
|
||||
this.xray = data.xray;
|
||||
switch (this.xray.state) {
|
||||
case State.Running:
|
||||
this.xray.color = "blue";
|
||||
this.xray.color = '#3dbd7d';
|
||||
break;
|
||||
case State.Stop:
|
||||
this.xray.color = "orange";
|
||||
@@ -435,7 +435,8 @@
|
||||
loading: false,
|
||||
show(logs) {
|
||||
this.visible = true;
|
||||
this.logs = logs? this.formatLogs(logs) : "No Record...";
|
||||
this.logs = logs;
|
||||
this.formattedLogs = this.logs.length > 0 ? this.formatLogs(this.logs) : "No Record...";
|
||||
},
|
||||
formatLogs(logs) {
|
||||
let formattedLogs = '';
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
}
|
||||
|
||||
.ant-tabs-bar {
|
||||
font-weight: bold;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@@ -322,4 +323,4 @@
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<td>[[ warpModal.warpData.access_token ]]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Devide ID</td>
|
||||
<td>Device ID</td>
|
||||
<td>[[ warpModal.warpData.device_id ]]</td>
|
||||
</tr>
|
||||
<tr class="client-table-odd-row">
|
||||
@@ -24,19 +24,19 @@
|
||||
<td>[[ warpModal.warpData.private_key ]]</td>
|
||||
</tr>
|
||||
</table>
|
||||
<a-divider style="margin: 0;">{{ i18n "pages.settings.toasts.modifySettings" }}</a-divider>
|
||||
<a-divider style="margin: 0;">{{ i18n "pages.xray.outbound.settings" }}</a-divider>
|
||||
<a-collapse style="margin: 10px 0;">
|
||||
<a-collapse-panel header='WARP/WARP+ License Key'>
|
||||
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-form-item label="License Key">
|
||||
<a-form-item label="Key">
|
||||
<a-input v-model="warpPlus"></a-input>
|
||||
<a-button @click="updateLicense(warpPlus)" :disabled="warpPlus.length<26" :loading="warpModal.confirmLoading">{{ i18n "pages.inbounds.update" }}</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-collapse-panel>
|
||||
</a-collapse>
|
||||
<a-divider style="margin: 0;">{{ i18n "pages.settings.toasts.getSettings" }}</a-divider>
|
||||
<a-button icon="sync" @click="getConfig" style="margin-bottom: 10px;" :loading="warpModal.confirmLoading">{{ i18n "info" }}</a-button>
|
||||
<a-divider style="margin: 0;">{{ i18n "pages.xray.outbound.accountInfo" }}</a-divider>
|
||||
<a-button icon="sync" @click="getConfig" style="margin-top: 5px; margin-bottom: 10px;" :loading="warpModal.confirmLoading" type="primary">{{ i18n "info" }}</a-button>
|
||||
<template v-if="!ObjectUtil.isEmpty(warpModal.warpConfig)">
|
||||
<table style="width: 100%">
|
||||
<tr class="client-table-odd-row">
|
||||
@@ -48,7 +48,7 @@
|
||||
<td>[[ warpModal.warpConfig.model ]]</td>
|
||||
</tr>
|
||||
<tr class="client-table-odd-row">
|
||||
<td>Device Active</td>
|
||||
<td>Device Enabled</td>
|
||||
<td>[[ warpModal.warpConfig.enabled ]]</td>
|
||||
</tr>
|
||||
<template v-if="!ObjectUtil.isEmpty(warpModal.warpConfig.account)">
|
||||
@@ -61,7 +61,7 @@
|
||||
<td>[[ warpModal.warpConfig.account.role ]]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Premium Data</td>
|
||||
<td>WARP+ Data</td>
|
||||
<td>[[ sizeFormat(warpModal.warpConfig.account.premium_data) ]]</td>
|
||||
</tr>
|
||||
<tr class="client-table-odd-row">
|
||||
@@ -74,16 +74,15 @@
|
||||
</tr>
|
||||
</template>
|
||||
</table>
|
||||
<a-divider style="margin: 10px 0;">WARP {{ i18n "pages.xray.rules.outbound" }}</a-divider>
|
||||
<a-divider style="margin: 10px 0;">{{ i18n "pages.xray.outbound.outboundStatus" }}</a-divider>
|
||||
<a-form :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-form-item label="{{ i18n "status" }}">
|
||||
<template v-if="warpOutboundIndex>=0">
|
||||
<a-tag color="green">{{ i18n "enabled" }}</a-tag>
|
||||
<a-button @click="resetOutbound" :loading="warpModal.confirmLoading">{{ i18n "reset" }}</a-button>
|
||||
<a-tag color="green" style="line-height: 31px;">{{ i18n "enabled" }}</a-tag>
|
||||
<a-button @click="resetOutbound" :loading="warpModal.confirmLoading" type="danger">{{ i18n "reset" }}</a-button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-tag color="orange">{{ i18n "disabled" }}</a-tag>
|
||||
<a-button @click="addOutbound" :loading="warpModal.confirmLoading">{{ i18n "pages.xray.outbound.addOutbound" }}</a-button>
|
||||
<a-tag color="orange" style="line-height: 31px;">{{ i18n "disabled" }}</a-tag>
|
||||
<a-button @click="addOutbound" :loading="warpModal.confirmLoading" type="primary">{{ i18n "pages.xray.outbound.addOutbound" }}</a-button>
|
||||
</template>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
@@ -140,6 +139,7 @@
|
||||
mtu: 1420,
|
||||
secretKey: warpModal.warpData.private_key,
|
||||
address: Object.values(config.interface.addresses),
|
||||
domainStrategy: 'ForceIP',
|
||||
peers: [{
|
||||
publicKey: peer.public_key,
|
||||
endpoint: peer.endpoint.host,
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
}
|
||||
|
||||
.ant-tabs-bar {
|
||||
font-weight: bold;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@@ -151,6 +152,49 @@
|
||||
</a-row>
|
||||
</a-list-item>
|
||||
</a-collapse-panel>
|
||||
<a-collapse-panel header='{{ i18n "pages.xray.logConfigs" }}'>
|
||||
<a-row :xs="24" :sm="24" :lg="12">
|
||||
<a-alert type="warning" style="text-align: center;">
|
||||
<template slot="message">
|
||||
<a-icon type="exclamation-circle" theme="filled" style="color: #FFA031"></a-icon>
|
||||
{{ i18n "pages.xray.logConfigsDesc" }}
|
||||
</template>
|
||||
</a-alert>
|
||||
</a-row>
|
||||
<a-list-item>
|
||||
<a-row style="padding: 20px">
|
||||
<a-col :lg="24" :xl="12">
|
||||
<a-list-item-meta title='Level'/>
|
||||
</a-col>
|
||||
<a-col :lg="24" :xl="12">
|
||||
<template>
|
||||
<a-select
|
||||
v-model="logLevel"
|
||||
style="width: 100%" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="level in ['none', 'debug', 'info', 'warning', 'error']" :value="level">[[ level ]]</a-select-option>
|
||||
</a-select>
|
||||
</template>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-list-item>
|
||||
<a-row style="padding: 20px">
|
||||
<a-col :lg="24" :xl="12">
|
||||
<a-list-item-meta title='Access Logs' />
|
||||
</a-col>
|
||||
<a-col :lg="24" :xl="12">
|
||||
<a-input v-model="logAccess"></a-input>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row style="padding: 20px">
|
||||
<a-col :lg="24" :xl="12">
|
||||
<a-list-item-meta title='Error Logs' />
|
||||
</a-col>
|
||||
<a-col :lg="24" :xl="12">
|
||||
<a-input v-model="logError"></a-input>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-list-item>
|
||||
</a-collapse-panel>
|
||||
<a-collapse-panel header='{{ i18n "pages.xray.blockConfigs"}}'>
|
||||
<a-row :xs="24" :sm="24" :lg="12">
|
||||
<a-alert type="warning" style="text-align: center;">
|
||||
@@ -223,6 +267,7 @@
|
||||
<setting-list-item type="switch" title='{{ i18n "pages.xray.OpenAIWARP"}}' desc='{{ i18n "pages.xray.OpenAIWARPDesc"}}' v-model="OpenAIWARPSettings"></setting-list-item>
|
||||
<setting-list-item type="switch" title='{{ i18n "pages.xray.NetflixWARP"}}' desc='{{ i18n "pages.xray.NetflixWARPDesc"}}' v-model="NetflixWARPSettings"></setting-list-item>
|
||||
<setting-list-item type="switch" title='{{ i18n "pages.xray.SpotifyWARP"}}' desc='{{ i18n "pages.xray.SpotifyWARPDesc"}}' v-model="SpotifyWARPSettings"></setting-list-item>
|
||||
<setting-list-item type="switch" title='{{ i18n "pages.xray.MetaWARP"}}' desc='{{ i18n "pages.xray.MetaWARPDesc"}}' v-model="MetaWARPSettings"></setting-list-item>
|
||||
</template>
|
||||
<a-button v-else style="margin: 10px 0;" @click="showWarp">WARP {{ i18n "pages.xray.rules.outbound" }}</a-button>
|
||||
</a-collapse-panel>
|
||||
@@ -356,6 +401,10 @@
|
||||
<a-dropdown :trigger="['click']">
|
||||
<a-icon @click="e => e.preventDefault()" type="more" style="font-size: 16px; text-decoration: bold;"></a-icon>
|
||||
<a-menu slot="overlay" :theme="themeSwitcher.currentTheme">
|
||||
<a-menu-item v-if="index>0" @click="setFirstOutbound(index)">
|
||||
<a-icon type="vertical-align-top"></a-icon>
|
||||
{{ i18n "pages.xray.rules.first"}}
|
||||
</a-menu-item>
|
||||
<a-menu-item @click="editOutbound(index)">
|
||||
<a-icon type="edit"></a-icon>
|
||||
{{ i18n "edit" }}
|
||||
@@ -542,6 +591,7 @@
|
||||
google: ["geosite:google"],
|
||||
spotify: ["geosite:spotify"],
|
||||
netflix: ["geosite:netflix"],
|
||||
meta: ["geosite:meta"],
|
||||
cn: [
|
||||
"geosite:cn",
|
||||
"regexp:.*\\.cn$"
|
||||
@@ -553,17 +603,17 @@
|
||||
ir: [
|
||||
"regexp:.*\\.ir$",
|
||||
"regexp:.*\\.xn--mgba3a4f16a$", // .ایران
|
||||
"ext:geosite_IR.dat:ir" // have rules to bypass all .ir domains.
|
||||
"ext:geosite_IR.dat:ir"
|
||||
]
|
||||
},
|
||||
familyProtectDNS: {
|
||||
"servers": [
|
||||
"1.1.1.3",
|
||||
"1.1.1.3", // https://developers.cloudflare.com/1.1.1.1/setup/
|
||||
"1.0.0.3",
|
||||
"94.140.14.15",
|
||||
"94.140.15.16"
|
||||
"2606:4700:4700::1113",
|
||||
"2606:4700:4700::1003"
|
||||
],
|
||||
"queryStrategy": "UseIPv4"
|
||||
"queryStrategy": "UseIP"
|
||||
},
|
||||
}
|
||||
},
|
||||
@@ -764,6 +814,11 @@
|
||||
outbounds.splice(index,1);
|
||||
this.outboundSettings = JSON.stringify(outbounds);
|
||||
},
|
||||
setFirstOutbound(index){
|
||||
outbounds = this.templateSettings.outbounds;
|
||||
outbounds.splice(0, 0, outbounds.splice(index, 1)[0]);
|
||||
this.outboundSettings = JSON.stringify(outbounds);
|
||||
},
|
||||
addReverse(){
|
||||
reverseModal.show({
|
||||
title: '{{ i18n "pages.xray.outbound.addReverse"}}',
|
||||
@@ -910,6 +965,38 @@
|
||||
get: function () { return this.xraySetting ? JSON.parse(this.xraySetting) : null; },
|
||||
set: function (newValue) { this.xraySetting = JSON.stringify(newValue, null, 2); },
|
||||
},
|
||||
logSettings: {
|
||||
get: function () { return this.templateSettings ? this.templateSettings.log : {}; },
|
||||
set: function (newValue) {
|
||||
newTemplateSettings = this.templateSettings;
|
||||
newTemplateSettings.log = newValue;
|
||||
this.templateSettings = newTemplateSettings;
|
||||
},
|
||||
},
|
||||
logLevel: {
|
||||
get: function () { return this.logSettings?.loglevel?? 'none'; },
|
||||
set: function (newValue) {
|
||||
newLogSettings = this.logSettings;
|
||||
newLogSettings.loglevel = newValue;
|
||||
this.logSettings = newLogSettings;
|
||||
},
|
||||
},
|
||||
logAccess: {
|
||||
get: function () { return this.logSettings?.access?? ''; },
|
||||
set: function (newValue) {
|
||||
newLogSettings = this.logSettings;
|
||||
newValue == "" ? delete newLogSettings.access : newLogSettings.access = newValue;
|
||||
this.logSettings = newLogSettings;
|
||||
},
|
||||
},
|
||||
logError: {
|
||||
get: function () { return this.logSettings?.error?? ''; },
|
||||
set: function (newValue) {
|
||||
newLogSettings = this.logSettings;
|
||||
newValue == "" ? delete newLogSettings.error : newLogSettings.error = newValue;
|
||||
this.logSettings = newLogSettings;
|
||||
},
|
||||
},
|
||||
inboundSettings: {
|
||||
get: function () { return this.templateSettings ? JSON.stringify(this.templateSettings.inbounds, null, 2) : null; },
|
||||
set: function (newValue) {
|
||||
@@ -1335,6 +1422,18 @@
|
||||
}
|
||||
},
|
||||
},
|
||||
MetaWARPSettings: {
|
||||
get: function () {
|
||||
return doAllItemsExist(this.settingsData.domains.meta, this.warpDomains);
|
||||
},
|
||||
set: function (newValue) {
|
||||
if (newValue) {
|
||||
this.warpDomains = [...this.warpDomains, ...this.settingsData.domains.meta];
|
||||
} else {
|
||||
this.warpDomains = this.warpDomains.filter(data => !this.settingsData.domains.meta.includes(data));
|
||||
}
|
||||
},
|
||||
},
|
||||
SpotifyWARPSettings: {
|
||||
get: function () {
|
||||
return doAllItemsExist(this.settingsData.domains.spotify, this.warpDomains);
|
||||
|
||||
@@ -474,6 +474,10 @@ func (s *InboundService) DelInboundClient(inboundId int, clientId string) (bool,
|
||||
}
|
||||
}
|
||||
|
||||
if len(newClients) == 0 {
|
||||
return false, common.NewError("no client remained in Inbound")
|
||||
}
|
||||
|
||||
settings["clients"] = newClients
|
||||
newSettings, err := json.MarshalIndent(settings, "", " ")
|
||||
if err != nil {
|
||||
|
||||
@@ -33,9 +33,9 @@ import (
|
||||
type ProcessState string
|
||||
|
||||
const (
|
||||
Running ProcessState = "running"
|
||||
Stop ProcessState = "stop"
|
||||
Error ProcessState = "error"
|
||||
Running ProcessState = "Running"
|
||||
Stop ProcessState = "Stop"
|
||||
Error ProcessState = "Error"
|
||||
)
|
||||
|
||||
type Status struct {
|
||||
|
||||
@@ -80,7 +80,7 @@ func (s *XraySettingService) RegWarp(secretKey string, publicKey string) (string
|
||||
hostName, _ := os.Hostname()
|
||||
data := fmt.Sprintf(`{"key":"%s","tos":"%s","type": "PC","model": "x-ui", "name": "%s"}`, publicKey, tos, hostName)
|
||||
|
||||
url := fmt.Sprintf("https://api.cloudflareclient.com/v0a2158/reg")
|
||||
url := "https://api.cloudflareclient.com/v0a2158/reg"
|
||||
|
||||
req, err := http.NewRequest("POST", url, bytes.NewBuffer([]byte(data)))
|
||||
if err != nil {
|
||||
|
||||
@@ -43,24 +43,24 @@
|
||||
"domainName" = "Domain Name"
|
||||
"monitor" = "Listen IP"
|
||||
"certificate" = "Certificate"
|
||||
"fail" = " Fail"
|
||||
"fail" = " Failed"
|
||||
"success" = " Successful"
|
||||
"getVersion" = "Get Version"
|
||||
"install" = "Install"
|
||||
"clients" = "Clients"
|
||||
"usage" = "Usage"
|
||||
"remained" = "Remained"
|
||||
"remained" = "Remaining"
|
||||
"secAlertTitle" = "Security Alert"
|
||||
"secAlertSsl" = "THIS CONNECTION IS NOT SECURE. PLEASE AVOID ENTERING SENSITIVE INFORMATION UNTIL TLS IS ACTIVATED FOR DATA PROTECTION."
|
||||
"secAlertSsl" = "This connection is not secure. Please avoid entering sensitive information until TLS is activated for data protection."
|
||||
"security" = "Security"
|
||||
|
||||
[menu]
|
||||
"dashboard" = "OVERVIEW"
|
||||
"inbounds" = "INBOUNDS"
|
||||
"settings" = "PANEL SETTINGS"
|
||||
"xray" = "XRAY CONFIGS"
|
||||
"logout" = "LOG OUT"
|
||||
"link" = "Management"
|
||||
"dashboard" = "Overview"
|
||||
"inbounds" = "Inbounds"
|
||||
"settings" = "Panel Settings"
|
||||
"xray" = "Xray Configs"
|
||||
"logout" = "Log Out"
|
||||
"link" = "Manage"
|
||||
|
||||
[pages.login]
|
||||
"title" = "Welcome"
|
||||
@@ -77,30 +77,34 @@
|
||||
"title" = "Overview"
|
||||
"memory" = "RAM"
|
||||
"hard" = "Disk"
|
||||
"xrayStatus" = "Status"
|
||||
"machineInfo" = "Machine"
|
||||
"hostname" = "Hostname"
|
||||
"xrayStatus" = "Xray"
|
||||
"stopXray" = "Stop"
|
||||
"restartXray" = "Restart"
|
||||
"xraySwitch" = "Change Xray Version"
|
||||
"xraySwitchClick" = "Choose the version you want to switch."
|
||||
"xraySwitchClickDesk" = "Choose carefully, as older versions may not be compatible with the current configurations."
|
||||
"operationHours" = "Uptime"
|
||||
"operationHoursDesc" = "Time since startup"
|
||||
"operationHoursDesc" = "Uptime since OS startup"
|
||||
"xrayoperationHoursDesc" = "Uptime since Xray last restart"
|
||||
"systemLoad" = "System Load"
|
||||
"connectionTcpCountDesc" = "Total TCP connections across all networks"
|
||||
"connectionUdpCountDesc" = "Total UDP connections across all networks"
|
||||
"upSpeed" = "Overall upload speed across all networks"
|
||||
"downSpeed" = "Overall download speed across all networks"
|
||||
"totalSent" = "Total traffic sent across all networks since OS startup"
|
||||
"totalReceive" = "Total traffic received across all networks since OS startup"
|
||||
"systemLoadDesc" = "Average load for the past 1, 5, and 15 minutes"
|
||||
"connectionTcpCountDesc" = "Total TCP connections"
|
||||
"connectionUdpCountDesc" = "Total UDP connections"
|
||||
"upSpeed" = "Overall upload speed"
|
||||
"downSpeed" = "Overall download speed"
|
||||
"totalSent" = "Total data sent since OS startup"
|
||||
"totalReceive" = "Total data received since OS startup"
|
||||
"xraySwitchVersionDialog" = "Change Xray Version"
|
||||
"xraySwitchVersionDialogDesc" = "Are you sure you want to change the Xray version to"
|
||||
"dontRefresh" = "Installation is in progress, please do not refresh this page."
|
||||
"logs" = "Logs"
|
||||
"config" = "Config"
|
||||
"backup" = "Backup & Restore"
|
||||
"backupTitle" = "Backup & Restore Database"
|
||||
"backupDescription" = "It is recommended to make a backup before importing a new database."
|
||||
"exportDatabase" = "Backup"
|
||||
"backupTitle" = "Database Backup & Restore"
|
||||
"backupDescription" = "It is recommended to make a backup before restoring a database."
|
||||
"exportDatabase" = "Get Backup"
|
||||
"importDatabase" = "Restore"
|
||||
|
||||
[pages.inbounds]
|
||||
@@ -116,13 +120,13 @@
|
||||
"traffic" = "Traffic"
|
||||
"details" = "Details"
|
||||
"transportConfig" = "Transport Config"
|
||||
"expireDate" = "Expiry Date"
|
||||
"expireDate" = "Expiration"
|
||||
"resetTraffic" = "Reset Traffic"
|
||||
"addInbound" = "Add Inbound"
|
||||
"generalActions" = "General Actions"
|
||||
"create" = "Create"
|
||||
"update" = "Update"
|
||||
"modifyInbound" = "Modify Inbound"
|
||||
"modifyInbound" = "Edit Inbound"
|
||||
"deleteInbound" = "Delete Inbound"
|
||||
"deleteInboundContent" = "Are you sure you want to delete inbound?"
|
||||
"deleteClient" = "Delete Client"
|
||||
@@ -134,8 +138,8 @@
|
||||
"destinationPort" = "Destination Port"
|
||||
"targetAddress" = "Target Address"
|
||||
"monitorDesc" = "Leave blank to listen on all IPs"
|
||||
"meansNoLimit" = "Means no limit"
|
||||
"totalFlow" = "Total Flow"
|
||||
"meansNoLimit" = "Zero means unlimited. (Unit: GB)"
|
||||
"totalFlow" = "Total Traffic"
|
||||
"leaveBlankToNeverExpire" = "Leave blank to never expire"
|
||||
"noRecommendKeepDefault" = "It is recommended to keep the default"
|
||||
"certificatePath" = "File Path"
|
||||
@@ -149,7 +153,7 @@
|
||||
"export" = "Export All URLs"
|
||||
"clone" = "Clone"
|
||||
"cloneInbound" = "Clone"
|
||||
"cloneInboundContent" = "All settings for this inbound, except Port, Listening IP, and Clients, will be applied to the clone."
|
||||
"cloneInboundContent" = "All settings for this inbound, except Port, Listen IP, and Clients, will be applied to the clone."
|
||||
"cloneInboundOk" = "Clone"
|
||||
"resetAllTraffic" = "Reset All Inbounds Traffic"
|
||||
"resetAllTrafficTitle" = "Reset All Inbounds Traffic"
|
||||
@@ -166,12 +170,12 @@
|
||||
"email" = "Email"
|
||||
"emailDesc" = "Please provide a unique email address"
|
||||
"setDefaultCert" = "Set Cert from Panel"
|
||||
"telegramDesc" = "Please provide Telegram or Chat ID(s) without using the '@'. (get it here @userinfobot) or (use '/id' command in the bot)"
|
||||
"subscriptionDesc" = "To find your subscription URL, navigate to the 'Details'. Additionally, you can use the same name for several clients."
|
||||
"telegramDesc" = "Please provide Chat ID(s) without using the '@' symbol. (Get it here @userinfobot) or (Use '/id' command in the bot)"
|
||||
"subscriptionDesc" = "To find your subscription URL, navigate to the 'More Information'. Additionally, you can use the same name for several clients."
|
||||
"info" = "Info"
|
||||
"same" = "Same"
|
||||
"inboundData" = "Inbound's Data"
|
||||
"copyToClipboard" = "Copy to Clipboard"
|
||||
"exportInbound" = "Export Inbound"
|
||||
"import" = "Import"
|
||||
"importInbound" = "Import an Inbound"
|
||||
|
||||
@@ -187,27 +191,28 @@
|
||||
"last" = "Last"
|
||||
"prefix" = "Prefix"
|
||||
"postfix" = "Postfix"
|
||||
"delayedStart" = "Start after First Use"
|
||||
"delayedStart" = "Start on Initial Use"
|
||||
"expireDays" = "Duration"
|
||||
"days" = "Day(s)"
|
||||
"renew" = "Auto Renew"
|
||||
"renewDesc" = "Auto-renewal after expiration. (0 = disable)(unit: day)"
|
||||
"renewDesc" = "Auto-renewal after expiration. (0 = disable)(Unit: day)"
|
||||
|
||||
[pages.inbounds.toasts]
|
||||
"obtain" = "Obtain"
|
||||
|
||||
[pages.inbounds.stream.general]
|
||||
"requestHeader" = "Request Header"
|
||||
"request" = "Request"
|
||||
"response" = "Response"
|
||||
"name" = "Name"
|
||||
"value" = "Value"
|
||||
|
||||
[pages.inbounds.stream.tcp]
|
||||
"requestVersion" = "Request Version"
|
||||
"requestMethod" = "Request Method"
|
||||
"requestPath" = "Request Path"
|
||||
"responseVersion" = "Response Version"
|
||||
"responseStatus" = "Response Status"
|
||||
"responseStatusDescription" = "Response Status Description"
|
||||
"version" = "Version"
|
||||
"method" = "Method"
|
||||
"path" = "Path"
|
||||
"status" = "Status"
|
||||
"statusDescription" = "Status Description"
|
||||
"requestHeader" = "Request Header"
|
||||
"responseHeader" = "Response Header"
|
||||
|
||||
[pages.inbounds.stream.quic]
|
||||
@@ -220,23 +225,23 @@
|
||||
"restartPanel" = "Restart Panel"
|
||||
"restartPanelDesc" = "Are you sure you want to restart the panel? If you are unable to access the panel after restarting, please check the logs in the terminal script"
|
||||
"resetDefaultConfig" = "Reset to Default"
|
||||
"panelConfig" = "Configuration"
|
||||
"panelConfig" = "General"
|
||||
"userSettings" = "Authentication"
|
||||
"TGBotSettings" = "Telegram Bot"
|
||||
"panelListeningIP" = "Listen IP"
|
||||
"panelListeningIPDesc" = "The IP address for the web panel. (leave blank to listen on all IPs)"
|
||||
"panelListeningIPDesc" = "The IP address for the web panel. (Leave blank to listen on all IPs)"
|
||||
"panelListeningDomain" = "Listen Domain"
|
||||
"panelListeningDomainDesc" = "The domain name for the web panel. (leave blank to listen on all domains and IPs)"
|
||||
"panelListeningDomainDesc" = "The domain name for the web panel. (Leave blank to listen on all domains and IPs)"
|
||||
"panelPort" = "Listen Port"
|
||||
"panelPortDesc" = "The port number for the web panel. (must be an unused port)"
|
||||
"panelPortDesc" = "The port number for the web panel. (Must be an unused port)"
|
||||
"publicKeyPath" = "Public Key Path"
|
||||
"publicKeyPathDesc" = "The public key file path for the web panel. (begins with ‘/‘)"
|
||||
"publicKeyPathDesc" = "The public key file path for the web panel. (Begins with ‘/‘)"
|
||||
"privateKeyPath" = "Private Key Path"
|
||||
"privateKeyPathDesc" = "The private key file path for the web panel. (begins with ‘/‘)"
|
||||
"privateKeyPathDesc" = "The private key file path for the web panel. (Begins with ‘/‘)"
|
||||
"panelUrlPath" = "URI Path"
|
||||
"panelUrlPathDesc" = "The URI path for the web panel. (begins with ‘/‘ and concludes with ‘/‘)"
|
||||
"panelUrlPathDesc" = "The URI path for the web panel. (Begins with ‘/‘ and concludes with ‘/‘)"
|
||||
"pageSize" = "Pagination Size"
|
||||
"pageSizeDesc" = "Define page size for the inbounds table. (0 = disable)"
|
||||
"pageSizeDesc" = "The page size for the inbounds table. (0 = disable)"
|
||||
"remarkModel" = "Remark Model & Separation Character"
|
||||
"sampleRemark" = "Sample Remark"
|
||||
"oldUsername" = "Current Username"
|
||||
@@ -246,53 +251,53 @@
|
||||
"telegramBotEnable" = "Enable Telegram Bot"
|
||||
"telegramBotEnableDesc" = "Enables the Telegram bot."
|
||||
"telegramToken" = "Telegram Token"
|
||||
"telegramTokenDesc" = "The Telegram bot token obtained from '@BotFather'."
|
||||
"telegramTokenDesc" = "The Telegram token. (Get it here @BotFather)"
|
||||
"telegramChatId" = "Admin Chat ID"
|
||||
"telegramChatIdDesc" = "The Telegram Admin Chat ID(s). (comma-separated)(use @userinfobot) or (use '/id' command in the bot)"
|
||||
"telegramChatIdDesc" = "The Telegram Admin Chat ID(s). (Comma-separated)(Get it here @userinfobot) or (Use '/id' command in the bot)"
|
||||
"telegramNotifyTime" = "Notification Time"
|
||||
"telegramNotifyTimeDesc" = "The Telegram bot notification time set for periodic reports. (use the crontab time format)"
|
||||
"telegramNotifyTimeDesc" = "The Telegram bot notification time set for periodic reports. (Use the crontab time format)"
|
||||
"tgNotifyBackup" = "Database Backup"
|
||||
"tgNotifyBackupDesc" = "Send a database backup file with a report."
|
||||
"tgNotifyBackupDesc" = "Get a database backup file with a report."
|
||||
"tgNotifyLogin" = "Login Notification"
|
||||
"tgNotifyLoginDesc" = "Get notified about the username, IP address, and time whenever someone attempts to log into your web panel."
|
||||
"tgNotifyLoginDesc" = "Get notified about the username, IP, and time whenever someone attempts to log into your web panel."
|
||||
"sessionMaxAge" = "Session Duration"
|
||||
"sessionMaxAgeDesc" = "The duration for which you can stay logged in. (unit: minute)"
|
||||
"expireTimeDiff" = "Client Expiration Threshold Notification"
|
||||
"expireTimeDiffDesc" = "Get notified about client expiration when reaching this threshold. (unit: day)"
|
||||
"trafficDiff" = "Traffic Exhaustion Threshold Notification"
|
||||
"trafficDiffDesc" = "Get notified about traffic exhaustion when reaching this threshold. (unit: GB)"
|
||||
"tgNotifyCpu" = "CPU Load Threshold Notification"
|
||||
"tgNotifyCpuDesc" = "Get notified if CPU usage exceeds this threshold. (unit: %)"
|
||||
"sessionMaxAgeDesc" = "The duration for which you can stay logged in. (Unit: minute)"
|
||||
"expireTimeDiff" = "Expiration Time Notification"
|
||||
"expireTimeDiffDesc" = "Get notified when the remaining time reaches the set threshold. (Unit: day)"
|
||||
"trafficDiff" = "Traffic Limit Notification"
|
||||
"trafficDiffDesc" = "Get notified when remaining traffic reaches the set threshold. (Unit: GB)"
|
||||
"tgNotifyCpu" = "CPU Load Notification"
|
||||
"tgNotifyCpuDesc" = "Get notified if CPU load exceeds the set threshold. (Unit: %)"
|
||||
"timeZone" = "Time Zone"
|
||||
"timeZoneDesc" = "Scheduled tasks run based on this time zone."
|
||||
"timeZoneDesc" = "Scheduled tasks will run based on this time zone."
|
||||
"subSettings" = "Subscription"
|
||||
"subEnable" = "Enable Subscription Service"
|
||||
"subEnableDesc" = "Enables the subscription service."
|
||||
"subListen" = "Listen IP"
|
||||
"subListenDesc" = "The IP address for the subscription service. (leave blank to listen on all IPs)"
|
||||
"subListenDesc" = "The IP address for the subscription service. (Leave blank to listen on all IPs)"
|
||||
"subPort" = "Listen Port"
|
||||
"subPortDesc" = "The port number for the subscription service. (must be an unused port)"
|
||||
"subPortDesc" = "The port number for the subscription service. (Must be an unused port)"
|
||||
"subCertPath" = "Public Key Path"
|
||||
"subCertPathDesc" = "The public key file path for the subscription service. (begins with ‘/‘)"
|
||||
"subCertPathDesc" = "The public key file path for the subscription service. (Begins with ‘/‘)"
|
||||
"subKeyPath" = "Private Key Path"
|
||||
"subKeyPathDesc" = "The private key file path for the subscription service. (begins with ‘/‘)"
|
||||
"subKeyPathDesc" = "The private key file path for the subscription service. (Begins with ‘/‘)"
|
||||
"subPath" = "URI Path"
|
||||
"subPathDesc" = "The URI path for the subscription service. (begins with ‘/‘ and concludes with ‘/‘)"
|
||||
"subPathDesc" = "The URI path for the subscription service. (Begins with ‘/‘ and concludes with ‘/‘)"
|
||||
"subDomain" = "Listen Domain"
|
||||
"subDomainDesc" = "The domain name for the subscription service. (leave blank to listen on all domains and IPs)"
|
||||
"subDomainDesc" = "The domain name for the subscription service. (Leave blank to listen on all domains and IPs)"
|
||||
"subUpdates" = "Update Intervals"
|
||||
"subUpdatesDesc" = "The update intervals of the subscription URL in the client apps. (unit: hour)"
|
||||
"subUpdatesDesc" = "The update intervals of the subscription URL in the client apps. (Unit: hour)"
|
||||
"subEncrypt" = "Encode"
|
||||
"subEncryptDesc" = "The returned content of subscription service will be Base64 encoded."
|
||||
"subShowInfo" = "Show Usage Info"
|
||||
"subShowInfoDesc" = "The remaining traffic and date will be displayed in the client apps."
|
||||
"subURI" = "Reverse Proxy URI"
|
||||
"subURIDesc" = "The URI path of the subscription URL for use behind proxies."
|
||||
"subURIDesc" = "The subscription service will use the URI that has been set up behind reverse proxies."
|
||||
|
||||
[pages.settings.toasts]
|
||||
"modifySettings" = "Modify Settings"
|
||||
"getSettings" = "Get Settings"
|
||||
"modifyUser" = "Modify User"
|
||||
"modifyUser" = "Modify Admin"
|
||||
"originalUserPassIncorrect" = "The current username or password is incorrect"
|
||||
"userPassMustBeNotEmpty" = "The new username or password is required"
|
||||
|
||||
@@ -300,10 +305,12 @@
|
||||
"title" = "Xray Configs"
|
||||
"save" = "Save"
|
||||
"restart" = "Restart Xray"
|
||||
"basicTemplate" = "Basic"
|
||||
"basicTemplate" = "Basics"
|
||||
"advancedTemplate" = "Advanced"
|
||||
"generalConfigs" = "General Strategy"
|
||||
"generalConfigsDesc" = "These options will determine general strategy adjustments."
|
||||
"logConfigs" = "Log"
|
||||
"logConfigsDesc" = "Logs may affect your server's efficiency. It is recommended to enable it wisely only in case of your needs"
|
||||
"blockConfigs" = "Protection Shield"
|
||||
"blockConfigsDesc" = "These options will block traffic based on specific requested protocols and websites."
|
||||
"blockCountryConfigs" = "Block Country"
|
||||
@@ -311,11 +318,11 @@
|
||||
"directCountryConfigs" = "Direct Country"
|
||||
"directCountryConfigsDesc" = "These options will directly forward traffic based on the specific requested country."
|
||||
"ipv4Configs" = "IPv4 Routing"
|
||||
"ipv4ConfigsDesc" = "These options will route requests to destination only via IPv4."
|
||||
"warpConfigs" = "WARP Routing"
|
||||
"warpConfigsDesc" = "WARP will route traffic to websites through Cloudflare servers."
|
||||
"ipv4ConfigsDesc" = "These options will route traffic based on specific requested destination via system's IPv4."
|
||||
"warpConfigs" = "WARP Config"
|
||||
"warpConfigsDesc" = "These options will route traffic based on specific requested destination via WARP."
|
||||
"Template" = "Advanced Xray Configuration Template"
|
||||
"TemplateDesc" = "The final Xray configuration file will be generated based on this template."
|
||||
"TemplateDesc" = "The final Xray config file will be generated based on this template."
|
||||
"FreedomStrategy" = "Freedom Protocol Strategy"
|
||||
"FreedomStrategyDesc" = "Set the output strategy for the network in the Freedom Protocol."
|
||||
"RoutingStrategy" = "Overall Routing Strategy"
|
||||
@@ -359,9 +366,11 @@
|
||||
"GoogleWARP" = "Google"
|
||||
"GoogleWARPDesc" = "Routes traffic to Google via WARP."
|
||||
"OpenAIWARP" = "ChatGPT"
|
||||
"OpenAIWARPDesc" = "Routes traffic to OpenAI (ChatGPT) via WARP."
|
||||
"OpenAIWARPDesc" = "Routes traffic to ChatGPT via WARP."
|
||||
"NetflixWARP" = "Netflix"
|
||||
"NetflixWARPDesc" = "Routes traffic to Netflix via WARP."
|
||||
"MetaWARP" = "Meta"
|
||||
"MetaWARPDesc" = "Routes traffic to Meta (Instagram, Facebook, WhatsApp, Threads,...) via WARP."
|
||||
"SpotifyWARP" = "Spotify"
|
||||
"SpotifyWARPDesc" = "Routes traffic to Spotify via WARP."
|
||||
"completeTemplate" = "All"
|
||||
@@ -398,6 +407,9 @@
|
||||
"bridge" = "Bridge"
|
||||
"portal" = "Portal"
|
||||
"intercon" = "Interconnection"
|
||||
"settings" = "Settings"
|
||||
"accountInfo" = "Account Information"
|
||||
"outboundStatus" = "Outbound Status"
|
||||
|
||||
[pages.xray.wireguard]
|
||||
"secretKey" = "Secret Key"
|
||||
@@ -427,11 +439,11 @@
|
||||
"status" = "✅ Bot is OK!"
|
||||
"usage" = "❗️ Please provide a text to search!"
|
||||
"getID" = "🆔 Your ID: <code>{{ .ID }}</code>"
|
||||
"helpAdminCommands" = "Search for a client email:\r\n<code>/usage [Email]</code>\r\n\r\nSearch for inbounds (with client stats):\r\n<code>/inbound [Remark]</code>"
|
||||
"helpClientCommands" = "To search for statistics, simply use the following command:\r\n\r\n<code>/usage [UUID|Password]</code>\r\n\r\nUse UUID for VMess/VLESS and password for Trojan/Shadowsocks."
|
||||
"helpAdminCommands" = "Search for a client email:\r\n<code>/Usage [Email]</code>\r\n\r\nSearch for inbounds (with client stats):\r\n<code>/inbound [Remark]</code>"
|
||||
"helpClientCommands" = "To search for statistics, simply use the following command:\r\n\r\n<code>/Usage [UUID|Password]</code>\r\n\r\nUse UUID for VMess/VLESS and Password for Trojan/Shadowsocks."
|
||||
|
||||
[tgbot.messages]
|
||||
"cpuThreshold" = "🔴 CPU load {{ .Percent }}% = CPU load {{ .Percent }}% is more than the threshold of {{ .Threshold }}%"
|
||||
"cpuThreshold" = "🔴 CPU load {{ .Percent }}% Exceeds the threshold of {{ .Threshold }}%"
|
||||
"loginSuccess" = "✅ Logged in to the web panel successfully.\r\n"
|
||||
"loginFailed" = "❗Log in to the web panel failed.\r\n"
|
||||
"report" = "🕰 Scheduled reports: {{ .RunTime }}\r\n"
|
||||
@@ -447,12 +459,12 @@
|
||||
"tcpCount" = "🔹 TCP: {{ .Count }}\r\n"
|
||||
"udpCount" = "🔸 UDP: {{ .Count }}\r\n"
|
||||
"traffic" = "🚦 Traffic: {{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n"
|
||||
"xrayStatus" = "ℹ️ Xray status: {{ .State }}\r\n"
|
||||
"xrayStatus" = "ℹ️ Status: {{ .State }}\r\n"
|
||||
"username" = "👤 Username: {{ .Username }}\r\n"
|
||||
"time" = "⏰ Time: {{ .Time }}\r\n"
|
||||
"inbound" = "📍 Inbound: {{ .Remark }}\r\n"
|
||||
"port" = "🔌 Port: {{ .Port }}\r\n"
|
||||
"expire" = "📅 Expire date: {{ .DateTime }}\r\n \r\n"
|
||||
"expire" = "📅 Expiry date: {{ .DateTime }}\r\n \r\n"
|
||||
"expireIn" = "📅 Expire in: {{ .Time }}\r\n \r\n"
|
||||
"active" = "💡 Active: {{ .Enable }}\r\n"
|
||||
"online" = "🌐 Connection status: {{ .Status }}\r\n"
|
||||
@@ -461,7 +473,7 @@
|
||||
"download" = "🔽 Download↓: {{ .Download }}\r\n"
|
||||
"total" = "🔄 Total: {{ .UpDown }} / {{ .Total }}\r\n"
|
||||
"exhaustedMsg" = "🚨 Exhausted {{ .Type }}:\r\n"
|
||||
"exhaustedCount" = "🚨 Exhausted {{ .Type }} count:\r\n"
|
||||
"exhaustedCount" = "🚨 Exhausted {{ .Type }} Count:\r\n"
|
||||
"onlinesCount" = "🌐 Online clients: {{ .Count }}\r\n"
|
||||
"disabled" = "🛑 Disabled: {{ .Disabled }}\r\n"
|
||||
"depleteSoon" = "🔜 Deplete soon: {{ .Deplete }}\r\n \r\n"
|
||||
@@ -470,8 +482,8 @@
|
||||
"no" = "❌ No"
|
||||
|
||||
[tgbot.buttons]
|
||||
"dbBackup" = "Get DB Backup"
|
||||
"serverUsage" = "Server Usage"
|
||||
"dbBackup" = "Get Backup"
|
||||
"serverUsage" = "System Usage"
|
||||
"getInbounds" = "Get Inbounds"
|
||||
"depleteSoon" = "Deplete Soon"
|
||||
"clientUsage" = "Get Usage"
|
||||
@@ -480,5 +492,5 @@
|
||||
|
||||
[tgbot.answers]
|
||||
"getInboundsFailed" = "❌ Failed to get inbounds"
|
||||
"askToAddUser" = "Your configuration is not found!\r\nYou should configure your Telegram username and ask your Admin to add it to your configuration(s)."
|
||||
"askToAddUserName" = "Your configuration is not found!\r\nPlease ask your Admin to use your Telegram username in your configuration(s).\r\n\r\nYour username: <b>@{{ .TgUserName }}</b>"
|
||||
"askToAddUser" = "Your configuration is not found!\r\nPlease set a Telegram username, and then ask the service admin to add it to the configuration(s)."
|
||||
"askToAddUserName" = "Your configuration is not found!\r\nPlease ask the service admin to add your Telegram username to the configuration(s).\r\n\r\nYour Username: <b>@{{ .TgUserName }}</b>"
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"indefinite" = "نامحدود"
|
||||
"unlimited" = "نامحدود"
|
||||
"none" = "هیچ"
|
||||
"qrCode" = "QRکد"
|
||||
"qrCode" = "QR کد"
|
||||
"info" = "اطلاعات بیشتر"
|
||||
"edit" = "ویرایش"
|
||||
"delete" = "حذف"
|
||||
@@ -41,7 +41,7 @@
|
||||
"offline" = "آفلاین"
|
||||
"online" = "آنلاین"
|
||||
"domainName" = "آدرس دامنه"
|
||||
"monitor" = "آیپی اتصال"
|
||||
"monitor" = "آدرس آیپی"
|
||||
"certificate" = "گواهی"
|
||||
"fail" = "ناموفق"
|
||||
"success" = " موفق"
|
||||
@@ -51,7 +51,7 @@
|
||||
"usage" = "استفاده"
|
||||
"remained" = "باقیمانده"
|
||||
"secAlertTitle" = "هشدارامنیتی"
|
||||
"secAlertSsl" = "ایناتصالامن نیست. لطفا تازمانیکه تیالاس برای محافظت از دادهها فعال نشدهاست، از وارد کردن اطلاعات حساس خودداریکنید"
|
||||
"secAlertSsl" = "ایناتصالامن نیست. لطفا تازمانیکه تیالاس برای محافظت از دادهها فعال نشدهاست، از وارد کردن اطلاعات حساس خودداری کنید"
|
||||
"security" = "امنیت"
|
||||
|
||||
[menu]
|
||||
@@ -64,7 +64,7 @@
|
||||
|
||||
[pages.login]
|
||||
"title" = "خوشآمدید"
|
||||
"loginAgain" = "مدت زمان استفاده بهاتمامرسیده، لطفا دوباره وارد شوید"
|
||||
"loginAgain" = "مدت زمان استفاده بهاتمام رسیده، لطفا دوباره وارد شوید"
|
||||
|
||||
[pages.login.toasts]
|
||||
"invalidFormData" = "اطلاعات بهدرستی وارد نشدهاست"
|
||||
@@ -77,44 +77,48 @@
|
||||
"title" = "نمای کلی"
|
||||
"memory" = "RAM"
|
||||
"hard" = "Disk"
|
||||
"xrayStatus" = "وضعیتایکسری"
|
||||
"machineInfo" = "ماشین"
|
||||
"hostname" = "نام میزبان"
|
||||
"xrayStatus" = "ایکسری"
|
||||
"stopXray" = "توقف"
|
||||
"restartXray" = "شروعمجدد"
|
||||
"xraySwitch" = "تغییرنسخه"
|
||||
"restartXray" = "ریستارت"
|
||||
"xraySwitch" = "تغییر نسخه ایکسری"
|
||||
"xraySwitchClick" = "نسخه مورد نظر را انتخاب کنید"
|
||||
"xraySwitchClickDesk" = "لطفا بادقت انتخاب کنید. درصورت انتخاب نسخه قدیمیتر، امکان ناهماهنگی با پیکربندی فعلی وجود دارد"
|
||||
"xraySwitchClickDesk" = "لطفا بادقت انتخاب کنید. درصورت انتخاب نسخه قدیمیتر، امکان ناهماهنگی با پیکربندیهای فعلی وجود دارد"
|
||||
"operationHours" = "مدتکارکرد"
|
||||
"operationHoursDesc" = "مدت فعالیت سیستمعامل پساز شروع بهکار"
|
||||
"operationHoursDesc" = "مدت کارکرد سیستمعامل پساز شروع بهکار"
|
||||
"xrayoperationHoursDesc" = "مدت کارکرد ایکسری پساز آخرین ریستارت"
|
||||
"systemLoad" = "بارسیستم"
|
||||
"connectionTcpCountDesc" = "در تمامشبکهها TCP مجموعاتصالات"
|
||||
"connectionUdpCountDesc" = "در تمامشبکهها UDP مجموعاتصالات"
|
||||
"upSpeed" = "سرعت کلی آپلود در تمامشبکهها"
|
||||
"downSpeed" = "سرعت کلی دانلود در تمامشبکهها"
|
||||
"totalSent" = "مجموع ترافیک ارسالشده پساز شروعبهکار سیستمعامل"
|
||||
"totalReceive" = "مجموع ترافیک دریافتشده پساز شروعبهکار سیستمعامل"
|
||||
"systemLoadDesc" = "میانگین بار در 1، 5 و 15 دقیقه گذشته"
|
||||
"connectionTcpCountDesc" = "TCP کل اتصالات"
|
||||
"connectionUdpCountDesc" = "UDP کل اتصالات"
|
||||
"upSpeed" = "سرعت کلی آپلود"
|
||||
"downSpeed" = "سرعت کلی دانلود"
|
||||
"totalSent" = "کل ترافیک ارسالی پساز شروع بهکار سیستمعامل"
|
||||
"totalReceive" = "کل ترافیک دریافتی پساز شروع بهکار سیستمعامل"
|
||||
"xraySwitchVersionDialog" = "تغییرنسخهایکسری"
|
||||
"xraySwitchVersionDialogDesc" = "آیا از تغییر نسخه مطمئن هستید؟"
|
||||
"dontRefresh" = "در حال نصب، لطفا صفحه را رفرش نکنید"
|
||||
"logs" = "گزارشها"
|
||||
"config" = "پیکربندی"
|
||||
"backup" = "پشتیبانگیری"
|
||||
"backupTitle" = "پشتیبانگیری دیتابیس"
|
||||
"backupDescription" = "توصیهمیشود قبلاز واردکردن یک دیتابیس جدید، نسخه پشتیبان تهیه کنید"
|
||||
"config" = "کانفیگ"
|
||||
"backup" = "پشتیبانگیری و بازیابی"
|
||||
"backupTitle" = "پشتیبانگیری و بازیابی دیتابیس"
|
||||
"backupDescription" = "توصیهمیشود قبلاز بازیابی دیتابیس، یک نسخه پشتیبان تهیه کنید"
|
||||
"exportDatabase" = "پشتیبانگیری"
|
||||
"importDatabase" = "بازگرداندن"
|
||||
"importDatabase" = "بازیابی"
|
||||
|
||||
[pages.inbounds]
|
||||
"title" = "کاربران"
|
||||
"totalDownUp" = "دریافت/ارسال کل"
|
||||
"totalUsage" = "مصرف کل"
|
||||
"inboundCount" = "کل ورودیها"
|
||||
"operate" = "عملیات"
|
||||
"operate" = "منو"
|
||||
"enable" = "فعال"
|
||||
"remark" = "نام"
|
||||
"protocol" = "پروتکل"
|
||||
"port" = "پورت"
|
||||
"traffic" = "ترافیک"
|
||||
"details" = "توضیحات"
|
||||
"details" = "جزئیات"
|
||||
"transportConfig" = "نحوه اتصال"
|
||||
"expireDate" = "تاریخ انقضا"
|
||||
"resetTraffic" = "ریست ترافیک"
|
||||
@@ -133,8 +137,8 @@
|
||||
"network" = "شبکه"
|
||||
"destinationPort" = "پورت مقصد"
|
||||
"targetAddress" = "آدرس مقصد"
|
||||
"monitorDesc" = "بهطور پیشفرض خالیبگذارید"
|
||||
"meansNoLimit" = "یعنیبدونمحدودیت"
|
||||
"monitorDesc" = "برای گوشدادن بهتمام آیپیها خالیبگذارید"
|
||||
"meansNoLimit" = "صفر یعنی نامحدود. واحد: گیگابایت"
|
||||
"totalFlow" = "ترافیک کل"
|
||||
"leaveBlankToNeverExpire" = "برای منقضینشدن خالیبگذارید"
|
||||
"noRecommendKeepDefault" = "توصیهمیشود بهطور پیشفرض حفظشود"
|
||||
@@ -144,7 +148,7 @@
|
||||
"publicKeyContent" = "محتوای کلید عمومی"
|
||||
"keyPath" = "مسیر کلید خصوصی"
|
||||
"keyContent" = "محتوای کلید خصوصی"
|
||||
"clickOnQRcode" = "برای کپی بر روی کدتصویری کلیک کنید"
|
||||
"clickOnQRcode" = "برای کپی لینک بر روی کدتصویری کلیک کنید"
|
||||
"client" = "کاربر"
|
||||
"export" = "استخراج لینکها"
|
||||
"clone" = "شبیهسازی"
|
||||
@@ -165,12 +169,12 @@
|
||||
"email" = "ایمیل"
|
||||
"emailDesc" = "باید یک ایمیل یکتا باشد"
|
||||
"setDefaultCert" = "استفاده از گواهی پنل"
|
||||
"telegramDesc" = " استفاده کنید'/id'یااز دستور @userinfobot آنرا اینجا دریافت کنید .از آیدی(های) چت تلگرام بدون '@' استفاده کنید"
|
||||
"subscriptionDesc" = "شما میتوانید لینک سابسکربپشن خودرا در 'جزئیات' پیدا کنید، همچنین میتوانید از همین نام برای چندین کاربر استفادهکنید"
|
||||
"telegramDesc" = "دریافت کنید '/id'یا دستور @userinfobot آیدی(های) چت مدیر را بدون '@' واردکنید. از"
|
||||
"subscriptionDesc" = "لینک سابسکربپشن خودرا در 'اطلاعات بیشتر' پیدا کنید، همچنین میتوانید از همین نام برای چندین کاربر استفادهکنید"
|
||||
"info" = "اطلاعات"
|
||||
"same" = "همسان"
|
||||
"inboundData" = "دادههای ورودی"
|
||||
"copyToClipboard" = "کپی در حافظه"
|
||||
"exportInbound" = "استخراج ورودی"
|
||||
"import" = "افزودن"
|
||||
"importInbound" = "افزودن یک ورودی"
|
||||
|
||||
@@ -196,17 +200,18 @@
|
||||
"obtain" = "فراهمسازی"
|
||||
|
||||
[pages.inbounds.stream.general]
|
||||
"requestHeader" = "درخواست سربرگ"
|
||||
"request" = "درخواست"
|
||||
"response" = "پاسخ"
|
||||
"name" = "نام"
|
||||
"value" = "مقدار"
|
||||
|
||||
[pages.inbounds.stream.tcp]
|
||||
"requestVersion" = "نسخه درخواست"
|
||||
"requestMethod" = "متد درخواست"
|
||||
"requestPath" = "مسیر درخواست"
|
||||
"responseVersion" = "نسخه پاسخ"
|
||||
"responseStatus" = "وضعیت پاسخ"
|
||||
"responseStatusDescription" = "توضیحات وضعیت پاسخ"
|
||||
"version" = "نسخه"
|
||||
"method" = "متد"
|
||||
"path" = "مسیر"
|
||||
"status" = "وضعیت"
|
||||
"statusDescription" = "توضیحات وضعیت"
|
||||
"requestHeader" = "سربرگ درخواست"
|
||||
"responseHeader" = "سربرگ پاسخ"
|
||||
|
||||
[pages.inbounds.stream.quic]
|
||||
@@ -217,23 +222,23 @@
|
||||
"save" = "ذخیره"
|
||||
"infoDesc" = "برای اعمال تغییرات در این بخش باید پس از ذخیره کردن، پنل را ریستارت کنید"
|
||||
"restartPanel" = "ریستارت پنل"
|
||||
"restartPanelDesc" = "آیا مطمئن به ریستارت پنل هستید؟ اگر پساز ریستارت نمیتوانید به پنل دسترسی پیدا کنید، لطفاً گزارشهای موجود در اسکریپت پنل را بررسی کنید"
|
||||
"restartPanelDesc" = "آیا مطمئن به ریستارت پنل هستید؟ اگر پساز ریستارت نتوانستید به پنل دسترسی پیدا کنید، گزارشهای موجود در اسکریپت پنل را بررسی کنید"
|
||||
"resetDefaultConfig" = "برگشت به پیشفرض"
|
||||
"panelConfig" = "پیکربندی"
|
||||
"panelConfig" = "عمومی"
|
||||
"userSettings" = "احرازهویت"
|
||||
"TGBotSettings" = "ربات تلگرام"
|
||||
"panelListeningIP" = "آدرس آیپی"
|
||||
"panelListeningIPDesc" = "آدرس آیپی برای وب پنل. برای گوشدادن بهتمام آیپیها خالیبگذارید"
|
||||
"panelListeningDomain" = "نام دامنه"
|
||||
"panelListeningDomainDesc" = "آدرس دامنه برای وب پنل. برای گوش دادن بهتمام دامنهها و آیپیها خالیبگذارید"
|
||||
"panelPort" = "پورت"
|
||||
"panelListeningDomainDesc" = "آدرس دامنه برای وب پنل. برای گوشدادن بهتمام دامنهها و آیپیها خالیبگذارید"
|
||||
"panelPort" = "شماره پورت"
|
||||
"panelPortDesc" = "شماره پورت برای وب پنل. باید پورت استفاده نشدهباشد"
|
||||
"publicKeyPath" = "مسیر کلید عمومی"
|
||||
"publicKeyPathDesc" = "مسیر فایل کلیدعمومی برای وب پنل. با '/' شروعمیشود"
|
||||
"privateKeyPath" = "مسیر کلید خصوصی"
|
||||
"privateKeyPathDesc" = "مسیر فایل کلیدخصوصی برای وب پنل. با '/' شروعمیشود"
|
||||
"panelUrlPath" = "URI مسیر"
|
||||
"panelUrlPathDesc" = "برای وب پنل. با '/' شروع و با '/' خاتمه مییابد URI مسیر"
|
||||
"panelUrlPathDesc" = "مسیر لینک وب پنل. با '/' شروع و با '/' خاتمه مییابد"
|
||||
"pageSize" = "اندازه صفحه بندی جدول"
|
||||
"pageSizeDesc" = "اندازه صفحه برای جدول ورودیها. 0 = غیرفعال"
|
||||
"remarkModel" = "نامکانفیگ و جداکننده"
|
||||
@@ -245,48 +250,48 @@
|
||||
"telegramBotEnable" = "فعالسازی ربات تلگرام"
|
||||
"telegramBotEnableDesc" = "ربات تلگرام را فعال میکند"
|
||||
"telegramToken" = "توکن تلگرام"
|
||||
"telegramTokenDesc" = "دریافت کنید @botfather توکن را میتوانید از"
|
||||
"telegramTokenDesc" = "دریافت کنید @botfather توکن تلگرام، از"
|
||||
"telegramChatId" = "آیدی چت مدیر"
|
||||
"telegramChatIdDesc" = "استفادهکنید'/id'یا دستور @userinfobot آیدی(های) چت تلگرام مدیر، برای دریافت شناسههای چت خود از"
|
||||
"telegramNotifyTime" = "زمان نوتیفیکیشن"
|
||||
"telegramNotifyTimeDesc" = "زماناطلاعرسانی ربات تلگرام برای گزارش های دورهای. از فرمت زمانبندی لینوکس استفادهکنید"
|
||||
"tgNotifyBackup" = "پشتیبانگیری از دیتابیس"
|
||||
"tgNotifyBackupDesc" = "فایل پشتیباندیتابیس را بههمراه گزارش ارسال میکند"
|
||||
"tgNotifyLogin" = "اعلان ورود"
|
||||
"tgNotifyLoginDesc" = "نامکاربری، آدرس آیپی، و زمان ورود، فردی که سعی میکند وارد پنل شود را نمایش میدهد"
|
||||
"sessionMaxAge" = "بیشینه زمان جلسه وب"
|
||||
"sessionMaxAgeDesc" = "بیشینه زمانی که میتوانید لاگین بمانید. واحد: دقیقه"
|
||||
"expireTimeDiff" = "آستانه زمان باقی مانده"
|
||||
"expireTimeDiffDesc" = "فاصله زمانی هشدار تا رسیدن به زمان انقضا. واحد: روز"
|
||||
"trafficDiff" = "آستانه ترافیک باقی مانده"
|
||||
"trafficDiffDesc" = "فاصله زمانی هشدار تا رسیدن به اتمام ترافیک. واحد: گیگابایت"
|
||||
"tgNotifyCpu" = "آستانه هشدار بار پردازنده"
|
||||
"tgNotifyCpuDesc" = "اگر بار روی پردازنده ازاین آستانه فراتر رفت، برای شما پیام ارسال میشود. واحد: درصد"
|
||||
"telegramChatIdDesc" = "دریافت کنید '/id'یا دستور @userinfobot آیدی(های) چت مدیر، از"
|
||||
"telegramNotifyTime" = "زمان اطلاعرسانی"
|
||||
"telegramNotifyTimeDesc" = "زماناطلاعرسانی ربات تلگرام برای ارسال گزارشهای دورهای. از فرمت زمانی کرونتاب استفادهکنید"
|
||||
"tgNotifyBackup" = "پشتیبانگیری دیتابیس"
|
||||
"tgNotifyBackupDesc" = "فایل پشتیبان دیتابیس را بههمراه گزارش دریافت میکنید"
|
||||
"tgNotifyLogin" = "اطلاعرسانی ورود"
|
||||
"tgNotifyLoginDesc" = "هر زمان کسی سعی به ورود به وب پنل شما را داشت. درباره نامکاربری، آیپی و زمان، مطلع میشوید"
|
||||
"sessionMaxAge" = "مدت جلسه"
|
||||
"sessionMaxAgeDesc" = "بیشینه مدت زمانیکه میتوانید لاگین بمانید. واحد: دقیقه"
|
||||
"expireTimeDiff" = "اطلاعرسانی زمانانقضا"
|
||||
"expireTimeDiffDesc" = "وقتی زمان باقیمانده بهآستانه تعیینشده رسید، مطلع میشوید. واحد: روز"
|
||||
"trafficDiff" = "اطلاعرسانی ترافیک باقیمانده"
|
||||
"trafficDiffDesc" = "وقتی ترافیک باقیمانده بهآستانه تعیینشده رسید، مطلع میشوید. واحد: گیگابایت"
|
||||
"tgNotifyCpu" = "اطلاعرسانی بار پردازنده"
|
||||
"tgNotifyCpuDesc" = "اگر بار پردازنده از آستانه تعیینشده فراتر رفت، مطلع میشوید. واحد: درصد"
|
||||
"timeZone" = "منطقه زمانی"
|
||||
"timeZoneDesc" = "وظایف برنامه ریزی شده بر اساس این منطقهزمانی اجرا میشود"
|
||||
"subSettings" = "سابسکریپشن"
|
||||
"subEnable" = "فعالسازی سرویس سابسکریپشن"
|
||||
"subEnableDesc" = " سرویس سابسکریپشن را فعالمیکند"
|
||||
"subEnableDesc" = " سرویس سابسکریپشن را فعال میکند"
|
||||
"subListen" = "آدرس آیپی"
|
||||
"subListenDesc" = "آدرس آیپی برای سرویس سابسکریپشن. برای گوش دادن بهتمام آیپیها خالیبگذارید"
|
||||
"subPort" = "پورت"
|
||||
"subPortDesc" = "شماره پورت برای سرویس سابسکریپشن. باید پورت استفاده نشدهباشد"
|
||||
"subListenDesc" = "آدرس آیپی برای سابسکریپشن. برای گوشدادن بهتمام آیپیها خالیبگذارید"
|
||||
"subPort" = "شماره پورت"
|
||||
"subPortDesc" = "شماره پورت برای سابسکریپشن. باید پورت استفاده نشدهباشد"
|
||||
"subCertPath" = "مسیر کلید عمومی"
|
||||
"subCertPathDesc" = "مسیر فایل کلیدعمومی برای سرویس سابیکریپشن. با '/' شروعمیشود"
|
||||
"subCertPathDesc" = "مسیر فایل کلیدعمومی برای سابیکریپشن. با '/' شروعمیشود"
|
||||
"subKeyPath" = "مسیر کلید خصوصی"
|
||||
"subKeyPathDesc" = "مسیر فایل کلیدخصوصی برای سرویس سابسکریپشن. با '/' شروعمیشود"
|
||||
"subKeyPathDesc" = "مسیر فایل کلیدخصوصی برای سابسکریپشن. با '/' شروعمیشود"
|
||||
"subPath" = "URI مسیر"
|
||||
"subPathDesc" = "برای سرویس سابسکریپشن. با '/' شروع و با '/' خاتمه مییابد URI مسیر"
|
||||
"subPathDesc" = "مسیر لینک سابسکریپشن. با '/' شروع و با '/' خاتمه مییابد"
|
||||
"subDomain" = "نام دامنه"
|
||||
"subDomainDesc" = "آدرس دامنه برای سرویس سابسکریپشن. برای گوش دادن به تمام دامنهها و آیپیها خالیبگذارید"
|
||||
"subUpdates" = "فاصله بروزرسانی سابسکریپشن"
|
||||
"subUpdatesDesc" = "فاصله مابین بروزرسانی در برنامههای کاربری - واحد: ساعت"
|
||||
"subDomainDesc" = "آدرس دامنه برای سابسکریپشن. برای گوشدادن بهتمام دامنهها و آیپیها خالیبگذارید"
|
||||
"subUpdates" = "فاصله بروزرسانی"
|
||||
"subUpdatesDesc" = "فاصله مابین بروزرسانی لینک سابسکریپشن در برنامههای کاربری. واحد: ساعت"
|
||||
"subEncrypt" = "کدگذاری"
|
||||
"subEncryptDesc" = "کدگذاری خواهدشد Base64 محتوای برگشتی سرویس سابسکریپشن برپایه"
|
||||
"subEncryptDesc" = " محتوای برگشتی سابسکریپشن برپایه بیس64 کدگذاری خواهدشد"
|
||||
"subShowInfo" = "نمایش اطلاعات مصرف"
|
||||
"subShowInfoDesc" = "ترافیک و زمان باقیمانده را در برنامههای کاربری نمایش میدهد"
|
||||
"subURI" = "پروکسی معکوس URI مسیر"
|
||||
"subURIDesc" = "سابسکریپشن را برای استفاده در پشت پراکسیها تغییر میدهد URI مسیر"
|
||||
"subURI" = "پراکسی معکوس URI"
|
||||
"subURIDesc" = "سابسکریپشن از لینکی که در پشت پراکسیهای معکوس تنظیم شده، استفاده خواهدکرد"
|
||||
|
||||
[pages.settings.toasts]
|
||||
"modifySettings" = "ویرایش تنظیمات"
|
||||
@@ -303,6 +308,8 @@
|
||||
"advancedTemplate" = "پیشرفته"
|
||||
"generalConfigs" = "استراتژی کلی"
|
||||
"generalConfigsDesc" = "این گزینهها استراتژی کلی ترافیک را تعیین میکنند"
|
||||
"logConfigs" = "لاگ"
|
||||
"logConfigsDesc" = "لاگ ها ممکن است بر کارایی سرور شما تأثیر بگذارند. توصیه می شود فقط در صورت نیاز آن را آگاهانه فعال کنید"
|
||||
"blockConfigs" = "سپر محافظ"
|
||||
"blockConfigsDesc" = "این گزینهها ترافیک را بر اساس پروتکلهای درخواستی خاص، و وب سایتها مسدود میکند"
|
||||
"blockCountryConfigs" = "مسدودسازی کشور"
|
||||
@@ -310,9 +317,9 @@
|
||||
"directCountryConfigs" = "اتصال مستقیم کشور"
|
||||
"directCountryConfigsDesc" = "این گزینهها ترافیک را بر اساس کشور درخواستی خاص بصورت مستقیم ارسال میکند"
|
||||
"ipv4Configs" = "IPv4 مسیریابی"
|
||||
"ipv4ConfigsDesc" = "این گزینهها درخواستها را فقط از طریق آیپینسخه4 به مقصد هدایت میکند"
|
||||
"warpConfigs" = "تنظیمات برای وارپ"
|
||||
"warpConfigsDesc" = ".وارپ ترافیک را از طریق سرورهای کلادفلر به وب سایت ها هدایت می کند"
|
||||
"ipv4ConfigsDesc" = "این گزینهها ترافیک را از طریق آیپی نسخه4 ماشین، به مقصد هدایت میکند"
|
||||
"warpConfigs" = "WARP تنظمیات"
|
||||
"warpConfigsDesc" = "این گزینهها ترافیک را از طریق وارپ کلادفلر به مقصد هدایت میکند"
|
||||
"Template" = "پیکربندی پیشرفته الگو ایکسری"
|
||||
"TemplateDesc" = "فایل پیکربندی نهایی ایکسری بر اساس این الگو ایجاد میشود"
|
||||
"FreedomStrategy" = "Freedom استراتژی پروتکل"
|
||||
@@ -325,7 +332,7 @@
|
||||
"PrivateIpDesc" = "اتصال به آیپیهای رنج خصوصی را مسدود میکند"
|
||||
"Ads" = "مسدودسازی تبلیغات"
|
||||
"AdsDesc" = "وبسایتهای تبلیغاتی را مسدود میکند"
|
||||
"Family" = "محافظت خانواده"
|
||||
"Family" = "محافظ خانواده"
|
||||
"FamilyDesc" = "محتوای مخصوص بزرگسالان، و وبسایتهای ناامن را مسدود میکند"
|
||||
"IRIp" = "مسدودسازی اتصال به آیپیهای ایران"
|
||||
"IRIpDesc" = "اتصال به آیپیهای کشور ایران را مسدود میکند"
|
||||
@@ -352,9 +359,9 @@
|
||||
"DirectRussiaDomain" = "ارتباط مستقیم دامنه های روسیه"
|
||||
"DirectRussiaDomainDesc" = "اتصال مستقیم به دامنههای کشور روسیه"
|
||||
"GoogleIPv4" = "گوگل"
|
||||
"GoogleIPv4Desc" = "ترافیک را از طریق آیپینسخه4 به گوگل هدایت میکند"
|
||||
"GoogleIPv4Desc" = "ترافیک را از طریق آیپی نسخه4، به گوگل هدایت میکند"
|
||||
"NetflixIPv4" = "نتفلیکس"
|
||||
"NetflixIPv4Desc" = "ترافیک را از طریق آیپینسخه4 به نتفلیکس هدایت میکند"
|
||||
"NetflixIPv4Desc" = "ترافیک را از طریق آیپی نسخه4، به نتفلیکس هدایت میکند"
|
||||
"completeTemplate" = "کامل"
|
||||
"GoogleWARP" = "گوگل"
|
||||
"GoogleWARPDesc" = "ترافیک را از طریق وارپ به گوگل هدایت میکند"
|
||||
@@ -362,6 +369,8 @@
|
||||
"OpenAIWARPDesc" = "ترافیک را از طریق وارپ به چت جیپیتی هدایت میکند"
|
||||
"NetflixWARP" = "نتفلیکس"
|
||||
"NetflixWARPDesc" = "ترافیک را از طریق وارپ به نتفلیکس هدایت میکند"
|
||||
"MetaWARP" = "متا"
|
||||
"MetaWARPDesc" = "ترافیک را از طریق وارپ به متا (اینستاگرام، فیس بوک، واتساپ، تردز و...) هدایت می کند."
|
||||
"SpotifyWARP" = "اسپاتیفای"
|
||||
"SpotifyWARPDesc" = " ترافیک را از طریق وارپ به اسپاتیفای هدایت میکند"
|
||||
"Inbounds" = "ورودیها"
|
||||
@@ -397,6 +406,9 @@
|
||||
"bridge" = "پل"
|
||||
"portal" = "پورتال"
|
||||
"intercon" = "اتصال میانی"
|
||||
"settings" = "تنظیمات"
|
||||
"accountInfo" = "اطلاعات حساب"
|
||||
"outboundStatus" = "وضعیت خروجی"
|
||||
|
||||
[pages.xray.wireguard]
|
||||
"secretKey" = "کلید شخصی"
|
||||
@@ -436,48 +448,48 @@
|
||||
"report" = "🕰 گزارشاتزمانبندیشده: {{ .RunTime }}\r\n"
|
||||
"datetime" = "⏰ تاریخوزمان: {{ .DateTime }}\r\n"
|
||||
"hostname" = "💻 ناممیزبان: {{ .Hostname }}\r\n"
|
||||
"version" = "🚀 نسخهپنل: {{ .Version }}\r\n"
|
||||
"version" = "🚀 X-UI: {{ .Version }}\r\n"
|
||||
"ipv6" = "🌐 IPv6: {{ .IPv6 }}\r\n"
|
||||
"ipv4" = "🌐 IPv4: {{ .IPv4 }}\r\n"
|
||||
"ip" = "🌐 آدرسآیپی: {{ .IP }}\r\n"
|
||||
"serverUpTime" = "⏳ مدتکارکردسیستم: {{ .UpTime }} {{ .Unit }}\r\n"
|
||||
"ip" = "🌐 IP: {{ .IP }}\r\n"
|
||||
"serverUpTime" = "⏳ مدتکارکرد: {{ .UpTime }} {{ .Unit }}\r\n"
|
||||
"serverLoad" = "📈 بارسیستم: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n"
|
||||
"serverMemory" = "📋 RAM: {{ .Current }}/{{ .Total }}\r\n"
|
||||
"tcpCount" = "🔹 TCP: {{ .Count }}\r\n"
|
||||
"udpCount" = "🔸 UDP: {{ .Count }}\r\n"
|
||||
"traffic" = "🚦 ترافیک: {{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n"
|
||||
"xrayStatus" = "ℹ️ وضعیتایکسری: {{ .State }}\r\n"
|
||||
"xrayStatus" = "ℹ️ وضعیت: {{ .State }}\r\n"
|
||||
"username" = "👤 نامکاربری: {{ .Username }}\r\n"
|
||||
"time" = "⏰ زمان: {{ .Time }}\r\n"
|
||||
"inbound" = "📍 نامورودی: {{ .Remark }}\r\n"
|
||||
"port" = "🔌 پورت: {{ .Port }}\r\n"
|
||||
"expire" = "📅 تاریخانقضا: {{ .DateTime }}\r\n \r\n"
|
||||
"expireIn" = "📅 باقیماندهتاانقضا: {{ .Time }}\r\n \r\n"
|
||||
"expireIn" = "📅 انقضا در: {{ .Time }}\r\n \r\n"
|
||||
"active" = "💡 فعال: {{ .Enable }}\r\n"
|
||||
"online" = "🌐 وضعیت اتصال: {{ .Status }}\r\n"
|
||||
"online" = "🌐 وضعیتاتصال: {{ .Status }}\r\n"
|
||||
"email" = "📧 ایمیل: {{ .Email }}\r\n"
|
||||
"upload" = "🔼 آپلود↑: {{ .Upload }}\r\n"
|
||||
"download" = "🔽 دانلود↓: {{ .Download }}\r\n"
|
||||
"total" = "🔄 کل: {{ .UpDown }} / {{ .Total }}\r\n"
|
||||
"exhaustedMsg" = "🚨 {{ .Type }} بهاتمامرسیدهاست:\r\n"
|
||||
"exhaustedCount" = "🚨 تعداد {{ .Type }} بهاتمامرسیدهاست:\r\n"
|
||||
"exhaustedCount" = "🚨 تعداد {{ .Type }} بهاتمام رسیدهاست:\r\n"
|
||||
"onlinesCount" = "🌐 کاربرانآنلاین: {{ .Count }}\r\n"
|
||||
"disabled" = "🛑 غیرفعال: {{ .Disabled }}\r\n"
|
||||
"depleteSoon" = "🔜 بهزودیبهپایانخواهدرسید: {{ .Deplete }}\r\n \r\n"
|
||||
"depleteSoon" = "🔜 بهزودی بهپایان خواهدرسید: {{ .Deplete }}\r\n \r\n"
|
||||
"backupTime" = "🗄 زمانپشتیبانگیری: {{ .Time }}\r\n"
|
||||
"yes" = "✅ بله"
|
||||
"no" = "❌ خیر"
|
||||
|
||||
[tgbot.buttons]
|
||||
"dbBackup" = "دریافت پشتیبان دیتابیس"
|
||||
"dbBackup" = "دریافت فایل پشتیبان"
|
||||
"serverUsage" = "استفاده از سیستم"
|
||||
"getInbounds" = "دریافت ورودیها"
|
||||
"depleteSoon" = "بهزودی به پایان خواهد رسید"
|
||||
"depleteSoon" = "بهزودی بهپایان خواهدرسید"
|
||||
"clientUsage" = "دریافت آمار کاربر"
|
||||
"onlines" = "کاربران آنلاین"
|
||||
"commands" = "دستورات"
|
||||
|
||||
[tgbot.answers]
|
||||
"getInboundsFailed" = "❌ دریافت ورودیها باخطا مواجه شد"
|
||||
"askToAddUser" = "پیکربندی شما پیدا نشد!\r\nشما باید نامکاربری تلگرام خود را پیکربندی کنید و از مدیر پنل خود بخواهید که آن را به پیکربندی شما اضافه کند"
|
||||
"askToAddUserName" = "پیکربندی شما یافت نشد!\r\nلطفاً از مدیر پنل درخواست کنید اطلاعات تلگرام شما را در پیکربندی(های) مربوط بهشما تنظیمکند \r\n\r\nنامکاربری شما: @{{ .TgUserName }}"
|
||||
"askToAddUser" = "پیکربندی شما پیدا نشد!\r\nشما باید نامکاربری تلگرام خود را تنظیم کنید و از مدیر سرویس خود بخواهید که آن را به پیکربندی(های) شما اضافه کند"
|
||||
"askToAddUserName" = "پیکربندی شما یافت نشد!\r\nلطفاً از مدیر سرویس خود بخواهید اطلاعات تلگرام شما را به پیکربندی(های) شما اضافه کند \r\n\r\nنامکاربری شما: @{{ .TgUserName }}"
|
||||
|
||||
@@ -77,15 +77,19 @@
|
||||
"title" = "Статус системы"
|
||||
"memory" = "ОЗУ"
|
||||
"hard" = "Место на диске"
|
||||
"xrayStatus" = "Статус Xray"
|
||||
"machineInfo" = "Машина"
|
||||
"hostname" = "Имя хоста"
|
||||
"xrayStatus" = "Xray"
|
||||
"stopXray" = "Остановка"
|
||||
"restartXray" = "Перезапуск Xray"
|
||||
"restartXray" = "Перезапуск"
|
||||
"xraySwitch" = "Сменить версию"
|
||||
"xraySwitchClick" = "Выберите желаемую версию"
|
||||
"xraySwitchClickDesk" = "Выбирайте внимательно, так как старые версии могут быть несовместимы с текущими конфигурациями"
|
||||
"operationHours" = "Время работы"
|
||||
"operationHoursDesc" = "Время работы системы: время с момента запуска."
|
||||
"operationHoursDesc" = "Время безотказной работы с момента запуска ОС"
|
||||
"xrayoperationHoursDesc" = "Время работы с момента последней перезагрузки Xray"
|
||||
"systemLoad" = "Системная нагрузка"
|
||||
"systemLoadDesc" = "Средняя нагрузка за последние 1, 5 и 15 минут"
|
||||
"connectionTcpCountDesc" = "Всего подключений TCP по всем сетевым картам."
|
||||
"connectionUdpCountDesc" = "Общее количество подключений UDP по всем сетевым картам."
|
||||
"upSpeed" = "Общая скорость отдачи"
|
||||
@@ -100,8 +104,8 @@
|
||||
"backup" = "Бекап и восстановление"
|
||||
"backupTitle" = "База данных бекапа и восстановления"
|
||||
"backupDescription" = "Не забудьте сделать резервную копию перед импортом новой базы данных"
|
||||
"exportDatabase" = "Экспорт базы данных"
|
||||
"importDatabase" = "Импорт базы данных"
|
||||
"exportDatabase" = "Резерв"
|
||||
"importDatabase" = "Восстановить"
|
||||
|
||||
[pages.inbounds]
|
||||
"title" = "Подключения"
|
||||
@@ -134,7 +138,7 @@
|
||||
"destinationPort" = "Порт назначения"
|
||||
"targetAddress" = "Целевой адрес"
|
||||
"monitorDesc" = "Оставьте пустым по умолчанию"
|
||||
"meansNoLimit" = "Значит без ограничений"
|
||||
"meansNoLimit" = "Ноль означает неограниченно. (значение: ГБ)"
|
||||
"totalFlow" = "Общий расход"
|
||||
"leaveBlankToNeverExpire" = "Оставьте пустым, чтобы сделать бессрочно"
|
||||
"noRecommendKeepDefault" = "Нет особых требований для сохранения настроек по умолчанию"
|
||||
@@ -171,7 +175,7 @@
|
||||
"info" = "Информация"
|
||||
"same" = "Тот же"
|
||||
"inboundData" = "Входящие данные"
|
||||
"copyToClipboard" = "Копировать в буфер обмена"
|
||||
"exportInbound" = "Экспорт входящих"
|
||||
"import" = "Импортировать"
|
||||
"importInbound" = "Импортировать входящее сообщение"
|
||||
|
||||
@@ -197,17 +201,18 @@
|
||||
"obtain" = "Получить"
|
||||
|
||||
[pages.inbounds.stream.general]
|
||||
"requestHeader" = "Заголовок запроса"
|
||||
"request" = "Запрос"
|
||||
"response" = "Ответ"
|
||||
"name" = "Имя"
|
||||
"value" = "Значение"
|
||||
"value" = "Ценить"
|
||||
|
||||
[pages.inbounds.stream.tcp]
|
||||
"requestVersion" = "Версия запроса"
|
||||
"requestMethod" = "Метод запроса"
|
||||
"requestPath" = "Петь запроса"
|
||||
"responseVersion" = "Версия ответа"
|
||||
"responseStatus" = "Статус ответа"
|
||||
"responseStatusDescription" = "Описание статуса ответа"
|
||||
"version" = "Версия"
|
||||
"method" = "Метод"
|
||||
"path" = "Путь"
|
||||
"status" = "Положение дел"
|
||||
"statusDescription" = "Описание статуса"
|
||||
"requestHeader" = "Заголовок запроса"
|
||||
"responseHeader" = "Заголовок ответа"
|
||||
|
||||
[pages.inbounds.stream.quic]
|
||||
@@ -304,6 +309,8 @@
|
||||
"advancedTemplate" = "Расширенные шаблоны"
|
||||
"generalConfigs" = "Основные настройки"
|
||||
"generalConfigsDesc" = "Общие настройки"
|
||||
"logConfigs" = "Журнал"
|
||||
"logConfigsDesc" = "Журналы могут повлиять на эффективность вашего сервера. Рекомендуется включать их с умом только в случае ваших нужд!"
|
||||
"blockConfigs" = "Блокирующие конфигурации"
|
||||
"blockConfigsDesc" = "Эти параметры не позволят пользователям подключаться к определенным протоколам и веб-сайтам."
|
||||
"blockCountryConfigs" = "Конфигурация блокировки стран"
|
||||
@@ -362,6 +369,8 @@
|
||||
"OpenAIWARPDesc" = "Добавить маршрутизацию для OpenAI (ChatGPT) через WARP"
|
||||
"NetflixWARP" = "Маршрутизация Netflix через WARP"
|
||||
"NetflixWARPDesc" = "Добавить маршрутизацию для Netflix через WARP"
|
||||
"MetaWARP" = "Мета"
|
||||
"MetaWARPDesc" = "Направляет трафик в Meta (Instagram, Facebook, WhatsApp, Threads...) через WARP."
|
||||
"SpotifyWARP" = "Маршрутизация Spotify через WARP"
|
||||
"SpotifyWARPDesc" = "Добавить маршрутизацию для Spotify через WARP"
|
||||
"completeTemplate" = "Все"
|
||||
@@ -398,6 +407,9 @@
|
||||
"bridge" = "Мост"
|
||||
"portal" = "Портал"
|
||||
"intercon" = "Соединение"
|
||||
"settings" = "Настройки"
|
||||
"accountInfo" = "Информация Об Учетной Записи"
|
||||
"outboundStatus" = "Исходящий статус"
|
||||
|
||||
[pages.xray.wireguard]
|
||||
"secretKey" = "Секретный ключ"
|
||||
|
||||
@@ -77,15 +77,19 @@
|
||||
"title" = "Trạng thái hệ thống"
|
||||
"memory" = "Bộ nhớ"
|
||||
"hard" = "Ổ cứng"
|
||||
"xrayStatus" = "Trạng thái của Xray"
|
||||
"stopXray" = "Dừng Xray"
|
||||
"restartXray" = "Khởi động lại Xray"
|
||||
"machineInfo" = "Máy"
|
||||
"hostname" = "Tên máy chủ"
|
||||
"xrayStatus" = "Xray"
|
||||
"stopXray" = "Dừng"
|
||||
"restartXray" = "Khởi động lại"
|
||||
"xraySwitch" = "Chuyển đổi phiên bản"
|
||||
"xraySwitchClick" = "Chọn phiên bản mà bạn muốn chuyển đổi sang."
|
||||
"xraySwitchClickDesk" = "Hãy lựa chọn thận trọng, vì các phiên bản cũ có thể không tương thích với các cấu hình hiện tại, của Bạn"
|
||||
"operationHours" = "Thời gian hoạt động"
|
||||
"operationHoursDesc" = "Thời gian hoạt động của hệ thống: thời gian kể từ khi khởi động."
|
||||
"operationHoursDesc" = "Thời gian hoạt động kể từ khi khởi động hệ điều hành"
|
||||
"xrayoperationHoursDesc" = "Thời gian hoạt động kể từ lần khởi động Xray cuối cùng"
|
||||
"systemLoad" = "Tải hệ thống"
|
||||
"systemLoadDesc" = "Tải trung bình trong 1, 5 và 15 phút qua"
|
||||
"connectionTcpCountDesc" = "Tổng số kết nối TCP trên tất cả các card mạng."
|
||||
"connectionUdpCountDesc" = "Tổng số kết nối UDP trên tất cả các card mạng."
|
||||
"upSpeed" = "Tổng tốc độ tải lên cho tất cả các thẻ mạng."
|
||||
@@ -134,7 +138,7 @@
|
||||
"destinationPort" = "Cổng đích"
|
||||
"targetAddress" = "Địa chỉ mục tiêu"
|
||||
"monitorDesc" = "Mặc định để trống"
|
||||
"meansNoLimit" = "Nghĩa là không giới hạn"
|
||||
"meansNoLimit" = "Số không có nghĩa là không giới hạn. (đơn vị: GB)"
|
||||
"totalFlow" = "Tổng lưu lượng"
|
||||
"leaveBlankToNeverExpire" = "Để trống để không bao giờ hết hạn"
|
||||
"noRecommendKeepDefault" = "Không yêu cầu đặc biệt để giữ nguyên cài đặt mặc định"
|
||||
@@ -171,7 +175,7 @@
|
||||
"info" = "Thông tin"
|
||||
"same" = "Như nhau"
|
||||
"inboundData" = "Dữ liệu gửi đến"
|
||||
"copyToClipboard" = "Sao chép vào bảng nhớ tạm"
|
||||
"exportInbound" = "Xuất nhập khẩu"
|
||||
"import" = "Nhập"
|
||||
"importInbound" = "Nhập hàng gửi về"
|
||||
|
||||
@@ -197,17 +201,18 @@
|
||||
"obtain" = "Nhận được"
|
||||
|
||||
[pages.inbounds.stream.general]
|
||||
"requestHeader" = "Tiêu đề yêu cầu"
|
||||
"request" = "Lời yêu cầu"
|
||||
"response" = "Phản ứng"
|
||||
"name" = "Tên"
|
||||
"value" = "Giá trị"
|
||||
|
||||
[pages.inbounds.stream.tcp]
|
||||
"requestVersion" = "Phiên bản yêu cầu"
|
||||
"requestMethod" = "Phương thức yêu cầu"
|
||||
"requestPath" = "Đường dẫn yêu cầu"
|
||||
"responseVersion" = "Phiên bản phản hồi"
|
||||
"responseStatus" = "Trạng thái phản hồi"
|
||||
"responseStatusDescription" = "Mô tả trạng thái phản hồi"
|
||||
"version" = "Phiên bản"
|
||||
"method" = "Phương pháp"
|
||||
"path" = "Con đường"
|
||||
"status" = "Trạng thái"
|
||||
"statusDescription" = "Tình trạng Mô tả"
|
||||
"requestHeader" = "Tiêu đề yêu cầu"
|
||||
"responseHeader" = "Tiêu đề phản hồi"
|
||||
|
||||
[pages.inbounds.stream.quic]
|
||||
@@ -304,6 +309,8 @@
|
||||
"advancedTemplate" = "Mẫu nâng cao"
|
||||
"generalConfigs" = "Cấu hình Chung"
|
||||
"generalConfigsDesc" = "Những tùy chọn này sẽ cung cấp điều chỉnh tổng quát."
|
||||
"logConfigs" = "Nhật ký"
|
||||
"logConfigsDesc" = "Nhật ký có thể ảnh hưởng đến hiệu suất máy chủ của bạn. Bạn chỉ nên kích hoạt nó một cách khôn ngoan trong trường hợp bạn cần"
|
||||
"blockConfigs" = "Cấu hình Chặn"
|
||||
"blockConfigsDesc" = "Những tùy chọn này sẽ ngăn người dùng kết nối đến các giao thức và trang web cụ thể."
|
||||
"blockCountryConfigs" = "Cấu hình Chặn Quốc gia"
|
||||
@@ -362,6 +369,8 @@
|
||||
"OpenAIWARPDesc" = "Thêm định tuyến cho OpenAI (ChatGPT) qua WARP."
|
||||
"NetflixWARP" = "Định tuyến Netflix qua WARP."
|
||||
"NetflixWARPDesc" = "Thêm định tuyến cho Netflix qua WARP."
|
||||
"MetaWARP" = "Meta"
|
||||
"MetaWARPDesc" = "Định tuyến lưu lượng truy cập tới Meta (Instagram, Facebook, WhatsApp, Threads,...) thông qua WARP."
|
||||
"SpotifyWARP" = "Định tuyến Spotify qua WARP."
|
||||
"SpotifyWARPDesc" = "Thêm định tuyến cho Spotify qua WARP."
|
||||
"completeTemplate" = "Tất cả"
|
||||
@@ -398,6 +407,9 @@
|
||||
"bridge" = "Liên kết"
|
||||
"portal" = "Cổng thông tin"
|
||||
"intercon" = "Kết nối"
|
||||
"settings" = "cài đặt"
|
||||
"accountInfo" = "Thông tin tài khoản"
|
||||
"outboundStatus" = "Trạng thái đầu ra"
|
||||
|
||||
[pages.xray.wireguard]
|
||||
"secretKey" = "Chìa khoá bí mật"
|
||||
@@ -454,7 +466,7 @@
|
||||
"port" = "🔌 Cổng: {{ .Port }}\r\n"
|
||||
"expire" = "📅 Hạn sử dụng: {{ .DateTime }}\r\n \r\n"
|
||||
"expireIn" = "📅 Hết hạn vào: {{ .Time }}\r\n \r\n"
|
||||
"active" = "💡 Có hiệu lực {{ .Enable }}\r\n"
|
||||
"active" = "💡 Có hiệu lực {{ .Enable }}\r\n"
|
||||
"online" = "🌐 Tình trạng kết nối: {{ .Status }}\r\n"
|
||||
"email" = "📧 Email: {{ .Email }}\r\n"
|
||||
"upload" = "🔼 Tải lên↑: {{ .Upload }}\r\n"
|
||||
|
||||
@@ -77,15 +77,19 @@
|
||||
"title" = "系统状态"
|
||||
"memory" = "内存"
|
||||
"hard" = "硬盘"
|
||||
"xrayStatus" = "Xray 状态"
|
||||
"machineInfo" = "机器"
|
||||
"hostname" = "主机名"
|
||||
"xrayStatus" = "Xray"
|
||||
"stopXray" = "停止"
|
||||
"restartXray" = "重启"
|
||||
"xraySwitch" = "切换版本"
|
||||
"xraySwitchClick" = "点击你想切换的版本"
|
||||
"xraySwitchClickDesk" = "请谨慎选择,旧版本可能配置不兼容"
|
||||
"operationHours" = "运行时间"
|
||||
"operationHoursDesc" = "系统自启动以来的运行时间"
|
||||
"operationHoursDesc" = "自操作系统启动以来的正常运行时间"
|
||||
"xrayoperationHoursDesc" = "自 Xray 上次重启以来的正常运行时间"
|
||||
"systemLoad" = "系统负载"
|
||||
"systemLoadDesc" = "过去 1 分钟、5 分钟和 15 分钟的平均负载"
|
||||
"connectionTcpCountDesc" = "所有网卡的总 TCP 连接数。"
|
||||
"connectionUdpCountDesc" = "所有网卡的总 UDP 连接数。"
|
||||
"upSpeed" = "所有网卡的总上传速度"
|
||||
@@ -134,7 +138,7 @@
|
||||
"destinationPort" = "目标端口"
|
||||
"targetAddress" = "目标地址"
|
||||
"monitorDesc" = "默认留空即可"
|
||||
"meansNoLimit" = "表示不限制"
|
||||
"meansNoLimit" = "零意味着无限。(单位:GB)"
|
||||
"totalFlow" = "总流量"
|
||||
"leaveBlankToNeverExpire" = "留空则永不到期"
|
||||
"noRecommendKeepDefault" = "没有特殊需求保持默认即可"
|
||||
@@ -171,7 +175,7 @@
|
||||
"info" = "信息"
|
||||
"same" = "相同"
|
||||
"inboundData" = "入站数据"
|
||||
"copyToClipboard" = "复制到剪贴板"
|
||||
"exportInbound" = "出口 入境"
|
||||
"import"="导入"
|
||||
"importInbound" = "导入入站"
|
||||
|
||||
@@ -197,17 +201,18 @@
|
||||
"obtain" = "获取"
|
||||
|
||||
[pages.inbounds.stream.general]
|
||||
"requestHeader" = "请求头"
|
||||
"name" = "名称"
|
||||
"value" = "值"
|
||||
"request" = "要求"
|
||||
"response" = "回复"
|
||||
"name" = "姓名"
|
||||
"value" = "价值"
|
||||
|
||||
[pages.inbounds.stream.tcp]
|
||||
"requestVersion" = "请求版本"
|
||||
"requestMethod" = "请求方法"
|
||||
"requestPath" = "请求路径"
|
||||
"responseVersion" = "响应版本"
|
||||
"responseStatus" = "响应状态"
|
||||
"responseStatusDescription" = "响应状态说明"
|
||||
"version" = "版本"
|
||||
"method" = "方法"
|
||||
"path" = "小路"
|
||||
"status" = "地位"
|
||||
"statusDescription" = "状态说明"
|
||||
"requestHeader" = "请求头"
|
||||
"responseHeader" = "响应头"
|
||||
|
||||
[pages.inbounds.stream.quic]
|
||||
@@ -304,6 +309,8 @@
|
||||
"advancedTemplate" = "高级模板部件"
|
||||
"generalConfigs" = "通用配置"
|
||||
"generalConfigsDesc" = "这些选项将提供一般调整"
|
||||
"logConfigs"="日志"
|
||||
"logConfigsDesc" = "日志可能会影响您服务器的效率。建议仅在您需要时明智地启用它"
|
||||
"blockConfigs" = "阻塞配置"
|
||||
"blockConfigsDesc" = "这些选项将阻止用户连接到特定协议和网站"
|
||||
"blockCountryConfigs" = "阻止国家配置"
|
||||
@@ -362,6 +369,8 @@
|
||||
"OpenAIWARPDesc" = "将OpenAI(ChatGPT)路由添加到WARP"
|
||||
"NetflixWARP" = "将 Netflix 路由到 WARP"
|
||||
"NetflixWARPDesc" = "为Netflix添加路由到WARP"
|
||||
"MetaWARP"="元"
|
||||
"MetaWARPDesc" = "通过 WARP 将流量路由到 Meta(Instagram、Facebook、WhatsApp、Threads...)"
|
||||
"SpotifyWARP" = "将 Spotify 路由到 WARP"
|
||||
"SpotifyWARPDesc" = "为Spotify添加路由到WARP"
|
||||
"completeTemplate" = "全部"
|
||||
@@ -398,6 +407,9 @@
|
||||
"bridge" = "桥"
|
||||
"portal" = "门户"
|
||||
"intercon" = "互连"
|
||||
"settings" = "设置"
|
||||
"accountInfo" = "帐户信息"
|
||||
"outboundStatus" = "出站状态"
|
||||
|
||||
[pages.xray.wireguard]
|
||||
"secretKey" = "密钥"
|
||||
|
||||
20
x-ui.sh
20
x-ui.sh
@@ -133,8 +133,14 @@ custom_version() {
|
||||
eval $install_command
|
||||
}
|
||||
|
||||
# Function to handle the deletion of the script file
|
||||
delete_script() {
|
||||
rm "$0" # Remove the script file itself
|
||||
exit 1
|
||||
}
|
||||
|
||||
uninstall() {
|
||||
confirm "Are you sure you want to uninstall the panel? xray will also uninstalled!" "n"
|
||||
confirm "Are you sure you want to uninstall the panel? xray will also uninstalled!" " n"
|
||||
if [[ $? != 0 ]]; then
|
||||
if [[ $# == 0 ]]; then
|
||||
show_menu
|
||||
@@ -148,14 +154,14 @@ uninstall() {
|
||||
systemctl reset-failed
|
||||
rm /etc/x-ui/ -rf
|
||||
rm /usr/local/x-ui/ -rf
|
||||
|
||||
echo -e "\nUninstalled Successfully."
|
||||
echo ""
|
||||
echo -e "Uninstalled Successfully,If you want to remove this script,then after exiting the script run ${green}rm /usr/bin/x-ui -f${plain} to delete it."
|
||||
echo -e "If you need to install this panel again, you can use below command:"
|
||||
echo -e "${green}bash <(curl -Ls https://raw.githubusercontent.com/alireza0/x-ui/master/install.sh)${plain}"
|
||||
echo ""
|
||||
|
||||
if [[ $# == 0 ]]; then
|
||||
before_show_menu
|
||||
fi
|
||||
# Trap the SIGTERM signal
|
||||
trap delete_script SIGTERM
|
||||
delete_script
|
||||
}
|
||||
|
||||
reset_user() {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package xray
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
"x-ui/logger"
|
||||
)
|
||||
@@ -14,26 +15,20 @@ type LogWriter struct {
|
||||
}
|
||||
|
||||
func (lw *LogWriter) Write(m []byte) (n int, err error) {
|
||||
regex := regexp.MustCompile(`^(\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2}) \[([^\]]+)\] (.+)$`)
|
||||
|
||||
// Convert the data to a string
|
||||
message := strings.TrimSpace(string(m))
|
||||
|
||||
messages := strings.Split(message, "\n")
|
||||
lw.lastLine = messages[len(messages)-1]
|
||||
|
||||
for _, msg := range messages {
|
||||
messageBody := msg
|
||||
matches := regex.FindStringSubmatch(msg)
|
||||
|
||||
// Remove timestamp
|
||||
splittedMsg := strings.SplitN(msg, " ", 3)
|
||||
if len(splittedMsg) > 2 {
|
||||
messageBody = strings.TrimSpace(strings.SplitN(msg, " ", 3)[2])
|
||||
}
|
||||
|
||||
// Find level in []
|
||||
startIndex := strings.Index(messageBody, "[")
|
||||
endIndex := strings.Index(messageBody, "]")
|
||||
if startIndex != -1 && endIndex != -1 {
|
||||
level := strings.TrimSpace(messageBody[startIndex+1 : endIndex])
|
||||
msgBody := "XRAY: " + strings.TrimSpace(messageBody[endIndex+1:])
|
||||
if len(matches) > 3 {
|
||||
level := matches[2]
|
||||
msgBody := matches[3]
|
||||
|
||||
// Map the level to the appropriate logger function
|
||||
switch level {
|
||||
|
||||
Reference in New Issue
Block a user