diff --git a/web/assets/js/model/xray.js b/web/assets/js/model/xray.js
index 93693e3339..a701c88ec5 100644
--- a/web/assets/js/model/xray.js
+++ b/web/assets/js/model/xray.js
@@ -919,16 +919,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;
@@ -1459,6 +1459,9 @@ Inbound.VmessSettings.Vmess = class extends XrayCommonClass {
if (this.expiryTime === 0 || this.expiryTime === "") {
return null;
}
+ if (this.expiryTime < 0){
+ return this.expiryTime / -84600000;
+ }
return moment(this.expiryTime);
}
@@ -1547,6 +1550,9 @@ Inbound.VLESSSettings.VLESS = class extends XrayCommonClass {
if (this.expiryTime === 0 || this.expiryTime === "") {
return null;
}
+ if (this.expiryTime < 0){
+ return this.expiryTime / -84600000;
+ }
return moment(this.expiryTime);
}
@@ -1678,6 +1684,9 @@ Inbound.TrojanSettings.Trojan = class extends XrayCommonClass {
if (this.expiryTime === 0 || this.expiryTime === "") {
return null;
}
+ if (this.expiryTime < 0){
+ return this.expiryTime / -84600000;
+ }
return moment(this.expiryTime);
}
diff --git a/web/html/xui/client_bulk_modal.html b/web/html/xui/client_bulk_modal.html
index 6ac1ec70cb..e6f5094bb7 100644
--- a/web/html/xui/client_bulk_modal.html
+++ b/web/html/xui/client_bulk_modal.html
@@ -51,7 +51,13 @@
-
+
+
+
+
+
+
+
{{ i18n "pages.inbounds.expireDate" }}
@@ -88,6 +94,7 @@
emailPostfix: "",
subId: "",
tgId: "",
+ delayedStart: false,
ok() {
method=clientsBulkModal.emailMethod;
if(method>1){
@@ -119,7 +126,7 @@
this.confirm = confirm;
this.quantity = 1;
this.totalGB = 0;
- this.expiryTime = '';
+ this.expiryTime = 0;
this.emailMethod= 0;
this.firstNum= 1;
this.lastNum= 1;
@@ -130,6 +137,7 @@
this.dbInbound = new DBInbound(dbInbound);
this.inbound = dbInbound.toInbound();
this.clients = this.getClients(this.inbound.protocol, this.inbound.settings);
+ this.delayedStart = false;
},
getClients(protocol, clientSettings) {
switch(protocol){
@@ -164,6 +172,12 @@
get inbound() {
return this.clientsBulkModal.inbound;
},
+ get delayedExpireDays() {
+ return this.clientsBulkModal.expiryTime < 0 ? this.clientsBulkModal.expiryTime / -84600000 : 0;
+ },
+ set delayedExpireDays(days){
+ this.clientsBulkModal.expiryTime = -84600000 * days;
+ },
},
});
diff --git a/web/html/xui/client_modal.html b/web/html/xui/client_modal.html
index 8afccbb38f..6b6cc5f7e4 100644
--- a/web/html/xui/client_modal.html
+++ b/web/html/xui/client_modal.html
@@ -18,6 +18,7 @@
clientStats: [],
index: null,
isExpired: false,
+ delayedStart: false,
ok() {
ObjectUtil.execute(clientModal.confirm, clientModal.inbound, clientModal.dbInbound, clientModal.index);
},
@@ -33,6 +34,10 @@
this.isExpired = isEdit ? this.inbound.isExpiry(this.index) : false;
if (!isEdit){
this.addClient(this.inbound.protocol, this.clients);
+ } else {
+ if (this.clients[index].expiryTime < 0){
+ this.delayedStart = true;
+ }
}
this.clientStats = this.dbInbound.clientStats.find(row => row.email === this.clients[this.index].email);
this.confirm = confirm;
@@ -81,7 +86,7 @@
},
get isTrafficExhausted() {
if(!clientStats) return false
- if(clientStats.total == 0) return false
+ if(clientStats.total <= 0) return false
if(clientStats.up + clientStats.down < clientStats.total) return false
return true
},
@@ -90,10 +95,16 @@
},
get statsColor() {
if(!clientStats) return 'blue'
- if(clientStats.total === 0) return 'blue'
+ if(clientStats.total <= 0) return 'blue'
else if(clientStats.total > 0 && (clientStats.down+clientStats.up) < clientStats.total) return 'cyan'
else return 'red'
- }
+ },
+ get delayedExpireDays() {
+ return clientModal.isEdit && this.client.expiryTime < 0 ? this.client.expiryTime / -84600000 : 0;
+ },
+ set delayedExpireDays(days){
+ this.client.expiryTime = -84600000 * days;
+ },
},
methods: {
getNewEmail(client) {
diff --git a/web/html/xui/form/client.html b/web/html/xui/form/client.html
index 1dac816568..b217cae1dc 100644
--- a/web/html/xui/form/client.html
+++ b/web/html/xui/form/client.html
@@ -65,7 +65,13 @@
-
+
+
+
+
+
+
+
{{ i18n "pages.inbounds.expireDate" }}
diff --git a/web/html/xui/inbound_client_table.html b/web/html/xui/inbound_client_table.html
index 024ded7cb2..6100cd9b71 100644
--- a/web/html/xui/inbound_client_table.html
+++ b/web/html/xui/inbound_client_table.html
@@ -37,11 +37,12 @@
{{ i18n "indefinite" }}
-
+
[[ DateUtil.formatMillis(client._expiryTime) ]]
+ [[ client._expiryTime ]] {{ i18n "pages.client.days" }}
{{ i18n "indefinite" }}
{{end}}
\ No newline at end of file
diff --git a/web/html/xui/inbound_info_modal.html b/web/html/xui/inbound_info_modal.html
index 4cb2946102..e819f1a6cf 100644
--- a/web/html/xui/inbound_info_modal.html
+++ b/web/html/xui/inbound_info_modal.html
@@ -96,6 +96,7 @@
[[ DateUtil.formatMillis(infoModal.clientSettings.expiryTime) ]]
+ [[ infoModal.clientSettings.expiryTime / -84600000 ]] {{ i18n "pages.client.days" }}
{{ i18n "indefinite" }}
@@ -183,7 +184,7 @@
inbound: new Inbound(),
dbInbound: new DBInbound(),
settings: null,
- clientSettings: new Inbound.Settings(),
+ clientSettings: null,
clientStats: [],
upStats: 0,
downStats: 0,
diff --git a/web/service/inbound.go b/web/service/inbound.go
index e78a10902e..c48900f753 100644
--- a/web/service/inbound.go
+++ b/web/service/inbound.go
@@ -366,11 +366,16 @@ func (s *InboundService) AddClientTraffic(traffics []*xray.ClientTraffic) (err e
if len(traffics) == 0 {
return nil
}
- db := database.GetDB()
- dbInbound := db.Model(model.Inbound{})
+ traffics, err = s.adjustTraffics(traffics)
+ if err != nil {
+ return err
+ }
+
+ db := database.GetDB()
db = db.Model(xray.ClientTraffic{})
tx := db.Begin()
+
defer func() {
if err != nil {
tx.Rollback()
@@ -378,7 +383,22 @@ func (s *InboundService) AddClientTraffic(traffics []*xray.ClientTraffic) (err e
tx.Commit()
}
}()
+
+ err = tx.Save(traffics).Error
+ if err != nil {
+ logger.Warning("AddClientTraffic update data ", err)
+ }
+
+ return nil
+}
+
+func (s *InboundService) adjustTraffics(traffics []*xray.ClientTraffic) (full_traffics []*xray.ClientTraffic, err error) {
+ db := database.GetDB()
+ dbInbound := db.Model(model.Inbound{})
txInbound := dbInbound.Begin()
+
+ tx := db.Model(xray.ClientTraffic{})
+
defer func() {
if err != nil {
txInbound.Rollback()
@@ -387,18 +407,20 @@ func (s *InboundService) AddClientTraffic(traffics []*xray.ClientTraffic) (err e
}
}()
- for _, traffic := range traffics {
+ for traffic_index, traffic := range traffics {
inbound := &model.Inbound{}
- client := &xray.ClientTraffic{}
- err := tx.Where("email = ?", traffic.Email).First(client).Error
+ client_traffic := &xray.ClientTraffic{}
+ err := tx.Where("email = ?", traffic.Email).First(client_traffic).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
logger.Warning(err, traffic.Email)
}
continue
}
+ client_traffic.Up += traffic.Up
+ client_traffic.Down += traffic.Down
- err = txInbound.Where("id=?", client.InboundId).First(inbound).Error
+ err = txInbound.Where("id=?", client_traffic.InboundId).First(inbound).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
logger.Warning(err, traffic.Email)
@@ -409,28 +431,35 @@ func (s *InboundService) AddClientTraffic(traffics []*xray.ClientTraffic) (err e
settings := map[string][]model.Client{}
json.Unmarshal([]byte(inbound.Settings), &settings)
clients := settings["clients"]
- for _, client := range clients {
+ needUpdate := false
+ for client_index, client := range clients {
if traffic.Email == client.Email {
- traffic.ExpiryTime = client.ExpiryTime
- traffic.Total = client.TotalGB
+ if client.ExpiryTime < 0 {
+ clients[client_index].ExpiryTime = (time.Now().Unix() * 1000) - client.ExpiryTime
+ needUpdate = true
+ }
+ client_traffic.ExpiryTime = client.ExpiryTime
+ client_traffic.Total = client.TotalGB
+ break
}
}
- if tx.Where("inbound_id = ? and email = ?", inbound.Id, traffic.Email).
- UpdateColumns(map[string]interface{}{
- "enable": true,
- "expiry_time": traffic.ExpiryTime,
- "total": traffic.Total,
- "up": gorm.Expr("up + ?", traffic.Up),
- "down": gorm.Expr("down + ?", traffic.Down)}).RowsAffected == 0 {
- err = tx.Create(traffic).Error
- }
- if err != nil {
- logger.Warning("AddClientTraffic update data ", err)
- continue
+ if needUpdate {
+ settings["clients"] = clients
+ modifiedSettings, err := json.MarshalIndent(settings, "", " ")
+ if err != nil {
+ return nil, err
+ }
+
+ err = txInbound.Where("id=?", inbound.Id).Update("settings", string(modifiedSettings)).Error
+ if err != nil {
+ return nil, err
+ }
}
+
+ traffics[traffic_index] = client_traffic
}
- return
+ return traffics, nil
}
func (s *InboundService) DisableInvalidInbounds() (int64, error) {
diff --git a/web/service/tgbot.go b/web/service/tgbot.go
index 87d3556f0f..6204ed1412 100644
--- a/web/service/tgbot.go
+++ b/web/service/tgbot.go
@@ -378,6 +378,8 @@ func (t *Tgbot) getClientUsage(chatId int64, tgUserName string) {
expiryTime := ""
if traffic.ExpiryTime == 0 {
expiryTime = "♾Unlimited"
+ } else if traffic.ExpiryTime < 0 {
+ expiryTime = fmt.Sprintf("%d days", traffic.ExpiryTime/-84600000)
} else {
expiryTime = time.Unix((traffic.ExpiryTime / 1000), 0).Format("2006-01-02 15:04:05")
}
@@ -412,6 +414,8 @@ func (t *Tgbot) searchClient(chatId int64, email string) {
expiryTime := ""
if traffic.ExpiryTime == 0 {
expiryTime = "♾Unlimited"
+ } else if traffic.ExpiryTime < 0 {
+ expiryTime = fmt.Sprintf("%d days", traffic.ExpiryTime/-84600000)
} else {
expiryTime = time.Unix((traffic.ExpiryTime / 1000), 0).Format("2006-01-02 15:04:05")
}
@@ -450,6 +454,8 @@ func (t *Tgbot) searchInbound(chatId int64, remark string) {
expiryTime := ""
if traffic.ExpiryTime == 0 {
expiryTime = "♾Unlimited"
+ } else if traffic.ExpiryTime < 0 {
+ expiryTime = fmt.Sprintf("%d days", traffic.ExpiryTime/-84600000)
} else {
expiryTime = time.Unix((traffic.ExpiryTime / 1000), 0).Format("2006-01-02 15:04:05")
}
@@ -483,6 +489,8 @@ func (t *Tgbot) searchForClient(chatId int64, query string) {
expiryTime := ""
if traffic.ExpiryTime == 0 {
expiryTime = "♾Unlimited"
+ } else if traffic.ExpiryTime < 0 {
+ expiryTime = fmt.Sprintf("%d days", traffic.ExpiryTime/-84600000)
} else {
expiryTime = time.Unix((traffic.ExpiryTime / 1000), 0).Format("2006-01-02 15:04:05")
}
@@ -560,6 +568,8 @@ func (t *Tgbot) getExhausted() string {
expiryTime := ""
if traffic.ExpiryTime == 0 {
expiryTime = "♾Unlimited"
+ } else if traffic.ExpiryTime < 0 {
+ expiryTime += fmt.Sprintf("%d days", traffic.ExpiryTime/-84600000)
} else {
expiryTime = time.Unix((traffic.ExpiryTime / 1000), 0).Format("2006-01-02 15:04:05")
}
diff --git a/web/translation/translate.en_US.toml b/web/translation/translate.en_US.toml
index 4670a61252..1c829e752a 100644
--- a/web/translation/translate.en_US.toml
+++ b/web/translation/translate.en_US.toml
@@ -145,6 +145,9 @@
"last" = "Last"
"prefix" = "Prefix"
"postfix" = "postfix"
+"delayedStart" = "Start after first use"
+"expireDays" = "Expire days"
+"days" = "day(s)"
[pages.inbounds.toasts]
"obtain" = "Obtain"
diff --git a/web/translation/translate.fa_IR.toml b/web/translation/translate.fa_IR.toml
index defc93b9ca..0499b980cb 100644
--- a/web/translation/translate.fa_IR.toml
+++ b/web/translation/translate.fa_IR.toml
@@ -145,6 +145,9 @@
"last" = "تا"
"prefix" = "پیشوند"
"postfix" = "پسوند"
+"delayedStart" = "شروع بعد از اولین استفاده"
+"expireDays" = "روزهای اعتبار"
+"days" = "(روز)"
[pages.inbounds.toasts]
"obtain" = "Obtain"
diff --git a/web/translation/translate.zh_Hans.toml b/web/translation/translate.zh_Hans.toml
index caa3b0b442..3a771181ee 100644
--- a/web/translation/translate.zh_Hans.toml
+++ b/web/translation/translate.zh_Hans.toml
@@ -145,6 +145,9 @@
"last" = "最后"
"prefix" = "前缀"
"postfix" = "后缀"
+"delayedStart" = "首次使用后开始"
+"expireDays" = "过期天数"
+"days" = "天"
[pages.inbounds.toasts]
"obtain" = "获取"