|
@@ -790,114 +790,85 @@ func (s *InboundService) AddInbound(inbound *model.Inbound) (*model.Inbound, boo
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
db := database.GetDB()
|
|
db := database.GetDB()
|
|
|
- tx := db.Begin()
|
|
|
|
|
- markDirty := false
|
|
|
|
|
- defer func() {
|
|
|
|
|
- if err != nil {
|
|
|
|
|
- tx.Rollback()
|
|
|
|
|
- return
|
|
|
|
|
|
|
+ needRestart := false
|
|
|
|
|
+ var postCommitApply func()
|
|
|
|
|
+ err = db.Transaction(func(tx *gorm.DB) error {
|
|
|
|
|
+ markDirty := false
|
|
|
|
|
+ if err := tx.Omit("ClientStats").Save(inbound).Error; err != nil {
|
|
|
|
|
+ return err
|
|
|
}
|
|
}
|
|
|
- if markDirty && inbound.NodeID != nil {
|
|
|
|
|
- if dErr := (&NodeService{}).MarkNodeDirtyTx(tx, *inbound.NodeID); dErr != nil {
|
|
|
|
|
- err = dErr
|
|
|
|
|
- tx.Rollback()
|
|
|
|
|
- return
|
|
|
|
|
|
|
+ for i := range inbound.ClientStats {
|
|
|
|
|
+ if inbound.ClientStats[i].Email == "" {
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+ inbound.ClientStats[i].Id = 0
|
|
|
|
|
+ inbound.ClientStats[i].InboundId = inbound.Id
|
|
|
|
|
+ if err := tx.Clauses(clause.OnConflict{
|
|
|
|
|
+ Columns: []clause.Column{{Name: "email"}},
|
|
|
|
|
+ DoNothing: true,
|
|
|
|
|
+ }).Create(&inbound.ClientStats[i]).Error; err != nil {
|
|
|
|
|
+ return err
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- tx.Commit()
|
|
|
|
|
- }()
|
|
|
|
|
-
|
|
|
|
|
- // Omit the ClientStats has-many association: GORM's cascade would INSERT
|
|
|
|
|
- // those rows with an ON CONFLICT target on the primary key only, which
|
|
|
|
|
- // collides with the globally-unique client_traffics.email when an imported
|
|
|
|
|
- // inbound carries clients that another inbound already created (e.g.
|
|
|
|
|
- // importing two inbounds that share the same clients). We insert the stats
|
|
|
|
|
- // ourselves below with the same email-conflict guard AddClientStat uses.
|
|
|
|
|
- err = tx.Omit("ClientStats").Save(inbound).Error
|
|
|
|
|
- if err != nil {
|
|
|
|
|
- return inbound, false, err
|
|
|
|
|
- }
|
|
|
|
|
- // Imported stats first, so their traffic counters survive; emails that
|
|
|
|
|
- // already own a (shared) row are skipped instead of tripping the unique
|
|
|
|
|
- // constraint.
|
|
|
|
|
- for i := range inbound.ClientStats {
|
|
|
|
|
- if inbound.ClientStats[i].Email == "" {
|
|
|
|
|
- continue
|
|
|
|
|
- }
|
|
|
|
|
- inbound.ClientStats[i].Id = 0
|
|
|
|
|
- inbound.ClientStats[i].InboundId = inbound.Id
|
|
|
|
|
- if err = tx.Clauses(clause.OnConflict{
|
|
|
|
|
- Columns: []clause.Column{{Name: "email"}},
|
|
|
|
|
- DoNothing: true,
|
|
|
|
|
- }).Create(&inbound.ClientStats[i]).Error; err != nil {
|
|
|
|
|
- return inbound, false, err
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- // Then make sure every client has a stats row. AddClientStat is a no-op
|
|
|
|
|
- // where one exists (including the rows just inserted), and fills the gap
|
|
|
|
|
- // for clients an import payload didn't carry stats for.
|
|
|
|
|
- for _, client := range clients {
|
|
|
|
|
- if err = s.AddClientStat(tx, inbound.Id, &client); err != nil {
|
|
|
|
|
- return inbound, false, err
|
|
|
|
|
|
|
+ for _, client := range clients {
|
|
|
|
|
+ if err := s.AddClientStat(tx, inbound.Id, &client); err != nil {
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if err = s.clientService.SyncInbound(tx, inbound.Id, clients); err != nil {
|
|
|
|
|
- return inbound, false, err
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // Legacy import: an inbound exported from a build that predated the hosts
|
|
|
|
|
- // table carries its external proxies inline in streamSettings.externalProxy.
|
|
|
|
|
- // The startup migration that converts those to host rows runs once and is
|
|
|
|
|
- // gated off afterwards, so it never sees a freshly imported inbound —
|
|
|
|
|
- // reproduce it here. No-op for inbounds without externalProxy (everything the
|
|
|
|
|
- // current UI builds), so this only fires on such imports.
|
|
|
|
|
- if _, err = database.CreateHostsFromExternalProxy(tx, inbound.Id, inbound.StreamSettings); err != nil {
|
|
|
|
|
- return inbound, false, err
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // Before the deferred commit, so a node in "selected" sync mode cannot
|
|
|
|
|
- // sweep the new central row in the gap before its tag is allowed.
|
|
|
|
|
- if inbound.NodeID != nil {
|
|
|
|
|
- if aErr := (&NodeService{}).EnsureInboundTagAllowed(*inbound.NodeID, inbound.Tag); aErr != nil {
|
|
|
|
|
- logger.Warning("allow inbound tag on node failed:", aErr)
|
|
|
|
|
|
|
+ if err := s.clientService.SyncInbound(tx, inbound.Id, clients); err != nil {
|
|
|
|
|
+ return err
|
|
|
}
|
|
}
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- needRestart := false
|
|
|
|
|
- if inbound.Enable {
|
|
|
|
|
- rt, push, dirty, perr := s.nodePushPlan(inbound)
|
|
|
|
|
- if perr != nil {
|
|
|
|
|
- err = perr
|
|
|
|
|
- return inbound, false, err
|
|
|
|
|
|
|
+ if _, err := database.CreateHostsFromExternalProxy(tx, inbound.Id, inbound.StreamSettings); err != nil {
|
|
|
|
|
+ return err
|
|
|
}
|
|
}
|
|
|
- if dirty {
|
|
|
|
|
- markDirty = true
|
|
|
|
|
|
|
+ if inbound.NodeID != nil {
|
|
|
|
|
+ nodeID := *inbound.NodeID
|
|
|
|
|
+ if err := (&NodeService{}).EnsureInboundTagAllowedTx(tx, nodeID, inbound.Tag); err != nil {
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
- if push {
|
|
|
|
|
- payload := inbound
|
|
|
|
|
- pushable := true
|
|
|
|
|
- if inbound.NodeID == nil && inbound.Protocol == model.MTProto {
|
|
|
|
|
- if built, bErr := s.buildRuntimeInboundForAPI(tx, inbound); bErr == nil {
|
|
|
|
|
- payload = built
|
|
|
|
|
- } else {
|
|
|
|
|
- logger.Debug("Unable to prepare runtime inbound config:", bErr)
|
|
|
|
|
- pushable = false
|
|
|
|
|
|
|
+ if inbound.Enable {
|
|
|
|
|
+ if inbound.NodeID != nil {
|
|
|
|
|
+ markDirty = true
|
|
|
|
|
+ } else {
|
|
|
|
|
+ rt, push, _, perr := s.nodePushPlan(inbound)
|
|
|
|
|
+ if perr != nil {
|
|
|
|
|
+ return perr
|
|
|
}
|
|
}
|
|
|
- }
|
|
|
|
|
- if pushable {
|
|
|
|
|
- if err1 := rt.AddInbound(context.Background(), payload); err1 == nil {
|
|
|
|
|
- logger.Debug("New inbound added on", rt.Name(), ":", inbound.Tag)
|
|
|
|
|
- } else {
|
|
|
|
|
- logger.Debug("Unable to add inbound on", rt.Name(), ":", err1)
|
|
|
|
|
- if inbound.NodeID != nil {
|
|
|
|
|
- markDirty = true
|
|
|
|
|
- } else if inbound.Protocol != model.MTProto {
|
|
|
|
|
- needRestart = true
|
|
|
|
|
|
|
+ if push {
|
|
|
|
|
+ payload := inbound
|
|
|
|
|
+ pushable := true
|
|
|
|
|
+ if inbound.Protocol == model.MTProto {
|
|
|
|
|
+ if built, bErr := s.buildRuntimeInboundForAPI(tx, inbound); bErr == nil {
|
|
|
|
|
+ payload = built
|
|
|
|
|
+ } else {
|
|
|
|
|
+ logger.Debug("Unable to prepare runtime inbound config:", bErr)
|
|
|
|
|
+ pushable = false
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if pushable {
|
|
|
|
|
+ postCommitApply = func() {
|
|
|
|
|
+ if err1 := rt.AddInbound(context.Background(), payload); err1 == nil {
|
|
|
|
|
+ logger.Debug("New inbound added on", rt.Name(), ":", inbound.Tag)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ logger.Debug("Unable to add inbound on", rt.Name(), ":", err1)
|
|
|
|
|
+ needRestart = true
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+ if markDirty && inbound.NodeID != nil {
|
|
|
|
|
+ return (&NodeService{}).MarkNodeDirtyTx(tx, *inbound.NodeID)
|
|
|
|
|
+ }
|
|
|
|
|
+ return nil
|
|
|
|
|
+ })
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return inbound, false, err
|
|
|
|
|
+ }
|
|
|
|
|
+ if postCommitApply != nil {
|
|
|
|
|
+ postCommitApply()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// A routed mtproto inbound is not an Xray inbound itself, so the runtime
|
|
// A routed mtproto inbound is not an Xray inbound itself, so the runtime
|
|
@@ -914,31 +885,41 @@ func (s *InboundService) DelInbound(id int) (bool, error) {
|
|
|
db := database.GetDB()
|
|
db := database.GetDB()
|
|
|
|
|
|
|
|
needRestart := false
|
|
needRestart := false
|
|
|
- markDirty := false
|
|
|
|
|
|
|
+ var postCommitApply func()
|
|
|
var ib model.Inbound
|
|
var ib model.Inbound
|
|
|
loadErr := db.Model(model.Inbound{}).Where("id = ?", id).First(&ib).Error
|
|
loadErr := db.Model(model.Inbound{}).Where("id = ?", id).First(&ib).Error
|
|
|
if loadErr == nil {
|
|
if loadErr == nil {
|
|
|
shouldPushToRuntime := ib.NodeID != nil || ib.Enable
|
|
shouldPushToRuntime := ib.NodeID != nil || ib.Enable
|
|
|
if shouldPushToRuntime {
|
|
if shouldPushToRuntime {
|
|
|
- rt, push, dirty, perr := s.nodePushPlan(&ib)
|
|
|
|
|
- if perr != nil {
|
|
|
|
|
- logger.Warning("DelInbound: node lookup failed, deleting central row anyway:", perr)
|
|
|
|
|
- markDirty = true
|
|
|
|
|
- } else if push {
|
|
|
|
|
- if err1 := rt.DelInbound(context.Background(), &ib); err1 == nil {
|
|
|
|
|
- logger.Debug("Inbound deleted on", rt.Name(), ":", ib.Tag)
|
|
|
|
|
- } else {
|
|
|
|
|
- logger.Warning("DelInbound on", rt.Name(), "failed, deleting central row anyway:", err1)
|
|
|
|
|
- if ib.NodeID == nil {
|
|
|
|
|
- needRestart = true
|
|
|
|
|
- } else {
|
|
|
|
|
- markDirty = true
|
|
|
|
|
|
|
+ if ib.NodeID != nil {
|
|
|
|
|
+ rt, push, _, perr := s.nodePushPlan(&ib)
|
|
|
|
|
+ if perr != nil {
|
|
|
|
|
+ logger.Warning("DelInbound: node runtime lookup failed, deleting central row anyway:", perr)
|
|
|
|
|
+ } else if push {
|
|
|
|
|
+ postCommitApply = func() {
|
|
|
|
|
+ if err1 := rt.DelInbound(context.Background(), &ib); err1 == nil {
|
|
|
|
|
+ logger.Debug("Inbound deleted on", rt.Name(), ":", ib.Tag)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ logger.Warning("DelInbound on", rt.Name(), "failed after commit:", err1)
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- } else if ib.NodeID == nil {
|
|
|
|
|
- needRestart = true
|
|
|
|
|
- } else if dirty {
|
|
|
|
|
- markDirty = true
|
|
|
|
|
|
|
+ } else {
|
|
|
|
|
+ rt, push, _, perr := s.nodePushPlan(&ib)
|
|
|
|
|
+ if perr != nil {
|
|
|
|
|
+ logger.Warning("DelInbound: runtime lookup failed, deleting central row anyway:", perr)
|
|
|
|
|
+ } else if push {
|
|
|
|
|
+ postCommitApply = func() {
|
|
|
|
|
+ if err1 := rt.DelInbound(context.Background(), &ib); err1 == nil {
|
|
|
|
|
+ logger.Debug("Inbound deleted on", rt.Name(), ":", ib.Tag)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ logger.Warning("DelInbound on", rt.Name(), "failed after commit:", err1)
|
|
|
|
|
+ needRestart = true
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ needRestart = true
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
} else {
|
|
} else {
|
|
|
logger.Debug("DelInbound: skipping runtime push for disabled local inbound id:", id)
|
|
logger.Debug("DelInbound: skipping runtime push for disabled local inbound id:", id)
|
|
@@ -947,35 +928,33 @@ func (s *InboundService) DelInbound(id int) (bool, error) {
|
|
|
logger.Debug("DelInbound: inbound not found, id:", id)
|
|
logger.Debug("DelInbound: inbound not found, id:", id)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if err := s.clientService.DetachInbound(db, id); err != nil {
|
|
|
|
|
- return false, err
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // Drop the deleted inbound's tag from any routing rules / loopback outbounds
|
|
|
|
|
- // in xrayTemplateConfig so they don't point at a tag that no longer exists.
|
|
|
|
|
- if loadErr == nil && ib.Tag != "" {
|
|
|
|
|
- if routingChanged, syncErr := (&XraySettingService{}).RemoveInboundTagReferences(ib.Tag); syncErr != nil {
|
|
|
|
|
- logger.Warning("DelInbound: sync routing on inbound delete failed:", syncErr)
|
|
|
|
|
- } else if routingChanged {
|
|
|
|
|
- needRestart = true
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
if err := db.Transaction(func(tx *gorm.DB) error {
|
|
if err := db.Transaction(func(tx *gorm.DB) error {
|
|
|
|
|
+ if err := s.clientService.DetachInbound(tx, id); err != nil {
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
if err := tx.Delete(model.Inbound{}, id).Error; err != nil {
|
|
if err := tx.Delete(model.Inbound{}, id).Error; err != nil {
|
|
|
return err
|
|
return err
|
|
|
}
|
|
}
|
|
|
- // Hosts have no hard FK; drop the inbound's hosts alongside it.
|
|
|
|
|
if err := tx.Where("inbound_id = ?", id).Delete(&model.Host{}).Error; err != nil {
|
|
if err := tx.Where("inbound_id = ?", id).Delete(&model.Host{}).Error; err != nil {
|
|
|
return err
|
|
return err
|
|
|
}
|
|
}
|
|
|
- if markDirty && ib.NodeID != nil {
|
|
|
|
|
|
|
+ if loadErr == nil && ib.NodeID != nil {
|
|
|
return (&NodeService{}).MarkNodeDirtyTx(tx, *ib.NodeID)
|
|
return (&NodeService{}).MarkNodeDirtyTx(tx, *ib.NodeID)
|
|
|
}
|
|
}
|
|
|
return nil
|
|
return nil
|
|
|
}); err != nil {
|
|
}); err != nil {
|
|
|
return needRestart, err
|
|
return needRestart, err
|
|
|
}
|
|
}
|
|
|
|
|
+ if postCommitApply != nil {
|
|
|
|
|
+ postCommitApply()
|
|
|
|
|
+ }
|
|
|
|
|
+ if loadErr == nil && ib.Tag != "" {
|
|
|
|
|
+ if routingChanged, syncErr := (&XraySettingService{}).RemoveInboundTagReferences(ib.Tag); syncErr != nil {
|
|
|
|
|
+ logger.Warning("DelInbound: sync routing on inbound delete failed:", syncErr)
|
|
|
|
|
+ } else if routingChanged {
|
|
|
|
|
+ needRestart = true
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
if !database.IsPostgres() {
|
|
if !database.IsPostgres() {
|
|
|
var count int64
|
|
var count int64
|
|
|
if err := db.Model(&model.Inbound{}).Count(&count).Error; err != nil {
|
|
if err := db.Model(&model.Inbound{}).Count(&count).Error; err != nil {
|
|
@@ -1152,18 +1131,8 @@ func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound,
|
|
|
oldTagWasAuto := isAutoGeneratedTag(tag, oldInbound.Port, oldInbound.NodeID, oldBits)
|
|
oldTagWasAuto := isAutoGeneratedTag(tag, oldInbound.Port, oldInbound.NodeID, oldBits)
|
|
|
|
|
|
|
|
needRestart := false
|
|
needRestart := false
|
|
|
|
|
+ var postCommitApply func()
|
|
|
|
|
|
|
|
- // Persist the client-stat sync, settings munging, runtime push and inbound
|
|
|
|
|
- // save as one transaction routed through the serial traffic writer, so it
|
|
|
|
|
- // never runs concurrently with the @every 5s traffic poll. Both touch
|
|
|
|
|
- // client_traffics and inbounds in opposite order, which Postgres aborts as a
|
|
|
|
|
- // deadlock (40P01); serializing removes the contention (runSerializedTx).
|
|
|
|
|
- //
|
|
|
|
|
- // The runtime push stays inside the transaction here (unlike the client-edit
|
|
|
|
|
- // paths that apply it after commit): EnsureInboundTagAllowed must reach the
|
|
|
|
|
- // node before the central row is committed, or a "selected"-mode node would
|
|
|
|
|
- // sweep the renamed inbound on its next pull. Inbound edits are rare, so
|
|
|
|
|
- // holding the writer across the node call is an acceptable trade.
|
|
|
|
|
txErr := runSerializedTx(func(tx *gorm.DB) error {
|
|
txErr := runSerializedTx(func(tx *gorm.DB) error {
|
|
|
if err := s.updateClientTraffics(tx, oldInbound, inbound); err != nil {
|
|
if err := s.updateClientTraffics(tx, oldInbound, inbound); err != nil {
|
|
|
return err
|
|
return err
|
|
@@ -1277,11 +1246,11 @@ func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound,
|
|
|
oldInbound.Tag = resolvedTag
|
|
oldInbound.Tag = resolvedTag
|
|
|
inbound.Tag = oldInbound.Tag
|
|
inbound.Tag = oldInbound.Tag
|
|
|
|
|
|
|
|
- rt, push, _, perr := s.nodePushPlan(oldInbound)
|
|
|
|
|
- if perr != nil {
|
|
|
|
|
- return perr
|
|
|
|
|
- }
|
|
|
|
|
if oldInbound.NodeID == nil {
|
|
if oldInbound.NodeID == nil {
|
|
|
|
|
+ rt, push, _, perr := s.nodePushPlan(oldInbound)
|
|
|
|
|
+ if perr != nil {
|
|
|
|
|
+ return perr
|
|
|
|
|
+ }
|
|
|
if !push {
|
|
if !push {
|
|
|
needRestart = true
|
|
needRestart = true
|
|
|
} else if oldProtocol == model.MTProto || oldInbound.Protocol == model.MTProto {
|
|
} else if oldProtocol == model.MTProto || oldInbound.Protocol == model.MTProto {
|
|
@@ -1311,15 +1280,23 @@ func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound,
|
|
|
} else {
|
|
} else {
|
|
|
oldSnapshot := *oldInbound
|
|
oldSnapshot := *oldInbound
|
|
|
oldSnapshot.Tag = tag
|
|
oldSnapshot.Tag = tag
|
|
|
- if err2 := rt.DelInbound(context.Background(), &oldSnapshot); err2 == nil {
|
|
|
|
|
- logger.Debug("Old inbound deleted on", rt.Name(), ":", tag)
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ var runtimeInbound *model.Inbound
|
|
|
if inbound.Enable {
|
|
if inbound.Enable {
|
|
|
- runtimeInbound, err2 := s.buildRuntimeInboundForAPI(tx, oldInbound)
|
|
|
|
|
|
|
+ var err2 error
|
|
|
|
|
+ runtimeInbound, err2 = s.buildRuntimeInboundForAPI(tx, oldInbound)
|
|
|
if err2 != nil {
|
|
if err2 != nil {
|
|
|
logger.Debug("Unable to prepare runtime inbound config:", err2)
|
|
logger.Debug("Unable to prepare runtime inbound config:", err2)
|
|
|
needRestart = true
|
|
needRestart = true
|
|
|
- } else if err2 := rt.AddInbound(context.Background(), runtimeInbound); err2 == nil {
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ postCommitApply = func() {
|
|
|
|
|
+ if err2 := rt.DelInbound(context.Background(), &oldSnapshot); err2 == nil {
|
|
|
|
|
+ logger.Debug("Old inbound deleted on", rt.Name(), ":", tag)
|
|
|
|
|
+ }
|
|
|
|
|
+ if runtimeInbound == nil {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ if err2 := rt.AddInbound(context.Background(), runtimeInbound); err2 == nil {
|
|
|
logger.Debug("Updated inbound added on", rt.Name(), ":", oldInbound.Tag)
|
|
logger.Debug("Updated inbound added on", rt.Name(), ":", oldInbound.Tag)
|
|
|
} else {
|
|
} else {
|
|
|
logger.Debug("Unable to update inbound on", rt.Name(), ":", err2)
|
|
logger.Debug("Unable to update inbound on", rt.Name(), ":", err2)
|
|
@@ -1327,24 +1304,10 @@ func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound,
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- } else if push {
|
|
|
|
|
- oldSnapshot := *oldInbound
|
|
|
|
|
- oldSnapshot.Tag = tag
|
|
|
|
|
- if !inbound.Enable {
|
|
|
|
|
- if err2 := rt.DelInbound(context.Background(), &oldSnapshot); err2 != nil {
|
|
|
|
|
- logger.Warning("Unable to disable inbound on", rt.Name(), ":", err2)
|
|
|
|
|
- }
|
|
|
|
|
- } else if err2 := rt.UpdateInbound(context.Background(), &oldSnapshot, oldInbound); err2 != nil {
|
|
|
|
|
- logger.Warning("Unable to update inbound on", rt.Name(), ":", err2)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // A rename must allow the new tag before the inbound row is committed, or a
|
|
|
|
|
- // node in "selected" sync mode would sweep the renamed central row on the
|
|
|
|
|
- // next pull.
|
|
|
|
|
- if oldInbound.NodeID != nil {
|
|
|
|
|
- if aErr := (&NodeService{}).EnsureInboundTagAllowed(*oldInbound.NodeID, oldInbound.Tag); aErr != nil {
|
|
|
|
|
- logger.Warning("allow inbound tag on node failed:", aErr)
|
|
|
|
|
|
|
+ } else {
|
|
|
|
|
+ nodeID := *oldInbound.NodeID
|
|
|
|
|
+ if err := (&NodeService{}).EnsureInboundTagAllowedTx(tx, nodeID, oldInbound.Tag); err != nil {
|
|
|
|
|
+ return err
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -1374,6 +1337,9 @@ func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound,
|
|
|
if txErr != nil {
|
|
if txErr != nil {
|
|
|
return inbound, false, txErr
|
|
return inbound, false, txErr
|
|
|
}
|
|
}
|
|
|
|
|
+ if postCommitApply != nil {
|
|
|
|
|
+ postCommitApply()
|
|
|
|
|
+ }
|
|
|
// After the rename is committed, point any routing rules / loopback outbounds
|
|
// After the rename is committed, point any routing rules / loopback outbounds
|
|
|
// in xrayTemplateConfig at the new tag (oldInbound.Tag now holds the resolved
|
|
// in xrayTemplateConfig at the new tag (oldInbound.Tag now holds the resolved
|
|
|
// new tag; tag holds the pre-edit one). Done post-commit so a sync failure
|
|
// new tag; tag holds the pre-edit one). Done post-commit so a sync failure
|