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
diff options
context:
space:
mode:
authorMHSanaei <ho3ein.sanaei@gmail.com>2023-04-09 22:43:18 +0300
committerMHSanaei <ho3ein.sanaei@gmail.com>2023-04-09 22:43:18 +0300
commite1da43053d23c995bcd6e7267cb20042398cd64f (patch)
tree08c4c371ba070ef765ec2be83270ee6032e54774 /web/service
parent3bb90cbf2463b31c6a921f7cd75cf32edd3a37f0 (diff)
alireza update pack
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
Diffstat (limited to 'web/service')
-rw-r--r--web/service/inbound.go126
-rw-r--r--web/service/server.go4
-rw-r--r--web/service/setting.go36
-rw-r--r--web/service/sub.go505
-rw-r--r--web/service/tgbot.go32
-rw-r--r--web/service/xray.go27
6 files changed, 672 insertions, 58 deletions
diff --git a/web/service/inbound.go b/web/service/inbound.go
index 9814b549..55d57997 100644
--- a/web/service/inbound.go
+++ b/web/service/inbound.go
@@ -394,11 +394,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()
@@ -406,7 +411,20 @@ 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()
+
defer func() {
if err != nil {
txInbound.Rollback()
@@ -415,22 +433,23 @@ 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 := db.Model(xray.ClientTraffic{}).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)
-
}
continue
}
@@ -438,29 +457,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) {
@@ -545,11 +570,58 @@ func (s *InboundService) ResetClientTraffic(id int, clientEmail string) error {
}
return nil
}
-func (s *InboundService) GetClientTrafficTgBot(tguname string) (traffic []*xray.ClientTraffic, err error) {
+
+func (s *InboundService) ResetAllClientTraffics(id int) error {
db := database.GetDB()
- var traffics []*xray.ClientTraffic
- err = db.Model(xray.ClientTraffic{}).Where("email like ?", "%@"+tguname).Find(&traffics).Error
+ result := db.Model(xray.ClientTraffic{}).
+ Where("inbound_id = ?", id).
+ Updates(map[string]interface{}{"enable": true, "up": 0, "down": 0})
+
+ err := result.Error
+
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (s *InboundService) ResetAllTraffics() error {
+ db := database.GetDB()
+
+ result := db.Model(model.Inbound{}).
+ Where("user_id > ?", 0).
+ Updates(map[string]interface{}{"up": 0, "down": 0})
+
+ err := result.Error
+
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (s *InboundService) GetClientTrafficTgBot(tguname string) ([]*xray.ClientTraffic, error) {
+ db := database.GetDB()
+ var inbounds []*model.Inbound
+ err := db.Model(model.Inbound{}).Where("settings like ?", fmt.Sprintf(`%%"tgId": "%s"%%`, tguname)).Find(&inbounds).Error
+ if err != nil && err != gorm.ErrRecordNotFound {
+ return nil, err
+ }
+ var emails []string
+ for _, inbound := range inbounds {
+ clients, err := s.getClients(inbound)
+ if err != nil {
+ logger.Error("Unable to get clients from inbound")
+ }
+ for _, client := range clients {
+ if client.TgID == tguname {
+ emails = append(emails, client.Email)
+ }
+ }
+ }
+ var traffics []*xray.ClientTraffic
+ err = db.Model(xray.ClientTraffic{}).Where("email IN ?", emails).Find(&traffics).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
logger.Warning(err)
@@ -643,4 +715,4 @@ func (s *InboundService) SearchInbounds(query string) ([]*model.Inbound, error)
return nil, err
}
return inbounds, nil
-} \ No newline at end of file
+}
diff --git a/web/service/server.go b/web/service/server.go
index 5e6065b5..91f9399a 100644
--- a/web/service/server.go
+++ b/web/service/server.go
@@ -327,11 +327,11 @@ func (s *ServerService) UpdateXray(version string) error {
}
-func (s *ServerService) GetLogs() ([]string, error) {
+func (s *ServerService) GetLogs(count string) ([]string, error) {
// Define the journalctl command and its arguments
var cmdArgs []string
if runtime.GOOS == "linux" {
- cmdArgs = []string{"journalctl", "-u", "x-ui", "--no-pager", "-n", "100"}
+ cmdArgs = []string{"journalctl", "-u", "x-ui", "--no-pager", "-n", count}
} else {
return []string{"Unsupported operating system"}, nil
}
diff --git a/web/service/setting.go b/web/service/setting.go
index c30bb929..ff22f847 100644
--- a/web/service/setting.go
+++ b/web/service/setting.go
@@ -28,14 +28,14 @@ var defaultValueMap = map[string]string{
"webKeyFile": "",
"secret": random.Seq(32),
"webBasePath": "/",
+ "expireDiff": "0",
+ "trafficDiff": "0",
"timeLocation": "Asia/Tehran",
"tgBotEnable": "false",
"tgBotToken": "",
"tgBotChatId": "",
"tgRunTime": "@daily",
"tgBotBackup": "false",
- "tgExpireDiff": "0",
- "tgTrafficDiff": "0",
"tgCpu": "0",
}
@@ -238,22 +238,6 @@ func (s *SettingService) SetTgBotBackup(value bool) error {
return s.setBool("tgBotBackup", value)
}
-func (s *SettingService) GetTgExpireDiff() (int, error) {
- return s.getInt("tgExpireDiff")
-}
-
-func (s *SettingService) SetTgExpireDiff(value int) error {
- return s.setInt("tgExpireDiff", value)
-}
-
-func (s *SettingService) GetTgTrafficDiff() (int, error) {
- return s.getInt("tgTrafficDiff")
-}
-
-func (s *SettingService) SetTgTrafficDiff(value int) error {
- return s.setInt("tgTrafficDiff", value)
-}
-
func (s *SettingService) GetTgCpu() (int, error) {
return s.getInt("tgCpu")
}
@@ -278,6 +262,22 @@ func (s *SettingService) GetKeyFile() (string, error) {
return s.getString("webKeyFile")
}
+func (s *SettingService) GetExpireDiff() (int, error) {
+ return s.getInt("expireDiff")
+}
+
+func (s *SettingService) SetExpireDiff(value int) error {
+ return s.setInt("expireDiff", value)
+}
+
+func (s *SettingService) GetTrafficDiff() (int, error) {
+ return s.getInt("trafficDiff")
+}
+
+func (s *SettingService) SetgetTrafficDiff(value int) error {
+ return s.setInt("trafficDiff", value)
+}
+
func (s *SettingService) GetSecret() ([]byte, error) {
secret, err := s.getString("secret")
if secret == defaultValueMap["secret"] {
diff --git a/web/service/sub.go b/web/service/sub.go
new file mode 100644
index 00000000..875a5b53
--- /dev/null
+++ b/web/service/sub.go
@@ -0,0 +1,505 @@
+package service
+
+import (
+ "encoding/base64"
+ "fmt"
+ "net/url"
+ "strings"
+ "x-ui/database"
+ "x-ui/database/model"
+ "x-ui/logger"
+
+ "github.com/goccy/go-json"
+ "gorm.io/gorm"
+)
+
+type SubService struct {
+ address string
+ inboundService InboundService
+}
+
+func (s *SubService) GetSubs(subId string, host string) ([]string, error) {
+ s.address = host
+ var result []string
+ inbounds, err := s.getInboundsBySubId(subId)
+ if err != nil {
+ return nil, err
+ }
+ for _, inbound := range inbounds {
+ clients, err := s.inboundService.getClients(inbound)
+ if err != nil {
+ logger.Error("SubService - GetSub: Unable to get clients from inbound")
+ }
+ if clients == nil {
+ continue
+ }
+ for _, client := range clients {
+ if client.SubID == subId {
+ link := s.getLink(inbound, client.Email)
+ result = append(result, link)
+ }
+ }
+ }
+ return result, nil
+}
+
+func (s *SubService) getInboundsBySubId(subId string) ([]*model.Inbound, error) {
+ db := database.GetDB()
+ var inbounds []*model.Inbound
+ err := db.Model(model.Inbound{}).Where("settings like ?", fmt.Sprintf(`%%"subId": "%s"%%`, subId)).Find(&inbounds).Error
+ if err != nil && err != gorm.ErrRecordNotFound {
+ return nil, err
+ }
+ return inbounds, nil
+}
+
+func (s *SubService) getLink(inbound *model.Inbound, email string) string {
+ switch inbound.Protocol {
+ case "vmess":
+ return s.genVmessLink(inbound, email)
+ case "vless":
+ return s.genVlessLink(inbound, email)
+ case "trojan":
+ return s.genTrojanLink(inbound, email)
+ }
+ return ""
+}
+
+func (s *SubService) genVmessLink(inbound *model.Inbound, email string) string {
+ address := s.address
+ if inbound.Protocol != model.VMess {
+ return ""
+ }
+ var stream map[string]interface{}
+ json.Unmarshal([]byte(inbound.StreamSettings), &stream)
+ network, _ := stream["network"].(string)
+ typeStr := "none"
+ host := ""
+ path := ""
+ sni := ""
+ fp := ""
+ var alpn []string
+ allowInsecure := false
+ switch network {
+ case "tcp":
+ tcp, _ := stream["tcpSettings"].(map[string]interface{})
+ header, _ := tcp["header"].(map[string]interface{})
+ typeStr, _ = header["type"].(string)
+ if typeStr == "http" {
+ request := header["request"].(map[string]interface{})
+ requestPath, _ := request["path"].([]interface{})
+ path = requestPath[0].(string)
+ headers, _ := request["headers"].(map[string]interface{})
+ host = searchHost(headers)
+ }
+ case "kcp":
+ kcp, _ := stream["kcpSettings"].(map[string]interface{})
+ header, _ := kcp["header"].(map[string]interface{})
+ typeStr, _ = header["type"].(string)
+ path, _ = kcp["seed"].(string)
+ case "ws":
+ ws, _ := stream["wsSettings"].(map[string]interface{})
+ path = ws["path"].(string)
+ headers, _ := ws["headers"].(map[string]interface{})
+ host = searchHost(headers)
+ case "http":
+ network = "h2"
+ http, _ := stream["httpSettings"].(map[string]interface{})
+ path, _ = http["path"].(string)
+ host = searchHost(http)
+ case "quic":
+ quic, _ := stream["quicSettings"].(map[string]interface{})
+ header := quic["header"].(map[string]interface{})
+ typeStr, _ = header["type"].(string)
+ host, _ = quic["security"].(string)
+ path, _ = quic["key"].(string)
+ case "grpc":
+ grpc, _ := stream["grpcSettings"].(map[string]interface{})
+ path = grpc["serviceName"].(string)
+ }
+
+ security, _ := stream["security"].(string)
+ if security == "tls" {
+ tlsSetting, _ := stream["tlsSettings"].(map[string]interface{})
+ alpns, _ := tlsSetting["alpn"].([]interface{})
+ for _, a := range alpns {
+ alpn = append(alpn, a.(string))
+ }
+ tlsSettings, _ := searchKey(tlsSetting, "settings")
+ if tlsSetting != nil {
+ if sniValue, ok := searchKey(tlsSettings, "serverName"); ok {
+ sni, _ = sniValue.(string)
+ }
+ if fpValue, ok := searchKey(tlsSettings, "fingerprint"); ok {
+ fp, _ = fpValue.(string)
+ }
+ if insecure, ok := searchKey(tlsSettings, "allowInsecure"); ok {
+ allowInsecure, _ = insecure.(bool)
+ }
+ }
+ serverName, _ := tlsSetting["serverName"].(string)
+ if serverName != "" {
+ address = serverName
+ }
+ }
+
+ clients, _ := s.inboundService.getClients(inbound)
+ clientIndex := -1
+ for i, client := range clients {
+ if client.Email == email {
+ clientIndex = i
+ break
+ }
+ }
+
+ obj := map[string]interface{}{
+ "v": "2",
+ "ps": email,
+ "add": address,
+ "port": inbound.Port,
+ "id": clients[clientIndex].ID,
+ "aid": clients[clientIndex].AlterIds,
+ "net": network,
+ "type": typeStr,
+ "host": host,
+ "path": path,
+ "tls": security,
+ "sni": sni,
+ "fp": fp,
+ "alpn": strings.Join(alpn, ","),
+ "allowInsecure": allowInsecure,
+ }
+ jsonStr, _ := json.MarshalIndent(obj, "", " ")
+ return "vmess://" + base64.StdEncoding.EncodeToString(jsonStr)
+}
+
+func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
+ address := s.address
+ if inbound.Protocol != model.VLESS {
+ return ""
+ }
+ var stream map[string]interface{}
+ json.Unmarshal([]byte(inbound.StreamSettings), &stream)
+ clients, _ := s.inboundService.getClients(inbound)
+ clientIndex := -1
+ for i, client := range clients {
+ if client.Email == email {
+ clientIndex = i
+ break
+ }
+ }
+ uuid := clients[clientIndex].ID
+ port := inbound.Port
+ streamNetwork := stream["network"].(string)
+ params := make(map[string]string)
+ params["type"] = streamNetwork
+
+ switch streamNetwork {
+ case "tcp":
+ tcp, _ := stream["tcpSettings"].(map[string]interface{})
+ header, _ := tcp["header"].(map[string]interface{})
+ typeStr, _ := header["type"].(string)
+ if typeStr == "http" {
+ request := header["request"].(map[string]interface{})
+ requestPath, _ := request["path"].([]interface{})
+ params["path"] = requestPath[0].(string)
+ headers, _ := request["headers"].(map[string]interface{})
+ params["host"] = searchHost(headers)
+ params["headerType"] = "http"
+ }
+ case "kcp":
+ kcp, _ := stream["kcpSettings"].(map[string]interface{})
+ header, _ := kcp["header"].(map[string]interface{})
+ params["headerType"] = header["type"].(string)
+ params["seed"] = kcp["seed"].(string)
+ case "ws":
+ ws, _ := stream["wsSettings"].(map[string]interface{})
+ params["path"] = ws["path"].(string)
+ headers, _ := ws["headers"].(map[string]interface{})
+ params["host"] = searchHost(headers)
+ case "http":
+ http, _ := stream["httpSettings"].(map[string]interface{})
+ params["path"] = http["path"].(string)
+ params["host"] = searchHost(http)
+ case "quic":
+ quic, _ := stream["quicSettings"].(map[string]interface{})
+ params["quicSecurity"] = quic["security"].(string)
+ params["key"] = quic["key"].(string)
+ header := quic["header"].(map[string]interface{})
+ params["headerType"] = header["type"].(string)
+ case "grpc":
+ grpc, _ := stream["grpcSettings"].(map[string]interface{})
+ params["serviceName"] = grpc["serviceName"].(string)
+ }
+
+ security, _ := stream["security"].(string)
+ if security == "tls" {
+ params["security"] = "tls"
+ tlsSetting, _ := stream["tlsSettings"].(map[string]interface{})
+ alpns, _ := tlsSetting["alpn"].([]interface{})
+ var alpn []string
+ for _, a := range alpns {
+ alpn = append(alpn, a.(string))
+ }
+ if len(alpn) > 0 {
+ params["alpn"] = strings.Join(alpn, ",")
+ }
+ tlsSettings, _ := searchKey(tlsSetting, "settings")
+ if tlsSetting != nil {
+ if sniValue, ok := searchKey(tlsSettings, "serverName"); ok {
+ params["sni"], _ = sniValue.(string)
+ }
+ if fpValue, ok := searchKey(tlsSettings, "fingerprint"); ok {
+ params["fp"], _ = fpValue.(string)
+ }
+ if insecure, ok := searchKey(tlsSettings, "allowInsecure"); ok {
+ if insecure.(bool) {
+ params["allowInsecure"] = "1"
+ }
+ }
+ }
+
+ if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 {
+ params["flow"] = clients[clientIndex].Flow
+ }
+
+ serverName, _ := tlsSetting["serverName"].(string)
+ if serverName != "" {
+ address = serverName
+ }
+ }
+
+ if security == "xtls" {
+ params["security"] = "xtls"
+ xtlsSetting, _ := stream["xtlsSettings"].(map[string]interface{})
+ alpns, _ := xtlsSetting["alpn"].([]interface{})
+ var alpn []string
+ for _, a := range alpns {
+ alpn = append(alpn, a.(string))
+ }
+ if len(alpn) > 0 {
+ params["alpn"] = strings.Join(alpn, ",")
+ }
+
+ xtlsSettings, _ := searchKey(xtlsSetting, "settings")
+ if xtlsSetting != nil {
+ if sniValue, ok := searchKey(xtlsSettings, "serverName"); ok {
+ params["sni"], _ = sniValue.(string)
+ }
+ if fpValue, ok := searchKey(xtlsSettings, "fingerprint"); ok {
+ params["fp"], _ = fpValue.(string)
+ }
+ if insecure, ok := searchKey(xtlsSettings, "allowInsecure"); ok {
+ if insecure.(bool) {
+ params["allowInsecure"] = "1"
+ }
+ }
+ }
+
+ if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 {
+ params["flow"] = clients[clientIndex].Flow
+ }
+
+ serverName, _ := xtlsSetting["serverName"].(string)
+ if serverName != "" {
+ address = serverName
+ }
+ }
+
+ link := fmt.Sprintf("vless://%s@%s:%d", uuid, address, port)
+ url, _ := url.Parse(link)
+ q := url.Query()
+
+ for k, v := range params {
+ q.Add(k, v)
+ }
+
+ // Set the new query values on the URL
+ url.RawQuery = q.Encode()
+
+ url.Fragment = email
+ return url.String()
+}
+
+func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string {
+ address := s.address
+ if inbound.Protocol != model.Trojan {
+ return ""
+ }
+ var stream map[string]interface{}
+ json.Unmarshal([]byte(inbound.StreamSettings), &stream)
+ clients, _ := s.inboundService.getClients(inbound)
+ clientIndex := -1
+ for i, client := range clients {
+ if client.Email == email {
+ clientIndex = i
+ break
+ }
+ }
+ password := clients[clientIndex].Password
+ port := inbound.Port
+ streamNetwork := stream["network"].(string)
+ params := make(map[string]string)
+ params["type"] = streamNetwork
+
+ switch streamNetwork {
+ case "tcp":
+ tcp, _ := stream["tcpSettings"].(map[string]interface{})
+ header, _ := tcp["header"].(map[string]interface{})
+ typeStr, _ := header["type"].(string)
+ if typeStr == "http" {
+ request := header["request"].(map[string]interface{})
+ requestPath, _ := request["path"].([]interface{})
+ params["path"] = requestPath[0].(string)
+ headers, _ := request["headers"].(map[string]interface{})
+ params["host"] = searchHost(headers)
+ params["headerType"] = "http"
+ }
+ case "kcp":
+ kcp, _ := stream["kcpSettings"].(map[string]interface{})
+ header, _ := kcp["header"].(map[string]interface{})
+ params["headerType"] = header["type"].(string)
+ params["seed"] = kcp["seed"].(string)
+ case "ws":
+ ws, _ := stream["wsSettings"].(map[string]interface{})
+ params["path"] = ws["path"].(string)
+ headers, _ := ws["headers"].(map[string]interface{})
+ params["host"] = searchHost(headers)
+ case "http":
+ http, _ := stream["httpSettings"].(map[string]interface{})
+ params["path"] = http["path"].(string)
+ params["host"] = searchHost(http)
+ case "quic":
+ quic, _ := stream["quicSettings"].(map[string]interface{})
+ params["quicSecurity"] = quic["security"].(string)
+ params["key"] = quic["key"].(string)
+ header := quic["header"].(map[string]interface{})
+ params["headerType"] = header["type"].(string)
+ case "grpc":
+ grpc, _ := stream["grpcSettings"].(map[string]interface{})
+ params["serviceName"] = grpc["serviceName"].(string)
+ }
+
+ security, _ := stream["security"].(string)
+ if security == "tls" {
+ params["security"] = "tls"
+ tlsSetting, _ := stream["tlsSettings"].(map[string]interface{})
+ alpns, _ := tlsSetting["alpn"].([]interface{})
+ var alpn []string
+ for _, a := range alpns {
+ alpn = append(alpn, a.(string))
+ }
+ if len(alpn) > 0 {
+ params["alpn"] = strings.Join(alpn, ",")
+ }
+ tlsSettings, _ := searchKey(tlsSetting, "settings")
+ if tlsSetting != nil {
+ if sniValue, ok := searchKey(tlsSettings, "serverName"); ok {
+ params["sni"], _ = sniValue.(string)
+ }
+ if fpValue, ok := searchKey(tlsSettings, "fingerprint"); ok {
+ params["fp"], _ = fpValue.(string)
+ }
+ if insecure, ok := searchKey(tlsSettings, "allowInsecure"); ok {
+ if insecure.(bool) {
+ params["allowInsecure"] = "1"
+ }
+ }
+ }
+
+ serverName, _ := tlsSetting["serverName"].(string)
+ if serverName != "" {
+ address = serverName
+ }
+ }
+
+ if security == "xtls" {
+ params["security"] = "xtls"
+ xtlsSetting, _ := stream["xtlsSettings"].(map[string]interface{})
+ alpns, _ := xtlsSetting["alpn"].([]interface{})
+ var alpn []string
+ for _, a := range alpns {
+ alpn = append(alpn, a.(string))
+ }
+ if len(alpn) > 0 {
+ params["alpn"] = strings.Join(alpn, ",")
+ }
+
+ xtlsSettings, _ := searchKey(xtlsSetting, "settings")
+ if xtlsSetting != nil {
+ if sniValue, ok := searchKey(xtlsSettings, "serverName"); ok {
+ params["sni"], _ = sniValue.(string)
+ }
+ if fpValue, ok := searchKey(xtlsSettings, "fingerprint"); ok {
+ params["fp"], _ = fpValue.(string)
+ }
+ if insecure, ok := searchKey(xtlsSettings, "allowInsecure"); ok {
+ if insecure.(bool) {
+ params["allowInsecure"] = "1"
+ }
+ }
+ }
+
+ if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 {
+ params["flow"] = clients[clientIndex].Flow
+ }
+
+ serverName, _ := xtlsSetting["serverName"].(string)
+ if serverName != "" {
+ address = serverName
+ }
+ }
+
+ link := fmt.Sprintf("trojan://%s@%s:%d", password, address, port)
+
+ url, _ := url.Parse(link)
+ q := url.Query()
+
+ for k, v := range params {
+ q.Add(k, v)
+ }
+
+ // Set the new query values on the URL
+ url.RawQuery = q.Encode()
+
+ url.Fragment = email
+ return url.String()
+}
+
+func searchKey(data interface{}, key string) (interface{}, bool) {
+ switch val := data.(type) {
+ case map[string]interface{}:
+ for k, v := range val {
+ if k == key {
+ return v, true
+ }
+ if result, ok := searchKey(v, key); ok {
+ return result, true
+ }
+ }
+ case []interface{}:
+ for _, v := range val {
+ if result, ok := searchKey(v, key); ok {
+ return result, true
+ }
+ }
+ }
+ return nil, false
+}
+
+func searchHost(headers interface{}) string {
+ data, _ := headers.(map[string]interface{})
+ for k, v := range data {
+ if strings.EqualFold(k, "host") {
+ switch v.(type) {
+ case []interface{}:
+ hosts, _ := v.([]interface{})
+ return hosts[0].(string)
+ case interface{}:
+ return v.(string)
+ }
+ }
+ }
+
+ return ""
+}
diff --git a/web/service/tgbot.go b/web/service/tgbot.go
index 86097b0f..2ab3dbb0 100644
--- a/web/service/tgbot.go
+++ b/web/service/tgbot.go
@@ -160,14 +160,14 @@ func (t *Tgbot) asnwerCallback(callbackQuery *tgbotapi.CallbackQuery, isAdmin bo
t.SendMsgToTgbot(callbackQuery.From.ID, t.getServerUsage())
case "inbounds":
t.SendMsgToTgbot(callbackQuery.From.ID, t.getInboundUsages())
- case "exhausted_soon":
+ case "deplete_soon":
t.SendMsgToTgbot(callbackQuery.From.ID, t.getExhausted())
case "get_backup":
t.sendBackup(callbackQuery.From.ID)
case "client_traffic":
t.getClientUsage(callbackQuery.From.ID, callbackQuery.From.UserName)
case "client_commands":
- t.SendMsgToTgbot(callbackQuery.From.ID, "To search for statistics, just use folowing command:\r\n \r\n<code>/usage [UID|Passowrd]</code>\r\n \r\nUse UID for vmess and vless and Password for Trojan.")
+ t.SendMsgToTgbot(callbackQuery.From.ID, "To search for statistics, just use folowing command:\r\n \r\n<code>/usage [UID|Passowrd]</code>\r\n \r\nUse UID for vmess/vless and Password for Trojan.")
case "commands":
t.SendMsgToTgbot(callbackQuery.From.ID, "Search for a client email:\r\n<code>/usage email</code>\r\n \r\nSearch for inbounds (with client stats):\r\n<code>/inbound [remark]</code>")
}
@@ -190,7 +190,7 @@ func (t *Tgbot) SendAnswer(chatId int64, msg string, isAdmin bool) {
),
tgbotapi.NewInlineKeyboardRow(
tgbotapi.NewInlineKeyboardButtonData("Get Inbounds", "inbounds"),
- tgbotapi.NewInlineKeyboardButtonData("Exhausted soon", "exhausted_soon"),
+ tgbotapi.NewInlineKeyboardButtonData("Deplete soon", "deplete_soon"),
),
tgbotapi.NewInlineKeyboardRow(
tgbotapi.NewInlineKeyboardButtonData("Commands", "commands"),
@@ -363,6 +363,11 @@ func (t *Tgbot) getInboundUsages() string {
}
func (t *Tgbot) getClientUsage(chatId int64, tgUserName string) {
+ if len(tgUserName) == 0 {
+ msg := "Your configuration is not found!\nYou should configure your telegram username and ask Admin to add it to your configuration."
+ t.SendMsgToTgbot(chatId, msg)
+ return
+ }
traffics, err := t.inboundService.GetClientTrafficTgBot(tgUserName)
if err != nil {
logger.Warning(err)
@@ -373,11 +378,14 @@ func (t *Tgbot) getClientUsage(chatId int64, tgUserName string) {
if len(traffics) == 0 {
msg := "Your configuration is not found!\nPlease ask your Admin to use your telegram username in your configuration(s).\n\nYour username: <b>@" + tgUserName + "</b>"
t.SendMsgToTgbot(chatId, msg)
+ return
}
for _, traffic := range traffics {
expiryTime := ""
if traffic.ExpiryTime == 0 {
expiryTime = "♾Unlimited"
+ } else if traffic.ExpiryTime < 0 {
+ expiryTime = fmt.Sprintf("%d days", traffic.ExpiryTime/-86400000)
} else {
expiryTime = time.Unix((traffic.ExpiryTime / 1000), 0).Format("2006-01-02 15:04:05")
}
@@ -412,6 +420,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/-86400000)
} else {
expiryTime = time.Unix((traffic.ExpiryTime / 1000), 0).Format("2006-01-02 15:04:05")
}
@@ -450,6 +460,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/-86400000)
} else {
expiryTime = time.Unix((traffic.ExpiryTime / 1000), 0).Format("2006-01-02 15:04:05")
}
@@ -483,6 +495,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/-86400000)
} else {
expiryTime = time.Unix((traffic.ExpiryTime / 1000), 0).Format("2006-01-02 15:04:05")
}
@@ -507,13 +521,13 @@ func (t *Tgbot) getExhausted() string {
var disabledInbounds []model.Inbound
var disabledClients []xray.ClientTraffic
output := ""
- TrafficThreshold, err := t.settingService.GetTgTrafficDiff()
+ TrafficThreshold, err := t.settingService.GetTrafficDiff()
if err == nil && TrafficThreshold > 0 {
trDiff = int64(TrafficThreshold) * 1073741824
}
- ExpireThreshold, err := t.settingService.GetTgExpireDiff()
+ ExpireThreshold, err := t.settingService.GetExpireDiff()
if err == nil && ExpireThreshold > 0 {
- exDiff = int64(ExpireThreshold) * 84600000
+ exDiff = int64(ExpireThreshold) * 86400000
}
inbounds, err := t.inboundService.GetAllInbounds()
if err != nil {
@@ -541,7 +555,7 @@ func (t *Tgbot) getExhausted() string {
disabledInbounds = append(disabledInbounds, *inbound)
}
}
- output += fmt.Sprintf("Exhausted Inbounds count:\r\n🛑 Disabled: %d\r\n🔜 Exhaust soon: %d\r\n \r\n", len(disabledInbounds), len(exhaustedInbounds))
+ output += fmt.Sprintf("Exhausted Inbounds count:\r\n🛑 Disabled: %d\r\n🔜 Deplete soon: %d\r\n \r\n", len(disabledInbounds), len(exhaustedInbounds))
if len(exhaustedInbounds) > 0 {
output += "Exhausted Inbounds:\r\n"
for _, inbound := range exhaustedInbounds {
@@ -553,13 +567,15 @@ func (t *Tgbot) getExhausted() string {
}
}
}
- output += fmt.Sprintf("Exhausted Clients count:\r\n🛑 Disabled: %d\r\n🔜 Exhaust soon: %d\r\n \r\n", len(disabledClients), len(exhaustedClients))
+ output += fmt.Sprintf("Exhausted Clients count:\r\n🛑 Exhausted: %d\r\n🔜 Deplete soon: %d\r\n \r\n", len(disabledClients), len(exhaustedClients))
if len(exhaustedClients) > 0 {
output += "Exhausted Clients:\r\n"
for _, traffic := range exhaustedClients {
expiryTime := ""
if traffic.ExpiryTime == 0 {
expiryTime = "♾Unlimited"
+ } else if traffic.ExpiryTime < 0 {
+ expiryTime += fmt.Sprintf("%d days", traffic.ExpiryTime/-86400000)
} else {
expiryTime = time.Unix((traffic.ExpiryTime / 1000), 0).Format("2006-01-02 15:04:05")
}
diff --git a/web/service/xray.go b/web/service/xray.go
index d9e65f8d..6e63d2d1 100644
--- a/web/service/xray.go
+++ b/web/service/xray.go
@@ -84,15 +84,16 @@ func (s *XrayService) GetXrayConfig() (*xray.Config, error) {
clients, ok := settings["clients"].([]interface{})
if ok {
// check users active or not
-
clientStats := inbound.ClientStats
for _, clientTraffic := range clientStats {
+ indexDecrease := 0
for index, client := range clients {
c := client.(map[string]interface{})
if c["email"] == clientTraffic.Email {
if !clientTraffic.Enable {
- clients = RemoveIndex(clients, index)
+ clients = RemoveIndex(clients, index-indexDecrease)
+ indexDecrease++
logger.Info("Remove Inbound User", c["email"], "due the expire or traffic limit")
}
@@ -101,7 +102,27 @@ func (s *XrayService) GetXrayConfig() (*xray.Config, error) {
}
}
- settings["clients"] = clients
+
+ // clear client config for additional parameters
+ var final_clients []interface{}
+ for _, client := range clients {
+
+ c := client.(map[string]interface{})
+
+ if c["enable"] != nil {
+ if enable, ok := c["enable"].(bool); ok && !enable {
+ continue
+ }
+ }
+ for key := range c {
+ if key != "email" && key != "id" && key != "password" && key != "flow" && key != "alterId" {
+ delete(c, key)
+ }
+ }
+ final_clients = append(final_clients, interface{}(c))
+ }
+
+ settings["clients"] = final_clients
modifiedSettings, err := json.Marshal(settings)
if err != nil {
return nil, err