package service import ( "encoding/json" "fmt" "os" "s-ui/database" "s-ui/database/model" "s-ui/util" "s-ui/util/common" "strings" "gorm.io/gorm" ) type InboundService struct{} func (s *InboundService) Get(ids string) (*[]map[string]interface{}, error) { if ids == "" { return s.GetAll() } return s.getById(ids) } func (s *InboundService) getById(ids string) (*[]map[string]interface{}, error) { var inbound []model.Inbound var result []map[string]interface{} db := database.GetDB() err := db.Model(model.Inbound{}).Where("id in ?", strings.Split(ids, ",")).Scan(&inbound).Error if err != nil { return nil, err } for _, inb := range inbound { inbData, err := inb.MarshalFull() if err != nil { return nil, err } result = append(result, *inbData) } return &result, nil } func (s *InboundService) GetAll() (*[]map[string]interface{}, error) { db := database.GetDB() inbounds := []model.Inbound{} err := db.Model(model.Inbound{}).Scan(&inbounds).Error if err != nil { return nil, err } var data []map[string]interface{} for _, inbound := range inbounds { var shadowtls_version uint inbData := map[string]interface{}{ "id": inbound.Id, "type": inbound.Type, "tag": inbound.Tag, "tls_id": inbound.TlsId, } if inbound.Options != nil { var restFields map[string]json.RawMessage if err := json.Unmarshal(inbound.Options, &restFields); err != nil { return nil, err } inbData["listen"] = restFields["listen"] inbData["listen_port"] = restFields["listen_port"] if inbound.Type == "shadowtls" { json.Unmarshal(restFields["version"], &shadowtls_version) } } if s.hasUser(inbound.Type) { if inbound.Type == "shadowtls" && shadowtls_version < 3 { break } users := []string{} err = db.Raw("SELECT clients.name FROM clients, json_each(clients.inbounds) as je WHERE je.value = ?", inbound.Id).Scan(&users).Error if err != nil { return nil, err } inbData["users"] = users } data = append(data, inbData) } return &data, nil } func (s *InboundService) FromIds(ids []uint) ([]*model.Inbound, error) { db := database.GetDB() inbounds := []*model.Inbound{} err := db.Model(model.Inbound{}).Where("id in ?", ids).Scan(&inbounds).Error if err != nil { return nil, err } return inbounds, nil } func (s *InboundService) Save(tx *gorm.DB, act string, data json.RawMessage, initUserIds string, hostname string) (uint, error) { var err error var id uint switch act { case "new", "edit": var inbound model.Inbound err = inbound.UnmarshalJSON(data) if err != nil { return 0, err } if inbound.TlsId > 0 { err = tx.Model(model.Tls{}).Where("id = ?", inbound.TlsId).Find(&inbound.Tls).Error if err != nil { return 0, err } } if corePtr.IsRunning() { if act == "edit" { var oldTag string err = tx.Model(model.Inbound{}).Select("tag").Where("id = ?", inbound.Id).Find(&oldTag).Error if err != nil { return 0, err } err = corePtr.RemoveInbound(oldTag) if err != nil && err != os.ErrInvalid { return 0, err } } inboundConfig, err := inbound.MarshalJSON() if err != nil { return 0, err } if act == "edit" { inboundConfig, err = s.addUsers(tx, inboundConfig, inbound.Id, inbound.Type) } else { inboundConfig, err = s.initUsers(tx, inboundConfig, initUserIds, inbound.Type) } if err != nil { return 0, err } err = corePtr.AddInbound(inboundConfig) if err != nil { return 0, err } } err = util.FillOutJson(&inbound, hostname) if err != nil { return 0, err } err = tx.Save(&inbound).Error if err != nil { return 0, err } id = inbound.Id case "del": var tag string err = json.Unmarshal(data, &tag) if err != nil { return 0, err } if corePtr.IsRunning() { err = corePtr.RemoveInbound(tag) if err != nil && err != os.ErrInvalid { return 0, err } } err = tx.Model(model.Inbound{}).Select("id").Where("tag = ?", tag).Scan(&id).Error if err != nil { return 0, err } err = tx.Where("tag = ?", tag).Delete(model.Inbound{}).Error if err != nil { return 0, err } default: return 0, common.NewErrorf("unknown action: %s", act) } return id, nil } func (s *InboundService) UpdateOutJsons(tx *gorm.DB, inboundIds []uint, hostname string) error { var inbounds []model.Inbound err := tx.Model(model.Inbound{}).Preload("Tls").Where("id in ?", inboundIds).Find(&inbounds).Error if err != nil { return err } for _, inbound := range inbounds { err = util.FillOutJson(&inbound, hostname) if err != nil { return err } err = tx.Model(model.Inbound{}).Where("tag = ?", inbound.Tag).Update("out_json", inbound.OutJson).Error if err != nil { return err } } return nil } func (s *InboundService) GetAllConfig(db *gorm.DB) ([]json.RawMessage, error) { var inboundsJson []json.RawMessage var inbounds []*model.Inbound err := db.Model(model.Inbound{}).Preload("Tls").Find(&inbounds).Error if err != nil { return nil, err } for _, inbound := range inbounds { inboundJson, err := inbound.MarshalJSON() if err != nil { return nil, err } inboundJson, err = s.addUsers(db, inboundJson, inbound.Id, inbound.Type) if err != nil { return nil, err } inboundsJson = append(inboundsJson, inboundJson) } return inboundsJson, nil } func (s *InboundService) hasUser(inboundType string) bool { switch inboundType { case "mixed", "socks", "http", "shadowsocks", "vmess", "trojan", "naive", "hysteria", "shadowtls", "tuic", "hysteria2", "vless": return true } return false } func (s *InboundService) fetchUsers(db *gorm.DB, inboundType string, condition string, inbound map[string]interface{}) ([]json.RawMessage, error) { if inboundType == "shadowtls" { version, _ := inbound["version"].(float64) if int(version) < 3 { return nil, nil } } if inboundType == "shadowsocks" { method, _ := inbound["method"].(string) if method == "2022-blake3-aes-128-gcm" { inboundType = "shadowsocks16" } } var users []string err := db.Raw( fmt.Sprintf(`SELECT json_extract(clients.config, "$.%s") FROM clients WHERE enable = true AND %s`, inboundType, condition)).Scan(&users).Error if err != nil { return nil, err } var usersJson []json.RawMessage for _, user := range users { if inboundType == "vless" && inbound["tls"] == nil { user = strings.Replace(user, "xtls-rprx-vision", "", -1) } usersJson = append(usersJson, json.RawMessage(user)) } return usersJson, nil } func (s *InboundService) addUsers(db *gorm.DB, inboundJson []byte, inboundId uint, inboundType string) ([]byte, error) { if !s.hasUser(inboundType) { return inboundJson, nil } var inbound map[string]interface{} err := json.Unmarshal(inboundJson, &inbound) if err != nil { return nil, err } condition := fmt.Sprintf("%d IN (SELECT json_each.value FROM json_each(clients.inbounds))", inboundId) inbound["users"], err = s.fetchUsers(db, inboundType, condition, inbound) if err != nil { return nil, err } return json.Marshal(inbound) } func (s *InboundService) initUsers(db *gorm.DB, inboundJson []byte, clientIds string, inboundType string) ([]byte, error) { ClientIds := strings.Split(clientIds, ",") if len(ClientIds) == 0 { return inboundJson, nil } if !s.hasUser(inboundType) { return inboundJson, nil } var inbound map[string]interface{} err := json.Unmarshal(inboundJson, &inbound) if err != nil { return nil, err } condition := fmt.Sprintf("id IN (%s)", strings.Join(ClientIds, ",")) inbound["users"], err = s.fetchUsers(db, inboundType, condition, inbound) if err != nil { return nil, err } return json.Marshal(inbound) } func (s *InboundService) RestartInbounds(tx *gorm.DB, ids []uint) error { var inbounds []*model.Inbound err := tx.Model(model.Inbound{}).Preload("Tls").Where("id in ?", ids).Find(&inbounds).Error if err != nil { return err } for _, inbound := range inbounds { err = corePtr.RemoveInbound(inbound.Tag) if err != nil && err != os.ErrInvalid { return err } inboundConfig, err := inbound.MarshalJSON() if err != nil { return err } inboundConfig, err = s.addUsers(tx, inboundConfig, inbound.Id, inbound.Type) if err != nil { return err } err = corePtr.AddInbound(inboundConfig) if err != nil { return err } } return nil }