feat(sub): per-client external links and remote subscriptions

Add a Links tab to the client form for attaching third-party share
links and remote subscription URLs per client. They are merged into
the client's raw/JSON/Clash subscription output: links are emitted
verbatim and parsed for JSON/Clash; subscription URLs are fetched
(cached, with a short timeout) and their configs merged in.

i18n keys added across all 13 locales.
This commit is contained in:
MHSanaei
2026-06-14 20:57:14 +02:00
parent 7c2598fae9
commit dcb923b4a1
33 changed files with 1204 additions and 28 deletions
+26 -1
View File
@@ -59,6 +59,7 @@ func (a *ClientController) initRouter(g *gin.RouterGroup) {
g.POST("/del/:email", a.delete)
g.POST("/:email/attach", a.attach)
g.POST("/:email/detach", a.detach)
g.POST("/:email/externalLinks", a.setExternalLinks)
g.POST("/resetAllTraffics", a.resetAllTraffics)
g.POST("/delDepleted", a.delDepleted)
g.POST("/bulkAdjust", a.bulkAdjust)
@@ -112,6 +113,11 @@ func (a *ClientController) get(c *gin.Context) {
jsonMsg(c, I18nWeb(c, "get"), err)
return
}
externalLinks, err := a.clientService.GetExternalLinksForRecord(rec.Id)
if err != nil {
jsonMsg(c, I18nWeb(c, "get"), err)
return
}
flow, err := a.clientService.EffectiveFlow(nil, rec.Id)
if err != nil {
jsonMsg(c, I18nWeb(c, "get"), err)
@@ -125,7 +131,7 @@ func (a *ClientController) get(c *gin.Context) {
if t, tErr := a.inboundService.GetClientTrafficByEmail(email); tErr == nil && t != nil {
usedTraffic = t.Up + t.Down
}
jsonObj(c, gin.H{"client": rec, "inboundIds": inboundIds, "usedTraffic": usedTraffic}, nil)
jsonObj(c, gin.H{"client": rec, "inboundIds": inboundIds, "externalLinks": externalLinks, "usedTraffic": usedTraffic}, nil)
}
func (a *ClientController) create(c *gin.Context) {
@@ -185,6 +191,10 @@ type attachDetachBody struct {
InboundIds []int `json:"inboundIds"`
}
type externalLinksBody struct {
ExternalLinks []service.ExternalLinkInput `json:"externalLinks"`
}
func (a *ClientController) attach(c *gin.Context) {
email := c.Param("email")
var body attachDetachBody
@@ -204,6 +214,21 @@ func (a *ClientController) attach(c *gin.Context) {
notifyClientsChanged()
}
func (a *ClientController) setExternalLinks(c *gin.Context) {
email := c.Param("email")
var body externalLinksBody
if err := c.ShouldBindJSON(&body); err != nil {
jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err)
return
}
if err := a.clientService.SetExternalLinksByEmail(email, body.ExternalLinks); err != nil {
jsonMsg(c, I18nWeb(c, "somethingWentWrong"), err)
return
}
jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.inboundClientUpdateSuccess"), nil)
notifyClientsChanged()
}
func (a *ClientController) resetAllTraffics(c *gin.Context) {
needRestart, err := a.clientService.ResetAllTraffics()
if err != nil {