Compare commits

..

No commits in common. "c2d6dd923f72cb044704653fc9ce6f1dd521e156" and "fe9f0d1d0e86f55a97e3905358888f595b9bd08b" have entirely different histories.

17 changed files with 54 additions and 143 deletions

View file

@ -146,79 +146,3 @@ jobs:
asset_name: x-ui-linux-${{ matrix.platform }}.tar.gz
overwrite: true
prerelease: true
# =================================
# Windows Build
# =================================
build-windows:
name: Build for Windows
permissions:
contents: write
strategy:
matrix:
platform:
- amd64
runs-on: windows-latest
steps:
- name: Checkout repository
uses: actions/checkout@v5
- name: Setup Go
uses: actions/setup-go@v6
with:
go-version-file: go.mod
check-latest: true
- name: Build 3X-UI for Windows
shell: pwsh
run: |
$env:CGO_ENABLED="1"
$env:GOOS="windows"
$env:GOARCH="amd64"
go build -ldflags "-w -s" -o xui-release.exe -v main.go
mkdir x-ui
Copy-Item xui-release.exe x-ui\
mkdir x-ui\bin
cd x-ui\bin
# Download Xray for Windows
$Xray_URL = "https://github.com/XTLS/Xray-core/releases/download/v25.6.8/"
Invoke-WebRequest -Uri "${Xray_URL}Xray-windows-64.zip" -OutFile "Xray-windows-64.zip"
Expand-Archive -Path "Xray-windows-64.zip" -DestinationPath .
Remove-Item "Xray-windows-64.zip"
Remove-Item geoip.dat, geosite.dat -ErrorAction SilentlyContinue
Invoke-WebRequest -Uri "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat" -OutFile "geoip.dat"
Invoke-WebRequest -Uri "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat" -OutFile "geosite.dat"
Invoke-WebRequest -Uri "https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geoip.dat" -OutFile "geoip_IR.dat"
Invoke-WebRequest -Uri "https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geosite.dat" -OutFile "geosite_IR.dat"
Invoke-WebRequest -Uri "https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geoip.dat" -OutFile "geoip_RU.dat"
Invoke-WebRequest -Uri "https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geosite.dat" -OutFile "geosite_RU.dat"
Rename-Item xray.exe xray-windows-amd64.exe
cd ..
Copy-Item -Path ..\windows_files\* -Destination . -Recurse
cd ..
- name: Package to Zip
shell: pwsh
run: |
Compress-Archive -Path .\x-ui -DestinationPath "x-ui-windows-amd64.zip"
- name: Upload files to Artifacts
uses: actions/upload-artifact@v4
with:
name: x-ui-windows-amd64
path: ./x-ui-windows-amd64.zip
- name: Upload files to GH release
uses: svenstaro/upload-release-action@v2
if: |
(github.event_name == 'release' && github.event.action == 'published') ||
(github.event_name == 'push' && startsWith(github.ref, 'refs/tags/'))
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ github.ref }}
file: x-ui-windows-amd64.zip
asset_name: x-ui-windows-amd64.zip
overwrite: true
prerelease: true

View file

@ -12,11 +12,11 @@ type Protocol string
const (
VMESS Protocol = "vmess"
VLESS Protocol = "vless"
Tunnel Protocol = "tunnel"
DOKODEMO Protocol = "dokodemo-door"
HTTP Protocol = "http"
Trojan Protocol = "trojan"
Shadowsocks Protocol = "shadowsocks"
Mixed Protocol = "mixed"
Socks Protocol = "socks"
WireGuard Protocol = "wireguard"
)

View file

@ -13,7 +13,7 @@
"inbounds": [
{
"port": 10808,
"protocol": "mixed",
"protocol": "socks",
"settings": {
"auth": "noauth",
"udp": true,
@ -28,7 +28,7 @@
],
"enabled": true
},
"tag": "mixed"
"tag": "socks"
},
{
"port": 10809,

View file

@ -49,8 +49,8 @@ class DBInbound {
return this.protocol === Protocols.SHADOWSOCKS;
}
get isMixed() {
return this.protocol === Protocols.MIXED;
get isSocks() {
return this.protocol === Protocols.SOCKS;
}
get isHTTP() {

View file

@ -3,8 +3,8 @@ const Protocols = {
VLESS: 'vless',
TROJAN: 'trojan',
SHADOWSOCKS: 'shadowsocks',
TUNNEL: 'tunnel',
MIXED: 'mixed',
DOKODEMO: 'dokodemo-door',
SOCKS: 'socks',
HTTP: 'http',
WIREGUARD: 'wireguard',
};
@ -729,7 +729,7 @@ class RealityStreamSettings extends XrayCommonClass {
constructor(
show = false,
xver = 0,
target = 'google.com:443',
dest = 'google.com:443',
serverNames = 'google.com,www.google.com',
privateKey = '',
minClientVer = '',
@ -742,7 +742,7 @@ class RealityStreamSettings extends XrayCommonClass {
super();
this.show = show;
this.xver = xver;
this.target = target;
this.dest = dest;
this.serverNames = Array.isArray(serverNames) ? serverNames.join(",") : serverNames;
this.privateKey = privateKey;
this.minClientVer = minClientVer;
@ -767,7 +767,7 @@ class RealityStreamSettings extends XrayCommonClass {
return new RealityStreamSettings(
json.show,
json.xver,
json.target,
json.dest,
json.serverNames,
json.privateKey,
json.minClientVer,
@ -783,7 +783,7 @@ class RealityStreamSettings extends XrayCommonClass {
return {
show: this.show,
xver: this.xver,
target: this.target,
dest: this.dest,
serverNames: this.serverNames.split(","),
privateKey: this.privateKey,
minClientVer: this.minClientVer,
@ -1712,8 +1712,8 @@ Inbound.Settings = class extends XrayCommonClass {
case Protocols.VLESS: return new Inbound.VLESSSettings(protocol);
case Protocols.TROJAN: return new Inbound.TrojanSettings(protocol);
case Protocols.SHADOWSOCKS: return new Inbound.ShadowsocksSettings(protocol);
case Protocols.TUNNEL: return new Inbound.TunnelSettings(protocol);
case Protocols.MIXED: return new Inbound.MixedSettings(protocol);
case Protocols.DOKODEMO: return new Inbound.DokodemoSettings(protocol);
case Protocols.SOCKS: return new Inbound.SocksSettings(protocol);
case Protocols.HTTP: return new Inbound.HttpSettings(protocol);
case Protocols.WIREGUARD: return new Inbound.WireguardSettings(protocol);
default: return null;
@ -1726,8 +1726,8 @@ Inbound.Settings = class extends XrayCommonClass {
case Protocols.VLESS: return Inbound.VLESSSettings.fromJson(json);
case Protocols.TROJAN: return Inbound.TrojanSettings.fromJson(json);
case Protocols.SHADOWSOCKS: return Inbound.ShadowsocksSettings.fromJson(json);
case Protocols.TUNNEL: return Inbound.TunnelSettings.fromJson(json);
case Protocols.MIXED: return Inbound.MixedSettings.fromJson(json);
case Protocols.DOKODEMO: return Inbound.DokodemoSettings.fromJson(json);
case Protocols.SOCKS: return Inbound.SocksSettings.fromJson(json);
case Protocols.HTTP: return Inbound.HttpSettings.fromJson(json);
case Protocols.WIREGUARD: return Inbound.WireguardSettings.fromJson(json);
default: return null;
@ -2327,7 +2327,7 @@ Inbound.ShadowsocksSettings.Shadowsocks = class extends XrayCommonClass {
};
Inbound.TunnelSettings = class extends Inbound.Settings {
Inbound.DokodemoSettings = class extends Inbound.Settings {
constructor(
protocol,
address,
@ -2345,8 +2345,8 @@ Inbound.TunnelSettings = class extends Inbound.Settings {
}
static fromJson(json = {}) {
return new Inbound.TunnelSettings(
Protocols.TUNNEL,
return new Inbound.DokodemoSettings(
Protocols.DOKODEMO,
json.address,
json.port,
XrayCommonClass.toHeaders(json.portMap),
@ -2366,8 +2366,8 @@ Inbound.TunnelSettings = class extends Inbound.Settings {
}
};
Inbound.MixedSettings = class extends Inbound.Settings {
constructor(protocol, auth = 'password', accounts = [new Inbound.MixedSettings.SocksAccount()], udp = false, ip = '127.0.0.1') {
Inbound.SocksSettings = class extends Inbound.Settings {
constructor(protocol, auth = 'password', accounts = [new Inbound.SocksSettings.SocksAccount()], udp = false, ip = '127.0.0.1') {
super(protocol);
this.auth = auth;
this.accounts = accounts;
@ -2387,11 +2387,11 @@ Inbound.MixedSettings = class extends Inbound.Settings {
let accounts;
if (json.auth === 'password') {
accounts = json.accounts.map(
account => Inbound.MixedSettings.SocksAccount.fromJson(account)
account => Inbound.SocksSettings.SocksAccount.fromJson(account)
)
}
return new Inbound.MixedSettings(
Protocols.MIXED,
return new Inbound.SocksSettings(
Protocols.SOCKS,
json.auth,
accounts,
json.udp,
@ -2408,7 +2408,7 @@ Inbound.MixedSettings = class extends Inbound.Settings {
};
}
};
Inbound.MixedSettings.SocksAccount = class extends XrayCommonClass {
Inbound.SocksSettings.SocksAccount = class extends XrayCommonClass {
constructor(user = RandomUtil.randomSeq(10), pass = RandomUtil.randomSeq(10)) {
super();
this.user = user;
@ -2416,7 +2416,7 @@ Inbound.MixedSettings.SocksAccount = class extends XrayCommonClass {
}
static fromJson(json = {}) {
return new Inbound.MixedSettings.SocksAccount(json.user, json.pass);
return new Inbound.SocksSettings.SocksAccount(json.user, json.pass);
}
};

View file

@ -6,7 +6,7 @@ const Protocols = {
VLESS: "vless",
Trojan: "trojan",
Shadowsocks: "shadowsocks",
Mixed: "mixed",
Socks: "socks",
HTTP: "http",
Wireguard: "wireguard"
};
@ -643,7 +643,7 @@ class Outbound extends CommonClass {
Protocols.Trojan,
Protocols.Shadowsocks,
Protocols.HTTP,
Protocols.Mixed
Protocols.Socks
].includes(this.protocol);
}
@ -652,7 +652,7 @@ class Outbound extends CommonClass {
}
hasServers() {
return [Protocols.Trojan, Protocols.Shadowsocks, Protocols.Mixed, Protocols.HTTP].includes(this.protocol);
return [Protocols.Trojan, Protocols.Shadowsocks, Protocols.Socks, Protocols.HTTP].includes(this.protocol);
}
hasAddressPort() {
@ -662,13 +662,13 @@ class Outbound extends CommonClass {
Protocols.VLESS,
Protocols.Trojan,
Protocols.Shadowsocks,
Protocols.Mixed,
Protocols.Socks,
Protocols.HTTP
].includes(this.protocol);
}
hasUsername() {
return [Protocols.Mixed, Protocols.HTTP].includes(this.protocol);
return [Protocols.Socks, Protocols.HTTP].includes(this.protocol);
}
static fromJson(json = {}) {
@ -847,7 +847,7 @@ Outbound.Settings = class extends CommonClass {
case Protocols.VLESS: return new Outbound.VLESSSettings();
case Protocols.Trojan: return new Outbound.TrojanSettings();
case Protocols.Shadowsocks: return new Outbound.ShadowsocksSettings();
case Protocols.Mixed: return new Outbound.MixedSettings();
case Protocols.Socks: return new Outbound.SocksSettings();
case Protocols.HTTP: return new Outbound.HttpSettings();
case Protocols.Wireguard: return new Outbound.WireguardSettings();
default: return null;
@ -863,7 +863,7 @@ Outbound.Settings = class extends CommonClass {
case Protocols.VLESS: return Outbound.VLESSSettings.fromJson(json);
case Protocols.Trojan: return Outbound.TrojanSettings.fromJson(json);
case Protocols.Shadowsocks: return Outbound.ShadowsocksSettings.fromJson(json);
case Protocols.Mixed: return Outbound.MixedSettings.fromJson(json);
case Protocols.Socks: return Outbound.SocksSettings.fromJson(json);
case Protocols.HTTP: return Outbound.HttpSettings.fromJson(json);
case Protocols.Wireguard: return Outbound.WireguardSettings.fromJson(json);
default: return null;
@ -1141,7 +1141,7 @@ Outbound.ShadowsocksSettings = class extends CommonClass {
}
};
Outbound.MixedSettings = class extends CommonClass {
Outbound.SocksSettings = class extends CommonClass {
constructor(address, port, user, pass) {
super();
this.address = address;
@ -1153,7 +1153,7 @@ Outbound.MixedSettings = class extends CommonClass {
static fromJson(json = {}) {
let servers = json.servers;
if (ObjectUtil.isArrEmpty(servers)) servers = [{ users: [{}] }];
return new Outbound.MixedSettings(
return new Outbound.SocksSettings(
servers[0].address,
servers[0].port,
ObjectUtil.isArrEmpty(servers[0].users) ? '' : servers[0].users[0].user,

View file

@ -83,14 +83,14 @@
{{template "form/shadowsocks"}}
</template>
<!-- tunnel -->
<template v-if="inbound.protocol === Protocols.TUNNEL">
{{template "form/tunnel"}}
<!-- dokodemo-door -->
<template v-if="inbound.protocol === Protocols.DOKODEMO">
{{template "form/dokodemo"}}
</template>
<!-- mixed -->
<template v-if="inbound.protocol === Protocols.MIXED">
{{template "form/mixed"}}
<!-- socks -->
<template v-if="inbound.protocol === Protocols.SOCKS">
{{template "form/socks"}}
</template>
<!-- http -->

View file

@ -241,9 +241,9 @@
</template>
</template>
<!-- Servers (trojan/shadowsocks/mixed/http) settings -->
<!-- Servers (trojan/shadowsocks/socks/http) settings -->
<template v-if="outbound.hasServers()">
<!-- http / mixed -->
<!-- http / socks -->
<template v-if="outbound.hasUsername()">
<a-form-item label='{{ i18n "username" }}'>
<a-input v-model.trim="outbound.settings.user"></a-input>

View file

@ -1,4 +1,4 @@
{{define "form/tunnel"}}
{{define "form/dokodemo"}}
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
<a-form-item label='{{ i18n "pages.inbounds.targetAddress"}}'>
<a-input v-model.trim="inbound.settings.address"></a-input>

View file

@ -1,4 +1,4 @@
{{define "form/mixed"}}
{{define "form/socks"}}
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
<a-form-item label='{{ i18n "pages.inbounds.enable" }} UDP'>
<a-switch v-model="inbound.settings.udp"></a-switch>
@ -15,7 +15,7 @@
<td width="45%">{{ i18n "username" }}</td>
<td width="45%">{{ i18n "password" }}</td>
<td>
<a-button icon="plus" size="small" @click="inbound.settings.addAccount(new Inbound.MixedSettings.SocksAccount())"></a-button>
<a-button icon="plus" size="small" @click="inbound.settings.addAccount(new Inbound.SocksSettings.SocksAccount())"></a-button>
</td>
</tr>
</table>

View file

@ -12,8 +12,8 @@
<a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
</a-select>
</a-form-item>
<a-form-item label='Target'>
<a-input v-model.trim="inbound.stream.reality.target"></a-input>
<a-form-item label='Dest (Target)'>
<a-input v-model.trim="inbound.stream.reality.dest"></a-input>
</a-form-item>
<a-form-item label='SNI'>
<a-input v-model.trim="inbound.stream.reality.serverNames"></a-input>

View file

@ -354,7 +354,7 @@
<code>[[ link.link ]]</code>
</tr-info-row>
</template>
<table v-if="inbound.protocol == Protocols.TUNNEL" class="tr-info-table">
<table v-if="inbound.protocol == Protocols.DOKODEMO" class="tr-info-table">
<tr>
<th>{{ i18n "pages.inbounds.targetAddress" }}</th>
<th>{{ i18n "pages.inbounds.destinationPort" }}</th>
@ -376,7 +376,7 @@
</td>
</tr>
</table>
<table v-if="dbInbound.isMixed" class="tr-info-table">
<table v-if="dbInbound.isSocks" class="tr-info-table">
<tr>
<th>{{ i18n "password" }} Auth</th>
<th>{{ i18n "pages.inbounds.enable" }} udp</th>

View file

@ -572,7 +572,7 @@
serverObj = o.settings.vnext;
break;
case Protocols.HTTP:
case Protocols.Mixed:
case Protocols.Socks:
case Protocols.Shadowsocks:
case Protocols.Trojan:
serverObj = o.settings.servers;

View file

@ -19,7 +19,7 @@
"tag": "api",
"listen": "127.0.0.1",
"port": 62789,
"protocol": "tunnel",
"protocol": "dokodemo-door",
"settings": {
"address": "127.0.0.1"
}

View file

@ -2129,8 +2129,8 @@ func (t *Tgbot) getInboundsAddClient() (*telego.InlineKeyboardMarkup, error) {
}
excludedProtocols := map[model.Protocol]bool{
model.Tunnel: true,
model.Mixed: true,
model.DOKODEMO: true,
model.Socks: true,
model.WireGuard: true,
model.HTTP: true,
}

View file

@ -1,13 +0,0 @@
you can't install fail2ban on windows
we don't have bash menu for windows
if you forgot your password you need to check your database with https://sqlitebrowser.org/
the app need to be open all the time
default setting:
http://localhost:2053/
user: admin
pass: admin
port: 2053
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout localhost.key -out localhost.crt