diff --git a/README.md b/README.md
index 1f5fdcd8..a24cbe45 100644
--- a/README.md
+++ b/README.md
@@ -79,6 +79,8 @@ Set the robot-related parameters in the panel background, including:
Reference syntax:
+- 30 * * * * * //Notify at the 30s of each point
+- 0 */10 * * * * //Notify at the first second of each 10 minutes
- @hourly // hourly notification
- @daily // Daily notification (00:00 in the morning)
- @every 8h // notify every 8 hours
@@ -89,13 +91,13 @@ Reference syntax:
- Login notification
- CPU threshold notification
- Threshold for Expiration time and Traffic to report in advance
-- Support client report if client's telegram username is added to the end of `email` like 'test123@telegram_username'
+- Support client report menu if client's telegram username added to the user's configurations
- Support telegram traffic report searched with UID (VMESS/VLESS) or Password (TROJAN) - anonymously
- Menu based bot
- Search client by email ( only admin )
- Check all inbounds
- Check server status
-- Check Exhausted users
+- Check depleted users
- Receive backup by request and in periodic reports
# A Special Thanks To
diff --git a/database/model/model.go b/database/model/model.go
index 606b922c..69724213 100644
--- a/database/model/model.go
+++ b/database/model/model.go
@@ -44,9 +44,9 @@ type Inbound struct {
Sniffing string `json:"sniffing" form:"sniffing"`
}
type InboundClientIps struct {
- Id int `json:"id" gorm:"primaryKey;autoIncrement"`
+ Id int `json:"id" gorm:"primaryKey;autoIncrement"`
ClientEmail string `json:"clientEmail" form:"clientEmail" gorm:"unique"`
- Ips string `json:"ips" form:"ips"`
+ Ips string `json:"ips" form:"ips"`
}
func (i *Inbound) GenXrayInboundConfig() *xray.InboundConfig {
@@ -80,4 +80,7 @@ type Client struct {
LimitIP int `json:"limitIp"`
TotalGB int64 `json:"totalGB" form:"totalGB"`
ExpiryTime int64 `json:"expiryTime" form:"expiryTime"`
+ Enable bool `json:"enable" from:"enable"`
+ TgID string `json:"tgId" from:"tgId"`
+ SubID string `json:"subId" from:"subId"`
}
diff --git a/web/assets/css/custom.css b/web/assets/css/custom.css
index 038e30a1..229d8500 100644
--- a/web/assets/css/custom.css
+++ b/web/assets/css/custom.css
@@ -156,6 +156,12 @@
padding:16px;
}
+.ant-table-expand-icon-th,
+.ant-table-row-expand-icon-cell {
+ width: 30px;
+ min-width: 30px;
+}
+
.ant-menu-dark,
.ant-menu-dark .ant-menu-sub,
.ant-layout-header,
@@ -174,6 +180,7 @@
.ant-card-dark:hover {
border-color: #e8e8e8;
+ box-shadow: 0 2px 8px rgba(255,255,255,.15);
}
.ant-card-dark .ant-table-thead th {
@@ -216,20 +223,25 @@
.ant-card-dark .ant-table-tbody>tr:hover:not(.ant-table-expanded-row):not(.ant-table-row-selected)>td,
.ant-card-dark .ant-select-dropdown-menu-item:hover:not(.ant-select-dropdown-menu-item-disabled),
-.ant-card-dark .ant-calendar-date:hover {
+.ant-card-dark .ant-calendar-date:hover,
+.ant-card-dark .ant-select-dropdown-menu-item-active,
+.ant-card-dark li.ant-calendar-time-picker-select-option-selected {
background-color: #004488;
}
-.ant-card-dark tbody .ant-table-expanded-row {
+.ant-card-dark tbody .ant-table-expanded-row,
+.ant-card-dark .ant-calendar-time-picker-inner {
color: hsla(0,0%,100%,.65);
background-color: #1a212a;
}
.ant-card-dark .ant-input,
.ant-card-dark .ant-input-number,
+.ant-card-dark .ant-input-number-handler-wrap,
.ant-card-dark .ant-calendar-input,
.ant-card-dark .ant-select-dropdown-menu-item-selected,
-.ant-card-dark .ant-select-selection {
+.ant-card-dark .ant-select-selection,
+.ant-card-dark .ant-calendar-picker-clear {
color: hsla(0,0%,100%,.65);
background-color: #2e3b52;
}
@@ -239,6 +251,12 @@
background-color: #161b22;
}
+.ant-dropdown-menu-dark,
+.ant-card-dark .ant-modal-content {
+ border: 1px solid rgba(255, 255, 255, 0.65);
+ box-shadow: 0 2px 8px rgba(255,255,255,.15);
+}
+
.ant-card-dark .ant-modal-content,
.ant-card-dark .ant-modal-body,
.ant-card-dark .ant-modal-header,
@@ -280,6 +298,12 @@
border: 1px solid hsla(0,0%,100%,.30);
}
+.ant-card-dark .ant-tag {
+ color: hsla(0,0%,100%,.65);
+ background: rgba(255,255,255,.04);
+ border-color: #434343;
+}
+
.ant-card-dark .ant-tag-blue {
color: #3c9ae8;
background: #111d2c;
@@ -334,6 +358,29 @@
color: hsla(0,0%,100%,.65);
background-color: #073763;
border-color: #1890ff;
- text-shadow: 0 -1px 0 rgba(0,0,0,.12);
- box-shadow: 0 2px 0 rgba(0,0,0,.045);
+ text-shadow: 0 -1px 0 rgba(255,255,255,.12);
+ box-shadow: 0 2px 0 rgba(255,255,255,.045);
+}
+.ant-card-dark .ant-btn-primary:hover {
+ background-color: #40a9ff;
+ border-color: #40a9ff;
+}
+
+.ant-dark .ant-popover-content {
+ border: 1px solid #e8e8e8;
+ border-radius: 4px;
+ box-shadow: 0 2px 8px rgba(255,255,255,.15);
+}
+
+.ant-dark .ant-popover-inner {
+ background: #222a37;
+}
+
+.ant-dark .ant-popover-title,
+.ant-dark .ant-popover-inner-content {
+ color: hsla(0,0%,100%,.65);
+}
+
+.ant-dark .ant-popover-placement-top>.ant-popover-content>.ant-popover-arrow {
+ border-color: transparent #2e3b52 #2e3b52 transparent;
}
\ No newline at end of file
diff --git a/web/assets/js/model/models.js b/web/assets/js/model/models.js
index 485464e4..1de76850 100644
--- a/web/assets/js/model/models.js
+++ b/web/assets/js/model/models.js
@@ -171,13 +171,13 @@ class AllSetting {
this.webCertFile = "";
this.webKeyFile = "";
this.webBasePath = "/";
+ this.expireDiff = "";
+ this.trafficDiff = "";
this.tgBotEnable = false;
this.tgBotToken = "";
this.tgBotChatId = "";
this.tgRunTime = "@daily";
this.tgBotBackup = false;
- this.tgExpireDiff = "";
- this.tgTrafficDiff = "";
this.tgCpu = "";
this.xrayTemplateConfig = "";
diff --git a/web/assets/js/model/xray.js b/web/assets/js/model/xray.js
index eb6d07f3..65f4bfd9 100644
--- a/web/assets/js/model/xray.js
+++ b/web/assets/js/model/xray.js
@@ -92,6 +92,7 @@ const UTLS_FINGERPRINT = {
};
const ALPN_OPTION = {
+ H3: "h3",
H2: "h2",
HTTP1: "http/1.1",
};
@@ -166,20 +167,20 @@ class XrayCommonClass {
}
class TcpStreamSettings extends XrayCommonClass {
- constructor(
- type = 'none',
- acceptProxyProtocol = false,
- request = new TcpStreamSettings.TcpRequest(),
- response = new TcpStreamSettings.TcpResponse(),
- ) {
+ constructor(acceptProxyProtocol=false,
+ type='none',
+ request=new TcpStreamSettings.TcpRequest(),
+ response=new TcpStreamSettings.TcpResponse(),
+ ) {
super();
+ this.acceptProxyProtocol = acceptProxyProtocol;
this.type = type;
this.request = request;
this.response = response;
this.acceptProxyProtocol = acceptProxyProtocol;
}
- static fromJson(json = {}) {
+ static fromJson(json={}) {
let header = json.header;
if (!header) {
header = {};
@@ -194,6 +195,7 @@ class TcpStreamSettings extends XrayCommonClass {
toJson() {
return {
+ acceptProxyProtocol: this.acceptProxyProtocol,
header: {
type: this.type,
request: this.type === 'http' ? this.request.toJson() : undefined,
@@ -205,10 +207,10 @@ class TcpStreamSettings extends XrayCommonClass {
}
TcpStreamSettings.TcpRequest = class extends XrayCommonClass {
- constructor(version = '1.1',
- method = 'GET',
- path = ['/'],
- headers = [],
+ constructor(version='1.1',
+ method='GET',
+ path=['/'],
+ headers=[],
) {
super();
this.version = version;
@@ -242,7 +244,7 @@ TcpStreamSettings.TcpRequest = class extends XrayCommonClass {
this.headers.splice(index, 1);
}
- static fromJson(json = {}) {
+ static fromJson(json={}) {
return new TcpStreamSettings.TcpRequest(
json.version,
json.method,
@@ -261,10 +263,10 @@ TcpStreamSettings.TcpRequest = class extends XrayCommonClass {
};
TcpStreamSettings.TcpResponse = class extends XrayCommonClass {
- constructor(version = '1.1',
- status = '200',
- reason = 'OK',
- headers = [],
+ constructor(version='1.1',
+ status='200',
+ reason='OK',
+ headers=[],
) {
super();
this.version = version;
@@ -281,7 +283,7 @@ TcpStreamSettings.TcpResponse = class extends XrayCommonClass {
this.headers.splice(index, 1);
}
- static fromJson(json = {}) {
+ static fromJson(json={}) {
return new TcpStreamSettings.TcpResponse(
json.version,
json.status,
@@ -474,9 +476,13 @@ class GrpcStreamSettings extends XrayCommonClass {
}
class TlsStreamSettings extends XrayCommonClass {
- constructor(serverName = '', minVersion = TLS_VERSION_OPTION.TLS10, maxVersion = TLS_VERSION_OPTION.TLS12,
- cipherSuites = '',
- certificates = [new TlsStreamSettings.Cert()], alpn=[''] ,settings=[new TlsStreamSettings.Settings()]) {
+ constructor(serverName='',
+ minVersion = TLS_VERSION_OPTION.TLS12,
+ maxVersion = TLS_VERSION_OPTION.TLS13,
+ cipherSuites = '',
+ certificates=[new TlsStreamSettings.Cert()],
+ alpn=[],
+ settings=[new TlsStreamSettings.Settings()]) {
super();
this.server = serverName;
this.minVersion = minVersion;
@@ -484,7 +490,7 @@ class TlsStreamSettings extends XrayCommonClass {
this.cipherSuites = cipherSuites;
this.certs = certificates;
this.alpn = alpn;
- this.settings = settings;
+ this.settings = settings;
}
addCert(cert) {
@@ -497,15 +503,15 @@ class TlsStreamSettings extends XrayCommonClass {
static fromJson(json={}) {
let certs;
- let settings;
+ let settings;
if (!ObjectUtil.isEmpty(json.certificates)) {
certs = json.certificates.map(cert => TlsStreamSettings.Cert.fromJson(cert));
}
+
if (!ObjectUtil.isEmpty(json.settings)) {
let values = json.settings[0];
settings = [new TlsStreamSettings.Settings(values.allowInsecure , values.fingerprint, values.serverName)];
}
-
return new TlsStreamSettings(
json.serverName,
json.minVersion,
@@ -513,7 +519,7 @@ class TlsStreamSettings extends XrayCommonClass {
json.cipherSuites,
certs,
json.alpn,
- settings,
+ settings,
);
}
@@ -526,7 +532,6 @@ class TlsStreamSettings extends XrayCommonClass {
certificates: TlsStreamSettings.toJsonArray(this.certs),
alpn: this.alpn,
settings: TlsStreamSettings.toJsonArray(this.settings),
-
};
}
}
@@ -573,40 +578,39 @@ TlsStreamSettings.Cert = class extends XrayCommonClass {
};
TlsStreamSettings.Settings = class extends XrayCommonClass {
- constructor(allowInsecure = false, fingerprint = '', serverName = '') {
- super();
- this.allowInsecure = allowInsecure;
- this.fingerprint = fingerprint;
- this.serverName = serverName;
- }
- static fromJson(json = {}) {
- return new TlsStreamSettings.Settings(
- json.allowInsecure,
- json.fingerprint,
- json.servername,
- );
- }
- toJson() {
- return {
- allowInsecure: this.allowInsecure,
- fingerprint: this.fingerprint,
- serverName: this.serverName,
- };
- }
+ constructor(allowInsecure = false, fingerprint = '', serverName = '') {
+ super();
+ this.allowInsecure = allowInsecure;
+ this.fingerprint = fingerprint;
+ this.serverName = serverName;
+ }
+ static fromJson(json = {}) {
+ return new TlsStreamSettings.Settings(
+ json.allowInsecure,
+ json.fingerprint,
+ json.servername,
+ );
+ }
+ toJson() {
+ return {
+ allowInsecure: this.allowInsecure,
+ fingerprint: this.fingerprint,
+ serverName: this.serverName,
+ };
+ }
};
-
class StreamSettings extends XrayCommonClass {
constructor(network='tcp',
- security='none',
- tlsSettings=new TlsStreamSettings(),
- tcpSettings=new TcpStreamSettings(),
- kcpSettings=new KcpStreamSettings(),
- wsSettings=new WsStreamSettings(),
- httpSettings=new HttpStreamSettings(),
- quicSettings=new QuicStreamSettings(),
- grpcSettings=new GrpcStreamSettings(),
- ) {
+ security='none',
+ tlsSettings=new TlsStreamSettings(),
+ tcpSettings=new TcpStreamSettings(),
+ kcpSettings=new KcpStreamSettings(),
+ wsSettings=new WsStreamSettings(),
+ httpSettings=new HttpStreamSettings(),
+ quicSettings=new QuicStreamSettings(),
+ grpcSettings=new GrpcStreamSettings(),
+ ) {
super();
this.network = network;
this.security = security;
@@ -728,14 +732,15 @@ class Inbound extends XrayCommonClass {
get protocol() {
return this._protocol;
}
-
+
set protocol(protocol) {
this._protocol = protocol;
this.settings = Inbound.Settings.getSettings(protocol);
if (protocol === Protocols.TROJAN) {
- this.tls = false;
+ this.tls = true;
}
}
+
get tls() {
return this.stream.security === 'tls';
}
@@ -918,16 +923,16 @@ class Inbound extends XrayCommonClass {
isExpiry(index) {
switch (this.protocol) {
case Protocols.VMESS:
- if(this.settings.vmesses[index]._expiryTime != null)
- return this.settings.vmesses[index]._expiryTime < new Date().getTime();
+ if(this.settings.vmesses[index].expiryTime > 0)
+ return this.settings.vmesses[index].expiryTime < new Date().getTime();
return false
case Protocols.VLESS:
- if(this.settings.vlesses[index]._expiryTime != null)
- return this.settings.vlesses[index]._expiryTime < new Date().getTime();
+ if(this.settings.vlesses[index].expiryTime > 0)
+ return this.settings.vlesses[index].expiryTime < new Date().getTime();
return false
case Protocols.TROJAN:
- if(this.settings.trojans[index]._expiryTime != null)
- return this.settings.trojans[index]._expiryTime < new Date().getTime();
+ if(this.settings.trojans[index].expiryTime > 0)
+ return this.settings.trojans[index].expiryTime < new Date().getTime();
return false
default:
return false;
@@ -955,7 +960,7 @@ class Inbound extends XrayCommonClass {
return false;
}
}
-
+
//this is used for xtls-rprx-vision
canEnableTlsFlow() {
if ((this.stream.security === 'tls') && (this.network === "tcp")) {
@@ -968,11 +973,10 @@ class Inbound extends XrayCommonClass {
}
return false;
}
-
+
canSetTls() {
return this.canEnableTls();
}
-
canEnableXTLS() {
switch (this.protocol) {
@@ -989,7 +993,7 @@ class Inbound extends XrayCommonClass {
switch (this.protocol) {
case Protocols.VMESS:
case Protocols.VLESS:
- case Protocols.TROJAN:
+ case Protocols.TROJAN:
return true;
default:
return false;
@@ -1065,7 +1069,7 @@ class Inbound extends XrayCommonClass {
address = this.stream.tls.server;
}
}
-
+
let obj = {
v: '2',
ps: remark,
@@ -1078,7 +1082,7 @@ class Inbound extends XrayCommonClass {
host: host,
path: path,
tls: this.stream.security,
- sni: this.stream.tls.settings[0]['serverName'],
+ sni: this.stream.tls.settings[0]['serverName'],
fp: this.stream.tls.settings[0]['fingerprint'],
alpn: this.stream.tls.alpn.join(','),
allowInsecure: this.stream.tls.settings[0].allowInsecure,
@@ -1148,25 +1152,25 @@ class Inbound extends XrayCommonClass {
if (!ObjectUtil.isEmpty(this.stream.tls.server)) {
address = this.stream.tls.server;
}
- if (this.stream.tls.settings[0]['serverName'] !== ''){
+ if (this.stream.tls.settings[0]['serverName'] !== ''){
params.set("sni", this.stream.tls.settings[0]['serverName']);
}
if (type === "tcp" && this.settings.vlesses[clientIndex].flow.length > 0) {
params.set("flow", this.settings.vlesses[clientIndex].flow);
}
}
-
- if (this.XTLS) {
+
+ if (this.XTLS) {
params.set("security", "xtls");
params.set("alpn", this.stream.tls.alpn);
if(this.stream.tls.settings[0].allowInsecure){
params.set("allowInsecure", "1");
}
- if (!ObjectUtil.isEmpty(this.stream.tls.server)) {
- address = this.stream.tls.server;
- }
- params.set("flow", this.settings.vlesses[clientIndex].flow);
- }
+ if (!ObjectUtil.isEmpty(this.stream.tls.server)) {
+ address = this.stream.tls.server;
+ }
+ params.set("flow", this.settings.vlesses[clientIndex].flow);
+ }
const link = `vless://${uuid}@${address}:${port}`;
const url = new URL(link);
@@ -1177,13 +1181,13 @@ class Inbound extends XrayCommonClass {
return url.toString();
}
- genSSLink(address = '', remark = '') {
+ genSSLink(address='', remark='') {
let settings = this.settings;
const server = this.stream.tls.server;
if (!ObjectUtil.isEmpty(server)) {
address = server;
}
- return 'ss://' + safeBase64(settings.method + ':' + settings.password) + `@${address}:${this.port}#${encodeURIComponent(remark)}`;
+ return 'ss://' + safeBase64(settings.method + ':' + settings.password) + `@${address}:${this.port}#${encodeURIComponent(remark)}`;
}
genTrojanLink(address = '', remark = '', clientIndex = 0) {
@@ -1191,7 +1195,7 @@ class Inbound extends XrayCommonClass {
const port = this.port;
const type = this.stream.network;
const params = new Map();
- params.set("type", this.stream.network);
+ params.set("type", this.stream.network);
switch (type) {
case "tcp":
const tcp = this.stream.tcp;
@@ -1246,12 +1250,12 @@ class Inbound extends XrayCommonClass {
}
if (!ObjectUtil.isEmpty(this.stream.tls.server)) {
address = this.stream.tls.server;
- }
- if (this.stream.tls.settings[0]['serverName'] !== ''){
- params.set("sni", this.stream.tls.settings[0]['serverName']);
}
+ if (this.stream.tls.settings[0]['serverName'] !== ''){
+ params.set("sni", this.stream.tls.settings[0]['serverName']);
+ }
}
-
+
if (this.XTLS) {
params.set("security", "xtls");
params.set("alpn", this.stream.tls.alpn);
@@ -1259,11 +1263,11 @@ class Inbound extends XrayCommonClass {
params.set("allowInsecure", "1");
}
if (!ObjectUtil.isEmpty(this.stream.tls.server)) {
- address = this.stream.tls.server;
- }
- params.set("flow", this.settings.trojans[clientIndex].flow);
- }
-
+ address = this.stream.tls.server;
+ }
+ params.set("flow", this.settings.trojans[clientIndex].flow);
+ }
+
const link = `trojan://${settings.trojans[clientIndex].password}@${address}:${this.port}#${encodeURIComponent(remark)}`;
const url = new URL(link);
for (const [key, value] of params) {
@@ -1294,8 +1298,9 @@ class Inbound extends XrayCommonClass {
default: return '';
}
}
+
genInboundLinks(address = '', remark = '') {
- let link = '';
+ let link = '';
switch (this.protocol) {
case Protocols.VMESS:
case Protocols.VLESS:
@@ -1308,7 +1313,7 @@ class Inbound extends XrayCommonClass {
return (this.genSSLink(address, remark) + '\r\n');
default: return '';
}
-}
+ }
static fromJson(json={}) {
return new Inbound(
@@ -1423,7 +1428,7 @@ Inbound.VmessSettings = class extends Inbound.Settings {
}
};
Inbound.VmessSettings.Vmess = class extends XrayCommonClass {
- constructor(id=RandomUtil.randomUUID(), alterId=0, email=RandomUtil.randomText(),limitIp=0, totalGB=0, expiryTime='') {
+ constructor(id=RandomUtil.randomUUID(), alterId=0, email=RandomUtil.randomText(),limitIp=0, totalGB=0, expiryTime=0, enable=true, tgId='', subId='') {
super();
this.id = id;
this.alterId = alterId;
@@ -1431,6 +1436,9 @@ Inbound.VmessSettings.Vmess = class extends XrayCommonClass {
this.limitIp = limitIp;
this.totalGB = totalGB;
this.expiryTime = expiryTime;
+ this.enable = enable;
+ this.tgId = tgId;
+ this.subId = subId;
}
static fromJson(json={}) {
@@ -1441,13 +1449,18 @@ Inbound.VmessSettings.Vmess = class extends XrayCommonClass {
json.limitIp,
json.totalGB,
json.expiryTime,
-
+ json.enable,
+ json.tgId,
+ json.subId,
);
}
get _expiryTime() {
if (this.expiryTime === 0 || this.expiryTime === "") {
return null;
}
+ if (this.expiryTime < 0){
+ return this.expiryTime / -86400000;
+ }
return moment(this.expiryTime);
}
@@ -1475,7 +1488,7 @@ Inbound.VLESSSettings = class extends Inbound.Settings {
fallbacks=[],) {
super(protocol);
this.vlesses = vlesses;
- this.decryption = 'none';
+ this.decryption = 'none'; // Using decryption is not implemented here
this.fallbacks = fallbacks;
}
@@ -1487,6 +1500,7 @@ Inbound.VLESSSettings = class extends Inbound.Settings {
this.fallbacks.splice(index, 1);
}
+ // decryption should be set to static value
static fromJson(json={}) {
return new Inbound.VLESSSettings(
Protocols.VLESS,
@@ -1506,8 +1520,7 @@ Inbound.VLESSSettings = class extends Inbound.Settings {
};
Inbound.VLESSSettings.VLESS = class extends XrayCommonClass {
-
- constructor(id=RandomUtil.randomUUID(), flow='', email=RandomUtil.randomText(),limitIp=0, totalGB=0, expiryTime='') {
+ constructor(id=RandomUtil.randomUUID(), flow='', email=RandomUtil.randomText(),limitIp=0, totalGB=0, expiryTime=0, enable=true, tgId='', subId='') {
super();
this.id = id;
this.flow = flow;
@@ -1515,7 +1528,9 @@ Inbound.VLESSSettings.VLESS = class extends XrayCommonClass {
this.limitIp = limitIp;
this.totalGB = totalGB;
this.expiryTime = expiryTime;
-
+ this.enable = enable;
+ this.tgId = tgId;
+ this.subId = subId;
}
static fromJson(json={}) {
@@ -1526,14 +1541,19 @@ Inbound.VLESSSettings.VLESS = class extends XrayCommonClass {
json.limitIp,
json.totalGB,
json.expiryTime,
-
+ json.enable,
+ json.tgId,
+ json.subId,
);
- }
+ }
get _expiryTime() {
if (this.expiryTime === 0 || this.expiryTime === "") {
return null;
}
+ if (this.expiryTime < 0){
+ return this.expiryTime / -86400000;
+ }
return moment(this.expiryTime);
}
@@ -1553,7 +1573,7 @@ Inbound.VLESSSettings.VLESS = class extends XrayCommonClass {
}
};
Inbound.VLESSSettings.Fallback = class extends XrayCommonClass {
- constructor(name="", alpn='', path='', dest='', xver=0) {
+ constructor(name="", alpn=[], path='', dest='', xver=0) {
super();
this.name = name;
this.alpn = alpn;
@@ -1593,8 +1613,8 @@ Inbound.VLESSSettings.Fallback = class extends XrayCommonClass {
Inbound.TrojanSettings = class extends Inbound.Settings {
constructor(protocol,
- trojans=[new Inbound.TrojanSettings.Trojan()],
- fallbacks=[],) {
+ trojans=[new Inbound.TrojanSettings.Trojan()],
+ fallbacks=[],) {
super(protocol);
this.trojans = trojans;
this.fallbacks = fallbacks;
@@ -1623,7 +1643,7 @@ Inbound.TrojanSettings = class extends Inbound.Settings {
}
};
Inbound.TrojanSettings.Trojan = class extends XrayCommonClass {
- constructor(password=RandomUtil.randomSeq(10), flow='', email=RandomUtil.randomText(),limitIp=0, totalGB=0, expiryTime='') {
+ constructor(password=RandomUtil.randomSeq(10), flow='', email=RandomUtil.randomText(),limitIp=0, totalGB=0, expiryTime=0, enable=true, tgId='', subId='') {
super();
this.password = password;
this.flow = flow;
@@ -1631,6 +1651,9 @@ Inbound.TrojanSettings.Trojan = class extends XrayCommonClass {
this.limitIp = limitIp;
this.totalGB = totalGB;
this.expiryTime = expiryTime;
+ this.enable = enable;
+ this.tgId = tgId;
+ this.subId = subId;
}
toJson() {
@@ -1641,10 +1664,13 @@ Inbound.TrojanSettings.Trojan = class extends XrayCommonClass {
limitIp: this.limitIp,
totalGB: this.totalGB,
expiryTime: this.expiryTime,
+ enable: this.enable,
+ tgId: this.tgId,
+ subId: this.subId,
};
}
- static fromJson(json={}) {
+ static fromJson(json = {}) {
return new Inbound.TrojanSettings.Trojan(
json.password,
json.flow,
@@ -1652,7 +1678,9 @@ Inbound.TrojanSettings.Trojan = class extends XrayCommonClass {
json.limitIp,
json.totalGB,
json.expiryTime,
-
+ json.enable,
+ json.tgId,
+ json.subId,
);
}
@@ -1660,6 +1688,9 @@ Inbound.TrojanSettings.Trojan = class extends XrayCommonClass {
if (this.expiryTime === 0 || this.expiryTime === "") {
return null;
}
+ if (this.expiryTime < 0){
+ return this.expiryTime / -86400000;
+ }
return moment(this.expiryTime);
}
@@ -1681,7 +1712,7 @@ Inbound.TrojanSettings.Trojan = class extends XrayCommonClass {
};
Inbound.TrojanSettings.Fallback = class extends XrayCommonClass {
- constructor(name="", alpn='', path='', dest='', xver=0) {
+ constructor(name="", alpn=[], path='', dest='', xver=0) {
super();
this.name = name;
this.alpn = alpn;
@@ -1721,9 +1752,9 @@ Inbound.TrojanSettings.Fallback = class extends XrayCommonClass {
Inbound.ShadowsocksSettings = class extends Inbound.Settings {
constructor(protocol,
- method = SSMethods.BLAKE3_AES_256_GCM,
- password = RandomUtil.randomSeq(44),
- network = 'tcp,udp'
+ method=SSMethods.BLAKE3_AES_256_GCM,
+ password=RandomUtil.randomSeq(44),
+ network='tcp,udp'
) {
super(protocol);
this.method = method;
@@ -1731,7 +1762,7 @@ Inbound.ShadowsocksSettings = class extends Inbound.Settings {
this.network = network;
}
- static fromJson(json = {}) {
+ static fromJson(json={}) {
return new Inbound.ShadowsocksSettings(
Protocols.SHADOWSOCKS,
json.method,
@@ -1755,7 +1786,7 @@ Inbound.DokodemoSettings = class extends Inbound.Settings {
this.address = address;
this.port = port;
this.network = network;
- this.followRedirect = followRedirect;
+ this.followRedirect = followRedirect;
}
static fromJson(json={}) {
@@ -1764,7 +1795,7 @@ Inbound.DokodemoSettings = class extends Inbound.Settings {
json.address,
json.port,
json.network,
- json.followRedirect,
+ json.followRedirect,
);
}
@@ -1773,7 +1804,7 @@ Inbound.DokodemoSettings = class extends Inbound.Settings {
address: this.address,
port: this.port,
network: this.network,
- followRedirect: this.followRedirect,
+ followRedirect: this.followRedirect,
};
}
};
diff --git a/web/controller/api.go b/web/controller/api.go
index 843ac7e5..f3021ea4 100644
--- a/web/controller/api.go
+++ b/web/controller/api.go
@@ -3,77 +3,74 @@ package controller
import "github.com/gin-gonic/gin"
type APIController struct {
- BaseController
- inboundController *InboundController
- settingController *SettingController
+ BaseController
+ inboundController *InboundController
}
func NewAPIController(g *gin.RouterGroup) *APIController {
- a := &APIController{}
- a.initRouter(g)
- return a
+ a := &APIController{}
+ a.initRouter(g)
+ return a
}
func (a *APIController) initRouter(g *gin.RouterGroup) {
- g = g.Group("/xui/API/inbounds")
- g.Use(a.checkLogin)
+ g = g.Group("/xui/API/inbounds")
+ g.Use(a.checkLogin)
- g.POST("/list", a.getAllInbounds)
- g.GET("/get/:id", a.getSingleInbound)
- g.POST("/add", a.addInbound)
- g.POST("/del/:id", a.delInbound)
- g.POST("/update/:id", a.updateInbound)
- g.POST("/clientIps/:email", a.getClientIps)
- g.POST("/clearClientIps/:email", a.clearClientIps)
- g.POST("/addClient/", a.addInboundClient)
- g.POST("/delClient/:email", a.delInboundClient)
- g.POST("/updateClient/:index", a.updateInboundClient)
- g.POST("/:id/resetClientTraffic/:email", a.resetClientTraffic)
+ g.POST("/list", a.getAllInbounds)
+ g.GET("/get/:id", a.getSingleInbound)
+ g.POST("/add", a.addInbound)
+ g.POST("/del/:id", a.delInbound)
+ g.POST("/update/:id", a.updateInbound)
+ g.POST("/clientIps/:email", a.getClientIps)
+ g.POST("/clearClientIps/:email", a.clearClientIps)
+ g.POST("/addClient/", a.addInboundClient)
+ g.POST("/delClient/:email", a.delInboundClient)
+ g.POST("/updateClient/:index", a.updateInboundClient)
+ g.POST("/:id/resetClientTraffic/:email", a.resetClientTraffic)
+ g.POST("/resetAllTraffics", a.resetAllTraffics)
+ g.POST("/resetAllClientTraffics/:id", a.resetAllClientTraffics)
- a.inboundController = NewInboundController(g)
+ a.inboundController = NewInboundController(g)
}
-
-
func (a *APIController) getAllInbounds(c *gin.Context) {
- a.inboundController.getInbounds(c)
+ a.inboundController.getInbounds(c)
}
-
func (a *APIController) getSingleInbound(c *gin.Context) {
- a.inboundController.getInbound(c)
+ a.inboundController.getInbound(c)
}
-
func (a *APIController) addInbound(c *gin.Context) {
- a.inboundController.addInbound(c)
+ a.inboundController.addInbound(c)
}
-
func (a *APIController) delInbound(c *gin.Context) {
- a.inboundController.delInbound(c)
+ a.inboundController.delInbound(c)
}
-
func (a *APIController) updateInbound(c *gin.Context) {
- a.inboundController.updateInbound(c)
+ a.inboundController.updateInbound(c)
}
func (a *APIController) getClientIps(c *gin.Context) {
- a.inboundController.getClientIps(c)
+ a.inboundController.getClientIps(c)
}
func (a *APIController) clearClientIps(c *gin.Context) {
- a.inboundController.clearClientIps(c)
+ a.inboundController.clearClientIps(c)
}
-
func (a *APIController) addInboundClient(c *gin.Context) {
- a.inboundController.addInboundClient(c)
+ a.inboundController.addInboundClient(c)
}
-
func (a *APIController) delInboundClient(c *gin.Context) {
- a.inboundController.delInboundClient(c)
+ a.inboundController.delInboundClient(c)
}
-
func (a *APIController) updateInboundClient(c *gin.Context) {
- a.inboundController.updateInboundClient(c)
+ a.inboundController.updateInboundClient(c)
}
-
func (a *APIController) resetClientTraffic(c *gin.Context) {
- a.inboundController.resetClientTraffic(c)
+ a.inboundController.resetClientTraffic(c)
+}
+func (a *APIController) resetAllTraffics(c *gin.Context) {
+ a.inboundController.resetAllTraffics(c)
+}
+func (a *APIController) resetAllClientTraffics(c *gin.Context) {
+ a.inboundController.resetAllClientTraffics(c)
}
diff --git a/web/controller/inbound.go b/web/controller/inbound.go
index b567af8c..f7ea35eb 100644
--- a/web/controller/inbound.go
+++ b/web/controller/inbound.go
@@ -37,6 +37,8 @@ func (a *InboundController) initRouter(g *gin.RouterGroup) {
g.POST("/delClient/:email", a.delInboundClient)
g.POST("/updateClient/:index", a.updateInboundClient)
g.POST("/:id/resetClientTraffic/:email", a.resetClientTraffic)
+ g.POST("/resetAllTraffics", a.resetAllTraffics)
+ g.POST("/resetAllClientTraffics/:id", a.resetAllClientTraffics)
}
@@ -131,7 +133,7 @@ func (a *InboundController) updateInbound(c *gin.Context) {
func (a *InboundController) getClientIps(c *gin.Context) {
email := c.Param("email")
- ips , err := a.inboundService.GetInboundClientIps(email)
+ ips, err := a.inboundService.GetInboundClientIps(email)
if err != nil {
jsonObj(c, "No IP Record", nil)
return
@@ -230,3 +232,27 @@ func (a *InboundController) resetClientTraffic(c *gin.Context) {
a.xrayService.SetToNeedRestart()
}
}
+
+func (a *InboundController) resetAllTraffics(c *gin.Context) {
+ err := a.inboundService.ResetAllTraffics()
+ if err != nil {
+ jsonMsg(c, "something worng!", err)
+ return
+ }
+ jsonMsg(c, "All traffics reseted", nil)
+}
+
+func (a *InboundController) resetAllClientTraffics(c *gin.Context) {
+ id, err := strconv.Atoi(c.Param("id"))
+ if err != nil {
+ jsonMsg(c, I18n(c, "pages.inbounds.revise"), err)
+ return
+ }
+
+ err = a.inboundService.ResetAllClientTraffics(id)
+ if err != nil {
+ jsonMsg(c, "something worng!", err)
+ return
+ }
+ jsonMsg(c, "All traffics of client reseted", nil)
+}
diff --git a/web/controller/server.go b/web/controller/server.go
index 7b7239d5..43a1fadc 100644
--- a/web/controller/server.go
+++ b/web/controller/server.go
@@ -38,7 +38,7 @@ func (a *ServerController) initRouter(g *gin.RouterGroup) {
g.POST("/stopXrayService", a.stopXrayService)
g.POST("/restartXrayService", a.restartXrayService)
g.POST("/installXray/:version", a.installXray)
- g.POST("/logs", a.getLogs)
+ g.POST("/logs/:count", a.getLogs)
}
func (a *ServerController) refreshStatus() {
@@ -109,7 +109,8 @@ func (a *ServerController) restartXrayService(c *gin.Context) {
}
func (a *ServerController) getLogs(c *gin.Context) {
- logs, err := a.serverService.GetLogs()
+ count := c.Param("count")
+ logs, err := a.serverService.GetLogs(count)
if err != nil {
jsonMsg(c, I18n(c, "getLogs"), err)
return
diff --git a/web/controller/setting.go b/web/controller/setting.go
index 0456bca3..261eeec8 100644
--- a/web/controller/setting.go
+++ b/web/controller/setting.go
@@ -33,6 +33,7 @@ func (a *SettingController) initRouter(g *gin.RouterGroup) {
g = g.Group("/setting")
g.POST("/all", a.getAllSetting)
+ g.POST("/defaultSettings", a.getDefaultSettings)
g.POST("/update", a.updateSetting)
g.POST("/updateUser", a.updateUser)
g.POST("/restartPanel", a.restartPanel)
@@ -47,6 +48,36 @@ func (a *SettingController) getAllSetting(c *gin.Context) {
jsonObj(c, allSetting, nil)
}
+func (a *SettingController) getDefaultSettings(c *gin.Context) {
+ expireDiff, err := a.settingService.GetExpireDiff()
+ if err != nil {
+ jsonMsg(c, I18n(c, "pages.setting.toasts.getSetting"), err)
+ return
+ }
+ trafficDiff, err := a.settingService.GetTrafficDiff()
+ if err != nil {
+ jsonMsg(c, I18n(c, "pages.setting.toasts.getSetting"), err)
+ return
+ }
+ defaultCert, err := a.settingService.GetCertFile()
+ if err != nil {
+ jsonMsg(c, I18n(c, "pages.setting.toasts.getSetting"), err)
+ return
+ }
+ defaultKey, err := a.settingService.GetKeyFile()
+ if err != nil {
+ jsonMsg(c, I18n(c, "pages.setting.toasts.getSetting"), err)
+ return
+ }
+ result := map[string]interface{}{
+ "expireDiff": expireDiff,
+ "trafficDiff": trafficDiff,
+ "defaultCert": defaultCert,
+ "defaultKey": defaultKey,
+ }
+ jsonObj(c, result, nil)
+}
+
func (a *SettingController) updateSetting(c *gin.Context) {
allSetting := &entity.AllSetting{}
err := c.ShouldBind(allSetting)
diff --git a/web/controller/sub.go b/web/controller/sub.go
new file mode 100644
index 00000000..5695f032
--- /dev/null
+++ b/web/controller/sub.go
@@ -0,0 +1,42 @@
+package controller
+
+import (
+ "encoding/base64"
+ "strings"
+ "x-ui/web/service"
+
+ "github.com/gin-gonic/gin"
+)
+
+type SUBController struct {
+ BaseController
+
+ subService service.SubService
+}
+
+func NewSUBController(g *gin.RouterGroup) *SUBController {
+ a := &SUBController{}
+ a.initRouter(g)
+ return a
+}
+
+func (a *SUBController) initRouter(g *gin.RouterGroup) {
+ g = g.Group("/sub")
+
+ g.GET("/:subid", a.subs)
+}
+
+func (a *SUBController) subs(c *gin.Context) {
+ subId := c.Param("subid")
+ host := strings.Split(c.Request.Host, ":")[0]
+ subs, err := a.subService.GetSubs(subId, host)
+ if err != nil {
+ c.String(400, "Error!")
+ } else {
+ result := ""
+ for _, sub := range subs {
+ result += sub + "\n"
+ }
+ c.String(200, base64.StdEncoding.EncodeToString([]byte(result)))
+ }
+}
diff --git a/web/entity/entity.go b/web/entity/entity.go
index 372f5caa..b464de00 100644
--- a/web/entity/entity.go
+++ b/web/entity/entity.go
@@ -32,13 +32,13 @@ type AllSetting struct {
WebCertFile string `json:"webCertFile" form:"webCertFile"`
WebKeyFile string `json:"webKeyFile" form:"webKeyFile"`
WebBasePath string `json:"webBasePath" form:"webBasePath"`
+ ExpireDiff int `json:"expireDiff" form:"expireDiff"`
+ TrafficDiff int `json:"trafficDiff" form:"trafficDiff"`
TgBotEnable bool `json:"tgBotEnable" form:"tgBotEnable"`
TgBotToken string `json:"tgBotToken" form:"tgBotToken"`
TgBotChatId string `json:"tgBotChatId" form:"tgBotChatId"`
TgRunTime string `json:"tgRunTime" form:"tgRunTime"`
TgBotBackup bool `json:"tgBotBackup" form:"tgBotBackup"`
- TgExpireDiff int `json:"tgExpireDiff" form:"tgExpireDiff"`
- TgTrafficDiff int `json:"tgTrafficDiff" form:"tgTrafficDiff"`
TgCpu int `json:"tgCpu" form:"tgCpu"`
XrayTemplateConfig string `json:"xrayTemplateConfig" form:"xrayTemplateConfig"`
TimeLocation string `json:"timeLocation" form:"timeLocation"`
diff --git a/web/html/common/head.html b/web/html/common/head.html
index f34ce62f..5e8b1fef 100644
--- a/web/html/common/head.html
+++ b/web/html/common/head.html
@@ -7,6 +7,7 @@
+