Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/MHSanaei/3x-ui.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/web
diff options
context:
space:
mode:
authorMHSanaei <ho3ein.sanaei@gmail.com>2026-04-20 17:05:27 +0300
committerMHSanaei <ho3ein.sanaei@gmail.com>2026-04-20 17:05:27 +0300
commitae5ad505d04fa347eb96a0d2bfb54ff541c3b709 (patch)
tree54a8088e98e15868238be54b063bb051ab84fcb0 /web
parentc188056f64be268dda8f7c16e23f7ef9c90d014f (diff)
add hysteria inbound
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
Diffstat (limited to 'web')
-rw-r--r--web/assets/js/model/dbinbound.js4
-rw-r--r--web/assets/js/model/inbound.js551
-rw-r--r--web/assets/js/model/outbound.js4
-rw-r--r--web/html/component/aClientTable.html4
-rw-r--r--web/html/form/client.html112
-rw-r--r--web/html/form/inbound.html8
-rw-r--r--web/html/form/protocol/hysteria.html32
-rw-r--r--web/html/form/stream/stream_hysteria.html75
-rw-r--r--web/html/form/stream/stream_settings.html8
-rw-r--r--web/html/form/tls_settings.html21
-rw-r--r--web/html/inbounds.html483
-rw-r--r--web/html/modals/client_bulk_modal.html104
-rw-r--r--web/html/modals/client_modal.html16
-rw-r--r--web/service/inbound.go22
-rw-r--r--web/service/xray.go4
15 files changed, 950 insertions, 498 deletions
diff --git a/web/assets/js/model/dbinbound.js b/web/assets/js/model/dbinbound.js
index c347a7eb..e1802635 100644
--- a/web/assets/js/model/dbinbound.js
+++ b/web/assets/js/model/dbinbound.js
@@ -125,7 +125,7 @@ class DBInbound {
sniffing: sniffing,
clientStats: this.clientStats,
};
-
+
this._cachedInbound = Inbound.fromJson(config);
return this._cachedInbound;
}
@@ -147,6 +147,7 @@ class DBInbound {
case Protocols.VMESS:
case Protocols.VLESS:
case Protocols.TROJAN:
+ case Protocols.HYSTERIA:
return true;
case Protocols.SHADOWSOCKS:
return this.toInbound().isSSMultiUser;
@@ -161,6 +162,7 @@ class DBInbound {
case Protocols.VLESS:
case Protocols.TROJAN:
case Protocols.SHADOWSOCKS:
+ case Protocols.HYSTERIA:
return true;
default:
return false;
diff --git a/web/assets/js/model/inbound.js b/web/assets/js/model/inbound.js
index b6059cf7..f74b2736 100644
--- a/web/assets/js/model/inbound.js
+++ b/web/assets/js/model/inbound.js
@@ -8,6 +8,7 @@ const Protocols = {
HTTP: 'http',
WIREGUARD: 'wireguard',
TUN: 'tun',
+ HYSTERIA: 'hysteria',
};
const SSMethods = {
@@ -589,6 +590,106 @@ class xHTTPStreamSettings extends XrayCommonClass {
}
}
+class HysteriaStreamSettings extends XrayCommonClass {
+ constructor(
+ protocol,
+ version = 2,
+ auth = '',
+ udpIdleTimeout = 60,
+ masquerade,
+ ) {
+ super(protocol);
+ this.version = version;
+ this.auth = auth;
+ this.udpIdleTimeout = udpIdleTimeout;
+ this.masquerade = masquerade;
+ }
+
+ static fromJson(json = {}) {
+ return new HysteriaStreamSettings(
+ json.protocol,
+ json.version ?? 2,
+ json.auth ?? '',
+ json.udpIdleTimeout ?? 60,
+ json.masquerade ? HysteriaMasquerade.fromJson(json.masquerade) : undefined,
+ );
+ }
+
+ toJson() {
+ return {
+ protocol: this.protocol,
+ version: this.version,
+ auth: this.auth,
+ udpIdleTimeout: this.udpIdleTimeout,
+ masquerade: this.masqueradeSwitch ? this.masquerade.toJson() : undefined,
+ };
+ }
+
+ get masqueradeSwitch() {
+ return this.masquerade != undefined;
+ }
+
+ set masqueradeSwitch(value) {
+ this.masquerade = value ? new HysteriaMasquerade() : undefined;
+ }
+};
+
+class HysteriaMasquerade extends XrayCommonClass {
+ constructor(
+ type = 'proxy',
+ dir = '',
+ url = '',
+ rewriteHost = false,
+ insecure = false,
+ content = '',
+ headers = [],
+ statusCode = 0,
+ ) {
+ super();
+ this.type = type;
+ this.dir = dir;
+ this.url = url;
+ this.rewriteHost = rewriteHost;
+ this.insecure = insecure;
+ this.content = content;
+ this.headers = headers;
+ this.statusCode = statusCode;
+ }
+
+ addHeader(name, value) {
+ this.headers.push({ name: name, value: value });
+ }
+
+ removeHeader(index) {
+ this.headers.splice(index, 1);
+ }
+
+ static fromJson(json = {}) {
+ return new HysteriaMasquerade(
+ json.type,
+ json.dir,
+ json.url,
+ json.rewriteHost,
+ json.insecure,
+ json.content,
+ XrayCommonClass.toHeaders(json.headers),
+ json.statusCode,
+ );
+ }
+
+ toJson() {
+ return {
+ type: this.type,
+ dir: this.dir,
+ url: this.url,
+ rewriteHost: this.rewriteHost,
+ insecure: this.insecure,
+ content: this.content,
+ headers: XrayCommonClass.toV2Headers(this.headers, false),
+ statusCode: this.statusCode,
+ };
+ }
+};
class TlsStreamSettings extends XrayCommonClass {
constructor(
serverName = '',
@@ -987,6 +1088,12 @@ class UdpMask extends XrayCommonClass {
case 'header-wechat':
case 'header-wireguard':
return {};
+ case 'header-custom':
+ return { client: [], server: [] };
+ case 'noise':
+ return { reset: 0, noise: [] };
+ case 'sudoku':
+ return { ascii: '', customTable: '', customTables: [], paddingMin: 0, paddingMax: 0 };
default:
return settings;
}
@@ -1021,7 +1128,6 @@ class FinalMaskStreamSettings extends XrayCommonClass {
return {
udp: this.udp.map(udp => udp.toJson())
};
-
}
}
@@ -1037,6 +1143,7 @@ class StreamSettings extends XrayCommonClass {
grpcSettings = new GrpcStreamSettings(),
httpupgradeSettings = new HTTPUpgradeStreamSettings(),
xhttpSettings = new xHTTPStreamSettings(),
+ hysteriaSettings = new HysteriaStreamSettings(),
finalmask = new FinalMaskStreamSettings(),
sockopt = undefined,
) {
@@ -1052,6 +1159,7 @@ class StreamSettings extends XrayCommonClass {
this.grpc = grpcSettings;
this.httpupgrade = httpupgradeSettings;
this.xhttp = xhttpSettings;
+ this.hysteria = hysteriaSettings;
this.finalmask = finalmask;
this.sockopt = sockopt;
}
@@ -1116,6 +1224,7 @@ class StreamSettings extends XrayCommonClass {
GrpcStreamSettings.fromJson(json.grpcSettings),
HTTPUpgradeStreamSettings.fromJson(json.httpupgradeSettings),
xHTTPStreamSettings.fromJson(json.xhttpSettings),
+ HysteriaStreamSettings.fromJson(json.hysteriaSettings),
FinalMaskStreamSettings.fromJson(json.finalmask),
SockoptStreamSettings.fromJson(json.sockopt),
);
@@ -1135,6 +1244,7 @@ class StreamSettings extends XrayCommonClass {
grpcSettings: network === 'grpc' ? this.grpc.toJson() : undefined,
httpupgradeSettings: network === 'httpupgrade' ? this.httpupgrade.toJson() : undefined,
xhttpSettings: network === 'xhttp' ? this.xhttp.toJson() : undefined,
+ hysteriaSettings: network === 'hysteria' ? this.hysteria.toJson() : undefined,
finalmask: this.hasFinalMask ? this.finalmask.toJson() : undefined,
sockopt: this.sockopt != undefined ? this.sockopt.toJson() : undefined,
};
@@ -1201,6 +1311,7 @@ class Inbound extends XrayCommonClass {
case Protocols.VLESS: return this.settings.vlesses;
case Protocols.TROJAN: return this.settings.trojans;
case Protocols.SHADOWSOCKS: return this.isSSMultiUser ? this.settings.shadowsockses : null;
+ case Protocols.HYSTERIA: return this.settings.hysterias;
default: return null;
}
}
@@ -1212,9 +1323,14 @@ class Inbound extends XrayCommonClass {
set protocol(protocol) {
this._protocol = protocol;
this.settings = Inbound.Settings.getSettings(protocol);
+ this.stream = new StreamSettings();
if (protocol === Protocols.TROJAN) {
this.tls = false;
}
+ if (protocol === Protocols.HYSTERIA) {
+ this.stream.network = 'hysteria';
+ this.stream.security = 'tls';
+ }
}
get network() {
@@ -1316,6 +1432,7 @@ class Inbound extends XrayCommonClass {
}
canEnableTls() {
+ if (this.protocol === Protocols.HYSTERIA) return true;
if (![Protocols.VMESS, Protocols.VLESS, Protocols.TROJAN, Protocols.SHADOWSOCKS].includes(this.protocol)) return false;
return ["tcp", "ws", "http", "grpc", "httpupgrade", "xhttp"].includes(this.network);
}
@@ -1342,7 +1459,7 @@ class Inbound extends XrayCommonClass {
}
canEnableStream() {
- return [Protocols.VMESS, Protocols.VLESS, Protocols.TROJAN, Protocols.SHADOWSOCKS].includes(this.protocol);
+ return [Protocols.VMESS, Protocols.VLESS, Protocols.TROJAN, Protocols.SHADOWSOCKS, Protocols.HYSTERIA].includes(this.protocol);
}
reset() {
@@ -1689,6 +1806,26 @@ class Inbound extends XrayCommonClass {
return url.toString();
}
+ genHysteriaLink(address = '', port = this.port, remark = '', clientAuth) {
+ const protocol = this.settings.version == 2 ? "hysteria2" : "hysteria";
+ const link = `${protocol}://${clientAuth}@${address}:${port}`;
+
+ const params = new Map();
+ params.set("security", "tls");
+ if (this.stream.tls.settings.fingerprint?.length > 0) params.set("fp", this.stream.tls.settings.fingerprint);
+ if (this.stream.tls.alpn?.length > 0) params.set("alpn", this.stream.tls.alpn);
+ if (this.stream.tls.settings.allowInsecure) params.set("insecure", "1");
+ if (this.stream.tls.settings.echConfigList?.length > 0) params.set("ech", this.stream.tls.settings.echConfigList.join(','));
+ if (this.stream.tls.sni?.length > 0) params.set("sni", this.stream.tls.sni);
+
+ const url = new URL(link);
+ for (const [key, value] of params) {
+ url.searchParams.set(key, value);
+ }
+ url.hash = encodeURIComponent(remark);
+ return url.toString();
+ }
+
getWireguardLink(address, port, remark, peerId) {
let txt = `[Interface]\n`
txt += `PrivateKey = ${this.settings.peers[peerId].privateKey}\n`
@@ -1721,6 +1858,8 @@ class Inbound extends XrayCommonClass {
return this.genSSLink(address, port, forceTls, remark, this.isSSMultiUser ? client.password : '');
case Protocols.TROJAN:
return this.genTrojanLink(address, port, forceTls, remark, client.password);
+ case Protocols.HYSTERIA:
+ return this.genHysteriaLink(address, port, remark, client.auth.length > 0 ? client.auth : this.stream.hysteria.auth);
default: return '';
}
}
@@ -1827,6 +1966,7 @@ Inbound.Settings = class extends XrayCommonClass {
case Protocols.HTTP: return new Inbound.HttpSettings(protocol);
case Protocols.WIREGUARD: return new Inbound.WireguardSettings(protocol);
case Protocols.TUN: return new Inbound.TunSettings(protocol);
+ case Protocols.HYSTERIA: return new Inbound.HysteriaSettings(protocol);
default: return null;
}
}
@@ -1842,6 +1982,7 @@ Inbound.Settings = class extends XrayCommonClass {
case Protocols.HTTP: return Inbound.HttpSettings.fromJson(json);
case Protocols.WIREGUARD: return Inbound.WireguardSettings.fromJson(json);
case Protocols.TUN: return Inbound.TunSettings.fromJson(json);
+ case Protocols.HYSTERIA: return Inbound.HysteriaSettings.fromJson(json);
default: return null;
}
}
@@ -1851,49 +1992,9 @@ Inbound.Settings = class extends XrayCommonClass {
}
};
-Inbound.VmessSettings = class extends Inbound.Settings {
- constructor(protocol,
- vmesses = [new Inbound.VmessSettings.VMESS()]) {
- super(protocol);
- this.vmesses = vmesses;
- }
-
- indexOfVmessById(id) {
- return this.vmesses.findIndex(VMESS => VMESS.id === id);
- }
-
- addVmess(VMESS) {
- if (this.indexOfVmessById(VMESS.id) >= 0) {
- return false;
- }
- this.vmesses.push(VMESS);
- }
-
- delVmess(VMESS) {
- const i = this.indexOfVmessById(VMESS.id);
- if (i >= 0) {
- this.vmesses.splice(i, 1);
- }
- }
-
- static fromJson(json = {}) {
- return new Inbound.VmessSettings(
- Protocols.VMESS,
- json.clients.map(client => Inbound.VmessSettings.VMESS.fromJson(client)),
- );
- }
-
- toJson() {
- return {
- clients: Inbound.VmessSettings.toJsonArray(this.vmesses),
- };
- }
-};
-
-Inbound.VmessSettings.VMESS = class extends XrayCommonClass {
+/** Shared user-quota fields and UI helpers for multi-user protocol clients. */
+Inbound.ClientBase = class extends XrayCommonClass {
constructor(
- id = RandomUtil.randomUUID(),
- security = USERS_SECURITY.AUTO,
email = RandomUtil.randomLowerAndNum(8),
limitIp = 0,
totalGB = 0,
@@ -1904,11 +2005,9 @@ Inbound.VmessSettings.VMESS = class extends XrayCommonClass {
comment = '',
reset = 0,
created_at = undefined,
- updated_at = undefined
+ updated_at = undefined,
) {
super();
- this.id = id;
- this.security = security;
this.email = email;
this.limitIp = limitIp;
this.totalGB = totalGB;
@@ -1922,10 +2021,8 @@ Inbound.VmessSettings.VMESS = class extends XrayCommonClass {
this.updated_at = updated_at;
}
- static fromJson(json = {}) {
- return new Inbound.VmessSettings.VMESS(
- json.id,
- json.security,
+ static commonArgsFromJson(json = {}) {
+ return [
json.email,
json.limitIp,
json.totalGB,
@@ -1937,10 +2034,27 @@ Inbound.VmessSettings.VMESS = class extends XrayCommonClass {
json.reset,
json.created_at,
json.updated_at,
- );
+ ];
}
+
+ _clientBaseToJson() {
+ return {
+ email: this.email,
+ limitIp: this.limitIp,
+ totalGB: this.totalGB,
+ expiryTime: this.expiryTime,
+ enable: this.enable,
+ tgId: this.tgId,
+ subId: this.subId,
+ comment: this.comment,
+ reset: this.reset,
+ created_at: this.created_at,
+ updated_at: this.updated_at,
+ };
+ }
+
get _expiryTime() {
- if (this.expiryTime === 0 || this.expiryTime === "") {
+ if (this.expiryTime === 0 || this.expiryTime === '') {
return null;
}
if (this.expiryTime < 0) {
@@ -1950,12 +2064,13 @@ Inbound.VmessSettings.VMESS = class extends XrayCommonClass {
}
set _expiryTime(t) {
- if (t == null || t === "") {
+ if (t == null || t === '') {
this.expiryTime = 0;
} else {
this.expiryTime = t.valueOf();
}
}
+
get _totalGB() {
return NumberFormatter.toFixed(this.totalGB / SizeFormatter.ONE_GB, 2);
}
@@ -1963,7 +2078,73 @@ Inbound.VmessSettings.VMESS = class extends XrayCommonClass {
set _totalGB(gb) {
this.totalGB = NumberFormatter.toFixed(gb * SizeFormatter.ONE_GB, 0);
}
+};
+Inbound.VmessSettings = class extends Inbound.Settings {
+ constructor(protocol,
+ vmesses = [new Inbound.VmessSettings.VMESS()]) {
+ super(protocol);
+ this.vmesses = vmesses;
+ }
+
+ indexOfVmessById(id) {
+ return this.vmesses.findIndex(VMESS => VMESS.id === id);
+ }
+
+ addVmess(VMESS) {
+ if (this.indexOfVmessById(VMESS.id) >= 0) {
+ return false;
+ }
+ this.vmesses.push(VMESS);
+ }
+
+ delVmess(VMESS) {
+ const i = this.indexOfVmessById(VMESS.id);
+ if (i >= 0) {
+ this.vmesses.splice(i, 1);
+ }
+ }
+
+ static fromJson(json = {}) {
+ return new Inbound.VmessSettings(
+ Protocols.VMESS,
+ (json.clients || []).map(client => Inbound.VmessSettings.VMESS.fromJson(client)),
+ );
+ }
+
+ toJson() {
+ return {
+ clients: Inbound.VmessSettings.toJsonArray(this.vmesses),
+ };
+ }
+};
+
+Inbound.VmessSettings.VMESS = class extends Inbound.ClientBase {
+ constructor(
+ id = RandomUtil.randomUUID(),
+ security = USERS_SECURITY.AUTO,
+ email, limitIp, totalGB, expiryTime, enable, tgId, subId, comment, reset, created_at, updated_at,
+ ) {
+ super(email, limitIp, totalGB, expiryTime, enable, tgId, subId, comment, reset, created_at, updated_at);
+ this.id = id;
+ this.security = security;
+ }
+
+ static fromJson(json = {}) {
+ return new Inbound.VmessSettings.VMESS(
+ json.id,
+ json.security,
+ ...Inbound.ClientBase.commonArgsFromJson(json),
+ );
+ }
+
+ toJson() {
+ return {
+ id: this.id,
+ security: this.security,
+ ...this._clientBaseToJson(),
+ };
+ }
};
Inbound.VLESSSettings = class extends Inbound.Settings {
@@ -2041,85 +2222,36 @@ Inbound.VLESSSettings = class extends Inbound.Settings {
return json;
}
-
-
};
-Inbound.VLESSSettings.VLESS = class extends XrayCommonClass {
+Inbound.VLESSSettings.VLESS = class extends Inbound.ClientBase {
constructor(
id = RandomUtil.randomUUID(),
flow = '',
- email = RandomUtil.randomLowerAndNum(8),
- limitIp = 0,
- totalGB = 0,
- expiryTime = 0,
- enable = true,
- tgId = '',
- subId = RandomUtil.randomLowerAndNum(16),
- comment = '',
- reset = 0,
- created_at = undefined,
- updated_at = undefined
+ email, limitIp, totalGB, expiryTime, enable, tgId, subId, comment, reset, created_at, updated_at,
) {
- super();
+ super(email, limitIp, totalGB, expiryTime, enable, tgId, subId, comment, reset, created_at, updated_at);
this.id = id;
this.flow = flow;
- this.email = email;
- this.limitIp = limitIp;
- this.totalGB = totalGB;
- this.expiryTime = expiryTime;
- this.enable = enable;
- this.tgId = tgId;
- this.subId = subId;
- this.comment = comment;
- this.reset = reset;
- this.created_at = created_at;
- this.updated_at = updated_at;
}
static fromJson(json = {}) {
return new Inbound.VLESSSettings.VLESS(
json.id,
json.flow,
- json.email,
- json.limitIp,
- json.totalGB,
- json.expiryTime,
- json.enable,
- json.tgId,
- json.subId,
- json.comment,
- json.reset,
- json.created_at,
- json.updated_at,
+ ...Inbound.ClientBase.commonArgsFromJson(json),
);
}
- get _expiryTime() {
- if (this.expiryTime === 0 || this.expiryTime === "") {
- return null;
- }
- if (this.expiryTime < 0) {
- return this.expiryTime / -86400000;
- }
- return moment(this.expiryTime);
- }
-
- set _expiryTime(t) {
- if (t == null || t === "") {
- this.expiryTime = 0;
- } else {
- this.expiryTime = t.valueOf();
- }
- }
- get _totalGB() {
- return NumberFormatter.toFixed(this.totalGB / SizeFormatter.ONE_GB, 2);
- }
-
- set _totalGB(gb) {
- this.totalGB = NumberFormatter.toFixed(gb * SizeFormatter.ONE_GB, 0);
+ toJson() {
+ return {
+ id: this.id,
+ flow: this.flow,
+ ...this._clientBaseToJson(),
+ };
}
};
+
Inbound.VLESSSettings.Fallback = class extends XrayCommonClass {
constructor(name = "", alpn = '', path = '', dest = '', xver = 0) {
super();
@@ -2179,7 +2311,7 @@ Inbound.TrojanSettings = class extends Inbound.Settings {
static fromJson(json = {}) {
return new Inbound.TrojanSettings(
Protocols.TROJAN,
- json.clients.map(client => Inbound.TrojanSettings.Trojan.fromJson(client)),
+ (json.clients || []).map(client => Inbound.TrojanSettings.Trojan.fromJson(client)),
Inbound.TrojanSettings.Fallback.fromJson(json.fallbacks),);
}
@@ -2191,95 +2323,28 @@ Inbound.TrojanSettings = class extends Inbound.Settings {
}
};
-Inbound.TrojanSettings.Trojan = class extends XrayCommonClass {
+Inbound.TrojanSettings.Trojan = class extends Inbound.ClientBase {
constructor(
password = RandomUtil.randomSeq(10),
- email = RandomUtil.randomLowerAndNum(8),
- limitIp = 0,
- totalGB = 0,
- expiryTime = 0,
- enable = true,
- tgId = '',
- subId = RandomUtil.randomLowerAndNum(16),
- comment = '',
- reset = 0,
- created_at = undefined,
- updated_at = undefined
+ email, limitIp, totalGB, expiryTime, enable, tgId, subId, comment, reset, created_at, updated_at,
) {
- super();
+ super(email, limitIp, totalGB, expiryTime, enable, tgId, subId, comment, reset, created_at, updated_at);
this.password = password;
- this.email = email;
- this.limitIp = limitIp;
- this.totalGB = totalGB;
- this.expiryTime = expiryTime;
- this.enable = enable;
- this.tgId = tgId;
- this.subId = subId;
- this.comment = comment;
- this.reset = reset;
- this.created_at = created_at;
- this.updated_at = updated_at;
}
toJson() {
return {
password: this.password,
- email: this.email,
- limitIp: this.limitIp,
- totalGB: this.totalGB,
- expiryTime: this.expiryTime,
- enable: this.enable,
- tgId: this.tgId,
- subId: this.subId,
- comment: this.comment,
- reset: this.reset,
- created_at: this.created_at,
- updated_at: this.updated_at,
+ ...this._clientBaseToJson(),
};
}
static fromJson(json = {}) {
return new Inbound.TrojanSettings.Trojan(
json.password,
- json.email,
- json.limitIp,
- json.totalGB,
- json.expiryTime,
- json.enable,
- json.tgId,
- json.subId,
- json.comment,
- json.reset,
- json.created_at,
- json.updated_at,
+ ...Inbound.ClientBase.commonArgsFromJson(json),
);
}
-
- get _expiryTime() {
- if (this.expiryTime === 0 || this.expiryTime === "") {
- return null;
- }
- if (this.expiryTime < 0) {
- return this.expiryTime / -86400000;
- }
- return moment(this.expiryTime);
- }
-
- set _expiryTime(t) {
- if (t == null || t === "") {
- this.expiryTime = 0;
- } else {
- this.expiryTime = t.valueOf();
- }
- }
- get _totalGB() {
- return NumberFormatter.toFixed(this.totalGB / SizeFormatter.ONE_GB, 2);
- }
-
- set _totalGB(gb) {
- this.totalGB = NumberFormatter.toFixed(gb * SizeFormatter.ONE_GB, 0);
- }
-
};
Inbound.TrojanSettings.Fallback = class extends XrayCommonClass {
@@ -2343,7 +2408,7 @@ Inbound.ShadowsocksSettings = class extends Inbound.Settings {
json.method,
json.password,
json.network,
- json.clients.map(client => Inbound.ShadowsocksSettings.Shadowsocks.fromJson(client)),
+ (json.clients || []).map(client => Inbound.ShadowsocksSettings.Shadowsocks.fromJson(client)),
json.ivCheck,
);
}
@@ -2359,53 +2424,22 @@ Inbound.ShadowsocksSettings = class extends Inbound.Settings {
}
};
-Inbound.ShadowsocksSettings.Shadowsocks = class extends XrayCommonClass {
+Inbound.ShadowsocksSettings.Shadowsocks = class extends Inbound.ClientBase {
constructor(
method = '',
password = RandomUtil.randomShadowsocksPassword(),
- email = RandomUtil.randomLowerAndNum(8),
- limitIp = 0,
- totalGB = 0,
- expiryTime = 0,
- enable = true,
- tgId = '',
- subId = RandomUtil.randomLowerAndNum(16),
- comment = '',
- reset = 0,
- created_at = undefined,
- updated_at = undefined
+ email, limitIp, totalGB, expiryTime, enable, tgId, subId, comment, reset, created_at, updated_at,
) {
- super();
+ super(email, limitIp, totalGB, expiryTime, enable, tgId, subId, comment, reset, created_at, updated_at);
this.method = method;
this.password = password;
- this.email = email;
- this.limitIp = limitIp;
- this.totalGB = totalGB;
- this.expiryTime = expiryTime;
- this.enable = enable;
- this.tgId = tgId;
- this.subId = subId;
- this.comment = comment;
- this.reset = reset;
- this.created_at = created_at;
- this.updated_at = updated_at;
}
toJson() {
return {
method: this.method,
password: this.password,
- email: this.email,
- limitIp: this.limitIp,
- totalGB: this.totalGB,
- expiryTime: this.expiryTime,
- enable: this.enable,
- tgId: this.tgId,
- subId: this.subId,
- comment: this.comment,
- reset: this.reset,
- created_at: this.created_at,
- updated_at: this.updated_at,
+ ...this._clientBaseToJson(),
};
}
@@ -2413,45 +2447,56 @@ Inbound.ShadowsocksSettings.Shadowsocks = class extends XrayCommonClass {
return new Inbound.ShadowsocksSettings.Shadowsocks(
json.method,
json.password,
- json.email,
- json.limitIp,
- json.totalGB,
- json.expiryTime,
- json.enable,
- json.tgId,
- json.subId,
- json.comment,
- json.reset,
- json.created_at,
- json.updated_at,
+ ...Inbound.ClientBase.commonArgsFromJson(json),
);
}
+};
- get _expiryTime() {
- if (this.expiryTime === 0 || this.expiryTime === "") {
- return null;
- }
- if (this.expiryTime < 0) {
- return this.expiryTime / -86400000;
- }
- return moment(this.expiryTime);
+Inbound.HysteriaSettings = class extends Inbound.Settings {
+ constructor(protocol, version = 2, hysterias = [new Inbound.HysteriaSettings.Hysteria()]) {
+ super(protocol);
+ this.version = version;
+ this.hysterias = hysterias;
}
- set _expiryTime(t) {
- if (t == null || t === "") {
- this.expiryTime = 0;
- } else {
- this.expiryTime = t.valueOf();
- }
+ static fromJson(json = {}) {
+ return new Inbound.HysteriaSettings(
+ Protocols.HYSTERIA,
+ json.version ?? 2,
+ (json.clients || []).map(client => Inbound.HysteriaSettings.Hysteria.fromJson(client)),
+ );
}
- get _totalGB() {
- return NumberFormatter.toFixed(this.totalGB / SizeFormatter.ONE_GB, 2);
+
+ toJson() {
+ return {
+ version: this.version,
+ clients: Inbound.HysteriaSettings.toJsonArray(this.hysterias),
+ };
}
+};
- set _totalGB(gb) {
- this.totalGB = NumberFormatter.toFixed(gb * SizeFormatter.ONE_GB, 0);
+Inbound.HysteriaSettings.Hysteria = class extends Inbound.ClientBase {
+ constructor(
+ auth = RandomUtil.randomSeq(10),
+ email, limitIp, totalGB, expiryTime, enable, tgId, subId, comment, reset, created_at, updated_at,
+ ) {
+ super(email, limitIp, totalGB, expiryTime, enable, tgId, subId, comment, reset, created_at, updated_at);
+ this.auth = auth;
+ }
+
+ toJson() {
+ return {
+ auth: this.auth,
+ ...this._clientBaseToJson(),
+ };
}
+ static fromJson(json = {}) {
+ return new Inbound.HysteriaSettings.Hysteria(
+ json.auth,
+ ...Inbound.ClientBase.commonArgsFromJson(json),
+ );
+ }
};
Inbound.TunnelSettings = class extends Inbound.Settings {
@@ -2707,4 +2752,4 @@ Inbound.TunSettings = class extends Inbound.Settings {
userLevel: this.userLevel || 0,
};
}
-};
+}; \ No newline at end of file
diff --git a/web/assets/js/model/outbound.js b/web/assets/js/model/outbound.js
index 56606231..b8ec4c12 100644
--- a/web/assets/js/model/outbound.js
+++ b/web/assets/js/model/outbound.js
@@ -782,8 +782,8 @@ class Outbound extends CommonClass {
}
canEnableTls() {
- if (![Protocols.VMess, Protocols.VLESS, Protocols.Trojan, Protocols.Shadowsocks, Protocols.Hysteria].includes(this.protocol)) return false;
- if (this.protocol === Protocols.Hysteria) return this.stream.network === 'hysteria';
+ if (this.protocol === Protocols.Hysteria) return true;
+ if (![Protocols.VMess, Protocols.VLESS, Protocols.Trojan, Protocols.Shadowsocks].includes(this.protocol)) return false;
return ["tcp", "ws", "http", "grpc", "httpupgrade", "xhttp"].includes(this.stream.network);
}
diff --git a/web/html/component/aClientTable.html b/web/html/component/aClientTable.html
index d9a9b5f5..0e32d45c 100644
--- a/web/html/component/aClientTable.html
+++ b/web/html/component/aClientTable.html
@@ -30,7 +30,7 @@
</a-tooltip>
</template>
<template slot="enable" slot-scope="text, client, index">
- <a-switch v-model="client.enable" @change="switchEnableClient(record.id,client)"></a-switch>
+ <a-switch v-model="client.enable" @change="switchEnableClient(record.id, client, $event)"></a-switch>
</template>
<template slot="online" slot-scope="text, client, index">
<a-popover :overlay-class-name="themeSwitcher.currentTheme">
@@ -165,7 +165,7 @@
<span :style="{ color: '#FF4D4F' }"> {{ i18n "delete"}}</span>
</a-menu-item>
<a-menu-item>
- <a-switch v-model="client.enable" size="small" @change="switchEnableClient(record.id,client)"></a-switch>
+ <a-switch v-model="client.enable" size="small" @change="switchEnableClient(record.id, client, $event)"></a-switch>
{{ i18n "enable"}}
</a-menu-item>
</a-menu>
diff --git a/web/html/form/client.html b/web/html/form/client.html
index 908f28d2..da19fe8f 100644
--- a/web/html/form/client.html
+++ b/web/html/form/client.html
@@ -1,5 +1,6 @@
{{define "form/client"}}
-<a-form layout="horizontal" v-if="client" :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
+<a-form layout="horizontal" v-if="client" :colon="false"
+ :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
<a-form-item label='{{ i18n "pages.inbounds.enable" }}'>
<a-switch v-model="client.enable"></a-switch>
</a-form-item>
@@ -10,38 +11,62 @@