Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3d54e33051 | ||
|
|
01be9fec95 | ||
|
|
0306e75c2a | ||
|
|
255ff9cc20 | ||
|
|
2fbb1ca6c9 | ||
|
|
3b47028060 | ||
|
|
d9ab8b4ce4 | ||
|
|
e6389f3fb3 | ||
|
|
96fd7d0e7c | ||
|
|
cf02f02210 | ||
|
|
4dc8974af0 | ||
|
|
b527a528ea | ||
|
|
1a53af0434 | ||
|
|
be8d55dadb | ||
|
|
d54e7a9b14 | ||
|
|
45c3d730d4 | ||
|
|
aab01ff11a |
24
.github/ISSUE_TEMPLATE/bug_report.md
vendored
24
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,24 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Version (please complete the following information):**
|
||||
- 3X-UI Version : [e.g. 2.3.5]
|
||||
- Xray Version : [e.g. 1.8.13]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
77
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
77
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
name: Bug report
|
||||
description: Create a report to help us improve
|
||||
title: "Bug report"
|
||||
labels: ["bug"]
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thank you for reporting a bug! Please fill out the following information.
|
||||
|
||||
- type: textarea
|
||||
id: what-happened
|
||||
attributes:
|
||||
label: Describe the bug
|
||||
description: A clear and concise description of what the bug is.
|
||||
placeholder: My problem is...
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: how-repeat-problem
|
||||
attributes:
|
||||
label: How to repeat the problem?
|
||||
description: Sequence of actions that allow you to reproduce the bug
|
||||
placeholder: |
|
||||
1. Open `Inbounds` page
|
||||
2. ...
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: expected-action
|
||||
attributes:
|
||||
label: Expected action
|
||||
description: What's going to happen
|
||||
placeholder: Must be...
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
id: received-action
|
||||
attributes:
|
||||
label: Received action
|
||||
description: What's really happening
|
||||
placeholder: It's actually happening...
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: input
|
||||
id: xui-version
|
||||
attributes:
|
||||
label: 3x-ui Version
|
||||
description: Which version of 3x-ui are you using?
|
||||
placeholder: 2.X.X
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: xray-version
|
||||
attributes:
|
||||
label: Xray-core Version
|
||||
description: Which version of Xray-core are you using?
|
||||
placeholder: 2.X.X
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: checkboxes
|
||||
id: checklist
|
||||
attributes:
|
||||
label: Checklist
|
||||
description: Please check all the checkboxes
|
||||
options:
|
||||
- label: This bug report is written entirely in English.
|
||||
required: true
|
||||
- label: This bug report is new and no one has reported it before me.
|
||||
required: true
|
||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,20 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
39
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
Normal file
39
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
name: Feature request
|
||||
description: Suggest an idea for this project
|
||||
title: "Feature request"
|
||||
labels: ["enhancement"]
|
||||
|
||||
body:
|
||||
- type: textarea
|
||||
id: is-related-problem
|
||||
attributes:
|
||||
label: Is your feature request related to a problem?
|
||||
description: A clear and concise description of what the problem is.
|
||||
placeholder: I'm always frustrated when...
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: solution
|
||||
attributes:
|
||||
label: Describe the solution you'd like
|
||||
description: A clear and concise description of what you want to happen.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: alternatives
|
||||
attributes:
|
||||
label: Describe alternatives you've considered
|
||||
description: A clear and concise description of any alternative solutions or features you've considered.
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: checkboxes
|
||||
id: checklist
|
||||
attributes:
|
||||
label: Checklist
|
||||
description: Please check all the checkboxes
|
||||
options:
|
||||
- label: This feature report is written entirely in English.
|
||||
required: true
|
||||
10
.github/ISSUE_TEMPLATE/question-.md
vendored
10
.github/ISSUE_TEMPLATE/question-.md
vendored
@@ -1,10 +0,0 @@
|
||||
---
|
||||
name: 'Question '
|
||||
about: Describe this issue template's purpose here.
|
||||
title: ''
|
||||
labels: question
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
||||
22
.github/ISSUE_TEMPLATE/question.yaml
vendored
Normal file
22
.github/ISSUE_TEMPLATE/question.yaml
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
name: Question
|
||||
description: Describe this issue template's purpose here.
|
||||
title: "Question"
|
||||
labels: ["question"]
|
||||
|
||||
body:
|
||||
- type: textarea
|
||||
id: question
|
||||
attributes:
|
||||
label: Question
|
||||
placeholder: I have a question, ..., how can I solve it?
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: checkboxes
|
||||
id: checklist
|
||||
attributes:
|
||||
label: Checklist
|
||||
description: Please check all the checkboxes
|
||||
options:
|
||||
- label: This question is written entirely in English.
|
||||
required: true
|
||||
20
.github/pull_request_template.yml
vendored
Normal file
20
.github/pull_request_template.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
## What is the pull request?
|
||||
|
||||
<!-- Briefly describe the changes introduced by this pull request -->
|
||||
|
||||
## Which part of the application is affected by the change?
|
||||
|
||||
- [ ] Frontend
|
||||
- [ ] Backend
|
||||
|
||||
## Type of Changes
|
||||
|
||||
- [ ] Bug fix
|
||||
- [ ] New feature
|
||||
- [ ] Refactoring
|
||||
- [ ] Other
|
||||
|
||||
## Screenshots
|
||||
|
||||
<!-- Add screenshots to illustrate the changes -->
|
||||
<!-- Remove this section if it is not applicable. -->
|
||||
16
.github/workflows/release.yml
vendored
16
.github/workflows/release.yml
vendored
@@ -1,13 +1,17 @@
|
||||
name: Release 3X-UI
|
||||
name: Build and Release 3X-UI
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types: [published]
|
||||
push:
|
||||
tags:
|
||||
- "v*.*.*"
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
permissions:
|
||||
contents: write
|
||||
strategy:
|
||||
matrix:
|
||||
platform:
|
||||
@@ -27,6 +31,7 @@ jobs:
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
@@ -45,7 +50,7 @@ jobs:
|
||||
sudo apt install gcc-s390x-linux-gnu
|
||||
fi
|
||||
|
||||
- name: Build x-ui
|
||||
- name: Build 3x-ui
|
||||
run: |
|
||||
export CGO_ENABLED=1
|
||||
export GOOS=linux
|
||||
@@ -83,7 +88,7 @@ jobs:
|
||||
cd x-ui/bin
|
||||
|
||||
# Download dependencies
|
||||
Xray_URL="https://github.com/XTLS/Xray-core/releases/download/v25.3.31/"
|
||||
Xray_URL="https://github.com/XTLS/Xray-core/releases/download/v25.4.30/"
|
||||
if [ "${{ matrix.platform }}" == "amd64" ]; then
|
||||
wget -q ${Xray_URL}Xray-linux-64.zip
|
||||
unzip Xray-linux-64.zip
|
||||
@@ -134,6 +139,7 @@ jobs:
|
||||
|
||||
- name: Upload files to GH release
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
if: github.event_name == 'release' && github.event.action == 'published'
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
tag: ${{ github.ref }}
|
||||
|
||||
@@ -27,7 +27,7 @@ case $1 in
|
||||
esac
|
||||
mkdir -p build/bin
|
||||
cd build/bin
|
||||
wget -q "https://github.com/XTLS/Xray-core/releases/download/v25.3.31/Xray-linux-${ARCH}.zip"
|
||||
wget -q "https://github.com/XTLS/Xray-core/releases/download/v25.4.30/Xray-linux-${ARCH}.zip"
|
||||
unzip "Xray-linux-${ARCH}.zip"
|
||||
rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat
|
||||
mv xray "xray-linux-${FNAME}"
|
||||
|
||||
@@ -40,7 +40,7 @@ bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.
|
||||
Para instalar la versión deseada, utiliza el siguiente comando de instalación. Por ejemplo, ver `v1.7.9`:
|
||||
|
||||
```
|
||||
VERSION=v1.7.9 && <(curl -Ls "https://raw.githubusercontent.com/mhsanaei/3x-ui/$VERSION/install.sh") $VERSION
|
||||
VERSION=v1.7.9 && bash <(curl -Ls "https://raw.githubusercontent.com/mhsanaei/3x-ui/$VERSION/install.sh") $VERSION
|
||||
```
|
||||
|
||||
## Certificado SSL
|
||||
@@ -586,4 +586,4 @@ XUI_BIN_FOLDER="bin" XUI_DB_FOLDER="/etc/x-ui" go build main.go
|
||||
|
||||
## Estrellas a lo largo del tiempo
|
||||
|
||||
[](https://starchart.cc/MHSanaei/3x-ui)
|
||||
[](https://starchart.cc/MHSanaei/3x-ui)
|
||||
|
||||
@@ -40,7 +40,7 @@ bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.
|
||||
Чтобы установить желаемую версию, используйте следующую команду установки. Например, ver `v1.7.9`:
|
||||
|
||||
```
|
||||
VERSION=v1.7.9 && <(curl -Ls "https://raw.githubusercontent.com/mhsanaei/3x-ui/$VERSION/install.sh") $VERSION
|
||||
VERSION=v1.7.9 && bash <(curl -Ls "https://raw.githubusercontent.com/mhsanaei/3x-ui/$VERSION/install.sh") $VERSION
|
||||
```
|
||||
|
||||
## SSL Сертификат
|
||||
@@ -593,4 +593,4 @@ XUI_BIN_FOLDER="bin" XUI_DB_FOLDER="/etc/x-ui" go build main.go
|
||||
|
||||
## Число звёзд со временем
|
||||
|
||||
[](https://starchart.cc/MHSanaei/3x-ui)
|
||||
[](https://starchart.cc/MHSanaei/3x-ui)
|
||||
|
||||
@@ -40,7 +40,7 @@ bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.
|
||||
要安装您想要的版本,请使用以下安装命令。例如,ver `v1.7.9`:
|
||||
|
||||
```
|
||||
VERSION=v1.7.9 && <(curl -Ls "https://raw.githubusercontent.com/mhsanaei/3x-ui/$VERSION/install.sh") $VERSION
|
||||
VERSION=v1.7.9 && bash <(curl -Ls "https://raw.githubusercontent.com/mhsanaei/3x-ui/$VERSION/install.sh") $VERSION
|
||||
```
|
||||
|
||||
### SSL证书
|
||||
@@ -586,4 +586,4 @@ XUI_BIN_FOLDER="bin" XUI_DB_FOLDER="/etc/x-ui" go build main.go
|
||||
|
||||
## Star趋势
|
||||
|
||||
[](https://starchart.cc/MHSanaei/3x-ui)
|
||||
[](https://starchart.cc/MHSanaei/3x-ui)
|
||||
|
||||
@@ -1 +1 @@
|
||||
2.5.7
|
||||
2.5.8
|
||||
@@ -1,6 +1,3 @@
|
||||
---
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
3x-ui:
|
||||
image: ghcr.io/mhsanaei/3x-ui:latest
|
||||
|
||||
19
go.mod
19
go.mod
@@ -14,11 +14,11 @@ require (
|
||||
github.com/pelletier/go-toml/v2 v2.2.4
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/shirou/gopsutil/v4 v4.25.3
|
||||
github.com/valyala/fasthttp v1.60.0
|
||||
github.com/xtls/xray-core v1.250306.1-0.20250331123338-ab5d7cf3d2d6
|
||||
github.com/valyala/fasthttp v1.61.0
|
||||
github.com/xtls/xray-core v1.250306.1-0.20250430044058-87ab8e512882
|
||||
go.uber.org/atomic v1.11.0
|
||||
golang.org/x/text v0.24.0
|
||||
google.golang.org/grpc v1.71.1
|
||||
google.golang.org/grpc v1.72.0
|
||||
gorm.io/driver/sqlite v1.5.7
|
||||
gorm.io/gorm v1.25.12
|
||||
)
|
||||
@@ -32,7 +32,7 @@ require (
|
||||
github.com/dgryski/go-metro v0.0.0-20250106013310-edb8663e5e33 // indirect
|
||||
github.com/ebitengine/purego v0.8.2 // indirect
|
||||
github.com/fasthttp/router v1.5.4 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
|
||||
github.com/gin-contrib/sse v1.1.0 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
@@ -40,7 +40,7 @@ require (
|
||||
github.com/go-playground/validator/v10 v10.26.0 // indirect
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||
github.com/google/btree v1.1.3 // indirect
|
||||
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect
|
||||
github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4 // indirect
|
||||
github.com/gorilla/context v1.1.2 // indirect
|
||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||
github.com/gorilla/sessions v1.4.0 // indirect
|
||||
@@ -61,8 +61,8 @@ require (
|
||||
github.com/pires/go-proxyproto v0.8.0 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||
github.com/quic-go/qpack v0.5.1 // indirect
|
||||
github.com/quic-go/quic-go v0.50.1 // indirect
|
||||
github.com/refraction-networking/utls v1.6.7 // indirect
|
||||
github.com/quic-go/quic-go v0.51.0 // indirect
|
||||
github.com/refraction-networking/utls v1.7.1 // indirect
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
|
||||
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
||||
github.com/sagernet/sing v0.6.6 // indirect
|
||||
@@ -81,11 +81,10 @@ require (
|
||||
github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
go.uber.org/automaxprocs v1.6.0 // indirect
|
||||
go.uber.org/mock v0.5.1 // indirect
|
||||
go.uber.org/mock v0.5.2 // indirect
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
|
||||
golang.org/x/arch v0.16.0 // indirect
|
||||
golang.org/x/crypto v0.37.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
|
||||
golang.org/x/mod v0.24.0 // indirect
|
||||
golang.org/x/net v0.39.0 // indirect
|
||||
golang.org/x/sync v0.13.0 // indirect
|
||||
@@ -97,6 +96,6 @@ require (
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250409194420-de1ac958c67a // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gvisor.dev/gvisor v0.0.0-20250403230555-2b1f43f26fbb // indirect
|
||||
gvisor.dev/gvisor v0.0.0-20250428193742-2d800c3129d5 // indirect
|
||||
lukechampine.com/blake3 v1.4.0 // indirect
|
||||
)
|
||||
|
||||
42
go.sum
42
go.sum
@@ -24,8 +24,8 @@ github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z
|
||||
github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/fasthttp/router v1.5.4 h1:oxdThbBwQgsDIYZ3wR1IavsNl6ZS9WdjKukeMikOnC8=
|
||||
github.com/fasthttp/router v1.5.4/go.mod h1:3/hysWq6cky7dTfzaaEPZGdptwjwx0qzTgFCKEWRjgc=
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
|
||||
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
|
||||
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
|
||||
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 h1:Arcl6UOIS/kgO2nW3A65HN+7CMjSDP/gofXL4CZt1V4=
|
||||
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I=
|
||||
github.com/gin-contrib/gzip v1.2.3 h1:dAhT722RuEG330ce2agAs75z7yB+NKvX/ZM1r8w0u2U=
|
||||
@@ -66,8 +66,8 @@ github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=
|
||||
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||
github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4 h1:gD0vax+4I+mAj+jEChEf25Ia07Jq7kYOFO5PPhAxFl4=
|
||||
github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o=
|
||||
@@ -104,8 +104,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.27 h1:drZCnuvf37yPfs95E5jd9s3XhdVWLal+6BOK6qrv6IU=
|
||||
github.com/mattn/go-sqlite3 v1.14.27/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/miekg/dns v1.1.64 h1:wuZgD9wwCE6XMT05UU/mlSko71eRSXEAm2EbjQXLKnQ=
|
||||
github.com/miekg/dns v1.1.64/go.mod h1:Dzw9769uoKVaLuODMDZz9M6ynFU6Em65csPuoi8G0ck=
|
||||
github.com/miekg/dns v1.1.65 h1:0+tIPHzUW0GCge7IiK3guGP57VAw7hoPDfApjkMD1Fc=
|
||||
github.com/miekg/dns v1.1.65/go.mod h1:Dzw9769uoKVaLuODMDZz9M6ynFU6Em65csPuoi8G0ck=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
@@ -135,10 +135,10 @@ github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4
|
||||
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
||||
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||
github.com/quic-go/quic-go v0.50.1 h1:unsgjFIUqW8a2oopkY7YNONpV1gYND6Nt9hnt1PN94Q=
|
||||
github.com/quic-go/quic-go v0.50.1/go.mod h1:Vim6OmUvlYdwBhXP9ZVrtGmCMWa3wEqhq3NgYrI8b4E=
|
||||
github.com/refraction-networking/utls v1.6.7 h1:zVJ7sP1dJx/WtVuITug3qYUq034cDq9B2MR1K67ULZM=
|
||||
github.com/refraction-networking/utls v1.6.7/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0=
|
||||
github.com/quic-go/quic-go v0.51.0 h1:K8exxe9zXxeRKxaXxi/GpUqYiTrtdiWP8bo1KFya6Wc=
|
||||
github.com/quic-go/quic-go v0.51.0/go.mod h1:MFlGGpcpJqRAfmYi6NC2cptDPSxRWTOGNuP4wqrWmzQ=
|
||||
github.com/refraction-networking/utls v1.7.1 h1:dxg+jla3uocgN8HtX+ccwDr68uCBBO3qLrkZUbqkcw0=
|
||||
github.com/refraction-networking/utls v1.7.1/go.mod h1:TUhh27RHMGtQvjQq+RyO11P6ZNQNBb3N0v7wsEjKAIQ=
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
@@ -178,8 +178,8 @@ github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e h1:5QefA066A1tF
|
||||
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e/go.mod h1:5t19P9LBIrNamL6AcMQOncg/r10y3Pc01AbHeMhwlpU=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasthttp v1.60.0 h1:kBRYS0lOhVJ6V+bYN8PqAHELKHtXqwq9zNMLKx1MBsw=
|
||||
github.com/valyala/fasthttp v1.60.0/go.mod h1:iY4kDgV3Gc6EqhRZ8icqcmlG6bqhcDXfuHgTO4FXCvc=
|
||||
github.com/valyala/fasthttp v1.61.0 h1:VV08V0AfoRaFurP1EWKvQQdPTZHiUzaVoulX1aBDgzU=
|
||||
github.com/valyala/fasthttp v1.61.0/go.mod h1:wRIV/4cMwUPWnRcDno9hGnYZGh78QzODFfo1LTUhBog=
|
||||
github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ=
|
||||
github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
|
||||
github.com/vishvananda/netlink v1.3.0 h1:X7l42GfcV4S6E4vHTsw48qbrV+9PVojNfIhZcwQdrZk=
|
||||
@@ -189,8 +189,8 @@ github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zd
|
||||
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||
github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463 h1:g1Cj7d+my6k/HHxLAyxPwyX8i7FGRr6ulBDMkBzg2BM=
|
||||
github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463/go.mod h1:BjIOLmkEEtAgloAiVUcYj0Mt+YU00JARZw8AEU0IwAg=
|
||||
github.com/xtls/xray-core v1.250306.1-0.20250331123338-ab5d7cf3d2d6 h1:12QXC7rYztOQhq/3ooiHrEc5X98478076aUiIW8mzR8=
|
||||
github.com/xtls/xray-core v1.250306.1-0.20250331123338-ab5d7cf3d2d6/go.mod h1:O+FFC64bjnOukaGHPdZ+wqGHTrgPDN+qH0U+YWCzbEo=
|
||||
github.com/xtls/xray-core v1.250306.1-0.20250430044058-87ab8e512882 h1:O/aN4TCrJ+fmaDOBoQhtTRev2hVHIENy2EJ70jQcyEY=
|
||||
github.com/xtls/xray-core v1.250306.1-0.20250430044058-87ab8e512882/go.mod h1:v7SYLVSg2wkuP8jo9/0qaJ5zrCQhmUig7bSnUOdMqu0=
|
||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
@@ -211,16 +211,14 @@ go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
||||
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
|
||||
go.uber.org/mock v0.5.1 h1:ASgazW/qBmR+A32MYFDB6E2POoTgOwT509VP0CT/fjs=
|
||||
go.uber.org/mock v0.5.1/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
||||
go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
|
||||
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||
golang.org/x/arch v0.16.0 h1:foMtLTdyOmIniqWCHjY6+JxuC54XP1fDwx4N0ASyW+U=
|
||||
golang.org/x/arch v0.16.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE=
|
||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
|
||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||
@@ -247,8 +245,8 @@ golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uI
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250409194420-de1ac958c67a h1:GIqLhp/cYUkuGuiT+vJk8vhOP86L4+SP5j8yXgeVpvI=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250409194420-de1ac958c67a/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.71.1 h1:ffsFWr7ygTUscGPI0KKK6TLrGz0476KUvvsbqWK0rPI=
|
||||
google.golang.org/grpc v1.71.1/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
|
||||
google.golang.org/grpc v1.72.0 h1:S7UkcVa60b5AAQTaO6ZKamFp1zMZSU0fGDK2WZLbBnM=
|
||||
google.golang.org/grpc v1.72.0/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
@@ -264,8 +262,8 @@ gorm.io/driver/sqlite v1.5.7 h1:8NvsrhP0ifM7LX9G4zPB97NwovUakUxc+2V2uuf3Z1I=
|
||||
gorm.io/driver/sqlite v1.5.7/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4=
|
||||
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
|
||||
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
|
||||
gvisor.dev/gvisor v0.0.0-20250403230555-2b1f43f26fbb h1:rOQHoZqzW4aOUPdXb3HpJmJkEUYqASpXKy4W3sUQfYE=
|
||||
gvisor.dev/gvisor v0.0.0-20250403230555-2b1f43f26fbb/go.mod h1:3r5CMtNQMKIvBlrmM9xWUNamjKBYPOWyXOjmg5Kts3g=
|
||||
gvisor.dev/gvisor v0.0.0-20250428193742-2d800c3129d5 h1:sfK5nHuG7lRFZ2FdTT3RimOqWBg8IrVm+/Vko1FVOsk=
|
||||
gvisor.dev/gvisor v0.0.0-20250428193742-2d800c3129d5/go.mod h1:3r5CMtNQMKIvBlrmM9xWUNamjKBYPOWyXOjmg5Kts3g=
|
||||
lukechampine.com/blake3 v1.4.0 h1:xDbKOZCVbnZsfzM6mHSYcGRHZ3YrLDzqz8XnV4uaD5w=
|
||||
lukechampine.com/blake3 v1.4.0/go.mod h1:MQJNQCTnR+kwOP/JEZSxj3MaQjp80FOFSNMMHXcSeX0=
|
||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||
|
||||
@@ -85,7 +85,7 @@ func (a *SUBController) subs(c *gin.Context) {
|
||||
// Add headers
|
||||
c.Writer.Header().Set("Subscription-Userinfo", header)
|
||||
c.Writer.Header().Set("Profile-Update-Interval", a.updateInterval)
|
||||
c.Writer.Header().Set("Profile-Title", a.subTitle)
|
||||
c.Writer.Header().Set("Profile-Title", "base64:" + base64.StdEncoding.EncodeToString([]byte(a.subTitle)))
|
||||
|
||||
if a.subEncrypt {
|
||||
c.String(200, base64.StdEncoding.EncodeToString([]byte(result)))
|
||||
@@ -119,7 +119,7 @@ func (a *SUBController) subJsons(c *gin.Context) {
|
||||
// Add headers
|
||||
c.Writer.Header().Set("Subscription-Userinfo", header)
|
||||
c.Writer.Header().Set("Profile-Update-Interval", a.updateInterval)
|
||||
c.Writer.Header().Set("Profile-Title", a.subTitle)
|
||||
c.Writer.Header().Set("Profile-Title", "base64:" + base64.StdEncoding.EncodeToString([]byte(a.subTitle)))
|
||||
|
||||
c.String(200, jsonSub)
|
||||
}
|
||||
|
||||
@@ -44,11 +44,11 @@ func getLinesNum(filename string) (int, error) {
|
||||
func GetTCPCount() (int, error) {
|
||||
root := HostProc()
|
||||
|
||||
tcp4, err := getLinesNum(fmt.Sprintf("%v/net/tcp", root))
|
||||
tcp4, err := safeGetLinesNum(fmt.Sprintf("%v/net/tcp", root))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
tcp6, err := getLinesNum(fmt.Sprintf("%v/net/tcp6", root))
|
||||
tcp6, err := safeGetLinesNum(fmt.Sprintf("%v/net/tcp6", root))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -59,14 +59,23 @@ func GetTCPCount() (int, error) {
|
||||
func GetUDPCount() (int, error) {
|
||||
root := HostProc()
|
||||
|
||||
udp4, err := getLinesNum(fmt.Sprintf("%v/net/udp", root))
|
||||
udp4, err := safeGetLinesNum(fmt.Sprintf("%v/net/udp", root))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
udp6, err := getLinesNum(fmt.Sprintf("%v/net/udp6", root))
|
||||
udp6, err := safeGetLinesNum(fmt.Sprintf("%v/net/udp6", root))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return udp4 + udp6, nil
|
||||
}
|
||||
|
||||
func safeGetLinesNum(path string) (int, error) {
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
return 0, nil
|
||||
} else if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return getLinesNum(path)
|
||||
}
|
||||
|
||||
2
web/assets/css/custom.min.css
vendored
2
web/assets/css/custom.min.css
vendored
File diff suppressed because one or more lines are too long
@@ -140,8 +140,10 @@ class RandomUtil {
|
||||
|
||||
static randomShadowsocksPassword() {
|
||||
const array = new Uint8Array(32);
|
||||
|
||||
window.crypto.getRandomValues(array);
|
||||
return Base64.encode(String.fromCharCode(...array));
|
||||
|
||||
return Base64.alternativeEncode(String.fromCharCode(...array));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -528,6 +530,12 @@ class Base64 {
|
||||
)
|
||||
}
|
||||
|
||||
static alternativeEncode(content) {
|
||||
return window.btoa(
|
||||
content
|
||||
)
|
||||
}
|
||||
|
||||
static decode(content = "") {
|
||||
return new TextDecoder()
|
||||
.decode(
|
||||
@@ -807,7 +815,7 @@ const MediaQueryMixin = {
|
||||
}
|
||||
|
||||
class FileManager {
|
||||
static downloadTextFile(content, filename='file.txt', options = { type: "text/plain" }) {
|
||||
static downloadTextFile(content, filename = 'file.txt', options = { type: "text/plain" }) {
|
||||
let link = window.document.createElement('a');
|
||||
|
||||
link.download = filename;
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
{{define "component/themeSwitchTemplateLogin"}}
|
||||
<template>
|
||||
<a-space direction="vertical" :size="10" :style="{ width: '100%' }">
|
||||
<a-space @mousedown="themeSwitcher.animationsOff()" id="change-theme" direction="vertical" :size="10" :style="{ width: '100%' }">
|
||||
<a-space direction="horizontal" size="small">
|
||||
<a-switch size="small" :default-checked="themeSwitcher.isDarkTheme" @change="themeSwitcher.toggleTheme()"></a-switch>
|
||||
<span>{{ i18n "menu.dark" }}</span>
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
/* margin: 20px 0 50px 0;*/
|
||||
/*margin: 20px 0 50px 0;*/
|
||||
height: 110px;
|
||||
}
|
||||
|
||||
.ant-btn,
|
||||
.ant-form-item-children .ant-btn,
|
||||
.ant-input {
|
||||
height: 50px;
|
||||
border-radius: 30px;
|
||||
@@ -42,7 +42,8 @@
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 32px;
|
||||
font-size: 2rem;
|
||||
margin-block-end: 2rem;
|
||||
}
|
||||
|
||||
.title b {
|
||||
@@ -57,7 +58,7 @@
|
||||
animation: charge 0.5s both;
|
||||
background-color: #fff;
|
||||
border-radius: 2rem;
|
||||
padding: 3rem;
|
||||
padding: 4rem 3rem;
|
||||
transition: all 0.3s;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
@@ -439,12 +440,11 @@
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
padding: 24px;
|
||||
padding: 22px;
|
||||
}
|
||||
|
||||
.setting-section > .ant-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.ant-space-item .ant-switch {
|
||||
margin: 2px 0 4px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -467,25 +467,25 @@
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="setting-section">
|
||||
<a-popover :overlay-class-name="themeSwitcher.currentTheme" title='{{ i18n "menu.settings" }}' placement="bottomRight" trigger="click">
|
||||
<template slot="content">
|
||||
<a-space direction="vertical" :size="10">
|
||||
<a-theme-switch-login></a-theme-switch-login>
|
||||
<span>{{ i18n "pages.settings.language" }}</span>
|
||||
<a-select ref="selectLang" :style="{ width: '100%' }" v-model="lang" @change="LanguageManager.setLanguage(lang)" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option :value="l.value" label="English" v-for="l in LanguageManager.supportedLanguages">
|
||||
<span role="img" aria-label="l.name" v-text="l.icon"></span>
|
||||
<span v-text="l.name"></span>
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-space>
|
||||
</template>
|
||||
<a-button shape="circle" icon="setting"></a-button>
|
||||
</a-popover>
|
||||
</div>
|
||||
<a-row type="flex" justify="center" align="middle" :style="{ height: '100%', overflow: 'auto', overflowX: 'hidden' }">
|
||||
<a-col :xs="22" :sm="20" :md="14" :lg="10" :xl="8" :xxl="6" id="login" :style="{ margin: '3rem 0' }">
|
||||
<a-col :xs="22" :sm="12" :md="10" :lg="8" :xl="6" :xxl="5" id="login" :style="{ margin: '3rem 0' }">
|
||||
<div class="setting-section">
|
||||
<a-popover :overlay-class-name="themeSwitcher.currentTheme" title='{{ i18n "menu.settings" }}' placement="bottomRight" trigger="click">
|
||||
<template slot="content">
|
||||
<a-space direction="vertical" :size="10">
|
||||
<a-theme-switch-login></a-theme-switch-login>
|
||||
<span>{{ i18n "pages.settings.language" }}</span>
|
||||
<a-select ref="selectLang" :style="{ width: '100%' }" v-model="lang" @change="LanguageManager.setLanguage(lang)" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option :value="l.value" label="English" v-for="l in LanguageManager.supportedLanguages">
|
||||
<span role="img" aria-label="l.name" v-text="l.icon"></span>
|
||||
<span v-text="l.name"></span>
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-space>
|
||||
</template>
|
||||
<a-button shape="circle" icon="setting"></a-button>
|
||||
</a-popover>
|
||||
</div>
|
||||
<a-row type="flex" justify="center">
|
||||
<a-col :style="{ width: '100%' }">
|
||||
<h2 class="title headline zoom">
|
||||
@@ -503,25 +503,24 @@
|
||||
<a-form-item>
|
||||
<a-input autocomplete="username" name="username" v-model.trim="user.username"
|
||||
placeholder='{{ i18n "username" }}' @keydown.enter.native="login" autofocus>
|
||||
<a-icon slot="prefix" type="user" :style="{ fontSize: '16px' }"></a-icon>
|
||||
<a-icon slot="prefix" type="user" :style="{ fontSize: '1rem' }"></a-icon>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-input-password autocomplete="password" name="password" icon="lock" v-model.trim="user.password"
|
||||
<a-input-password autocomplete="password" name="password" v-model.trim="user.password"
|
||||
placeholder='{{ i18n "password" }}' @keydown.enter.native="login">
|
||||
<a-icon slot="prefix" type="lock" :style="{ fontSize: '16px' }"></a-icon>
|
||||
<a-icon slot="prefix" type="lock" :style="{ fontSize: '1rem' }"></a-icon>
|
||||
</a-input-password>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="secretEnable">
|
||||
<a-input-password autocomplete="secret" name="secret" icon="lock" v-model.trim="user.loginSecret"
|
||||
<a-input-password autocomplete="secret" name="secret" v-model.trim="user.loginSecret"
|
||||
placeholder='{{ i18n "secretToken" }}' @keydown.enter.native="login">
|
||||
<a-icon slot="prefix" type="key" :style="{ fontSize: '16px' }"></a-icon>
|
||||
<a-icon slot="prefix" type="key" :style="{ fontSize: '1rem' }"></a-icon>
|
||||
</a-input-password>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-row justify="center" class="centered">
|
||||
<div :style="{ height: '50px', marginTop: '16px' }" class="wave-btn-bg wave-btn-bg-cl"
|
||||
:style="loading ? { width: '52px' } : { display: 'inline-block' }">
|
||||
<div :style="{ height: '50px', marginTop: '1rem', ...loading ? { width: '52px' } : { display: 'inline-block' } }" class="wave-btn-bg wave-btn-bg-cl">
|
||||
<a-button class="ant-btn-primary-login" type="primary" :loading="loading" @click="login"
|
||||
:icon="loading ? 'poweroff' : undefined">
|
||||
[[ loading ? '' : '{{ i18n "login" }}' ]]
|
||||
|
||||
@@ -1,9 +1,25 @@
|
||||
{{define "modals/qrcodeModal"}}
|
||||
<a-modal id="qrcode-modal" v-model="qrModal.visible" :title="qrModal.title"
|
||||
:dialog-style="isMobile ? { top: '18px' } : {}"
|
||||
:closable="true"
|
||||
:class="themeSwitcher.currentTheme"
|
||||
:footer="null" width="fit-content">
|
||||
<a-modal id="qrcode-modal" v-model="qrModal.visible" :closable="true" :class="themeSwitcher.currentTheme"
|
||||
width="fit-content" :dialog-style="isMobile ? { top: '18px' } : {}" :footer="null">
|
||||
<template #title>
|
||||
<a-space direction="horizontal">
|
||||
<span>[[ qrModal.title ]]</span>
|
||||
<a-popover :overlay-class-name="themeSwitcher.currentTheme" trigger="click" placement="bottom">
|
||||
<template slot="content">
|
||||
<a-space direction="vertical">
|
||||
<template v-for="(row, index) in qrModal.qrcodes">
|
||||
<b>[[ row.remark ]]</b>
|
||||
<a-space direction="horizontal">
|
||||
<a-switch size="small" :checked="row.useIPv4" @click="toggleIPv4(index)"></a-switch>
|
||||
<span>{{ i18n "useIPv4ForHost" }}</span>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-space>
|
||||
</template>
|
||||
<a-icon type="setting"></a-icon>
|
||||
</a-popover>
|
||||
</a-space>
|
||||
</template>
|
||||
<tr-qr-modal class="qr-modal">
|
||||
<template v-if="app.subSettings.enable && qrModal.subId">
|
||||
<tr-qr-box class="qr-box">
|
||||
@@ -34,6 +50,53 @@
|
||||
</tr-qr-modal>
|
||||
</a-modal>
|
||||
|
||||
<style>
|
||||
.ant-table:not(.ant-table-expanded-row .ant-table) {
|
||||
outline: 1px solid #f0f0f0;
|
||||
outline-offset: -1px;
|
||||
border-radius: 1rem;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* QR code transition effects */
|
||||
.qr-cv {
|
||||
transition: all 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.qr-transition-enter-active, .qr-transition-leave-active {
|
||||
transition: opacity 0.3s, transform 0.3s;
|
||||
}
|
||||
|
||||
.qr-transition-enter, .qr-transition-leave-to {
|
||||
opacity: 0;
|
||||
transform: scale(0.9);
|
||||
}
|
||||
|
||||
.qr-transition-enter-to, .qr-transition-leave {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
.qr-flash {
|
||||
animation: qr-flash-animation 0.6s;
|
||||
}
|
||||
|
||||
@keyframes qr-flash-animation {
|
||||
0% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
opacity: 0.5;
|
||||
transform: scale(0.95);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
const qrModal = {
|
||||
title: '',
|
||||
@@ -42,31 +105,37 @@
|
||||
qrcodes: [],
|
||||
visible: false,
|
||||
subId: '',
|
||||
show: function(title = '', dbInbound, client) {
|
||||
show: function (title = '', dbInbound, client) {
|
||||
this.title = title;
|
||||
this.dbInbound = dbInbound;
|
||||
this.inbound = dbInbound.toInbound();
|
||||
this.client = client;
|
||||
this.subId = '';
|
||||
this.qrcodes = [];
|
||||
// Reset the status fetched flag when showing the modal
|
||||
if (qrModalApp) qrModalApp.statusFetched = false;
|
||||
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
|
||||
link: l,
|
||||
useIPv4: false,
|
||||
originalLink: l
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.inbound.genAllLinks(this.dbInbound.remark, app.remarkModel, client).forEach(l => {
|
||||
this.qrcodes.push({
|
||||
remark: l.remark,
|
||||
link: l.link
|
||||
link: l.link,
|
||||
useIPv4: false,
|
||||
originalLink: l.link
|
||||
});
|
||||
});
|
||||
}
|
||||
this.visible = true;
|
||||
},
|
||||
close: function() {
|
||||
close: function () {
|
||||
this.visible = false;
|
||||
},
|
||||
};
|
||||
@@ -76,8 +145,72 @@
|
||||
mixins: [MediaQueryMixin],
|
||||
data: {
|
||||
qrModal: qrModal,
|
||||
serverStatus: null,
|
||||
statusFetched: false,
|
||||
},
|
||||
methods: {
|
||||
async getStatus() {
|
||||
try {
|
||||
const msg = await HttpUtil.post('/server/status');
|
||||
if (msg.success) {
|
||||
this.serverStatus = msg.obj;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Failed to get status:", e);
|
||||
}
|
||||
},
|
||||
|
||||
toggleIPv4(index) {
|
||||
const row = qrModal.qrcodes[index];
|
||||
row.useIPv4 = !row.useIPv4;
|
||||
this.updateLink(index);
|
||||
},
|
||||
updateLink(index) {
|
||||
const row = qrModal.qrcodes[index];
|
||||
if (!this.serverStatus || !this.serverStatus.publicIP) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (row.useIPv4 && this.serverStatus.publicIP.ipv4) {
|
||||
// Replace the hostname or IP in the link with the IPv4 address
|
||||
const originalLink = row.originalLink;
|
||||
const url = new URL(originalLink);
|
||||
const ipv4 = this.serverStatus.publicIP.ipv4;
|
||||
|
||||
if (qrModal.inbound.protocol == Protocols.WIREGUARD) {
|
||||
// Special handling for WireGuard config
|
||||
const endpointRegex = /Endpoint = ([^:]+):(\d+)/;
|
||||
const match = originalLink.match(endpointRegex);
|
||||
if (match) {
|
||||
row.link = originalLink.replace(
|
||||
`Endpoint = ${match[1]}:${match[2]}`,
|
||||
`Endpoint = ${ipv4}:${match[2]}`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// For other protocols using URL format
|
||||
url.hostname = ipv4;
|
||||
row.link = url.toString();
|
||||
}
|
||||
} else {
|
||||
// Restore original link
|
||||
row.link = row.originalLink;
|
||||
}
|
||||
|
||||
// Update QR code with transition effect
|
||||
const canvasElement = document.querySelector('#qrCode-' + index);
|
||||
if (canvasElement) {
|
||||
// Add flash animation class
|
||||
canvasElement.classList.add('qr-flash');
|
||||
|
||||
// Remove the class after animation completes
|
||||
setTimeout(() => {
|
||||
canvasElement.classList.remove('qr-flash');
|
||||
}, 600);
|
||||
}
|
||||
|
||||
this.setQrCode("qrCode-" + index, row.link);
|
||||
},
|
||||
copy(content) {
|
||||
ClipboardManager
|
||||
.copyText(content)
|
||||
@@ -117,8 +250,14 @@
|
||||
updated() {
|
||||
if (this.qrModal.visible) {
|
||||
fixOverflow();
|
||||
if (!this.statusFetched) {
|
||||
this.getStatus();
|
||||
this.statusFetched = true;
|
||||
}
|
||||
} else {
|
||||
this.revertOverflow();
|
||||
// Reset the flag when modal is closed so it will fetch again next time
|
||||
this.statusFetched = false;
|
||||
}
|
||||
if (qrModal.client && qrModal.client.subId) {
|
||||
qrModal.subId = qrModal.client.subId;
|
||||
@@ -127,6 +266,10 @@
|
||||
}
|
||||
qrModal.qrcodes.forEach((element, index) => {
|
||||
this.setQrCode("qrCode-" + index, element.link);
|
||||
// Update links based on current toggle state
|
||||
if (element.useIPv4 && this.serverStatus && this.serverStatus.publicIP) {
|
||||
this.updateLink(index);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -142,8 +285,7 @@
|
||||
|
||||
function wrapContentsInMarquee(element) {
|
||||
element.classList.add("tr-marquee");
|
||||
element.children[0].style.animation = `move-ltr ${
|
||||
(element.children[0].clientWidth / element.clientWidth) * 5
|
||||
element.children[0].style.animation = `move-ltr ${(element.children[0].clientWidth / element.clientWidth) * 5
|
||||
}s ease-in-out infinite`;
|
||||
const marqueeText = element.children[0];
|
||||
if (element.children.length < 2) {
|
||||
@@ -159,4 +301,4 @@
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{{end}}
|
||||
{{end}}
|
||||
@@ -319,19 +319,11 @@
|
||||
this.loading(false);
|
||||
await this.updateAllSetting();
|
||||
},
|
||||
generateRandomString(length) {
|
||||
var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
|
||||
let randomString = "";
|
||||
for (let i = 0; i < length; i++) {
|
||||
randomString += chars[Math.floor(Math.random() * chars.length)];
|
||||
}
|
||||
return randomString;
|
||||
},
|
||||
async getNewSecret() {
|
||||
if (!this.changeSecret) {
|
||||
this.changeSecret = true;
|
||||
this.user.loginSecret = '';
|
||||
const newSecret = this.generateRandomString(64);
|
||||
const newSecret = RandomUtil.randomSeq(64);
|
||||
await PromiseUtil.sleep(1000);
|
||||
this.user.loginSecret = newSecret;
|
||||
this.changeSecret = false;
|
||||
|
||||
@@ -131,7 +131,7 @@
|
||||
{{template "modals/fakednsModal"}}
|
||||
{{template "modals/warpModal"}}
|
||||
<script>
|
||||
const rulesColumns = [
|
||||
const rulesColumns = [
|
||||
{ title: "#", align: 'center', width: 15, scopedSlots: { customRender: 'action' } },
|
||||
{ title: '{{ i18n "pages.xray.rules.source"}}', children: [
|
||||
{ title: 'IP', dataIndex: "source", align: 'center', width: 20, ellipsis: true },
|
||||
@@ -383,47 +383,6 @@
|
||||
if(msg.obj.length > 1) Vue.prototype.$message.error(msg.obj);
|
||||
}
|
||||
},
|
||||
async fetchUserSecret() {
|
||||
this.loading(true);
|
||||
const userMessage = await HttpUtil.post("/panel/setting/getUserSecret", this.user);
|
||||
if (userMessage.success) {
|
||||
this.user = userMessage.obj;
|
||||
}
|
||||
this.loading(false);
|
||||
},
|
||||
async updateSecret() {
|
||||
this.loading(true);
|
||||
const msg = await HttpUtil.post("/panel/setting/updateUserSecret", this.user);
|
||||
if (msg.success) {
|
||||
this.user = msg.obj;
|
||||
window.location.replace(basePath + "logout");
|
||||
}
|
||||
this.loading(false);
|
||||
await this.updateXraySetting();
|
||||
},
|
||||
generateRandomString(length) {
|
||||
var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
|
||||
let randomString = "";
|
||||
for (let i = 0; i < length; i++) {
|
||||
randomString += chars[Math.floor(Math.random() * chars.length)];
|
||||
}
|
||||
return randomString;
|
||||
},
|
||||
async getNewSecret() {
|
||||
this.loading(true);
|
||||
await PromiseUtil.sleep(600);
|
||||
const newSecret = this.generateRandomString(64);
|
||||
this.user.loginSecret = newSecret;
|
||||
document.getElementById("token").textContent = newSecret;
|
||||
this.loading(false);
|
||||
},
|
||||
async toggleToken(value) {
|
||||
if (value) {
|
||||
await this.getNewSecret();
|
||||
} else {
|
||||
this.user.loginSecret = "";
|
||||
}
|
||||
},
|
||||
async resetXrayConfigToDefault() {
|
||||
this.loading(true);
|
||||
const msg = await HttpUtil.get("/panel/setting/getDefaultJsonConfig");
|
||||
|
||||
@@ -1413,6 +1413,16 @@ func (t *Tgbot) answerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool
|
||||
t.deleteMessageTgBot(chatId, callbackQuery.Message.GetMessageID())
|
||||
t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.answers.successfulOperation"), tu.ReplyKeyboardRemove())
|
||||
}
|
||||
case "add_client_submit_enable":
|
||||
client_Enable = true
|
||||
_, err := t.SubmitAddClient()
|
||||
if err != nil {
|
||||
errorMessage := fmt.Sprintf("%v", err)
|
||||
t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.messages.error_add_client", "error=="+errorMessage), tu.ReplyKeyboardRemove())
|
||||
} else {
|
||||
t.deleteMessageTgBot(chatId, callbackQuery.Message.GetMessageID())
|
||||
t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.answers.successfulOperation"), tu.ReplyKeyboardRemove())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2216,6 +2226,9 @@ func (t *Tgbot) addClient(chatId int64, msg string, messageID ...int) {
|
||||
),
|
||||
tu.InlineKeyboardRow(
|
||||
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.submitDisable")).WithCallbackData("add_client_submit_disable"),
|
||||
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.submitEnable")).WithCallbackData("add_client_submit_enable"),
|
||||
),
|
||||
tu.InlineKeyboardRow(
|
||||
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.cancel")).WithCallbackData("add_client_cancel"),
|
||||
),
|
||||
)
|
||||
@@ -2239,6 +2252,9 @@ func (t *Tgbot) addClient(chatId int64, msg string, messageID ...int) {
|
||||
),
|
||||
tu.InlineKeyboardRow(
|
||||
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.submitDisable")).WithCallbackData("add_client_submit_disable"),
|
||||
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.submitEnable")).WithCallbackData("add_client_submit_enable"),
|
||||
),
|
||||
tu.InlineKeyboardRow(
|
||||
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.cancel")).WithCallbackData("add_client_cancel"),
|
||||
),
|
||||
)
|
||||
@@ -2262,6 +2278,9 @@ func (t *Tgbot) addClient(chatId int64, msg string, messageID ...int) {
|
||||
),
|
||||
tu.InlineKeyboardRow(
|
||||
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.submitDisable")).WithCallbackData("add_client_submit_disable"),
|
||||
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.submitEnable")).WithCallbackData("add_client_submit_enable"),
|
||||
),
|
||||
tu.InlineKeyboardRow(
|
||||
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.cancel")).WithCallbackData("add_client_cancel"),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
"copySuccess" = "اتنسخ بنجاح"
|
||||
"sure" = "متأكد؟"
|
||||
"encryption" = "تشفير"
|
||||
"useIPv4ForHost" = "استخدم IPv4 للمضيف"
|
||||
"transmission" = "نقل"
|
||||
"host" = "المستضيف"
|
||||
"path" = "مسار"
|
||||
@@ -641,13 +642,14 @@
|
||||
"getBanLogs" = "احصل على سجلات الحظر"
|
||||
"allClients" = "كل العملاء"
|
||||
|
||||
"addClient" = "أضف عميل"
|
||||
"submitDisable" = "اعتمد على إنه معطل ✅"
|
||||
"use_default" = "🏷️ استخدم الافتراضي"
|
||||
"change_id" = "⚙️🔑 تغيير الـ ID"
|
||||
"change_password" = "⚙️🔑 تغيير الباسورد"
|
||||
"change_email" = "⚙️📧 تغيير الإيميل"
|
||||
"change_comment" = "⚙️💬 تغيير التعليق"
|
||||
"addClient" = "إضافة عميل"
|
||||
"submitDisable" = "إرسال كمعطّل ☑️"
|
||||
"submitEnable" = "إرسال كمفعّل ✅"
|
||||
"use_default" = "🏷️ استخدام الإعدادات الافتراضية"
|
||||
"change_id" = "⚙️🔑 المعرّف"
|
||||
"change_password" = "⚙️🔑 كلمة السر"
|
||||
"change_email" = "⚙️📧 البريد الإلكتروني"
|
||||
"change_comment" = "⚙️💬 تعليق"
|
||||
|
||||
[tgbot.answers]
|
||||
"successfulOperation" = "✅ العملية نجحت!"
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
"copySuccess" = "Copied Successful"
|
||||
"sure" = "Sure"
|
||||
"encryption" = "Encryption"
|
||||
"useIPv4ForHost" = "Use IPv4 for host"
|
||||
"transmission" = "Transmission"
|
||||
"host" = "Host"
|
||||
"path" = "Path"
|
||||
@@ -642,7 +643,8 @@
|
||||
"allClients" = "All Clients"
|
||||
|
||||
"addClient" = "Add Client"
|
||||
"submitDisable" = "Submit As Disable ✅"
|
||||
"submitDisable" = "Submit As Disable ☑️"
|
||||
"submitEnable" = "Submit As Enable ✅"
|
||||
"use_default" = "🏷️ Use default"
|
||||
"change_id" = "⚙️🔑 ID"
|
||||
"change_password" = "⚙️🔑 Password"
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
"copySuccess" = "Copiado exitosamente"
|
||||
"sure" = "Seguro"
|
||||
"encryption" = "Encriptación"
|
||||
"useIPv4ForHost" = "Usar IPv4 para el host"
|
||||
"transmission" = "Transmisión"
|
||||
"host" = "Anfitrión"
|
||||
"path" = "Ruta"
|
||||
@@ -644,15 +645,17 @@
|
||||
"getBanLogs" = "Registros de prohibición"
|
||||
"allClients" = "Todos los Clientes"
|
||||
|
||||
"addClient" = "Añadir Cliente"
|
||||
"submitDisable" = "Enviar como Deshabilitado ✅"
|
||||
"use_default" = "🏷️ Usar predeterminado"
|
||||
"addClient" = "Añadir cliente"
|
||||
"submitDisable" = "Enviar como deshabilitado ☑️"
|
||||
"submitEnable" = "Enviar como habilitado ✅"
|
||||
"use_default" = "🏷️ Usar por defecto"
|
||||
"change_id" = "⚙️🔑 ID"
|
||||
"change_password" = "⚙️🔑 Contraseña"
|
||||
"change_email" = "⚙️📧 Correo electrónico"
|
||||
"change_comment" = "⚙️💬 Comentario"
|
||||
|
||||
|
||||
|
||||
[tgbot.answers]
|
||||
"successfulOperation" = "✅ ¡Exitosa!"
|
||||
"errorOperation" = "❗ Error en la Operación."
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
"copySuccess" = "باموفقیت کپیشد"
|
||||
"sure" = "مطمئن"
|
||||
"encryption" = "رمزگذاری"
|
||||
"useIPv4ForHost" = "از IPv4 برای میزبان استفاده کنید"
|
||||
"transmission" = "راهاتصال"
|
||||
"host" = "آدرس"
|
||||
"path" = "مسیر"
|
||||
@@ -644,15 +645,15 @@
|
||||
"getBanLogs" = "گزارش های بلوک را دریافت کنید"
|
||||
"allClients" = "همه مشتریان"
|
||||
|
||||
"addClient" = "اضافه کردن مشتری"
|
||||
"submitDisable" = "ارسال به عنوان غیرفعال ✅"
|
||||
"addClient" = "افزودن مشتری"
|
||||
"submitDisable" = "ارسال به عنوان غیرفعال ☑️"
|
||||
"submitEnable" = "ارسال به عنوان فعال ✅"
|
||||
"use_default" = "🏷️ استفاده از پیشفرض"
|
||||
"change_id" = "⚙️🔑 شناسه"
|
||||
"change_password" = "⚙️🔑 رمز عبور"
|
||||
"change_password" = "⚙️🔑 گذرواژه"
|
||||
"change_email" = "⚙️📧 ایمیل"
|
||||
"change_comment" = "⚙️💬 نظر"
|
||||
|
||||
|
||||
[tgbot.answers]
|
||||
"successfulOperation" = "✅ انجام شد!"
|
||||
"errorOperation" = "❗ خطا در عملیات."
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
"copySuccess" = "Berhasil Disalin"
|
||||
"sure" = "Yakin"
|
||||
"encryption" = "Enkripsi"
|
||||
"useIPv4ForHost" = "Gunakan IPv4 untuk host"
|
||||
"transmission" = "Transmisi"
|
||||
"host" = "Host"
|
||||
"path" = "Jalur"
|
||||
@@ -645,14 +646,16 @@
|
||||
"allClients" = "Semua Klien"
|
||||
|
||||
"addClient" = "Tambah Klien"
|
||||
"submitDisable" = "Kirim Sebagai Nonaktif ✅"
|
||||
"use_default" = "🏷️ Gunakan default"
|
||||
"submitDisable" = "Kirim Sebagai Nonaktif ☑️"
|
||||
"submitEnable" = "Kirim Sebagai Aktif ✅"
|
||||
"use_default" = "🏷️ Gunakan Default"
|
||||
"change_id" = "⚙️🔑 ID"
|
||||
"change_password" = "⚙️🔑 Kata Sandi"
|
||||
"change_email" = "⚙️📧 Email"
|
||||
"change_comment" = "⚙️💬 Komentar"
|
||||
|
||||
|
||||
|
||||
[tgbot.answers]
|
||||
"successfulOperation" = "✅ Operasi berhasil!"
|
||||
"errorOperation" = "❗ Kesalahan dalam operasi."
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
"copySuccess" = "コピー成功"
|
||||
"sure" = "確定"
|
||||
"encryption" = "暗号化"
|
||||
"useIPv4ForHost" = "ホストにIPv4を使用"
|
||||
"transmission" = "伝送"
|
||||
"host" = "ホスト"
|
||||
"path" = "パス"
|
||||
@@ -645,14 +646,14 @@
|
||||
"allClients" = "すべてのクライアント"
|
||||
|
||||
"addClient" = "クライアントを追加"
|
||||
"submitDisable" = "無効として送信 ✅"
|
||||
"submitDisable" = "無効として送信 ☑️"
|
||||
"submitEnable" = "有効として送信 ✅"
|
||||
"use_default" = "🏷️ デフォルトを使用"
|
||||
"change_id" = "⚙️🔑 ID"
|
||||
"change_password" = "⚙️🔑 パスワード"
|
||||
"change_email" = "⚙️📧 メール"
|
||||
"change_email" = "⚙️📧 メールアドレス"
|
||||
"change_comment" = "⚙️💬 コメント"
|
||||
|
||||
|
||||
[tgbot.answers]
|
||||
"successfulOperation" = "✅ 成功!"
|
||||
"errorOperation" = "❗ 操作エラー。"
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
"copySuccess" = "Copiado com Sucesso"
|
||||
"sure" = "Certo"
|
||||
"encryption" = "Criptografia"
|
||||
"useIPv4ForHost" = "Usar IPv4 para o host"
|
||||
"transmission" = "Transmissão"
|
||||
"host" = "Servidor"
|
||||
"path" = "Caminho"
|
||||
@@ -645,7 +646,8 @@
|
||||
"allClients" = "Todos os clientes"
|
||||
|
||||
"addClient" = "Adicionar Cliente"
|
||||
"submitDisable" = "Enviar como Desativado ✅"
|
||||
"submitDisable" = "Enviar como Desativado ☑️"
|
||||
"submitEnable" = "Enviar como Ativado ✅"
|
||||
"use_default" = "🏷️ Usar padrão"
|
||||
"change_id" = "⚙️🔑 ID"
|
||||
"change_password" = "⚙️🔑 Senha"
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
"copySuccess" = "Скопировано"
|
||||
"sure" = "Да"
|
||||
"encryption" = "Шифрование"
|
||||
"useIPv4ForHost" = "Использовать IPv4 для хоста"
|
||||
"transmission" = "Протокол"
|
||||
"host" = "Хост"
|
||||
"path" = "Путь"
|
||||
@@ -645,7 +646,8 @@
|
||||
"allClients" = "Все клиенты"
|
||||
|
||||
"addClient" = "Добавить клиента"
|
||||
"submitDisable" = "Отправить отключенным ✅"
|
||||
"submitDisable" = "Отправить как отключённый ☑️"
|
||||
"submitEnable" = "Отправить как включённый ✅"
|
||||
"use_default" = "🏷️ Использовать по умолчанию"
|
||||
"change_id" = "⚙️🔑 ID"
|
||||
"change_password" = "⚙️🔑 Пароль"
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
"copySuccess" = "Başarıyla Kopyalandı"
|
||||
"sure" = "Emin misiniz"
|
||||
"encryption" = "Şifreleme"
|
||||
"useIPv4ForHost" = "Ana bilgisayar için IPv4 kullan"
|
||||
"transmission" = "İletim"
|
||||
"host" = "Sunucu"
|
||||
"path" = "Yol"
|
||||
@@ -645,9 +646,10 @@
|
||||
"allClients" = "Tüm Müşteriler"
|
||||
|
||||
"addClient" = "Müşteri Ekle"
|
||||
"submitDisable" = "Devre Dışı Olarak Gönder ✅"
|
||||
"submitDisable" = "Devre Dışı Olarak Gönder ☑️"
|
||||
"submitEnable" = "Etkin Olarak Gönder ✅"
|
||||
"use_default" = "🏷️ Varsayılanı Kullan"
|
||||
"change_id" = "⚙️🔑 ID"
|
||||
"change_id" = "⚙️🔑 Kimlik"
|
||||
"change_password" = "⚙️🔑 Şifre"
|
||||
"change_email" = "⚙️📧 E-posta"
|
||||
"change_comment" = "⚙️💬 Yorum"
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
"copySuccess" = "Скопійовано успішно"
|
||||
"sure" = "Звичайно"
|
||||
"encryption" = "Шифрування"
|
||||
"useIPv4ForHost" = "Використовувати IPv4 для хоста"
|
||||
"transmission" = "Протокол передачи"
|
||||
"host" = "Хост"
|
||||
"path" = "Шлях"
|
||||
@@ -645,8 +646,9 @@
|
||||
"allClients" = "Всі Клієнти"
|
||||
|
||||
"addClient" = "Додати клієнта"
|
||||
"submitDisable" = "Надіслати відключеним ✅"
|
||||
"use_default" = "🏷️ Використовувати за замовчуванням"
|
||||
"submitDisable" = "Надіслати як вимкнено ☑️"
|
||||
"submitEnable" = "Надіслати як увімкнено ✅"
|
||||
"use_default" = "🏷️ Використати типове"
|
||||
"change_id" = "⚙️🔑 ID"
|
||||
"change_password" = "⚙️🔑 Пароль"
|
||||
"change_email" = "⚙️📧 Електронна пошта"
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
"copySuccess" = "Đã sao chép thành công"
|
||||
"sure" = "Chắc chắn"
|
||||
"encryption" = "Mã hóa"
|
||||
"useIPv4ForHost" = "Sử dụng IPv4 cho máy chủ"
|
||||
"transmission" = "Truyền tải"
|
||||
"host" = "Máy chủ"
|
||||
"path" = "Đường dẫn"
|
||||
@@ -645,12 +646,13 @@
|
||||
"allClients" = "Tất cả Khách hàng"
|
||||
|
||||
"addClient" = "Thêm Khách Hàng"
|
||||
"submitDisable" = "Gửi Dưới Dạng Tắt ✅"
|
||||
"use_default" = "🏷️ Sử dụng mặc định"
|
||||
"submitDisable" = "Gửi Dưới Dạng Vô Hiệu ☑️"
|
||||
"submitEnable" = "Gửi Dưới Dạng Kích Hoạt ✅"
|
||||
"use_default" = "🏷️ Sử Dụng Mặc Định"
|
||||
"change_id" = "⚙️🔑 ID"
|
||||
"change_password" = "⚙️🔑 Mật khẩu"
|
||||
"change_password" = "⚙️🔑 Mật Khẩu"
|
||||
"change_email" = "⚙️📧 Email"
|
||||
"change_comment" = "⚙️💬 Bình luận"
|
||||
"change_comment" = "⚙️💬 Bình Luận"
|
||||
|
||||
|
||||
[tgbot.answers]
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
"copySuccess" = "复制成功"
|
||||
"sure" = "确定"
|
||||
"encryption" = "加密"
|
||||
"useIPv4ForHost" = "使用 IPv4 连接主机"
|
||||
"transmission" = "传输"
|
||||
"host" = "主机"
|
||||
"path" = "路径"
|
||||
@@ -645,7 +646,8 @@
|
||||
"allClients" = "所有客户"
|
||||
|
||||
"addClient" = "添加客户"
|
||||
"submitDisable" = "提交为禁用 ✅"
|
||||
"submitDisable" = "提交为禁用 ☑️"
|
||||
"submitEnable" = "提交为启用 ✅"
|
||||
"use_default" = "🏷️ 使用默认"
|
||||
"change_id" = "⚙️🔑 ID"
|
||||
"change_password" = "⚙️🔑 密码"
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
"copySuccess" = "複製成功"
|
||||
"sure" = "確定"
|
||||
"encryption" = "加密"
|
||||
"useIPv4ForHost" = "使用 IPv4 連接主機"
|
||||
"transmission" = "傳輸"
|
||||
"host" = "主機"
|
||||
"path" = "路徑"
|
||||
@@ -645,8 +646,9 @@
|
||||
"allClients" = "所有客戶"
|
||||
|
||||
"addClient" = "新增客戶"
|
||||
"submitDisable" = "提交為停用 ✅"
|
||||
"use_default" = "🏷️ 使用預設"
|
||||
"submitDisable" = "以停用方式送出 ☑️"
|
||||
"submitEnable" = "以啟用方式送出 ✅"
|
||||
"use_default" = "🏷️ 使用預設值"
|
||||
"change_id" = "⚙️🔑 ID"
|
||||
"change_password" = "⚙️🔑 密碼"
|
||||
"change_email" = "⚙️📧 電子郵件"
|
||||
|
||||
Reference in New Issue
Block a user