CI / go-test (push) Canceled after 0s
CI / postgres-durable-first (push) Canceled after 0s
CI / codegen (push) Canceled after 0s
CI / govulncheck (push) Canceled after 0s
CI / race (push) Canceled after 0s
CI / fuzz-smoke (push) Canceled after 0s
CI / golangci (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CodeQL Advanced / Analyze (go) (push) Canceled after 0s
CodeQL Advanced / Analyze (actions) (push) Canceled after 0s
CodeQL Advanced / Analyze (javascript-typescript) (push) Canceled after 0s
Docs CI / build (push) Canceled after 0s
Docs Deploy (GitHub Pages) / build (push) Canceled after 0s
Release 3X-UI / build (386) (push) Canceled after 0s
Release 3X-UI / build (amd64) (push) Canceled after 0s
Release 3X-UI / build (arm64) (push) Canceled after 0s
Release 3X-UI / build (armv5) (push) Canceled after 0s
Release 3X-UI / build (armv6) (push) Canceled after 0s
Release 3X-UI / build (armv7) (push) Canceled after 0s
Release 3X-UI / build (s390x) (push) Canceled after 0s
Release 3X-UI / Build for Windows (amd64) (push) Canceled after 0s
Deploy Smoke Tests / noninteractive-install (ubuntu-24.04-arm) (push) Canceled after 0s
Deploy Smoke Tests / noninteractive-install (ubuntu-latest) (push) Canceled after 0s
Deploy Smoke Tests / release-tag-install (ubuntu-24.04-arm) (push) Canceled after 0s
Deploy Smoke Tests / release-tag-install (ubuntu-latest) (push) Canceled after 0s
Docs Deploy (GitHub Pages) / deploy (push) Canceled after 0s
Release 3X-UI / Publish rolling dev release (push) Canceled after 0s
131 lines
4.4 KiB
Go
131 lines
4.4 KiB
Go
package sub
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/mhsanaei/3x-ui/v3/internal/database/model"
|
|
)
|
|
|
|
// ShareEndpoint is one render target for a subscription link: the address/port
|
|
// to dial plus an optional set of TLS overrides. It unifies two sources behind
|
|
// one type so the per-protocol link builders don't branch on where the override
|
|
// came from:
|
|
//
|
|
// - a legacy externalProxy entry (Phase 1): the source map is carried in `ep`
|
|
// and applied through the unchanged applyExternalProxyTLS* helpers, so the
|
|
// emitted link is byte-identical to the pre-refactor output;
|
|
// - a Host row (Phase 4): leaves `ep` nil and uses typed override fields.
|
|
//
|
|
// ForceTls is the verbatim "same"/"tls"/"none"/"" value — never pre-resolved,
|
|
// because three behaviors branch on the raw string (keep-base, obj["tls"]
|
|
// rewrite, none-strip).
|
|
type ShareEndpoint struct {
|
|
Address string
|
|
Port int
|
|
Remark string // extra remark slot fed to genRemark, not a rendered remark
|
|
ForceTls string
|
|
|
|
// ep is the source externalProxy entry. nil for host/default endpoints.
|
|
ep map[string]any
|
|
}
|
|
|
|
// externalProxyToEndpoint maps one externalProxy entry to an endpoint that
|
|
// carries the entry for delegated, provably-identical TLS application.
|
|
func externalProxyToEndpoint(ep map[string]any) ShareEndpoint {
|
|
e := ShareEndpoint{ep: ep}
|
|
e.Address, _ = ep["dest"].(string)
|
|
if p, ok := ep["port"].(float64); ok {
|
|
e.Port = int(p)
|
|
}
|
|
e.Remark, _ = ep["remark"].(string)
|
|
e.ForceTls, _ = ep["forceTls"].(string)
|
|
return e
|
|
}
|
|
|
|
// inboundDefaultEndpoint is the endpoint for an inbound's own resolved
|
|
// address/port (the no-externalProxy default). forceTls "same" keeps the base
|
|
// security; no per-endpoint TLS override.
|
|
func (s *SubService) inboundDefaultEndpoint(inbound *model.Inbound) ShareEndpoint {
|
|
return ShareEndpoint{
|
|
Address: s.resolveInboundAddress(inbound),
|
|
Port: inbound.Port,
|
|
ForceTls: "same",
|
|
}
|
|
}
|
|
|
|
// applyEndpointTLSParams applies an endpoint's TLS overrides onto a URL-param
|
|
// map. External-proxy endpoints delegate to the unchanged helper; host/default
|
|
// endpoints carry no override yet (Phase 4).
|
|
func applyEndpointTLSParams(e ShareEndpoint, params map[string]string, security string) {
|
|
if e.ep != nil {
|
|
applyExternalProxyTLSParams(e.ep, params, security)
|
|
}
|
|
}
|
|
|
|
// applyEndpointTLSObj is applyEndpointTLSParams for the VMess base64-JSON form.
|
|
func applyEndpointTLSObj(e ShareEndpoint, obj map[string]any, security string) {
|
|
if e.ep != nil {
|
|
applyExternalProxyTLSObj(e.ep, obj, security)
|
|
}
|
|
}
|
|
|
|
// buildEndpointLinks renders one URL-param link per endpoint (vless/trojan/ss).
|
|
// securityToApply mirrors the legacy externalProxy loop: "same" keeps the base
|
|
// security, otherwise the endpoint's forceTls wins; "none" strips TLS hint
|
|
// fields at emit time.
|
|
func (s *SubService) buildEndpointLinks(
|
|
eps []ShareEndpoint,
|
|
params map[string]string,
|
|
baseSecurity string,
|
|
makeLink func(e ShareEndpoint) string,
|
|
makeRemark func(e ShareEndpoint) string,
|
|
) string {
|
|
links := make([]string, 0, len(eps))
|
|
for _, e := range eps {
|
|
securityToApply := baseSecurity
|
|
if e.ForceTls != "same" {
|
|
securityToApply = e.ForceTls
|
|
}
|
|
nextParams := cloneStringMap(params)
|
|
applyEndpointTLSParams(e, nextParams, securityToApply)
|
|
applyEndpointRealityParams(e, nextParams, securityToApply)
|
|
applyEndpointHostPath(e, nextParams)
|
|
applyEndpointFinalMask(e, nextParams)
|
|
applyEndpointAllowInsecure(e, nextParams, securityToApply)
|
|
links = append(links, buildLinkWithParamsAndSecurity(
|
|
makeLink(e),
|
|
nextParams,
|
|
makeRemark(e),
|
|
securityToApply,
|
|
e.ForceTls == "none",
|
|
))
|
|
}
|
|
return strings.Join(links, "\n")
|
|
}
|
|
|
|
// buildEndpointVmessLinks renders one VMess base64-JSON link per endpoint.
|
|
func (s *SubService) buildEndpointVmessLinks(eps []ShareEndpoint, baseObj map[string]any, inbound *model.Inbound, email string, transport string) string {
|
|
var links strings.Builder
|
|
for index, e := range eps {
|
|
securityToApply, _ := baseObj["tls"].(string)
|
|
if e.ForceTls != "same" {
|
|
securityToApply = e.ForceTls
|
|
}
|
|
newObj := cloneVmessShareObj(baseObj, e.ForceTls)
|
|
newObj["ps"] = s.endpointRemark(inbound, email, e.ep, transport)
|
|
newObj["add"] = e.Address
|
|
newObj["port"] = e.Port
|
|
if e.ForceTls != "same" {
|
|
newObj["tls"] = e.ForceTls
|
|
}
|
|
applyEndpointTLSObj(e, newObj, securityToApply)
|
|
applyEndpointHostPathObj(e, newObj)
|
|
applyEndpointFinalMaskObj(e, newObj)
|
|
if index > 0 {
|
|
links.WriteString("\n")
|
|
}
|
|
links.WriteString(buildVmessLink(newObj))
|
|
}
|
|
return links.String()
|
|
}
|