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:
Diffstat (limited to 'web/service')
-rw-r--r--web/service/inbound.go167
-rw-r--r--web/service/server.go26
-rw-r--r--web/service/sub.go70
3 files changed, 188 insertions, 75 deletions
diff --git a/web/service/inbound.go b/web/service/inbound.go
index b7eb6789..c3f92e5a 100644
--- a/web/service/inbound.go
+++ b/web/service/inbound.go
@@ -64,28 +64,45 @@ func (s *InboundService) getClients(inbound *model.Inbound) ([]model.Client, err
return clients, nil
}
-func (s *InboundService) checkEmailsExist(emails map[string]bool, ignoreId int) (string, error) {
+func (s *InboundService) getAllEmails() ([]string, error) {
db := database.GetDB()
- var inbounds []*model.Inbound
- db = db.Model(model.Inbound{}).Where("Protocol in ?", []model.Protocol{model.VMess, model.VLESS, model.Trojan})
- if ignoreId > 0 {
- db = db.Where("id != ?", ignoreId)
- }
- db = db.Find(&inbounds)
- if db.Error != nil {
- return "", db.Error
+ var emails []string
+ err := db.Raw(`
+ SELECT JSON_EXTRACT(client.value, '$.email')
+ FROM inbounds,
+ JSON_EACH(JSON_EXTRACT(inbounds.settings, '$.clients')) AS client
+ `).Scan(&emails).Error
+
+ if err != nil {
+ return nil, err
}
+ return emails, nil
+}
- for _, inbound := range inbounds {
- clients, err := s.getClients(inbound)
- if err != nil {
- return "", err
+func (s *InboundService) contains(slice []string, str string) bool {
+ for _, s := range slice {
+ if s == str {
+ return true
}
+ }
+ return false
+}
- for _, client := range clients {
- if emails[client.Email] {
+func (s *InboundService) checkEmailsExistForClients(clients []model.Client) (string, error) {
+ allEmails, err := s.getAllEmails()
+ if err != nil {
+ return "", err
+ }
+ var emails []string
+ for _, client := range clients {
+ if client.Email != "" {
+ if s.contains(emails, client.Email) {
+ return client.Email, nil
+ }
+ if s.contains(allEmails, client.Email) {
return client.Email, nil
}
+ emails = append(emails, client.Email)
}
}
return "", nil
@@ -96,16 +113,23 @@ func (s *InboundService) checkEmailExistForInbound(inbound *model.Inbound) (stri
if err != nil {
return "", err
}
- emails := make(map[string]bool)
+ allEmails, err := s.getAllEmails()
+ if err != nil {
+ return "", err
+ }
+ var emails []string
for _, client := range clients {
if client.Email != "" {
- if emails[client.Email] {
+ if s.contains(emails, client.Email) {
+ return client.Email, nil
+ }
+ if s.contains(allEmails, client.Email) {
return client.Email, nil
}
- emails[client.Email] = true
+ emails = append(emails, client.Email)
}
}
- return s.checkEmailsExist(emails, inbound.Id)
+ return "", nil
}
func (s *InboundService) AddInbound(inbound *model.Inbound) (*model.Inbound, error) {
@@ -215,14 +239,6 @@ func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound,
return inbound, common.NewError("Port already exists:", inbound.Port)
}
- existEmail, err := s.checkEmailExistForInbound(inbound)
- if err != nil {
- return inbound, err
- }
- if existEmail != "" {
- return inbound, common.NewError("Duplicate email:", existEmail)
- }
-
oldInbound, err := s.GetInbound(inbound.Id)
if err != nil {
return inbound, err
@@ -245,8 +261,12 @@ func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound,
return inbound, db.Save(oldInbound).Error
}
-func (s *InboundService) AddInboundClient(inbound *model.Inbound) error {
- existEmail, err := s.checkEmailExistForInbound(inbound)
+func (s *InboundService) AddInboundClient(data *model.Inbound) error {
+ clients, err := s.getClients(data)
+ if err != nil {
+ return err
+ }
+ existEmail, err := s.checkEmailsExistForClients(clients)
if err != nil {
return err
}
@@ -255,29 +275,35 @@ func (s *InboundService) AddInboundClient(inbound *model.Inbound) error {
return common.NewError("Duplicate email:", existEmail)
}
- clients, err := s.getClients(inbound)
+ oldInbound, err := s.GetInbound(data.Id)
if err != nil {
return err
}
- oldInbound, err := s.GetInbound(inbound.Id)
+ var settings map[string]interface{}
+ err = json.Unmarshal([]byte(oldInbound.Settings), &settings)
if err != nil {
return err
}
- oldClients, err := s.getClients(oldInbound)
+ oldClients := settings["clients"].([]interface{})
+ var newClients []interface{}
+ for _, client := range clients {
+ newClients = append(newClients, client)
+ }
+
+ settings["clients"] = append(oldClients, newClients...)
+
+ newSettings, err := json.MarshalIndent(settings, "", " ")
if err != nil {
return err
}
- oldInbound.Settings = inbound.Settings
+ oldInbound.Settings = string(newSettings)
- if len(clients[len(clients)-1].Email) > 0 {
- s.AddClientStat(inbound.Id, &clients[len(clients)-1])
- }
- for i := len(oldClients); i < len(clients); i++ {
- if len(clients[i].Email) > 0 {
- s.AddClientStat(inbound.Id, &clients[i])
+ for _, client := range clients {
+ if len(client.Email) > 0 {
+ s.AddClientStat(data.Id, &client)
}
}
db := database.GetDB()
@@ -309,37 +335,56 @@ func (s *InboundService) DelInboundClient(inbound *model.Inbound, email string)
return db.Save(oldInbound).Error
}
-func (s *InboundService) UpdateInboundClient(inbound *model.Inbound, index int) error {
- existEmail, err := s.checkEmailExistForInbound(inbound)
+func (s *InboundService) UpdateInboundClient(data *model.Inbound, index int) error {
+ clients, err := s.getClients(data)
if err != nil {
return err
}
- if existEmail != "" {
- return common.NewError("Duplicate email:", existEmail)
- }
- clients, err := s.getClients(inbound)
+ oldInbound, err := s.GetInbound(data.Id)
if err != nil {
return err
}
- oldInbound, err := s.GetInbound(inbound.Id)
+ oldClients, err := s.getClients(oldInbound)
if err != nil {
return err
}
- oldClients, err := s.getClients(oldInbound)
+ if len(clients[0].Email) > 0 && clients[0].Email != oldClients[index].Email {
+ existEmail, err := s.checkEmailsExistForClients(clients)
+ if err != nil {
+ return err
+ }
+ if existEmail != "" {
+ return common.NewError("Duplicate email:", existEmail)
+ }
+ }
+
+ var settings map[string]interface{}
+ err = json.Unmarshal([]byte(oldInbound.Settings), &settings)
if err != nil {
return err
}
- oldInbound.Settings = inbound.Settings
+ settingsClients := settings["clients"].([]interface{})
+ var newClients []interface{}
+ newClients = append(newClients, clients[0])
+ settingsClients[index] = newClients[0]
+
+ settings["clients"] = settingsClients
+
+ newSettings, err := json.MarshalIndent(settings, "", " ")
+ if err != nil {
+ return err
+ }
+ oldInbound.Settings = string(newSettings)
db := database.GetDB()
- if len(clients[index].Email) > 0 {
+ if len(clients[0].Email) > 0 {
if len(oldClients[index].Email) > 0 {
- err = s.UpdateClientStat(oldClients[index].Email, &clients[index])
+ err = s.UpdateClientStat(oldClients[index].Email, &clients[0])
if err != nil {
return err
}
@@ -348,7 +393,7 @@ func (s *InboundService) UpdateInboundClient(inbound *model.Inbound, index int)
return err
}
} else {
- s.AddClientStat(inbound.Id, &clients[index])
+ s.AddClientStat(data.Id, &clients[0])
}
} else {
err = s.DelClientStat(db, oldClients[index].Email)
@@ -507,6 +552,16 @@ func (s *InboundService) DisableInvalidInbounds() (int64, error) {
count := result.RowsAffected
return count, err
}
+func (s *InboundService) DisableInvalidClients() (int64, error) {
+ db := database.GetDB()
+ now := time.Now().Unix() * 1000
+ result := db.Model(xray.ClientTraffic{}).
+ Where("((total > 0 and up + down >= total) or (expiry_time > 0 and expiry_time <= ?)) and enable = ?", now, true).
+ Update("enable", false)
+ err := result.Error
+ count := result.RowsAffected
+ return count, err
+}
func (s *InboundService) RemoveOrphanedTraffics() {
db := database.GetDB()
db.Exec(`
@@ -518,16 +573,6 @@ func (s *InboundService) RemoveOrphanedTraffics() {
)
`)
}
-func (s *InboundService) DisableInvalidClients() (int64, error) {
- db := database.GetDB()
- now := time.Now().Unix() * 1000
- result := db.Model(xray.ClientTraffic{}).
- Where("((total > 0 and up + down >= total) or (expiry_time > 0 and expiry_time <= ?)) and enable = ?", now, true).
- Update("enable", false)
- err := result.Error
- count := result.RowsAffected
- return count, err
-}
func (s *InboundService) AddClientStat(inboundId int, client *model.Client) error {
db := database.GetDB()
diff --git a/web/service/server.go b/web/service/server.go
index c73fce57..f2540252 100644
--- a/web/service/server.go
+++ b/web/service/server.go
@@ -390,3 +390,29 @@ func (s *ServerService) GetDb() ([]byte, error) {
return fileContents, nil
}
+
+func (s *ServerService) GetNewX25519Cert() (interface{}, error) {
+ // Run the command
+ cmd := exec.Command(xray.GetBinaryPath(), "x25519")
+ var out bytes.Buffer
+ cmd.Stdout = &out
+ err := cmd.Run()
+ if err != nil {
+ return nil, err
+ }
+
+ lines := strings.Split(out.String(), "\n")
+
+ privateKeyLine := strings.Split(lines[0], ":")
+ publicKeyLine := strings.Split(lines[1], ":")
+
+ privateKey := strings.TrimSpace(privateKeyLine[1])
+ publicKey := strings.TrimSpace(publicKeyLine[1])
+
+ keyPair := map[string]interface{}{
+ "privateKey": privateKey,
+ "publicKey": publicKey,
+ }
+
+ return keyPair, nil
+}
diff --git a/web/service/sub.go b/web/service/sub.go
index 06c597e8..3041b721 100644
--- a/web/service/sub.go
+++ b/web/service/sub.go
@@ -8,6 +8,7 @@ import (
"x-ui/database"
"x-ui/database/model"
"x-ui/logger"
+ "x-ui/xray"
"github.com/goccy/go-json"
"gorm.io/gorm"
@@ -18,12 +19,15 @@ type SubService struct {
inboundService InboundService
}
-func (s *SubService) GetSubs(subId string, host string) ([]string, error) {
+func (s *SubService) GetSubs(subId string, host string) ([]string, string, error) {
s.address = host
var result []string
+ var header string
+ var traffic xray.ClientTraffic
+ var clientTraffics []xray.ClientTraffic
inbounds, err := s.getInboundsBySubId(subId)
if err != nil {
- return nil, err
+ return nil, "", err
}
for _, inbound := range inbounds {
clients, err := s.inboundService.getClients(inbound)
@@ -37,22 +41,60 @@ func (s *SubService) GetSubs(subId string, host string) ([]string, error) {
if client.SubID == subId {
link := s.getLink(inbound, client.Email)
result = append(result, link)
+ clientTraffics = append(clientTraffics, s.getClientTraffics(inbound.ClientStats, client.Email))
+ }
+ }
+ }
+ for index, clientTraffic := range clientTraffics {
+ if index == 0 {
+ traffic.Up = clientTraffic.Up
+ traffic.Down = clientTraffic.Down
+ traffic.Total = clientTraffic.Total
+ if clientTraffic.ExpiryTime > 0 {
+ traffic.ExpiryTime = clientTraffic.ExpiryTime
+ }
+ } else {
+ traffic.Up += clientTraffic.Up
+ traffic.Down += clientTraffic.Down
+ if traffic.Total == 0 || clientTraffic.Total == 0 {
+ traffic.Total = 0
+ } else {
+ traffic.Total += clientTraffic.Total
+ }
+ if clientTraffic.ExpiryTime != traffic.ExpiryTime {
+ traffic.ExpiryTime = 0
}
}
}
- return result, nil
+ header = fmt.Sprintf("upload=%d;download=%d", traffic.Up, traffic.Down)
+ if traffic.Total > 0 {
+ header = header + fmt.Sprintf(";total=%d", traffic.Total)
+ }
+ if traffic.ExpiryTime > 0 {
+ header = header + fmt.Sprintf(";expire=%d", traffic.ExpiryTime)
+ }
+ return result, header, 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
+ err := db.Model(model.Inbound{}).Preload("ClientStats").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) getClientTraffics(traffics []xray.ClientTraffic, email string) xray.ClientTraffic {
+ for _, traffic := range traffics {
+ if traffic.Email == email {
+ return traffic
+ }
+ }
+ return xray.ClientTraffic{}
+}
+
func (s *SubService) getLink(inbound *model.Inbound, email string) string {
switch inbound.Protocol {
case "vmess":
@@ -296,7 +338,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 {
@@ -306,15 +348,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"
}
@@ -465,7 +507,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 {
@@ -475,15 +517,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"
}