Compare commits

..

19 Commits

Author SHA1 Message Date
Alireza Ahmadi
1700e85d06 Update docker.yml 2023-02-16 09:40:01 +01:00
Alireza Ahmadi
931e949442 docker workflow 2023-02-15 23:04:58 +01:00
Alireza Ahmadi
8bca416142 Ready for new version 2023-02-15 21:29:01 +01:00
Alireza Ahmadi
adf2b96f38 small github related changes 2023-02-15 20:52:01 +01:00
Alireza Ahmadi
e47ea983bd New xray settings options 2023-02-15 20:51:25 +01:00
Alireza Ahmadi
b62f747e88 Default timezone change to Tehran 2023-02-15 16:35:19 +01:00
Alireza Ahmadi
784e6e24e2 Enhanced search 2023-02-15 12:33:34 +01:00
Alireza Ahmadi
4af4aadd14 infoModal add more info 2023-02-15 12:33:13 +01:00
Alireza Ahmadi
9b3415865a SMall change in view and Readme 2023-02-15 12:21:18 +01:00
Alireza Ahmadi
8b6ea9e0d5 initiate email field for new client 2023-02-12 12:43:38 +01:00
Alireza Ahmadi
2a6f678b53 version increase to 0.2.2 2023-02-12 01:35:36 +01:00
Alireza Ahmadi
7225f00c2b revert github builder to ubuntu 18.04 2023-02-12 01:26:26 +01:00
Alireza Ahmadi
85db586d95 revert github builder to ubuntu 18.04 2023-02-12 01:24:35 +01:00
Alireza Ahmadi
2d7663f971 Merge pull request #2 from alireza0:fix-remark
Add user's email to remark if exists
Fix false reports
Fix some translations
Fix dockerfile
2023-02-11 17:50:01 +01:00
Alireza Ahmadi
c935014ba9 Increse version 2023-02-11 17:07:28 +01:00
Alireza Ahmadi
2c61b5a426 Fix dockerfile 2023-02-11 17:07:00 +01:00
Alireza Ahmadi
a5cfe4fc1e Fix in translations 2023-02-11 17:05:58 +01:00
Alireza Ahmadi
3cc3b3b04d Fix false reports 2023-02-11 17:05:35 +01:00
Alireza Ahmadi
8e550bc308 Add user's email to remark if exists 2023-02-11 14:48:02 +01:00
16 changed files with 435 additions and 179 deletions

10
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,10 @@
version: 2
updates:
- package-ecosystem: "gomod"
directory: "/"
schedule:
interval: "daily"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"

View File

@@ -1,18 +1,12 @@
name: Docker Image CI
on:
push:
branches: [ master ]
tags:
- "*"
workflow_dispatch:
inputs:
project:
description: 'Project'
required: true
default: ''
jobs:
build:
runs-on: ubuntu-latest
steps:
@@ -20,30 +14,42 @@ jobs:
with:
submodules: true
- name: Docker meta
id: meta
uses: docker/metadata-action@v4
with:
images: |
alireza7/x-ui
ghcr.io/alireza0/x-ui
tags: |
type=ref,event=branch
type=ref,event=tag
type=pep440,pattern={{version}}
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Docker Hub login
env:
DOCKERHUB_USERNAME: ${{ secrets.DOCKER_HUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKER_HUB_TOKEN }}
run: |
echo "${DOCKERHUB_TOKEN}" | docker login --username ${DOCKERHUB_USERNAME} --password-stdin
- name: Set up Docker Buildx
id: buildx
uses: crazy-max/ghaction-docker-buildx@v1
with:
buildx-version: latest
uses: docker/setup-buildx-action@v2
- name: Build Dockerfile
env:
DOCKERHUB_USERNAME: ${{ secrets.DOCKER_HUB_USERNAME }}
DOCKERHUB_REPO: ${{ secrets.DOCKER_HUB_REPOSITORY }}
run: |
docker buildx build \
--platform=linux/amd64,linux/arm64 \
--output "type=image,push=true" \
--file ./Dockerfile ./ \
--tag $(echo "${DOCKERHUB_USERNAME}" | tr '[:upper:]' '[:lower:]')/${{ github.event.inputs.project }}:latest
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_TOKEN }}
- name: Login to GHCR
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v4
with:
context: .
push: true
platforms: linux/amd64,linux/arm64/v8
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

View File

@@ -4,9 +4,10 @@ on:
tags:
- "*"
workflow_dispatch:
jobs:
release:
runs-on: ubuntu-22.04
runs-on: ubuntu-18.04
outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
steps:
@@ -14,7 +15,7 @@ jobs:
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GAYHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: ${{ github.ref }}
@@ -23,7 +24,7 @@ jobs:
linuxamd64build:
name: build x-ui amd64 version
needs: release
runs-on: ubuntu-22.04
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2
- name: Set up Go
@@ -54,7 +55,7 @@ jobs:
- name: upload
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GAYHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.release.outputs.upload_url }}
asset_path: x-ui-linux-amd64.tar.gz
@@ -63,7 +64,7 @@ jobs:
linuxarm64build:
name: build x-ui arm64 version
needs: release
runs-on: ubuntu-22.04
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2
- name: Set up Go
@@ -96,7 +97,7 @@ jobs:
- name: upload
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GAYHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.release.outputs.upload_url }}
asset_path: x-ui-linux-arm64.tar.gz
@@ -105,7 +106,7 @@ jobs:
linuxs390xbuild:
name: build x-ui s390x version
needs: release
runs-on: ubuntu-22.04
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2
- name: Set up Go
@@ -138,7 +139,7 @@ jobs:
- name: upload
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GAYHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.release.outputs.upload_url }}
asset_path: x-ui-linux-s390x.tar.gz

View File

@@ -7,10 +7,10 @@ RUN go build main.go
FROM alpine
LABEL org.opencontainers.image.authors="alireza7@gmail.com"
RUN apk add ca-certificates tzdata
ENV TZ=Asia/Tehran
WORKDIR /app
RUN apk add ca-certificates tzdata && mkdir bin
COPY --from=builder /app/main /app/x-ui
COPY ./bin/. /app/bin/.
VOLUME [ "/etc/x-ui" ]
CMD [ "./x-ui" ]

View File

@@ -1,4 +1,9 @@
# x-ui
![](https://img.shields.io/github/v/release/alireza0/x-ui.svg)
![](https://img.shields.io/docker/pulls/alireza7/x-ui.svg)
[![Go Report Card](https://goreportcard.com/badge/github.com/alireza0/x-ui)](https://goreportcard.com/report/github.com/alireza0/x-ui)
[![Downloads](https://img.shields.io/github/downloads/alireza0/x-ui/total.svg)](https://img.shields.io/github/downloads/alireza0/x-ui/total.svg)
[![License](https://img.shields.io/badge/license-GPL%20V3-blue.svg?longCache=true)](https://www.gnu.org/licenses/gpl-3.0.en.html)
> **Disclaimer: This project is only for personal learning and communication, please do not use it for illegal purposes, please do not use it in a production environment**
@@ -28,7 +33,7 @@ xray panel supporting multi-protocol, **Multi-lang (English,Farsi,Chinese)**
- Support one-click SSL certificate application and automatic renewal
- For more advanced configuration items, please refer to the panel
# Inbouds picture
# Screenshoot from Inbouds page
![inbounds](./media/inbounds.png)

View File

@@ -1 +1 @@
0.2.0
0.2.3

View File

@@ -171,7 +171,7 @@ class AllSetting {
this.tgRunTime = "";
this.xrayTemplateConfig = "";
this.timeLocation = "Asia/Shanghai";
this.timeLocation = "Asia/Tehran";
if (data == null) {
return

View File

@@ -952,8 +952,7 @@ class Inbound extends XrayCommonClass {
address = this.stream.tls.server;
}
}
remark = this.settings.vmesses[clientIndex].email ?? remark;
let obj = {
v: '2',
ps: remark,
@@ -976,7 +975,6 @@ class Inbound extends XrayCommonClass {
const port = this.port;
const type = this.stream.network;
const params = new Map();
remark = settings.vlesses[clientIndex].email ?? remark;
params.set("type", this.stream.network);
if (this.xtls) {
params.set("security", "xtls");
@@ -1061,16 +1059,27 @@ class Inbound extends XrayCommonClass {
genTrojanLink(address='', remark='', clientIndex=0) {
let settings = this.settings;
remark = settings.trojans[clientIndex].email ?? remark;
return `trojan://${settings.trojans[clientIndex].password}@${address}:${this.port}#${encodeURIComponent(remark)}`;
}
genLink(address='', remark='', clientIndex=0) {
switch (this.protocol) {
case Protocols.VMESS: return this.genVmessLink(address, remark, clientIndex);
case Protocols.VLESS: return this.genVLESSLink(address, remark, clientIndex);
case Protocols.VMESS:
if (this.settings.vmesses[clientIndex].email != ""){
remark += '-' + this.settings.vmesses[clientIndex].email
}
return this.genVmessLink(address, remark, clientIndex);
case Protocols.VLESS:
if (this.settings.vlesses[clientIndex].email != ""){
remark += '-' + this.settings.vlesses[clientIndex].email
}
return this.genVLESSLink(address, remark, clientIndex);
case Protocols.SHADOWSOCKS: return this.genSSLink(address, remark);
case Protocols.TROJAN: return this.genTrojanLink(address, remark, clientIndex);
case Protocols.TROJAN:
if (this.settings.trojans[clientIndex].email != ""){
remark += '-' + this.settings.trojans[clientIndex].email
}
return this.genTrojanLink(address, remark, clientIndex);
default: return '';
}
}
@@ -1188,7 +1197,7 @@ Inbound.VmessSettings = class extends Inbound.Settings {
}
};
Inbound.VmessSettings.Vmess = class extends XrayCommonClass {
constructor(id=RandomUtil.randomUUID(), alterId=0, email='', totalGB=0, expiryTime='') {
constructor(id=RandomUtil.randomUUID(), alterId=0, email=RandomUtil.randomText(), totalGB=0, expiryTime='') {
super();
this.id = id;
this.alterId = alterId;
@@ -1270,7 +1279,7 @@ Inbound.VLESSSettings = class extends Inbound.Settings {
};
Inbound.VLESSSettings.VLESS = class extends XrayCommonClass {
constructor(id=RandomUtil.randomUUID(), flow=FLOW_CONTROL.DIRECT, email='', totalGB=0, expiryTime='') {
constructor(id=RandomUtil.randomUUID(), flow=FLOW_CONTROL.DIRECT, email=RandomUtil.randomText(), totalGB=0, expiryTime='') {
super();
this.id = id;
this.flow = flow;
@@ -1384,7 +1393,7 @@ Inbound.TrojanSettings = class extends Inbound.Settings {
}
};
Inbound.TrojanSettings.Trojan = class extends XrayCommonClass {
constructor(password=RandomUtil.randomSeq(10), flow=FLOW_CONTROL.DIRECT, email='', totalGB=0, expiryTime='') {
constructor(password=RandomUtil.randomSeq(10), flow=FLOW_CONTROL.DIRECT, email=RandomUtil.randomText(), totalGB=0, expiryTime='') {
super();
this.password = password;
this.flow = flow;

View File

@@ -136,6 +136,16 @@ class RandomUtil {
return (c === 'x' ? r : (r & 0x7 | 0x8)).toString(16);
});
}
static randomText() {
var chars = 'abcdefghijklmnopqrstuvwxyz1234567890';
var string = '';
var len = 6 + Math.floor(Math.random() * 5)
for(var ii=0; ii<len; ii++){
string += chars[Math.floor(Math.random() * chars.length)];
}
return string;
}
}
class ObjectUtil {

View File

@@ -6,26 +6,26 @@
<a-menu>
<a-menu-item v-if="record.hasLink()" @click="showQrcode(record,index);"><a-icon type="qrcode"></a-icon>{{ i18n "qrCode" }}</a-menu-item>
<a-menu-item @click="showInfo(record,index);"><a-icon type="info-circle"></a-icon>{{ i18n "info" }}</a-menu-item>
<a-menu-item @click="resetClientTraffic(client,record,$event)"><a-icon type="retweet"></a-icon>{{ i18n "pages.inbounds.resetTraffic" }}</a-menu-item>
<a-menu-item @click="resetClientTraffic(client,record,$event)" v-if="client.email != ''"><a-icon type="retweet"></a-icon>{{ i18n "pages.inbounds.resetTraffic" }}</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</template>
<template slot="client" slot-scope="text, client">
[[ client.email ]]
<a-tag v-if="!isClientEnabled(record, client.email)" color="red"> expired</a-tag>
<a-tag v-if="!isClientEnabled(record, client.email)" color="red">{{ i18n "disabled" }}</a-tag>
</template>
<template slot="traffic" slot-scope="text, client">
<a-tag v-if="client._totalGB === 0" color="blue">{{ i18n "usage" }}: [[ sizeFormat(getUpStats(record, client.email) + getDownStats(record, client.email)) ]]</a-tag>
<a-tag v-if="client._totalGB > 0 && !isTrafficExhausted(record, client.email)" color="green">{{ i18n "usage" }}: [[ sizeFormat(getUpStats(record, client.email) + getDownStats(record, client.email)) ]] / [[client._totalGB]]GB</a-tag>
<a-tag v-if="client._totalGB > 0 && isTrafficExhausted(record, client.email)" color="red">{{ i18n "usage" }}: [[ sizeFormat(getUpStats(record, client.email) + getDownStats(record, client.email)) ]] / [[client._totalGB]]GB</a-tag>
<a-tag color="blue">[[ sizeFormat(getUpStats(record, client.email)) ]] / [[ sizeFormat(getDownStats(record, client.email)) ]]</a-tag>
<template v-if="client._totalGB > 0">
<a-tag v-if="isTrafficExhausted(record, client.email)" color="red">[[client._totalGB]]GB</a-tag>
<a-tag v-else color="cyan">[[client._totalGB]]GB</a-tag>
</template>
<a-tag v-else color="green">{{ i18n "indefinite" }}</a-tag>
</template>
<template slot="expiryTime" slot-scope="text, client, index">
<template v-if="client._expiryTime > 0">
<a-tag v-if="isExpiry(record, index)" color="red">
[[ DateUtil.formatMillis(client._expiryTime) ]]
</a-tag>
<a-tag v-else color="blue">
<a-tag :color="isExpiry(record, index)? 'red' : 'blue'">
[[ DateUtil.formatMillis(client._expiryTime) ]]
</a-tag>
</template>

View File

@@ -1,9 +1,11 @@
{{define "inboundInfoModal"}}
<a-modal id="inbound-info-modal" v-model="infoModal.visible" title='{{ i18n "pages.inbounds.details"}}'
:closable="true"
:mask-closable="true"
:footer="null"
>
<a-modal id="inbound-info-modal"
v-model="infoModal.visible" title='{{ i18n "pages.inbounds.details"}}'
:closable="true"
:mask-closable="true"
:footer="null"
width="600px"
>
<table style="margin-bottom: 10px; width: 100%;">
<tr><td>
<table>
@@ -55,11 +57,47 @@
</tr>
</table>
<a-divider>{{ i18n "pages.inbounds.client" }}</a-divider>
<template v-if="dbInbound.hasLink()">
<p>Client URL:</p>
<table style="margin-bottom: 10px; width: 100%;">
<tr><th>[[ Object.keys(infoModal.clientSettings)[0] ]]</th><th>[[ Object.keys(infoModal.clientSettings)[1] ]]</th><th>[[ Object.keys(infoModal.clientSettings)[2] ]]</th></tr>
<tr>
<td><a-tag color="green">[[ Object.values(infoModal.clientSettings)[0] ]]</a-tag></td>
<td><a-tag color="green">[[ Object.values(infoModal.clientSettings)[1] ]]</a-tag></td>
<td><a-tag color="green">[[ Object.values(infoModal.clientSettings)[2] ]]</a-tag></td>
</tr>
</table>
<table style="margin-bottom: 10px; width: 100%;">
<tr><th>{{ i18n "usage" }}</th><th>{{ i18n "pages.inbounds.totalFlow" }}</th><th>{{ i18n "pages.inbounds.expireDate" }}</th><th>{{ i18n "enable" }}</th></tr>
<tr>
<td>
<a-tag :color="statsColor(infoModal.clientStats)">
[[ sizeFormat(infoModal.clientStats['up']) ]] /
[[ sizeFormat(infoModal.clientStats['down']) ]]
([[ sizeFormat(infoModal.clientStats['up'] + infoModal.clientStats['down']) ]])
</a-tag>
</td>
<td>
<a-tag v-if="infoModal.clientSettings.totalGB > 0" :color="statsColor(infoModal.clientStats)">[[ sizeFormat(infoModal.clientSettings.totalGB) ]]</a-tag>
<a-tag v-else color="green">{{ i18n "indefinite" }}</a-tag>
</td>
<td>
<template v-if="infoModal.clientSettings.expiryTime > 0">
<a-tag :color="infoModal.isExpired ? 'red' : 'blue'">
[[ DateUtil.formatMillis(infoModal.clientSettings.expiryTime) ]]
</a-tag>
</template>
<a-tag v-else color="green">{{ i18n "indefinite" }}</a-tag>
</td>
<td>
<a-tag v-if="infoModal.clientStats.enable" color="blue">{{ i18n "enabled" }}</a-tag>
<a-tag v-else color="red">{{ i18n "disabled" }}</a-tag>
</td>
</tr>
</table>
<div v-if="dbInbound.hasLink()">
<a-divider>URL</a-divider>
<p>[[ infoModal.link ]]</p>
<button class="btn" id="copy-url-link"><a-icon type="snippets"></a-icon>{{ i18n "copy" }}</button>
</template>
<button class="ant-btn ant-btn-primary" id="copy-url-link"><a-icon type="snippets"></a-icon>{{ i18n "copy" }}</button>
</div>
</a-modal>
<script>
@@ -67,14 +105,32 @@
visible: false,
inbound: new Inbound(),
dbInbound: new DBInbound(),
clientSettings: new Inbound.Settings(),
clientStats: [],
upStats: 0,
downStats: 0,
clipboard: null,
link: null,
index: 0,
isExpired: false,
show(dbInbound, index=0) {
this.index = index;
this.inbound = dbInbound.toInbound();
this.dbInbound = new DBInbound(dbInbound);
this.link = dbInbound.genLink(index);
this.clientSettings = Object.values(JSON.parse(this.inbound.settings).clients)[index];
this.clientStats = dbInbound.clientStats;
this.isExpired = this.inbound.isExpiry(index);
if(dbInbound.clientStats.length > 0)
{
for (const key in dbInbound.clientStats) {
if (Object.hasOwnProperty.call(dbInbound.clientStats, key)) {
if(dbInbound.clientStats[key]['email'] == this.clientSettings.email)
this.clientStats = dbInbound.clientStats[key];
}
}
}
this.visible = true;
infoModalApp.$nextTick(() => {
if (this.clipboard === null) {
@@ -120,6 +176,11 @@
app.$message.success('{{ i18n "copied" }}')
this.infoModal.clipboard.destroy();
});
},
statsColor(stats) {
if(stats['total'] === 0) return 'blue'
else if(stats['total'] > 0 && (stats['down']+stats['up']) < stats['total']) return 'cyan'
else return 'red'
}
},

View File

@@ -196,18 +196,18 @@
}];
const innerColumns = [
{ title: '', width: 50, scopedSlots: { customRender: 'actions' } },
{ title: '', width: 20, scopedSlots: { customRender: 'actions' } },
{ title: '{{ i18n "pages.inbounds.client" }}', width: 80, scopedSlots: { customRender: 'client' } },
{ title: '{{ i18n "pages.inbounds.traffic" }}', width: 100, scopedSlots: { customRender: 'traffic' } },
{ title: '{{ i18n "pages.inbounds.expireDate" }}', width: 80, scopedSlots: { customRender: 'expiryTime' } },
{ title: '{{ i18n "pages.inbounds.traffic" }}↑|↓', width: 80, scopedSlots: { customRender: 'traffic' } },
{ title: '{{ i18n "pages.inbounds.expireDate" }}', width: 70, scopedSlots: { customRender: 'expiryTime' } },
{ title: 'UID', width: 150, dataIndex: "id" },
];
const innerTrojanColumns = [
{ title: '', width: 50, scopedSlots: { customRender: 'actions' } },
{ title: '', width: 20, scopedSlots: { customRender: 'actions' } },
{ title: '{{ i18n "pages.inbounds.client" }}', width: 80, scopedSlots: { customRender: 'client' } },
{ title: '{{ i18n "pages.inbounds.traffic" }}', width: 100, scopedSlots: { customRender: 'traffic' } },
{ title: '{{ i18n "pages.inbounds.expireDate" }}', width: 80, scopedSlots: { customRender: 'expiryTime' } },
{ title: '{{ i18n "pages.inbounds.traffic" }}↑|↓', width: 80, scopedSlots: { customRender: 'traffic' } },
{ title: '{{ i18n "pages.inbounds.expireDate" }}', width: 70, scopedSlots: { customRender: 'expiryTime' } },
{ title: 'Password', width: 150, dataIndex: "password" },
];
@@ -257,7 +257,18 @@
this.searchedInbounds.splice(0, this.searchedInbounds.length);
this.dbInbounds.forEach(inbound => {
if (ObjectUtil.deepSearch(inbound, key)) {
this.searchedInbounds.push(inbound);
const newInbound = new DBInbound(inbound);
const inboundSettings = JSON.parse(inbound.settings);
if (inboundSettings.hasOwnProperty('clients')){
const searchedSettings = { "clients": [] };
inboundSettings.clients.forEach(client => {
if (ObjectUtil.deepSearch(client, key)){
searchedSettings.clients.push(client);
}
});
newInbound.settings = Inbound.Settings.fromJson(inbound.protocol, searchedSettings);
}
this.searchedInbounds.push(newInbound);
}
});
}
@@ -475,6 +486,9 @@
}
}
}
else{
return true
}
},
},
watch: {

View File

@@ -87,13 +87,28 @@
style="max-width: 300px"></a-input>
</a-form-item>
<a-form-item>
<!-- <a-button type="primary" @click="updateUser">修改</a-button>-->
<!-- <a-button type="primary" @click="updateUser">Revise</a-button>-->
<a-button type="primary" @click="updateUser">{{ i18n "confirm" }}</a-button>
</a-form-item>
</a-form>
</a-tab-pane>
<a-tab-pane key="3" tab='{{ i18n "pages.setting.xrayConfiguration"}}'>
<a-list item-layout="horizontal" style="background: white">
<setting-list-item type="switch" title='{{ i18n "pages.setting.xrayConfigTorrent"}}' desc='{{ i18n "pages.setting.xrayConfigTorrentDesc"}}' v-model="torrentSettings"></setting-list-item>
<setting-list-item type="switch" title='{{ i18n "pages.setting.xrayConfigPrivateIp"}}' desc='{{ i18n "pages.setting.xrayConfigPrivateIpDesc"}}' v-model="privateIpSettings"></setting-list-item>
<a-divider>{{ i18n "pages.setting.advancedTemplate"}}</a-divider>
<a-collapse>
<a-collapse-panel header="{{ i18n "pages.setting.xrayConfigInbounds"}}">
<setting-list-item type="textarea" title='{{ i18n "pages.setting.xrayConfigInbounds"}}' desc='{{ i18n "pages.setting.xrayConfigInboundsDesc"}}' v-model ="inboundSettings"></setting-list-item>
</a-collapse-panel>
<a-collapse-panel header="{{ i18n "pages.setting.xrayConfigOutbounds"}}">
<setting-list-item type="textarea" title='{{ i18n "pages.setting.xrayConfigOutbounds"}}' desc='{{ i18n "pages.setting.xrayConfigOutboundsDesc"}}' v-model ="outboundSettings"></setting-list-item>
</a-collapse-panel>
<a-collapse-panel header="{{ i18n "pages.setting.xrayConfigRoutings"}}">
<setting-list-item type="textarea" title='{{ i18n "pages.setting.xrayConfigRoutings"}}' desc='{{ i18n "pages.setting.xrayConfigRoutingsDesc"}}' v-model ="routingRuleSettings"></setting-list-item>
</a-collapse-panel>
</a-collapse>
<a-divider>{{ i18n "pages.setting.completeTemplate"}}</a-divider>
<setting-list-item type="textarea" title='{{ i18n "pages.setting.xrayConfigTemplate"}}' desc='{{ i18n "pages.setting.xrayConfigTemplateDesc"}}' v-model="allSetting.xrayTemplateConfig"></setting-list-item>
</a-list>
</a-tab-pane>
@@ -189,6 +204,102 @@
this.saveBtnDisable = this.oldAllSetting.equals(this.allSetting);
}
},
computed: {
templateSettings: {
get: function () { return this.allSetting.xrayTemplateConfig ? JSON.parse(this.allSetting.xrayTemplateConfig) : null ; },
set: function (newValue) { this.allSetting.xrayTemplateConfig = JSON.stringify(newValue, null, 2) },
},
inboundSettings: {
get: function () { return this.templateSettings ? JSON.stringify(this.templateSettings.inbounds, null, 2) : null; },
set: function (newValue) {
newTemplateSettings = this.templateSettings;
newTemplateSettings.inbounds = JSON.parse(newValue)
this.templateSettings = newTemplateSettings
},
},
outboundSettings: {
get: function () { return this.templateSettings ? JSON.stringify(this.templateSettings.outbounds, null, 2) : null; },
set: function (newValue) {
newTemplateSettings = this.templateSettings;
newTemplateSettings.outbounds = JSON.parse(newValue)
this.templateSettings = newTemplateSettings
},
},
routingRuleSettings: {
get: function () { return this.templateSettings ? JSON.stringify(this.templateSettings.routing.rules, null, 2) : null; },
set: function (newValue) {
newTemplateSettings = this.templateSettings;
newTemplateSettings.routing.rules = JSON.parse(newValue)
this.templateSettings = newTemplateSettings
},
},
torrentSettings: {
get: function () {
torrentFilter = false
if(this.templateSettings != null){
this.templateSettings.routing.rules.forEach(routingRule => {
if(routingRule.hasOwnProperty("protocol")){
if (routingRule.protocol[0] === "bittorrent" && routingRule.outboundTag == "blocked"){
torrentFilter = true
}
}
});
}
return torrentFilter
},
set: function (newValue) {
newTemplateSettings = JSON.parse(this.allSetting.xrayTemplateConfig);
if (newValue){
newTemplateSettings.routing.rules.push(JSON.parse("{\"outboundTag\": \"blocked\",\"protocol\": [\"bittorrent\"],\"type\": \"field\"}"))
}
else {
newTemplateSettings.routing.rules = [];
this.templateSettings.routing.rules.forEach(routingRule => {
if (routingRule.hasOwnProperty('protocol')){
if (routingRule.protocol[0] === "bittorrent" && routingRule.outboundTag == "blocked"){
return;
}
}
newTemplateSettings.routing.rules.push(routingRule);
});
}
this.templateSettings = newTemplateSettings
},
},
privateIpSettings: {
get: function () {
localIpFilter = false
if(this.templateSettings != null){
this.templateSettings.routing.rules.forEach(routingRule => {
if(routingRule.hasOwnProperty("ip")){
if (routingRule.ip[0] === "geoip:private" && routingRule.outboundTag == "blocked"){
localIpFilter = true
}
}
});
}
return localIpFilter
},
set: function (newValue) {
newTemplateSettings = JSON.parse(this.allSetting.xrayTemplateConfig);
if (newValue){
newTemplateSettings.routing.rules.push(JSON.parse("{\"outboundTag\": \"blocked\",\"ip\": [\"geoip:private\"],\"type\": \"field\"}"))
}
else {
newTemplateSettings.routing.rules = [];
this.templateSettings.routing.rules.forEach(routingRule => {
if (routingRule.hasOwnProperty('ip')){
if (routingRule.ip[0] === "geoip:private" && routingRule.outboundTag == "blocked"){
return;
}
}
newTemplateSettings.routing.rules.push(routingRule);
});
}
this.templateSettings = newTemplateSettings
},
},
}
});
</script>

View File

@@ -1,48 +1,48 @@
"username" = "username"
"password" = "password"
"login" = "login"
"confirm" = "confirm"
"cancel" = "cancel"
"close" = "close"
"copy" = "copy"
"copied" = "copied"
"download" = "download"
"remark" = "remark"
"enable" = "enable"
"protocol" = "protocol"
"search" = "search"
"username" = "Username"
"password" = "Password"
"login" = "Login"
"confirm" = "Confirm"
"cancel" = "Cancel"
"close" = "Close"
"copy" = "Copy"
"copied" = "Copied"
"download" = "Download"
"remark" = "Remark"
"enable" = "Enable"
"protocol" = "Protocol"
"search" = "Search"
"loading" = "Loading"
"second" = "second"
"minute" = "minute"
"hour" = "hour"
"day" = "day"
"check" = "check"
"indefinite" = "indefinite"
"unlimited" = "unlimited"
"none" = "none"
"second" = "Second"
"minute" = "Minute"
"hour" = "Hour"
"day" = "Day"
"check" = "Check"
"indefinite" = "Indefinite"
"unlimited" = "Unlimited"
"none" = "None"
"qrCode" = "QR Code"
"info" = "More information"
"edit" = "edit"
"delete" = "delete"
"reset" = "reset"
"edit" = "Edit"
"delete" = "Delete"
"reset" = "Reset"
"copySuccess" = "Copy successfully"
"sure" = "Sure"
"encryption" = "encryption"
"transmission" = "transmission"
"host" = "host"
"path" = "path"
"camouflage" = "camouflage"
"enabled" = "enabled"
"disabled" = "disabled"
"domainName" = "domain name"
"additional" = "alter"
"encryption" = "Encryption"
"transmission" = "Transmission"
"host" = "Host"
"path" = "Path"
"camouflage" = "Camouflage"
"enabled" = "Enabled"
"disabled" = "Disabled"
"domainName" = "Domain name"
"additional" = "Alter"
"monitor" = "Listen IP"
"certificate" = "certificat"
"fail" = "fail"
"success" = " success"
"getVersion" = "get version"
"install" = "install"
"certificate" = "Certificat"
"fail" = "Fail"
"success" = " Success"
"getVersion" = "Get version"
"install" = "Install"
"clients" = "Clients"
"usage" = "Usage"
@@ -64,11 +64,10 @@
"wrongUsernameOrPassword" = "Invalid username or password"
"successLogin" = "Login"
[pages.index]
"title" = "system status"
"memory" = "memory"
"hard" = "hard disk"
"title" = "System status"
"memory" = "Memory"
"hard" = "Hard disk"
"xrayStatus" = "xray Status"
"xraySwitch" = "Switch Version"
"xraySwitchClick" = "Click on the version you want to switch"
@@ -82,27 +81,26 @@
"downSpeed" = "Total download speed for all network cards"
"totalSent" = "Total upload traffic of all network cards since system startup"
"totalReceive" = "Total download traffic of all network cards since system startup"
"xraySwitchVersionDialog" = "switch xray version"
"xraySwitchVersionDialogDesc" = "whether to switch the xray version to"
"xraySwitchVersionDialog" = "Switch xray version"
"xraySwitchVersionDialogDesc" = "Whether to switch the xray version to"
"dontRefreshh" = "Installation is in progress, please do not refresh this page"
[pages.inbounds]
"title" = "Inbounds"
"totalDownUp" = "Total uploads/downloads"
"totalUsage" = "Total usage"
"inboundCount" = "Number of inbound"
"operate" = "operate"
"enable" = "enable"
"remark" = "remark"
"protocol" = "protocol"
"port" = "port"
"traffic" = "traffic"
"details" = "details"
"transportConfig" = "transport config"
"expireDate" = "expire date"
"resetTraffic" = "reset traffic"
"addInbound" = "addInbound"
"operate" = "Operate"
"enable" = "Enable"
"remark" = "Remark"
"protocol" = "Protocol"
"port" = "Port"
"traffic" = "Traffic"
"details" = "Details"
"transportConfig" = "Transport config"
"expireDate" = "Expire date"
"resetTraffic" = "Reset traffic"
"addInbound" = "Add Inbound"
"addTo" = "Add To"
"revise" = "Revise"
"modifyInbound" = "Modify InBound"
@@ -110,45 +108,44 @@
"deleteInboundContent" = "Are you sure you want to delete inbound?"
"resetTrafficContent" = "Are you sure you want to reset traffic?"
"copyLink" = "Copy Link"
"address" = "address"
"network" = "network"
"destinationPort" = "destination port"
"targetAddress" = "target address"
"address" = "Address"
"network" = "Network"
"destinationPort" = "Destination port"
"targetAddress" = "Target address"
"disableInsecureEncryption" = "Disable insecure encryption"
"monitorDesc" = "Leave blank by default"
"meansNoLimit" = "means no limit"
"totalFlow" = "total flow"
"meansNoLimit" = "Means no limit"
"totalFlow" = "Total flow"
"leaveBlankToNeverExpire" = "Leave blank to never expire"
"noRecommendKeepDefault" = "There are no special requirements to keep the default"
"certificatePath" = "certificate file path"
"certificateContent" = "certificate file content"
"publicKeyPath" = "public key file path"
"publicKeyContent" = "public key content"
"keyPath" = "key file path"
"keyContent" = "key content"
"clickOnQRcode" = "click on QR Code to Copy"
"certificatePath" = "Certificate file path"
"certificateContent" = "Certificate file content"
"publicKeyPath" = "Public key file path"
"publicKeyContent" = "Public key content"
"keyPath" = "Key file path"
"keyContent" = "Key content"
"clickOnQRcode" = "Click on QR Code to Copy"
"client" = "Client"
[pages.inbounds.toasts]
"obtain" = "Obtain"
[pages.inbounds.stream.general]
"requestHeader" = "request header"
"name" = "name"
"value" = "value"
"requestHeader" = "Request header"
"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"
"responseHeader" = "response header"
"requestVersion" = "Request version"
"requestMethod" = "Request method"
"requestPath" = "Request path"
"responseVersion" = "Response version"
"responseStatus" = "Response status"
"responseStatusDescription" = "Response status description"
"responseHeader" = "Response header"
[pages.inbounds.stream.quic]
"encryption" = "encryption"
"encryption" = "Encryption"
[pages.setting]
"title" = "Setting"
@@ -174,8 +171,20 @@
"currentPassword" = "Current Password"
"newUsername" = "New Username"
"newPassword" = "New Password"
"xrayConfigTemplate" = "xray Configuration Template"
"xrayConfigTemplateDesc" = "Generate the final xray configuration file based on this template, restart the panel to take effect"
"advancedTemplate" = "Advanced template parts"
"completeTemplate" = "Complete template of Xray configuration"
"xrayConfigTemplate" = "Xray Configuration Template"
"xrayConfigTemplateDesc" = "Generate the final xray configuration file based on this template, restart the panel to take effect."
"xrayConfigTorrent" = "Ban bittorrent usage"
"xrayConfigTorrentDesc" = "Change the configuration temlate to avoid using bittorrent by users, restart the panel to take effect"
"xrayConfigPrivateIp" = "Ban private ip range to connect"
"xrayConfigPrivateIpDesc" = "Change the configuration temlate to avoid connecting with private IP ranges, restart the panel to take effect"
"xrayConfigInbounds" = "Configuration of Inbounds"
"xrayConfigInboundsDesc" = "Change the configuration temlate to accept special clients, restart the panel to take effect"
"xrayConfigOutbounds" = "Configuration of Outbounds"
"xrayConfigOutboundsDesc" = "Change the configuration temlate to define outgoing ways for this server, restart the panel to take effect"
"xrayConfigRoutings" = "Configuration of Routing rules"
"xrayConfigRoutingsDesc" = "Change the configuration temlate to define Routing rules for this server, restart the panel to take effect"
"telegramBotEnable" = "Enable telegram bot"
"telegramBotEnableDesc" = "Restart the panel to take effect"
"telegramToken" = "Telegram Token"
@@ -188,8 +197,8 @@
"timeZoneDesc" = "The scheduled task runs according to the time in the time zone, and restarts the panel to take effect"
[pages.setting.toasts]
"modifySetting" = "modify setting"
"getSetting" = "get setting"
"modifyUser" = "modify user"
"modifySetting" = "Modify setting"
"getSetting" = "Get setting"
"modifyUser" = "Modify user"
"originalUserPassIncorrect" = "The original user name or original password is incorrect"
"userPassMustBeNotEmpty" = "New username and new password cannot be empty"

View File

@@ -34,7 +34,7 @@
"path" = "مسیر"
"camouflage" = "استتار"
"enabled" = "فعال"
"disabled" = "disabled"
"disabled" = "غیرفعال"
"domainName" = "آدرس دامنه"
"additional" = "آی دی جایگزین"
"monitor" = "آی پی اتصال"
@@ -48,7 +48,7 @@
[menu]
"dashboard" = "وضعیت سیستم"
"inbounds" = "سروریس ها"
"inbounds" = "سرویس ها"
"setting" = "تنظیمات پنل"
"logout" = "خروج"
"link" = "دیگر"
@@ -64,7 +64,6 @@
"wrongUsernameOrPassword" = "نام کاربری و رمز عبور اشتباه میباشد"
"successLogin" = "خوش آمدید"
[pages.index]
"title" = "وضعیت سیستم"
"memory" = "حافظه رم"
@@ -86,7 +85,6 @@
"xraySwitchVersionDialogDesc" = "آیا از تغییر ورژن مطمئن هستین"
"dontRefreshh" = "در حال نصب ، لطفا رفرش نکنید "
[pages.inbounds]
"title" = "کاربران"
"totalDownUp" = "جمع آپلود/دانلود"
@@ -149,7 +147,6 @@
[pages.inbounds.stream.quic]
"encryption" = "رمزنگاری"
[pages.setting]
"title" = "تنظیمات"
"save" = "ذخیره"
@@ -174,8 +171,20 @@
"currentPassword" = "رمز عبور فعلی"
"newUsername" = "نام کاربری جدید"
"newPassword" = "رمز عبور جدید"
"xrayConfigTemplate" = "تنظیمات قالب Xray"
"xrayConfigTemplateDesc" = "فایل پیکربندی xray نهایی را بر اساس این الگو ایجاد کنید. لطفاً این را تغییر ندهید مگر اینکه دقیقاً بدانید که چه کاری انجام می دهید! پنل را مجدداً راه اندازی کنید تا اعمال شود"
"advancedTemplate" = "بخش های پیشرفته الگو"
"completeTemplate" = "الگوی کامل تنظیمات ایکس ری"
"xrayConfigTemplate" = "تنظیمات الگو ایکس ری"
"xrayConfigTemplateDesc" = "فایل پیکربندی ایکس ری نهایی بر اساس این الگو ایجاد میشود. لطفاً این را تغییر ندهید مگر اینکه دقیقاً بدانید که چه کاری انجام می دهید! پنل را مجدداً راه اندازی کنید تا اعمال شود"
"xrayConfigTorrent" = "فیلتر کردن بیت تورنت"
"xrayConfigTorrentDesc" = "الگوی تنظیمات را برای فیلتر کردن پروتکل بیت تورنت برای کاربران تغییر میدهد. پنل را مجدداً راه اندازی کنید تا اعمال شود"
"xrayConfigPrivateIp" = "جلوگیری از اتصال آی پی های نامعتبر"
"xrayConfigPrivateIpDesc" = "الگوی تنظیمات را برای فیلتر کردن اتصال آی پی های نامعتبر و بسته های سرگردان تغییر میدهد. پنل را مجدداً راه اندازی کنید تا اعمال شود"
"xrayConfigInbounds" = "تنظیمات ورودی"
"xrayConfigInboundsDesc" = "میتوانید الگوی تنظیمات را برای ورودی های خاص تنظیم نمایید. پنل را مجدداً راه اندازی کنید تا اعمال شود"
"xrayConfigOutbounds" = "تنظیمات خروجی"
"xrayConfigOutboundsDesc" = "میتوانید الگوی تنظیمات را برای خروجی اینترنت تنظیم نمایید. پنل را مجدداً راه اندازی کنید تا اعمال شود"
"xrayConfigRoutings" = "تنظیمات قوانین مسیریابی"
"xrayConfigRoutingsDesc" = "میتوانید الگوی تنظیمات را برای مسیریابی تنظیم نمایید. پنل را مجدداً راه اندازی کنید تا اعمال شود"
"telegramBotEnable" = "فعالسازی ربات تلگرام"
"telegramBotEnableDesc" = "پنل را مجدداً راه اندازی کنید تا اعمال شود"
"telegramToken" = "توکن تلگرام"

View File

@@ -44,6 +44,7 @@
"getVersion" = "获取版本"
"install" = "安装"
"clients" = "客户端"
"usage" = "用法"
[menu]
"dashboard" = "系统状态"
@@ -84,7 +85,6 @@
"xraySwitchVersionDialogDesc" = "是否切换 xray 版本至"
"dontRefreshh" = "安装中,请不要刷新此页面"
[pages.inbounds]
"title" = "入站列表"
"totalDownUp" = "总上传 / 下载"
@@ -124,7 +124,8 @@
"publicKeyContent" = "公钥内容"
"keyPath" = "密钥文件路径"
"keyContent" = "密钥内容"
"clickOnQRcode" = "click on QR Code to Copy"
"clickOnQRcode" = "点击二维码复制"
"client" = "客户"
[pages.inbounds.toasts]
"obtain" = "获取"
@@ -146,7 +147,6 @@
[pages.inbounds.stream.quic]
"encryption" = "加密"
[pages.setting]
"title" = "设置"
"save" = "保存配置"
@@ -171,8 +171,20 @@
"currentPassword" = "原密码"
"newUsername" = "新用户名"
"newPassword" = "新密码"
"xrayConfigTemplate" = "xray 配置模版"
"xrayConfigTemplateDesc" = "以该模版为基础生成最终的 xray 配置文件,重启面板生效"
"advancedTemplate" = "高级模板部件"
"completeTemplate" = "Xray 配置的完整模板"
"xrayConfigTemplate" = "xray 配置模板"
"xrayConfigTemplateDesc" = "以该模型为基础生成最终的xray配置文件重新启动面板生成效率"
"xrayConfigTorrent" = "禁止使用 bittorrent"
"xrayConfigTorrentDesc" = "更改配置模板避免用户使用bittorrent重启面板生效"
"xrayConfigPrivateIp" = "禁止私人 ip 范围连接"
"xrayConfigPrivateIpDesc" = "更改配置模板以避免连接私有 IP 范围,重启面板生效"
"xrayConfigInbounds" = "入站配置"
"xrayConfigInboundsDesc" = "更改配置模板接受特殊客户端,重启面板生效"
"xrayConfigOutbounds" = "出站配置"
"xrayConfigOutboundsDesc" = "更改配置模板定义此服务器的传出方式,重启面板生效"
"xrayConfigRoutings" = "路由规则配置"
"xrayConfigRoutingsDesc" = "更改配置模板为该服务器定义路由规则,重启面板生效"
"telegramBotEnable" = "启用电报机器人"
"telegramBotEnableDesc" = "重启面板生效"
"telegramToken" = "电报机器人TOKEN"
@@ -189,5 +201,4 @@
"getSetting" = "获取设置"
"modifyUser" = "修改用户"
"originalUserPassIncorrect" = "原用户名或原密码错误"
"userPassMustBeNotEmpty" = "新用户名和新密码不能为空"
"userPassMustBeNotEmpty" = "新用户名和新密码不能为空"