package service import ( "encoding/json" "s-ui/database" "s-ui/database/model" "s-ui/logger" "s-ui/util" "s-ui/util/common" "strings" "time" "gorm.io/gorm" ) type ClientService struct{} func (s *ClientService) Get(id string) (*[]model.Client, error) { if id == "" { return s.GetAll() } return s.getById(id) } func (s *ClientService) getById(id string) (*[]model.Client, error) { db := database.GetDB() var client []model.Client err := db.Model(model.Client{}).Where("id in ?", strings.Split(id, ",")).Scan(&client).Error if err != nil { return nil, err } return &client, nil } func (s *ClientService) GetAll() (*[]model.Client, error) { db := database.GetDB() var clients []model.Client err := db.Model(model.Client{}).Select("`id`, `enable`, `name`, `desc`, `group`, `inbounds`, `up`, `down`, `volume`, `expiry`").Scan(&clients).Error if err != nil { return nil, err } return &clients, nil } func (s *ClientService) Save(tx *gorm.DB, act string, data json.RawMessage, hostname string) ([]uint, error) { var err error var inboundIds []uint switch act { case "new", "edit": var client model.Client err = json.Unmarshal(data, &client) if err != nil { return nil, err } err = json.Unmarshal(client.Inbounds, &inboundIds) if err != nil { return nil, err } err = s.updateLinksWithFixedInbounds(tx, []*model.Client{&client}, inboundIds, hostname) if err != nil { return nil, err } err = tx.Save(&client).Error if err != nil { return nil, err } case "addbulk": var clients []*model.Client err = json.Unmarshal(data, &clients) if err != nil { return nil, err } err = json.Unmarshal(clients[0].Inbounds, &inboundIds) if err != nil { return nil, err } err = s.updateLinksWithFixedInbounds(tx, clients, inboundIds, hostname) if err != nil { return nil, err } err = tx.Save(clients).Error if err != nil { return nil, err } case "del": var id uint err = json.Unmarshal(data, &id) if err != nil { return nil, err } var client model.Client err = tx.Where("id = ?", id).First(&client).Error if err != nil { return nil, err } err = json.Unmarshal(client.Inbounds, &inboundIds) if err != nil { return nil, err } err = tx.Where("id = ?", id).Delete(model.Client{}).Error if err != nil { return nil, err } default: return nil, common.NewErrorf("unknown action: %s", act) } return inboundIds, nil } func (s *ClientService) updateLinksWithFixedInbounds(tx *gorm.DB, clients []*model.Client, inbounIds []uint, hostname string) error { var err error var inbounds []model.Inbound // Zero inbounds means removing local links only if len(inbounIds) > 0 { err = tx.Model(model.Inbound{}).Preload("Tls").Where("id in ? and type in ?", inbounIds, util.InboundTypeWithLink).Find(&inbounds).Error if err != nil { return err } } for index, client := range clients { var clientLinks []map[string]string err = json.Unmarshal(client.Links, &clientLinks) if err != nil { return err } newClientLinks := []map[string]string{} for _, inbound := range inbounds { newLinks := util.LinkGenerator(client.Config, &inbound, hostname) for _, newLink := range newLinks { newClientLinks = append(newClientLinks, map[string]string{ "remark": inbound.Tag, "type": "local", "uri": newLink, }) } } // Add no local links for _, clientLink := range clientLinks { if clientLink["type"] != "local" { newClientLinks = append(newClientLinks, clientLink) } } clients[index].Links, err = json.MarshalIndent(newClientLinks, "", " ") if err != nil { return err } } return nil } func (s *ClientService) UpdateClientsOnInboundAdd(tx *gorm.DB, initIds string, inboundId uint, hostname string) error { clientIds := strings.Split(initIds, ",") var clients []model.Client err := tx.Model(model.Client{}).Where("id in ?", clientIds).Find(&clients).Error if err != nil { return err } var inbound model.Inbound err = tx.Model(model.Inbound{}).Preload("Tls").Where("id = ?", inboundId).Find(&inbound).Error if err != nil { return err } for _, client := range clients { // Add inbounds var clientInbounds []uint json.Unmarshal(client.Inbounds, &clientInbounds) clientInbounds = append(clientInbounds, inboundId) client.Inbounds, err = json.MarshalIndent(clientInbounds, "", " ") if err != nil { return err } // Add links var clientLinks, newClientLinks []map[string]string json.Unmarshal(client.Links, &clientLinks) newLinks := util.LinkGenerator(client.Config, &inbound, hostname) for _, newLink := range newLinks { newClientLinks = append(newClientLinks, map[string]string{ "remark": inbound.Tag, "type": "local", "uri": newLink, }) } for _, clientLink := range clientLinks { if clientLink["remark"] != inbound.Tag { newClientLinks = append(newClientLinks, clientLink) } } client.Links, err = json.MarshalIndent(newClientLinks, "", " ") if err != nil { return err } err = tx.Save(&client).Error if err != nil { return err } } return nil } func (s *ClientService) UpdateClientsOnInboundDelete(tx *gorm.DB, id uint, tag string) error { var clients []model.Client err := tx.Table("clients"). Where("EXISTS (SELECT 1 FROM json_each(clients.inbounds) WHERE json_each.value = ?)", id). Find(&clients).Error if err != nil { return err } for _, client := range clients { // Delete inbounds var clientInbounds, newClientInbounds []uint json.Unmarshal(client.Inbounds, &clientInbounds) for _, clientInbound := range clientInbounds { if clientInbound != id { newClientInbounds = append(newClientInbounds, clientInbound) } } client.Inbounds, err = json.MarshalIndent(newClientInbounds, "", " ") if err != nil { return err } // Delete links var clientLinks, newClientLinks []map[string]string json.Unmarshal(client.Links, &clientLinks) for _, clientLink := range clientLinks { if clientLink["remark"] != tag { newClientLinks = append(newClientLinks, clientLink) } } client.Links, err = json.MarshalIndent(newClientLinks, "", " ") if err != nil { return err } err = tx.Save(&client).Error if err != nil { return err } } return nil } func (s *ClientService) UpdateLinksByInboundChange(tx *gorm.DB, inbounds *[]model.Inbound, hostname string, oldTag string) error { var err error for _, inbound := range *inbounds { var clients []model.Client err = tx.Table("clients"). Where("EXISTS (SELECT 1 FROM json_each(clients.inbounds) WHERE json_each.value = ?)", inbound.Id). Find(&clients).Error if err != nil { return err } for _, client := range clients { var clientLinks, newClientLinks []map[string]string json.Unmarshal(client.Links, &clientLinks) newLinks := util.LinkGenerator(client.Config, &inbound, hostname) for _, newLink := range newLinks { newClientLinks = append(newClientLinks, map[string]string{ "remark": inbound.Tag, "type": "local", "uri": newLink, }) } for _, clientLink := range clientLinks { if clientLink["remark"] != inbound.Tag && clientLink["remark"] != oldTag { newClientLinks = append(newClientLinks, clientLink) } } client.Links, err = json.MarshalIndent(newClientLinks, "", " ") if err != nil { return err } err = tx.Save(&client).Error if err != nil { return err } } } return nil } func (s *ClientService) DepleteClients() ([]uint, error) { var err error var clients []model.Client var changes []model.Changes var users []string var inboundIds []uint now := time.Now().Unix() db := database.GetDB() tx := db.Begin() defer func() { if err == nil { tx.Commit() } else { tx.Rollback() } }() err = tx.Model(model.Client{}).Where("enable = true AND ((volume >0 AND up+down > volume) OR (expiry > 0 AND expiry < ?))", now).Scan(&clients).Error if err != nil { return nil, err } dt := time.Now().Unix() for _, client := range clients { logger.Debug("Client ", client.Name, " is going to be disabled") users = append(users, client.Name) var userInbounds []uint json.Unmarshal(client.Inbounds, &userInbounds) inboundIds = s.uniqueAppendInboundIds(inboundIds, userInbounds) changes = append(changes, model.Changes{ DateTime: dt, Actor: "DepleteJob", Key: "clients", Action: "disable", Obj: json.RawMessage("\"" + client.Name + "\""), }) } // Save changes if len(changes) > 0 { err = tx.Model(model.Client{}).Where("enable = true AND ((volume >0 AND up+down > volume) OR (expiry > 0 AND expiry < ?))", now).Update("enable", false).Error if err != nil { return nil, err } err = tx.Model(model.Changes{}).Create(&changes).Error if err != nil { return nil, err } LastUpdate = dt } return inboundIds, nil } // avoid duplicate inboundIds func (s *ClientService) uniqueAppendInboundIds(a []uint, b []uint) []uint { m := make(map[uint]bool) for _, v := range a { m[v] = true } for _, v := range b { m[v] = true } var res []uint for k := range m { res = append(res, k) } return res }