mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-07-08 21:56:08 +00:00
feat(inbounds): add Port-with-Fallback inbound type
Adds a new "portfallback" protocol that emits as a VLESS-TLS inbound under the hood but is paired with a sidecar table of child inbounds. Panel auto-builds settings.fallbacks at Xray-config-gen time from the sidecar — each child's listen+port becomes the fallback dest, with SNI/ALPN/path/xver match criteria pulled from the row. No more typing loopback ports by hand or keeping settings.fallbacks in sync. Backend: new FallbackService (Get/SetChildren, BuildFallbacksJSON); two new routes (GET/POST /panel/api/inbounds/:id/fallbackChildren); xray.GetXrayConfig injects fallbacks for PortFallback inbounds; the inbound model emits protocol="vless" so Xray accepts the config. Frontend: PORTFALLBACK joins the protocol dropdown; selecting it shows the standard VLESS controls plus a Fallback Children table (inbound picker + per-row SNI/ALPN/path/xver). Children are loaded on edit and replaced atomically on save. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -18,8 +18,9 @@ import (
|
||||
|
||||
// InboundController handles HTTP requests related to Xray inbounds management.
|
||||
type InboundController struct {
|
||||
inboundService service.InboundService
|
||||
xrayService service.XrayService
|
||||
inboundService service.InboundService
|
||||
xrayService service.XrayService
|
||||
fallbackService service.FallbackService
|
||||
}
|
||||
|
||||
// NewInboundController creates a new InboundController and sets up its routes.
|
||||
@@ -87,6 +88,8 @@ func (a *InboundController) initRouter(g *gin.RouterGroup) {
|
||||
g.POST("/lastOnline", a.lastOnline)
|
||||
g.POST("/updateClientTraffic/:email", a.updateClientTraffic)
|
||||
g.POST("/:id/delClientByEmail/:email", a.delInboundClientByEmail)
|
||||
g.GET("/:id/fallbackChildren", a.getFallbackChildren)
|
||||
g.POST("/:id/fallbackChildren", a.setFallbackChildren)
|
||||
}
|
||||
|
||||
type CopyInboundClientsRequest struct {
|
||||
@@ -632,6 +635,42 @@ func (a *InboundController) getSubLinks(c *gin.Context) {
|
||||
jsonObj(c, links, nil)
|
||||
}
|
||||
|
||||
func (a *InboundController) getFallbackChildren(c *gin.Context) {
|
||||
id, err := strconv.Atoi(c.Param("id"))
|
||||
if err != nil {
|
||||
jsonMsg(c, I18nWeb(c, "get"), err)
|
||||
return
|
||||
}
|
||||
rows, err := a.fallbackService.GetChildren(id)
|
||||
if err != nil {
|
||||
jsonMsg(c, I18nWeb(c, "get"), err)
|
||||
return
|
||||
}
|
||||
jsonObj(c, rows, nil)
|
||||
}
|
||||
|
||||
func (a *InboundController) setFallbackChildren(c *gin.Context) {
|
||||
id, err := strconv.Atoi(c.Param("id"))
|
||||
if err != nil {
|
||||
jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err)
|
||||
return
|
||||
}
|
||||
type body struct {
|
||||
Children []service.FallbackChildInput `json:"children"`
|
||||
}
|
||||
var b body
|
||||
if err := c.ShouldBindJSON(&b); err != nil {
|
||||
jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err)
|
||||
return
|
||||
}
|
||||
if err := a.fallbackService.SetChildren(id, b.Children); err != nil {
|
||||
jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err)
|
||||
return
|
||||
}
|
||||
a.xrayService.SetToNeedRestart()
|
||||
jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.inboundUpdateSuccess"), nil)
|
||||
}
|
||||
|
||||
// getClientLinks returns the URL(s) for one client on one inbound — the same
|
||||
// string the Copy URL button copies in the panel UI. Empty array when the
|
||||
// protocol has no URL form, or when the email isn't found on the inbound.
|
||||
|
||||
Reference in New Issue
Block a user