From e1da43053d23c995bcd6e7267cb20042398cd64f Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Sun, 9 Apr 2023 23:13:18 +0330 Subject: alireza update pack Co-Authored-By: Alireza Ahmadi --- web/service/inbound.go | 126 +++++++++--- web/service/server.go | 4 +- web/service/setting.go | 36 ++-- web/service/sub.go | 505 +++++++++++++++++++++++++++++++++++++++++++++++++ web/service/tgbot.go | 32 +++- web/service/xray.go | 27 ++- 6 files changed, 672 insertions(+), 58 deletions(-) create mode 100644 web/service/sub.go (limited to 'web/service') 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/usage [UID|Passowrd]\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/usage [UID|Passowrd]\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/usage email\r\n \r\nSearch for inbounds (with client stats):\r\n/inbound [remark]") } @@ -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: @" + tgUserName + "" 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 -- cgit v1.2.3 From de26dbbc96de3c09fb3dda3086f125f591a1cbd6 Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Mon, 10 Apr 2023 00:55:47 +0330 Subject: fixed --- web/service/tgbot.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'web/service') diff --git a/web/service/tgbot.go b/web/service/tgbot.go index 2ab3dbb0..6da0df57 100644 --- a/web/service/tgbot.go +++ b/web/service/tgbot.go @@ -536,14 +536,14 @@ func (t *Tgbot) getExhausted() string { for _, inbound := range inbounds { if inbound.Enable { if (inbound.ExpiryTime > 0 && (inbound.ExpiryTime-now < exDiff)) || - (inbound.Total > 0 && (inbound.Total-inbound.Up+inbound.Down < trDiff)) { + (inbound.Total > 0 && (inbound.Total-(inbound.Up+inbound.Down) < trDiff)) { exhaustedInbounds = append(exhaustedInbounds, *inbound) } if len(inbound.ClientStats) > 0 { for _, client := range inbound.ClientStats { if client.Enable { if (client.ExpiryTime > 0 && (client.ExpiryTime-now < exDiff)) || - (client.Total > 0 && (client.Total-client.Up+client.Down < trDiff)) { + (client.Total > 0 && (client.Total-(client.Up+client.Down) < trDiff)) { exhaustedClients = append(exhaustedClients, client) } } else { -- cgit v1.2.3 From 519f2b462ee3340c959c57f5f92319dc011833e6 Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Tue, 11 Apr 2023 15:40:45 +0330 Subject: reality sub --- web/service/sub.go | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'web/service') diff --git a/web/service/sub.go b/web/service/sub.go index 875a5b53..d1891e14 100644 --- a/web/service/sub.go +++ b/web/service/sub.go @@ -269,6 +269,35 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string { } } + if security == "reality" { + params["security"] = "reality" + realitySetting, _ := stream["realitySettings"].(map[string]interface{}) + realitySettings, _ := searchKey(realitySetting, "settings") + if realitySetting != nil { + if sniValue, ok := searchKey(realitySettings, "serverName"); ok { + params["sni"], _ = sniValue.(string) + } + if pbkValue, ok := searchKey(realitySettings, "publicKey"); ok { + params["pbk"], _ = pbkValue.(string) + } + if sidValue, ok := searchKey(realitySettings, "shortIds"); ok { + params["sid"], _ = sidValue.(string) + } + if fpValue, ok := searchKey(realitySettings, "fingerprint"); ok { + params["fp"], _ = fpValue.(string) + } + } + + if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 { + params["flow"] = clients[clientIndex].Flow + } + + serverName, _ := realitySetting["serverName"].(string) + if serverName != "" { + address = serverName + } + } + if security == "xtls" { params["security"] = "xtls" xtlsSetting, _ := stream["xtlsSettings"].(map[string]interface{}) -- cgit v1.2.3 From 7412bf17a927df4715b65962de5f683dade83395 Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Tue, 11 Apr 2023 15:41:04 +0330 Subject: bug fix Co-Authored-By: Alireza Ahmadi --- web/service/inbound.go | 22 ++++++++++++---------- web/service/server.go | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 10 deletions(-) (limited to 'web/service') diff --git a/web/service/inbound.go b/web/service/inbound.go index 55d57997..74765ab3 100644 --- a/web/service/inbound.go +++ b/web/service/inbound.go @@ -454,19 +454,21 @@ func (s *InboundService) adjustTraffics(traffics []*xray.ClientTraffic) (full_tr continue } // get settings clients - settings := map[string][]model.Client{} + settings := map[string]interface{}{} json.Unmarshal([]byte(inbound.Settings), &settings) - clients := settings["clients"] + clients, ok := settings["clients"].([]model.Client) needUpdate := false - for client_index, client := range clients { - if traffic.Email == client.Email { - if client.ExpiryTime < 0 { - clients[client_index].ExpiryTime = (time.Now().Unix() * 1000) - client.ExpiryTime - needUpdate = true + if ok { + for client_index, client := range clients { + if traffic.Email == client.Email { + 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 } - client_traffic.ExpiryTime = client.ExpiryTime - client_traffic.Total = client.TotalGB - break } } diff --git a/web/service/server.go b/web/service/server.go index 91f9399a..c73fce57 100644 --- a/web/service/server.go +++ b/web/service/server.go @@ -13,6 +13,7 @@ import ( "runtime" "strings" "time" + "x-ui/config" "x-ui/logger" "x-ui/util/sys" "x-ui/xray" @@ -349,3 +350,43 @@ func (s *ServerService) GetLogs(count string) ([]string, error) { return lines, nil } + +func (s *ServerService) GetConfigJson() (interface{}, error) { + // Open the file for reading + file, err := os.Open(xray.GetConfigPath()) + if err != nil { + return nil, err + } + defer file.Close() + + // Read the file contents + fileContents, err := io.ReadAll(file) + if err != nil { + return nil, err + } + + var jsonData interface{} + err = json.Unmarshal(fileContents, &jsonData) + if err != nil { + return nil, err + } + + return jsonData, nil +} + +func (s *ServerService) GetDb() ([]byte, error) { + // Open the file for reading + file, err := os.Open(config.GetDBPath()) + if err != nil { + return nil, err + } + defer file.Close() + + // Read the file contents + fileContents, err := io.ReadAll(file) + if err != nil { + return nil, err + } + + return fileContents, nil +} -- cgit v1.2.3 From 8c40e7281fe40b3dfe40093446a339a99c2f2e1a Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Tue, 11 Apr 2023 18:39:07 +0330 Subject: bug fix Co-Authored-By: Alireza Ahmadi --- web/service/inbound.go | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) (limited to 'web/service') diff --git a/web/service/inbound.go b/web/service/inbound.go index 74765ab3..2372e574 100644 --- a/web/service/inbound.go +++ b/web/service/inbound.go @@ -453,12 +453,10 @@ func (s *InboundService) adjustTraffics(traffics []*xray.ClientTraffic) (full_tr } continue } - // get settings clients - settings := map[string]interface{}{} - json.Unmarshal([]byte(inbound.Settings), &settings) - clients, ok := settings["clients"].([]model.Client) + // get clients + clients, err := s.getClients(inbound) needUpdate := false - if ok { + if err == nil { for client_index, client := range clients { if traffic.Email == client.Email { if client.ExpiryTime < 0 { @@ -473,7 +471,16 @@ func (s *InboundService) adjustTraffics(traffics []*xray.ClientTraffic) (full_tr } if needUpdate { - settings["clients"] = clients + settings := map[string]interface{}{} + json.Unmarshal([]byte(inbound.Settings), &settings) + + // Convert clients to []interface to update clients in settings + var clientsInterface []interface{} + for _, c := range clients { + clientsInterface = append(clientsInterface, interface{}(c)) + } + + settings["clients"] = clientsInterface modifiedSettings, err := json.MarshalIndent(settings, "", " ") if err != nil { return nil, err -- cgit v1.2.3 From 4e7ad9e6de7520a3ed510fadb9644a47237aeb53 Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Tue, 11 Apr 2023 19:23:40 +0330 Subject: bug fix --- web/service/sub.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'web/service') diff --git a/web/service/sub.go b/web/service/sub.go index d1891e14..8d96866c 100644 --- a/web/service/sub.go +++ b/web/service/sub.go @@ -300,7 +300,7 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string { if security == "xtls" { params["security"] = "xtls" - xtlsSetting, _ := stream["xtlsSettings"].(map[string]interface{}) + xtlsSetting, _ := stream["XTLSSettings"].(map[string]interface{}) alpns, _ := xtlsSetting["alpn"].([]interface{}) var alpn []string for _, a := range alpns { @@ -310,15 +310,15 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string { params["alpn"] = strings.Join(alpn, ",") } - xtlsSettings, _ := searchKey(xtlsSetting, "settings") + XTLSSettings, _ := searchKey(xtlsSetting, "settings") if xtlsSetting != nil { - if sniValue, ok := searchKey(xtlsSettings, "serverName"); ok { + if sniValue, ok := searchKey(XTLSSettings, "serverName"); ok { params["sni"], _ = sniValue.(string) } - if fpValue, ok := searchKey(xtlsSettings, "fingerprint"); ok { + if fpValue, ok := searchKey(XTLSSettings, "fingerprint"); ok { params["fp"], _ = fpValue.(string) } - if insecure, ok := searchKey(xtlsSettings, "allowInsecure"); ok { + if insecure, ok := searchKey(XTLSSettings, "allowInsecure"); ok { if insecure.(bool) { params["allowInsecure"] = "1" } @@ -444,7 +444,7 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string if security == "xtls" { params["security"] = "xtls" - xtlsSetting, _ := stream["xtlsSettings"].(map[string]interface{}) + xtlsSetting, _ := stream["XTLSSettings"].(map[string]interface{}) alpns, _ := xtlsSetting["alpn"].([]interface{}) var alpn []string for _, a := range alpns { @@ -454,15 +454,15 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string params["alpn"] = strings.Join(alpn, ",") } - xtlsSettings, _ := searchKey(xtlsSetting, "settings") + XTLSSettings, _ := searchKey(xtlsSetting, "settings") if xtlsSetting != nil { - if sniValue, ok := searchKey(xtlsSettings, "serverName"); ok { + if sniValue, ok := searchKey(XTLSSettings, "serverName"); ok { params["sni"], _ = sniValue.(string) } - if fpValue, ok := searchKey(xtlsSettings, "fingerprint"); ok { + if fpValue, ok := searchKey(XTLSSettings, "fingerprint"); ok { params["fp"], _ = fpValue.(string) } - if insecure, ok := searchKey(xtlsSettings, "allowInsecure"); ok { + if insecure, ok := searchKey(XTLSSettings, "allowInsecure"); ok { if insecure.(bool) { params["allowInsecure"] = "1" } -- cgit v1.2.3 From c38d75f3d901e7a3f550ab05aef711f95421cae6 Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Tue, 11 Apr 2023 22:30:24 +0330 Subject: reality for trojan --- web/service/sub.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'web/service') diff --git a/web/service/sub.go b/web/service/sub.go index 8d96866c..f0a5a160 100644 --- a/web/service/sub.go +++ b/web/service/sub.go @@ -442,6 +442,30 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string } } + if security == "reality" { + params["security"] = "reality" + realitySetting, _ := stream["realitySettings"].(map[string]interface{}) + realitySettings, _ := searchKey(realitySetting, "settings") + if realitySetting != nil { + if sniValue, ok := searchKey(realitySettings, "serverName"); ok { + params["sni"], _ = sniValue.(string) + } + if pbkValue, ok := searchKey(realitySettings, "publicKey"); ok { + params["pbk"], _ = pbkValue.(string) + } + if sidValue, ok := searchKey(realitySettings, "shortIds"); ok { + params["sid"], _ = sidValue.(string) + } + if fpValue, ok := searchKey(realitySettings, "fingerprint"); ok { + params["fp"], _ = fpValue.(string) + } + } + serverName, _ := realitySetting["serverName"].(string) + if serverName != "" { + address = serverName + } + } + if security == "xtls" { params["security"] = "xtls" xtlsSetting, _ := stream["XTLSSettings"].(map[string]interface{}) -- cgit v1.2.3 From c4162e3eb4573a71ca2988bc351b352af16c4def Mon Sep 17 00:00:00 2001 From: Alireza Ahmadi Date: Wed, 12 Apr 2023 10:44:07 +0200 Subject: [sub] fix reality link --- web/service/sub.go | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) (limited to 'web/service') diff --git a/web/service/sub.go b/web/service/sub.go index f0a5a160..06c597e8 100644 --- a/web/service/sub.go +++ b/web/service/sub.go @@ -271,17 +271,18 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string { if security == "reality" { params["security"] = "reality" - realitySetting, _ := stream["realitySettings"].(map[string]interface{}) - realitySettings, _ := searchKey(realitySetting, "settings") - if realitySetting != nil { - if sniValue, ok := searchKey(realitySettings, "serverName"); ok { - params["sni"], _ = sniValue.(string) + realitySettings, _ := stream["realitySettings"].(map[string]interface{}) + if realitySettings != nil { + if sniValue, ok := searchKey(realitySettings, "serverNames"); ok { + sNames, _ := sniValue.([]interface{}) + params["sni"], _ = sNames[0].(string) } if pbkValue, ok := searchKey(realitySettings, "publicKey"); ok { params["pbk"], _ = pbkValue.(string) } if sidValue, ok := searchKey(realitySettings, "shortIds"); ok { - params["sid"], _ = sidValue.(string) + shortIds, _ := sidValue.([]interface{}) + params["sid"], _ = shortIds[0].(string) } if fpValue, ok := searchKey(realitySettings, "fingerprint"); ok { params["fp"], _ = fpValue.(string) @@ -291,11 +292,6 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string { if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 { params["flow"] = clients[clientIndex].Flow } - - serverName, _ := realitySetting["serverName"].(string) - if serverName != "" { - address = serverName - } } if security == "xtls" { @@ -444,25 +440,26 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string if security == "reality" { params["security"] = "reality" - realitySetting, _ := stream["realitySettings"].(map[string]interface{}) - realitySettings, _ := searchKey(realitySetting, "settings") - if realitySetting != nil { - if sniValue, ok := searchKey(realitySettings, "serverName"); ok { - params["sni"], _ = sniValue.(string) + realitySettings, _ := stream["realitySettings"].(map[string]interface{}) + if realitySettings != nil { + if sniValue, ok := searchKey(realitySettings, "serverNames"); ok { + sNames, _ := sniValue.([]interface{}) + params["sni"], _ = sNames[0].(string) } if pbkValue, ok := searchKey(realitySettings, "publicKey"); ok { params["pbk"], _ = pbkValue.(string) } if sidValue, ok := searchKey(realitySettings, "shortIds"); ok { - params["sid"], _ = sidValue.(string) + shortIds, _ := sidValue.([]interface{}) + params["sid"], _ = shortIds[0].(string) } if fpValue, ok := searchKey(realitySettings, "fingerprint"); ok { params["fp"], _ = fpValue.(string) } } - serverName, _ := realitySetting["serverName"].(string) - if serverName != "" { - address = serverName + + if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 { + params["flow"] = clients[clientIndex].Flow } } -- cgit v1.2.3