|
|
@@ -130,7 +130,7 @@ func (s *InboundService) enrichClientStats(db *gorm.DB, inbounds []*model.Inboun
|
|
|
func (s *InboundService) GetInbounds(userId int) ([]*model.Inbound, error) {
|
|
|
db := database.GetDB()
|
|
|
var inbounds []*model.Inbound
|
|
|
- err := db.Model(model.Inbound{}).Preload("ClientStats").Where("user_id = ?", userId).Find(&inbounds).Error
|
|
|
+ err := db.Model(model.Inbound{}).Preload("ClientStats").Where("user_id = ?", userId).Order("id ASC").Find(&inbounds).Error
|
|
|
if err != nil && err != gorm.ErrRecordNotFound {
|
|
|
return nil, err
|
|
|
}
|
|
|
@@ -152,7 +152,7 @@ func (s *InboundService) GetInbounds(userId int) ([]*model.Inbound, error) {
|
|
|
func (s *InboundService) GetInboundsSlim(userId int) ([]*model.Inbound, error) {
|
|
|
db := database.GetDB()
|
|
|
var inbounds []*model.Inbound
|
|
|
- err := db.Model(model.Inbound{}).Preload("ClientStats").Where("user_id = ?", userId).Find(&inbounds).Error
|
|
|
+ err := db.Model(model.Inbound{}).Preload("ClientStats").Where("user_id = ?", userId).Order("id ASC").Find(&inbounds).Error
|
|
|
if err != nil && err != gorm.ErrRecordNotFound {
|
|
|
return nil, err
|
|
|
}
|
|
|
@@ -618,16 +618,14 @@ func (s *InboundService) DelInbound(id int) (bool, error) {
|
|
|
return needRestart, err
|
|
|
}
|
|
|
if !database.IsPostgres() {
|
|
|
- var maxId int
|
|
|
- if err := db.Model(&model.Inbound{}).Select("COALESCE(MAX(id), 0)").Scan(&maxId).Error; err != nil {
|
|
|
+ var count int64
|
|
|
+ if err := db.Model(&model.Inbound{}).Count(&count).Error; err != nil {
|
|
|
return needRestart, err
|
|
|
}
|
|
|
- if maxId == 0 {
|
|
|
+ if count == 0 {
|
|
|
if err := db.Exec("DELETE FROM sqlite_sequence WHERE name = ?", "inbounds").Error; err != nil {
|
|
|
return needRestart, err
|
|
|
}
|
|
|
- } else if err := db.Exec("UPDATE sqlite_sequence SET seq = ? WHERE name = ?", maxId, "inbounds").Error; err != nil {
|
|
|
- return needRestart, err
|
|
|
}
|
|
|
}
|
|
|
return needRestart, nil
|
|
|
@@ -760,8 +758,11 @@ func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound,
|
|
|
if err != nil {
|
|
|
return inbound, false, err
|
|
|
}
|
|
|
+ inbound.NodeID = oldInbound.NodeID
|
|
|
|
|
|
tag := oldInbound.Tag
|
|
|
+ oldBits := inboundTransports(oldInbound.Protocol, oldInbound.StreamSettings, oldInbound.Settings)
|
|
|
+ oldTagWasAuto := isAutoGeneratedTag(tag, oldInbound.Listen, oldInbound.Port, oldInbound.NodeID, oldBits)
|
|
|
|
|
|
db := database.GetDB()
|
|
|
tx := db.Begin()
|
|
|
@@ -849,10 +850,14 @@ func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound,
|
|
|
oldInbound.Settings = inbound.Settings
|
|
|
oldInbound.StreamSettings = inbound.StreamSettings
|
|
|
oldInbound.Sniffing = inbound.Sniffing
|
|
|
+ if oldTagWasAuto && inbound.Tag == tag {
|
|
|
+ inbound.Tag = ""
|
|
|
+ }
|
|
|
oldInbound.Tag, err = s.resolveInboundTag(inbound, inbound.Id)
|
|
|
if err != nil {
|
|
|
return inbound, false, err
|
|
|
}
|
|
|
+ inbound.Tag = oldInbound.Tag
|
|
|
|
|
|
needRestart := false
|
|
|
rt, rterr := s.runtimeFor(oldInbound)
|
|
|
@@ -1269,14 +1274,19 @@ func (s *InboundService) setRemoteTrafficLocked(nodeID int, snap *runtime.Traffi
|
|
|
Find(¢ral).Error; err != nil {
|
|
|
return false, err
|
|
|
}
|
|
|
- // Index under both stored tag and the prefix-stripped form so a snap's
|
|
|
- // bare tag resolves whether or not we rewrote it with n<id>- at create.
|
|
|
+ // Index under the stored tag and its prefix-flipped form so a snap matches
|
|
|
+ // whether the n<id>- prefix lives on the node side, the central side, or
|
|
|
+ // neither — a mismatch must never spawn a duplicate central inbound.
|
|
|
tagToCentral := make(map[string]*model.Inbound, len(central)*2)
|
|
|
prefix := nodeTagPrefix(&nodeID)
|
|
|
for i := range central {
|
|
|
tagToCentral[central[i].Tag] = ¢ral[i]
|
|
|
- if prefix != "" && strings.HasPrefix(central[i].Tag, prefix) {
|
|
|
- tagToCentral[strings.TrimPrefix(central[i].Tag, prefix)] = ¢ral[i]
|
|
|
+ if prefix != "" {
|
|
|
+ if stripped, found := strings.CutPrefix(central[i].Tag, prefix); found {
|
|
|
+ tagToCentral[stripped] = ¢ral[i]
|
|
|
+ } else {
|
|
|
+ tagToCentral[prefix+central[i].Tag] = ¢ral[i]
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -1331,6 +1341,15 @@ func (s *InboundService) setRemoteTrafficLocked(nodeID int, snap *runtime.Traffi
|
|
|
continue
|
|
|
}
|
|
|
snapTags[snapIb.Tag] = struct{}{}
|
|
|
+ // Record the prefix-flipped form too so the orphan sweep below keeps a
|
|
|
+ // central inbound whether its tag carries the n<id>- prefix or not.
|
|
|
+ if prefix != "" {
|
|
|
+ if stripped, found := strings.CutPrefix(snapIb.Tag, prefix); found {
|
|
|
+ snapTags[stripped] = struct{}{}
|
|
|
+ } else {
|
|
|
+ snapTags[prefix+snapIb.Tag] = struct{}{}
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
c, ok := tagToCentral[snapIb.Tag]
|
|
|
if !ok {
|
|
|
@@ -1756,8 +1775,8 @@ func (s *InboundService) addClientTraffic(tx *gorm.DB, traffics []*xray.ClientTr
|
|
|
}
|
|
|
dbClientTraffics := make([]*xray.ClientTraffic, 0, len(traffics))
|
|
|
err = tx.Model(xray.ClientTraffic{}).
|
|
|
- Where("email IN (?) AND inbound_id IN (?)", emails,
|
|
|
- tx.Model(&model.Inbound{}).Select("id").Where("node_id IS NULL")).
|
|
|
+ Where("email IN (?) AND inbound_id NOT IN (?)", emails,
|
|
|
+ tx.Model(&model.Inbound{}).Select("id").Where("node_id IS NOT NULL")).
|
|
|
Find(&dbClientTraffics).Error
|
|
|
if err != nil {
|
|
|
return err
|
|
|
@@ -1884,7 +1903,7 @@ func (s *InboundService) autoRenewClients(tx *gorm.DB) (bool, int64, error) {
|
|
|
|
|
|
err = tx.Model(xray.ClientTraffic{}).
|
|
|
Where("reset > 0 and expiry_time > 0 and expiry_time <= ?", now).
|
|
|
- Where("inbound_id IN (?)", tx.Model(&model.Inbound{}).Select("id").Where("node_id IS NULL")).
|
|
|
+ Where("inbound_id NOT IN (?)", tx.Model(&model.Inbound{}).Select("id").Where("node_id IS NOT NULL")).
|
|
|
Find(&traffics).Error
|
|
|
if err != nil {
|
|
|
return false, 0, err
|
|
|
@@ -3124,11 +3143,19 @@ func (s *InboundService) MigrationRequirements() {
|
|
|
Port int
|
|
|
StreamSettings []byte
|
|
|
}
|
|
|
- err = tx.Raw(`select id, port, stream_settings
|
|
|
+ externalProxyQuery := `select id, port, stream_settings
|
|
|
from inbounds
|
|
|
WHERE protocol in ('vmess','vless','trojan')
|
|
|
AND json_extract(stream_settings, '$.security') = 'tls'
|
|
|
- AND json_extract(stream_settings, '$.tlsSettings.settings.domains') IS NOT NULL`).Scan(&externalProxy).Error
|
|
|
+ AND json_extract(stream_settings, '$.tlsSettings.settings.domains') IS NOT NULL`
|
|
|
+ if database.IsPostgres() {
|
|
|
+ externalProxyQuery = `select id, port, stream_settings
|
|
|
+ from inbounds
|
|
|
+ WHERE protocol in ('vmess','vless','trojan')
|
|
|
+ AND NULLIF(stream_settings, '')::jsonb #>> '{security}' = 'tls'
|
|
|
+ AND NULLIF(stream_settings, '')::jsonb #> '{tlsSettings,settings,domains}' IS NOT NULL`
|
|
|
+ }
|
|
|
+ err = tx.Raw(externalProxyQuery).Scan(&externalProxy).Error
|
|
|
if err != nil || len(externalProxy) == 0 {
|
|
|
return
|
|
|
}
|