mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2025-07-01 20:42:07 +00:00
Delete sub directory
This commit is contained in:
parent
81e1e36c1f
commit
d20964f037
5 changed files with 0 additions and 1813 deletions
|
@ -1,90 +0,0 @@
|
||||||
{
|
|
||||||
"remarks": "",
|
|
||||||
"dns": {
|
|
||||||
"tag": "dns_out",
|
|
||||||
"queryStrategy": "UseIP",
|
|
||||||
"servers": [
|
|
||||||
{
|
|
||||||
"address": "8.8.8.8",
|
|
||||||
"skipFallback": false
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"inbounds": [
|
|
||||||
{
|
|
||||||
"port": 10808,
|
|
||||||
"protocol": "socks",
|
|
||||||
"settings": {
|
|
||||||
"auth": "noauth",
|
|
||||||
"udp": true,
|
|
||||||
"userLevel": 8
|
|
||||||
},
|
|
||||||
"sniffing": {
|
|
||||||
"destOverride": [
|
|
||||||
"http",
|
|
||||||
"tls",
|
|
||||||
"quic",
|
|
||||||
"fakedns"
|
|
||||||
],
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
"tag": "socks"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"port": 10809,
|
|
||||||
"protocol": "http",
|
|
||||||
"settings": {
|
|
||||||
"userLevel": 8
|
|
||||||
},
|
|
||||||
"tag": "http"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"log": {
|
|
||||||
"loglevel": "warning"
|
|
||||||
},
|
|
||||||
"outbounds": [
|
|
||||||
{
|
|
||||||
"tag": "direct",
|
|
||||||
"protocol": "freedom",
|
|
||||||
"settings": {
|
|
||||||
"domainStrategy": "AsIs",
|
|
||||||
"redirect": "",
|
|
||||||
"noises": []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"tag": "block",
|
|
||||||
"protocol": "blackhole",
|
|
||||||
"settings": {
|
|
||||||
"response": {
|
|
||||||
"type": "http"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"policy": {
|
|
||||||
"levels": {
|
|
||||||
"8": {
|
|
||||||
"connIdle": 300,
|
|
||||||
"downlinkOnly": 1,
|
|
||||||
"handshake": 4,
|
|
||||||
"uplinkOnly": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"system": {
|
|
||||||
"statsOutboundUplink": true,
|
|
||||||
"statsOutboundDownlink": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"routing": {
|
|
||||||
"domainStrategy": "AsIs",
|
|
||||||
"rules": [
|
|
||||||
{
|
|
||||||
"type": "field",
|
|
||||||
"network": "tcp,udp",
|
|
||||||
"outboundTag": "proxy"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"stats": {}
|
|
||||||
}
|
|
208
sub/sub.go
208
sub/sub.go
|
@ -1,208 +0,0 @@
|
||||||
package sub
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"crypto/tls"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"x-ui/config"
|
|
||||||
"x-ui/logger"
|
|
||||||
"x-ui/util/common"
|
|
||||||
"x-ui/web/middleware"
|
|
||||||
"x-ui/web/network"
|
|
||||||
"x-ui/web/service"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Server struct {
|
|
||||||
httpServer *http.Server
|
|
||||||
listener net.Listener
|
|
||||||
|
|
||||||
sub *SUBController
|
|
||||||
settingService service.SettingService
|
|
||||||
|
|
||||||
ctx context.Context
|
|
||||||
cancel context.CancelFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewServer() *Server {
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
return &Server{
|
|
||||||
ctx: ctx,
|
|
||||||
cancel: cancel,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) initRouter() (*gin.Engine, error) {
|
|
||||||
if config.IsDebug() {
|
|
||||||
gin.SetMode(gin.DebugMode)
|
|
||||||
} else {
|
|
||||||
gin.DefaultWriter = io.Discard
|
|
||||||
gin.DefaultErrorWriter = io.Discard
|
|
||||||
gin.SetMode(gin.ReleaseMode)
|
|
||||||
}
|
|
||||||
|
|
||||||
engine := gin.Default()
|
|
||||||
|
|
||||||
subDomain, err := s.settingService.GetSubDomain()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if subDomain != "" {
|
|
||||||
engine.Use(middleware.DomainValidatorMiddleware(subDomain))
|
|
||||||
}
|
|
||||||
|
|
||||||
LinksPath, err := s.settingService.GetSubPath()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonPath, err := s.settingService.GetSubJsonPath()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
Encrypt, err := s.settingService.GetSubEncrypt()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ShowInfo, err := s.settingService.GetSubShowInfo()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
RemarkModel, err := s.settingService.GetRemarkModel()
|
|
||||||
if err != nil {
|
|
||||||
RemarkModel = "-ieo"
|
|
||||||
}
|
|
||||||
|
|
||||||
SubUpdates, err := s.settingService.GetSubUpdates()
|
|
||||||
if err != nil {
|
|
||||||
SubUpdates = "10"
|
|
||||||
}
|
|
||||||
|
|
||||||
SubJsonFragment, err := s.settingService.GetSubJsonFragment()
|
|
||||||
if err != nil {
|
|
||||||
SubJsonFragment = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
SubJsonNoises, err := s.settingService.GetSubJsonNoises()
|
|
||||||
if err != nil {
|
|
||||||
SubJsonNoises = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
SubJsonMux, err := s.settingService.GetSubJsonMux()
|
|
||||||
if err != nil {
|
|
||||||
SubJsonMux = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
SubJsonRules, err := s.settingService.GetSubJsonRules()
|
|
||||||
if err != nil {
|
|
||||||
SubJsonRules = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
g := engine.Group("/")
|
|
||||||
|
|
||||||
s.sub = NewSUBController(
|
|
||||||
g, LinksPath, JsonPath, Encrypt, ShowInfo, RemarkModel, SubUpdates,
|
|
||||||
SubJsonFragment, SubJsonNoises, SubJsonMux, SubJsonRules)
|
|
||||||
|
|
||||||
return engine, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) Start() (err error) {
|
|
||||||
// This is an anonymous function, no function name
|
|
||||||
defer func() {
|
|
||||||
if err != nil {
|
|
||||||
s.Stop()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
subEnable, err := s.settingService.GetSubEnable()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !subEnable {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
engine, err := s.initRouter()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
certFile, err := s.settingService.GetSubCertFile()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
keyFile, err := s.settingService.GetSubKeyFile()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
listen, err := s.settingService.GetSubListen()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
port, err := s.settingService.GetSubPort()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
listenAddr := net.JoinHostPort(listen, strconv.Itoa(port))
|
|
||||||
listener, err := net.Listen("tcp", listenAddr)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if certFile != "" || keyFile != "" {
|
|
||||||
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
|
|
||||||
if err == nil {
|
|
||||||
c := &tls.Config{
|
|
||||||
Certificates: []tls.Certificate{cert},
|
|
||||||
}
|
|
||||||
listener = network.NewAutoHttpsListener(listener)
|
|
||||||
listener = tls.NewListener(listener, c)
|
|
||||||
logger.Info("Sub server running HTTPS on", listener.Addr())
|
|
||||||
} else {
|
|
||||||
logger.Error("Error loading certificates:", err)
|
|
||||||
logger.Info("Sub server running HTTP on", listener.Addr())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logger.Info("Sub server running HTTP on", listener.Addr())
|
|
||||||
}
|
|
||||||
s.listener = listener
|
|
||||||
|
|
||||||
s.httpServer = &http.Server{
|
|
||||||
Handler: engine,
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
s.httpServer.Serve(listener)
|
|
||||||
}()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) Stop() error {
|
|
||||||
s.cancel()
|
|
||||||
|
|
||||||
var err1 error
|
|
||||||
var err2 error
|
|
||||||
if s.httpServer != nil {
|
|
||||||
err1 = s.httpServer.Shutdown(s.ctx)
|
|
||||||
}
|
|
||||||
if s.listener != nil {
|
|
||||||
err2 = s.listener.Close()
|
|
||||||
}
|
|
||||||
return common.Combine(err1, err2)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) GetCtx() context.Context {
|
|
||||||
return s.ctx
|
|
||||||
}
|
|
|
@ -1,134 +0,0 @@
|
||||||
package sub
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/base64"
|
|
||||||
"net"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
type SUBController struct {
|
|
||||||
subPath string
|
|
||||||
subJsonPath string
|
|
||||||
subEncrypt bool
|
|
||||||
updateInterval string
|
|
||||||
|
|
||||||
subService *SubService
|
|
||||||
subJsonService *SubJsonService
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSUBController(
|
|
||||||
g *gin.RouterGroup,
|
|
||||||
subPath string,
|
|
||||||
jsonPath string,
|
|
||||||
encrypt bool,
|
|
||||||
showInfo bool,
|
|
||||||
rModel string,
|
|
||||||
update string,
|
|
||||||
jsonFragment string,
|
|
||||||
jsonNoise string,
|
|
||||||
jsonMux string,
|
|
||||||
jsonRules string,
|
|
||||||
) *SUBController {
|
|
||||||
sub := NewSubService(showInfo, rModel)
|
|
||||||
a := &SUBController{
|
|
||||||
subPath: subPath,
|
|
||||||
subJsonPath: jsonPath,
|
|
||||||
subEncrypt: encrypt,
|
|
||||||
updateInterval: update,
|
|
||||||
|
|
||||||
subService: sub,
|
|
||||||
subJsonService: NewSubJsonService(jsonFragment, jsonNoise, jsonMux, jsonRules, sub),
|
|
||||||
}
|
|
||||||
a.initRouter(g)
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *SUBController) initRouter(g *gin.RouterGroup) {
|
|
||||||
gLink := g.Group(a.subPath)
|
|
||||||
gJson := g.Group(a.subJsonPath)
|
|
||||||
|
|
||||||
gLink.GET(":subid", a.subs)
|
|
||||||
|
|
||||||
gJson.GET(":subid", a.subJsons)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *SUBController) subs(c *gin.Context) {
|
|
||||||
subId := c.Param("subid")
|
|
||||||
var host string
|
|
||||||
if h, err := getHostFromXFH(c.GetHeader("X-Forwarded-Host")); err == nil {
|
|
||||||
host = h
|
|
||||||
}
|
|
||||||
if host == "" {
|
|
||||||
host = c.GetHeader("X-Real-IP")
|
|
||||||
}
|
|
||||||
if host == "" {
|
|
||||||
var err error
|
|
||||||
host, _, err = net.SplitHostPort(c.Request.Host)
|
|
||||||
if err != nil {
|
|
||||||
host = c.Request.Host
|
|
||||||
}
|
|
||||||
}
|
|
||||||
subs, header, err := a.subService.GetSubs(subId, host)
|
|
||||||
if err != nil || len(subs) == 0 {
|
|
||||||
c.String(400, "Error!")
|
|
||||||
} else {
|
|
||||||
result := ""
|
|
||||||
for _, sub := range subs {
|
|
||||||
result += sub + "\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add headers
|
|
||||||
c.Writer.Header().Set("Subscription-Userinfo", header)
|
|
||||||
c.Writer.Header().Set("Profile-Update-Interval", a.updateInterval)
|
|
||||||
c.Writer.Header().Set("Profile-Title", subId)
|
|
||||||
|
|
||||||
if a.subEncrypt {
|
|
||||||
c.String(200, base64.StdEncoding.EncodeToString([]byte(result)))
|
|
||||||
} else {
|
|
||||||
c.String(200, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *SUBController) subJsons(c *gin.Context) {
|
|
||||||
subId := c.Param("subid")
|
|
||||||
var host string
|
|
||||||
if h, err := getHostFromXFH(c.GetHeader("X-Forwarded-Host")); err == nil {
|
|
||||||
host = h
|
|
||||||
}
|
|
||||||
if host == "" {
|
|
||||||
host = c.GetHeader("X-Real-IP")
|
|
||||||
}
|
|
||||||
if host == "" {
|
|
||||||
var err error
|
|
||||||
host, _, err = net.SplitHostPort(c.Request.Host)
|
|
||||||
if err != nil {
|
|
||||||
host = c.Request.Host
|
|
||||||
}
|
|
||||||
}
|
|
||||||
jsonSub, header, err := a.subJsonService.GetJson(subId, host)
|
|
||||||
if err != nil || len(jsonSub) == 0 {
|
|
||||||
c.String(400, "Error!")
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// Add headers
|
|
||||||
c.Writer.Header().Set("Subscription-Userinfo", header)
|
|
||||||
c.Writer.Header().Set("Profile-Update-Interval", a.updateInterval)
|
|
||||||
c.Writer.Header().Set("Profile-Title", subId)
|
|
||||||
|
|
||||||
c.String(200, jsonSub)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getHostFromXFH(s string) (string, error) {
|
|
||||||
if strings.Contains(s, ":") {
|
|
||||||
realHost, _, err := net.SplitHostPort(s)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return realHost, nil
|
|
||||||
}
|
|
||||||
return s, nil
|
|
||||||
}
|
|
|
@ -1,394 +0,0 @@
|
||||||
package sub
|
|
||||||
|
|
||||||
import (
|
|
||||||
_ "embed"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"x-ui/database/model"
|
|
||||||
"x-ui/logger"
|
|
||||||
"x-ui/util/json_util"
|
|
||||||
"x-ui/util/random"
|
|
||||||
"x-ui/web/service"
|
|
||||||
"x-ui/xray"
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:embed default.json
|
|
||||||
var defaultJson string
|
|
||||||
|
|
||||||
type SubJsonService struct {
|
|
||||||
configJson map[string]interface{}
|
|
||||||
defaultOutbounds []json_util.RawMessage
|
|
||||||
fragment string
|
|
||||||
noises string
|
|
||||||
mux string
|
|
||||||
|
|
||||||
inboundService service.InboundService
|
|
||||||
SubService *SubService
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSubJsonService(fragment string, noises string, mux string, rules string, subService *SubService) *SubJsonService {
|
|
||||||
var configJson map[string]interface{}
|
|
||||||
var defaultOutbounds []json_util.RawMessage
|
|
||||||
json.Unmarshal([]byte(defaultJson), &configJson)
|
|
||||||
if outboundSlices, ok := configJson["outbounds"].([]interface{}); ok {
|
|
||||||
for _, defaultOutbound := range outboundSlices {
|
|
||||||
jsonBytes, _ := json.Marshal(defaultOutbound)
|
|
||||||
defaultOutbounds = append(defaultOutbounds, jsonBytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if rules != "" {
|
|
||||||
var newRules []interface{}
|
|
||||||
routing, _ := configJson["routing"].(map[string]interface{})
|
|
||||||
defaultRules, _ := routing["rules"].([]interface{})
|
|
||||||
json.Unmarshal([]byte(rules), &newRules)
|
|
||||||
defaultRules = append(newRules, defaultRules...)
|
|
||||||
routing["rules"] = defaultRules
|
|
||||||
configJson["routing"] = routing
|
|
||||||
}
|
|
||||||
|
|
||||||
if fragment != "" {
|
|
||||||
defaultOutbounds = append(defaultOutbounds, json_util.RawMessage(fragment))
|
|
||||||
}
|
|
||||||
|
|
||||||
if noises != "" {
|
|
||||||
defaultOutbounds = append(defaultOutbounds, json_util.RawMessage(noises))
|
|
||||||
}
|
|
||||||
|
|
||||||
return &SubJsonService{
|
|
||||||
configJson: configJson,
|
|
||||||
defaultOutbounds: defaultOutbounds,
|
|
||||||
fragment: fragment,
|
|
||||||
noises: noises,
|
|
||||||
mux: mux,
|
|
||||||
SubService: subService,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SubJsonService) GetJson(subId string, host string) (string, string, error) {
|
|
||||||
inbounds, err := s.SubService.getInboundsBySubId(subId)
|
|
||||||
if err != nil || len(inbounds) == 0 {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
var header string
|
|
||||||
var traffic xray.ClientTraffic
|
|
||||||
var clientTraffics []xray.ClientTraffic
|
|
||||||
var configArray []json_util.RawMessage
|
|
||||||
|
|
||||||
// Prepare Inbounds
|
|
||||||
for _, inbound := range inbounds {
|
|
||||||
clients, err := s.inboundService.GetClients(inbound)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error("SubJsonService - GetClients: Unable to get clients from inbound")
|
|
||||||
}
|
|
||||||
if clients == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if len(inbound.Listen) > 0 && inbound.Listen[0] == '@' {
|
|
||||||
listen, port, streamSettings, err := s.SubService.getFallbackMaster(inbound.Listen, inbound.StreamSettings)
|
|
||||||
if err == nil {
|
|
||||||
inbound.Listen = listen
|
|
||||||
inbound.Port = port
|
|
||||||
inbound.StreamSettings = streamSettings
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, client := range clients {
|
|
||||||
if client.Enable && client.SubID == subId {
|
|
||||||
clientTraffics = append(clientTraffics, s.SubService.getClientTraffics(inbound.ClientStats, client.Email))
|
|
||||||
newConfigs := s.getConfig(inbound, client, host)
|
|
||||||
configArray = append(configArray, newConfigs...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(configArray) == 0 {
|
|
||||||
return "", "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare statistics
|
|
||||||
for index, clientTraffic := range clientTraffics {
|
|
||||||
if index == 0 {
|
|
||||||
traffic.Up = clientTraffic.Up
|
|
||||||
traffic.Down = clientTraffic.Down
|
|
||||||
traffic.Total = clientTraffic.Total
|
|
||||||
if clientTraffic.ExpiryTime > 0 {
|
|
||||||
traffic.ExpiryTime = clientTraffic.ExpiryTime
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
traffic.Up += clientTraffic.Up
|
|
||||||
traffic.Down += clientTraffic.Down
|
|
||||||
if traffic.Total == 0 || clientTraffic.Total == 0 {
|
|
||||||
traffic.Total = 0
|
|
||||||
} else {
|
|
||||||
traffic.Total += clientTraffic.Total
|
|
||||||
}
|
|
||||||
if clientTraffic.ExpiryTime != traffic.ExpiryTime {
|
|
||||||
traffic.ExpiryTime = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Combile outbounds
|
|
||||||
var finalJson []byte
|
|
||||||
if len(configArray) == 1 {
|
|
||||||
finalJson, _ = json.MarshalIndent(configArray[0], "", " ")
|
|
||||||
} else {
|
|
||||||
finalJson, _ = json.MarshalIndent(configArray, "", " ")
|
|
||||||
}
|
|
||||||
|
|
||||||
header = fmt.Sprintf("upload=%d; download=%d; total=%d; expire=%d", traffic.Up, traffic.Down, traffic.Total, traffic.ExpiryTime/1000)
|
|
||||||
return string(finalJson), header, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SubJsonService) getConfig(inbound *model.Inbound, client model.Client, host string) []json_util.RawMessage {
|
|
||||||
var newJsonArray []json_util.RawMessage
|
|
||||||
stream := s.streamData(inbound.StreamSettings)
|
|
||||||
|
|
||||||
externalProxies, ok := stream["externalProxy"].([]interface{})
|
|
||||||
if !ok || len(externalProxies) == 0 {
|
|
||||||
externalProxies = []interface{}{
|
|
||||||
map[string]interface{}{
|
|
||||||
"forceTls": "same",
|
|
||||||
"dest": host,
|
|
||||||
"port": float64(inbound.Port),
|
|
||||||
"remark": "",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delete(stream, "externalProxy")
|
|
||||||
|
|
||||||
for _, ep := range externalProxies {
|
|
||||||
extPrxy := ep.(map[string]interface{})
|
|
||||||
inbound.Listen = extPrxy["dest"].(string)
|
|
||||||
inbound.Port = int(extPrxy["port"].(float64))
|
|
||||||
newStream := stream
|
|
||||||
switch extPrxy["forceTls"].(string) {
|
|
||||||
case "tls":
|
|
||||||
if newStream["security"] != "tls" {
|
|
||||||
newStream["security"] = "tls"
|
|
||||||
newStream["tslSettings"] = map[string]interface{}{}
|
|
||||||
}
|
|
||||||
case "none":
|
|
||||||
if newStream["security"] != "none" {
|
|
||||||
newStream["security"] = "none"
|
|
||||||
delete(newStream, "tslSettings")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
streamSettings, _ := json.MarshalIndent(newStream, "", " ")
|
|
||||||
|
|
||||||
var newOutbounds []json_util.RawMessage
|
|
||||||
|
|
||||||
switch inbound.Protocol {
|
|
||||||
case "vmess", "vless":
|
|
||||||
newOutbounds = append(newOutbounds, s.genVnext(inbound, streamSettings, client))
|
|
||||||
case "trojan", "shadowsocks":
|
|
||||||
newOutbounds = append(newOutbounds, s.genServer(inbound, streamSettings, client))
|
|
||||||
}
|
|
||||||
|
|
||||||
newOutbounds = append(newOutbounds, s.defaultOutbounds...)
|
|
||||||
newConfigJson := make(map[string]interface{})
|
|
||||||
for key, value := range s.configJson {
|
|
||||||
newConfigJson[key] = value
|
|
||||||
}
|
|
||||||
newConfigJson["outbounds"] = newOutbounds
|
|
||||||
newConfigJson["remarks"] = s.SubService.genRemark(inbound, client.Email, extPrxy["remark"].(string))
|
|
||||||
|
|
||||||
newConfig, _ := json.MarshalIndent(newConfigJson, "", " ")
|
|
||||||
newJsonArray = append(newJsonArray, newConfig)
|
|
||||||
}
|
|
||||||
|
|
||||||
return newJsonArray
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SubJsonService) streamData(stream string) map[string]interface{} {
|
|
||||||
var streamSettings map[string]interface{}
|
|
||||||
json.Unmarshal([]byte(stream), &streamSettings)
|
|
||||||
security, _ := streamSettings["security"].(string)
|
|
||||||
if security == "tls" {
|
|
||||||
streamSettings["tlsSettings"] = s.tlsData(streamSettings["tlsSettings"].(map[string]interface{}))
|
|
||||||
} else if security == "reality" {
|
|
||||||
streamSettings["realitySettings"] = s.realityData(streamSettings["realitySettings"].(map[string]interface{}))
|
|
||||||
}
|
|
||||||
delete(streamSettings, "sockopt")
|
|
||||||
|
|
||||||
if s.fragment != "" {
|
|
||||||
streamSettings["sockopt"] = json_util.RawMessage(`{"dialerProxy": "fragment", "tcpKeepAliveIdle": 100, "tcpMptcp": true, "penetrate": true}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove proxy protocol
|
|
||||||
network, _ := streamSettings["network"].(string)
|
|
||||||
switch network {
|
|
||||||
case "tcp":
|
|
||||||
streamSettings["tcpSettings"] = s.removeAcceptProxy(streamSettings["tcpSettings"])
|
|
||||||
case "ws":
|
|
||||||
streamSettings["wsSettings"] = s.removeAcceptProxy(streamSettings["wsSettings"])
|
|
||||||
case "httpupgrade":
|
|
||||||
streamSettings["httpupgradeSettings"] = s.removeAcceptProxy(streamSettings["httpupgradeSettings"])
|
|
||||||
}
|
|
||||||
return streamSettings
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SubJsonService) removeAcceptProxy(setting interface{}) map[string]interface{} {
|
|
||||||
netSettings, ok := setting.(map[string]interface{})
|
|
||||||
if ok {
|
|
||||||
delete(netSettings, "acceptProxyProtocol")
|
|
||||||
}
|
|
||||||
return netSettings
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SubJsonService) tlsData(tData map[string]interface{}) map[string]interface{} {
|
|
||||||
tlsData := make(map[string]interface{}, 1)
|
|
||||||
tlsClientSettings, _ := tData["settings"].(map[string]interface{})
|
|
||||||
|
|
||||||
tlsData["serverName"] = tData["serverName"]
|
|
||||||
tlsData["alpn"] = tData["alpn"]
|
|
||||||
if allowInsecure, ok := tlsClientSettings["allowInsecure"].(bool); ok {
|
|
||||||
tlsData["allowInsecure"] = allowInsecure
|
|
||||||
}
|
|
||||||
if fingerprint, ok := tlsClientSettings["fingerprint"].(string); ok {
|
|
||||||
tlsData["fingerprint"] = fingerprint
|
|
||||||
}
|
|
||||||
return tlsData
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SubJsonService) realityData(rData map[string]interface{}) map[string]interface{} {
|
|
||||||
rltyData := make(map[string]interface{}, 1)
|
|
||||||
rltyClientSettings, _ := rData["settings"].(map[string]interface{})
|
|
||||||
|
|
||||||
rltyData["show"] = false
|
|
||||||
rltyData["publicKey"] = rltyClientSettings["publicKey"]
|
|
||||||
rltyData["fingerprint"] = rltyClientSettings["fingerprint"]
|
|
||||||
|
|
||||||
// Set random data
|
|
||||||
rltyData["spiderX"] = "/" + random.Seq(15)
|
|
||||||
shortIds, ok := rData["shortIds"].([]interface{})
|
|
||||||
if ok && len(shortIds) > 0 {
|
|
||||||
rltyData["shortId"] = shortIds[random.Num(len(shortIds))].(string)
|
|
||||||
} else {
|
|
||||||
rltyData["shortId"] = ""
|
|
||||||
}
|
|
||||||
serverNames, ok := rData["serverNames"].([]interface{})
|
|
||||||
if ok && len(serverNames) > 0 {
|
|
||||||
rltyData["serverName"] = serverNames[random.Num(len(serverNames))].(string)
|
|
||||||
} else {
|
|
||||||
rltyData["serverName"] = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
return rltyData
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SubJsonService) genVnext(inbound *model.Inbound, streamSettings json_util.RawMessage, client model.Client) json_util.RawMessage {
|
|
||||||
outbound := Outbound{}
|
|
||||||
usersData := make([]UserVnext, 1)
|
|
||||||
|
|
||||||
usersData[0].ID = client.ID
|
|
||||||
usersData[0].Level = 8
|
|
||||||
if inbound.Protocol == model.VMESS {
|
|
||||||
usersData[0].Security = client.Security
|
|
||||||
}
|
|
||||||
if inbound.Protocol == model.VLESS {
|
|
||||||
usersData[0].Flow = client.Flow
|
|
||||||
usersData[0].Encryption = "none"
|
|
||||||
}
|
|
||||||
|
|
||||||
vnextData := make([]VnextSetting, 1)
|
|
||||||
vnextData[0] = VnextSetting{
|
|
||||||
Address: inbound.Listen,
|
|
||||||
Port: inbound.Port,
|
|
||||||
Users: usersData,
|
|
||||||
}
|
|
||||||
|
|
||||||
outbound.Protocol = string(inbound.Protocol)
|
|
||||||
outbound.Tag = "proxy"
|
|
||||||
if s.mux != "" {
|
|
||||||
outbound.Mux = json_util.RawMessage(s.mux)
|
|
||||||
}
|
|
||||||
outbound.StreamSettings = streamSettings
|
|
||||||
outbound.Settings = OutboundSettings{
|
|
||||||
Vnext: vnextData,
|
|
||||||
}
|
|
||||||
|
|
||||||
result, _ := json.MarshalIndent(outbound, "", " ")
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SubJsonService) genServer(inbound *model.Inbound, streamSettings json_util.RawMessage, client model.Client) json_util.RawMessage {
|
|
||||||
outbound := Outbound{}
|
|
||||||
|
|
||||||
serverData := make([]ServerSetting, 1)
|
|
||||||
serverData[0] = ServerSetting{
|
|
||||||
Address: inbound.Listen,
|
|
||||||
Port: inbound.Port,
|
|
||||||
Level: 8,
|
|
||||||
Password: client.Password,
|
|
||||||
}
|
|
||||||
|
|
||||||
if inbound.Protocol == model.Shadowsocks {
|
|
||||||
var inboundSettings map[string]interface{}
|
|
||||||
json.Unmarshal([]byte(inbound.Settings), &inboundSettings)
|
|
||||||
method, _ := inboundSettings["method"].(string)
|
|
||||||
serverData[0].Method = method
|
|
||||||
|
|
||||||
// server password in multi-user 2022 protocols
|
|
||||||
if strings.HasPrefix(method, "2022") {
|
|
||||||
if serverPassword, ok := inboundSettings["password"].(string); ok {
|
|
||||||
serverData[0].Password = fmt.Sprintf("%s:%s", serverPassword, client.Password)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
outbound.Protocol = string(inbound.Protocol)
|
|
||||||
outbound.Tag = "proxy"
|
|
||||||
if s.mux != "" {
|
|
||||||
outbound.Mux = json_util.RawMessage(s.mux)
|
|
||||||
}
|
|
||||||
outbound.StreamSettings = streamSettings
|
|
||||||
outbound.Settings = OutboundSettings{
|
|
||||||
Servers: serverData,
|
|
||||||
}
|
|
||||||
|
|
||||||
result, _ := json.MarshalIndent(outbound, "", " ")
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
type Outbound struct {
|
|
||||||
Protocol string `json:"protocol"`
|
|
||||||
Tag string `json:"tag"`
|
|
||||||
StreamSettings json_util.RawMessage `json:"streamSettings"`
|
|
||||||
Mux json_util.RawMessage `json:"mux,omitempty"`
|
|
||||||
ProxySettings map[string]interface{} `json:"proxySettings,omitempty"`
|
|
||||||
Settings OutboundSettings `json:"settings,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type OutboundSettings struct {
|
|
||||||
Vnext []VnextSetting `json:"vnext,omitempty"`
|
|
||||||
Servers []ServerSetting `json:"servers,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type VnextSetting struct {
|
|
||||||
Address string `json:"address"`
|
|
||||||
Port int `json:"port"`
|
|
||||||
Users []UserVnext `json:"users"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type UserVnext struct {
|
|
||||||
Encryption string `json:"encryption,omitempty"`
|
|
||||||
Flow string `json:"flow,omitempty"`
|
|
||||||
ID string `json:"id"`
|
|
||||||
Security string `json:"security,omitempty"`
|
|
||||||
Level int `json:"level"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ServerSetting struct {
|
|
||||||
Password string `json:"password"`
|
|
||||||
Level int `json:"level"`
|
|
||||||
Address string `json:"address"`
|
|
||||||
Port int `json:"port"`
|
|
||||||
Flow string `json:"flow,omitempty"`
|
|
||||||
Method string `json:"method,omitempty"`
|
|
||||||
}
|
|
|
@ -1,987 +0,0 @@
|
||||||
package sub
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/base64"
|
|
||||||
"fmt"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"x-ui/database"
|
|
||||||
"x-ui/database/model"
|
|
||||||
"x-ui/logger"
|
|
||||||
"x-ui/util/common"
|
|
||||||
"x-ui/util/random"
|
|
||||||
"x-ui/web/service"
|
|
||||||
"x-ui/xray"
|
|
||||||
|
|
||||||
"github.com/goccy/go-json"
|
|
||||||
)
|
|
||||||
|
|
||||||
type SubService struct {
|
|
||||||
address string
|
|
||||||
showInfo bool
|
|
||||||
remarkModel string
|
|
||||||
datepicker string
|
|
||||||
inboundService service.InboundService
|
|
||||||
settingService service.SettingService
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSubService(showInfo bool, remarkModel string) *SubService {
|
|
||||||
return &SubService{
|
|
||||||
showInfo: showInfo,
|
|
||||||
remarkModel: remarkModel,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SubService) GetSubs(subId string, host string) ([]string, string, error) {
|
|
||||||
s.address = host
|
|
||||||
var result []string
|
|
||||||
var header string
|
|
||||||
var traffic xray.ClientTraffic
|
|
||||||
var clientTraffics []xray.ClientTraffic
|
|
||||||
inbounds, err := s.getInboundsBySubId(subId)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(inbounds) == 0 {
|
|
||||||
return nil, "", common.NewError("No inbounds found with ", subId)
|
|
||||||
}
|
|
||||||
|
|
||||||
s.datepicker, err = s.settingService.GetDatepicker()
|
|
||||||
if err != nil {
|
|
||||||
s.datepicker = "gregorian"
|
|
||||||
}
|
|
||||||
for _, inbound := range inbounds {
|
|
||||||
clients, err := s.inboundService.GetClients(inbound)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error("SubService - GetClients: Unable to get clients from inbound")
|
|
||||||
}
|
|
||||||
if clients == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if len(inbound.Listen) > 0 && inbound.Listen[0] == '@' {
|
|
||||||
listen, port, streamSettings, err := s.getFallbackMaster(inbound.Listen, inbound.StreamSettings)
|
|
||||||
if err == nil {
|
|
||||||
inbound.Listen = listen
|
|
||||||
inbound.Port = port
|
|
||||||
inbound.StreamSettings = streamSettings
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, client := range clients {
|
|
||||||
if client.Enable && client.SubID == subId {
|
|
||||||
link := s.getLink(inbound, client.Email)
|
|
||||||
result = append(result, link)
|
|
||||||
clientTraffics = append(clientTraffics, s.getClientTraffics(inbound.ClientStats, client.Email))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare statistics
|
|
||||||
for index, clientTraffic := range clientTraffics {
|
|
||||||
if index == 0 {
|
|
||||||
traffic.Up = clientTraffic.Up
|
|
||||||
traffic.Down = clientTraffic.Down
|
|
||||||
traffic.Total = clientTraffic.Total
|
|
||||||
if clientTraffic.ExpiryTime > 0 {
|
|
||||||
traffic.ExpiryTime = clientTraffic.ExpiryTime
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
traffic.Up += clientTraffic.Up
|
|
||||||
traffic.Down += clientTraffic.Down
|
|
||||||
if traffic.Total == 0 || clientTraffic.Total == 0 {
|
|
||||||
traffic.Total = 0
|
|
||||||
} else {
|
|
||||||
traffic.Total += clientTraffic.Total
|
|
||||||
}
|
|
||||||
if clientTraffic.ExpiryTime != traffic.ExpiryTime {
|
|
||||||
traffic.ExpiryTime = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
header = fmt.Sprintf("upload=%d; download=%d; total=%d; expire=%d", traffic.Up, traffic.Down, traffic.Total, traffic.ExpiryTime/1000)
|
|
||||||
return result, header, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SubService) getInboundsBySubId(subId string) ([]*model.Inbound, error) {
|
|
||||||
db := database.GetDB()
|
|
||||||
var inbounds []*model.Inbound
|
|
||||||
err := db.Model(model.Inbound{}).Preload("ClientStats").Where(`id in (
|
|
||||||
SELECT DISTINCT inbounds.id
|
|
||||||
FROM inbounds,
|
|
||||||
JSON_EACH(JSON_EXTRACT(inbounds.settings, '$.clients')) AS client
|
|
||||||
WHERE
|
|
||||||
protocol in ('vmess','vless','trojan','shadowsocks')
|
|
||||||
AND JSON_EXTRACT(client.value, '$.subId') = ? AND enable = ?
|
|
||||||
)`, subId, true).Find(&inbounds).Error
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return inbounds, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SubService) getClientTraffics(traffics []xray.ClientTraffic, email string) xray.ClientTraffic {
|
|
||||||
for _, traffic := range traffics {
|
|
||||||
if traffic.Email == email {
|
|
||||||
return traffic
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return xray.ClientTraffic{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SubService) getFallbackMaster(dest string, streamSettings string) (string, int, string, error) {
|
|
||||||
db := database.GetDB()
|
|
||||||
var inbound *model.Inbound
|
|
||||||
err := db.Model(model.Inbound{}).
|
|
||||||
Where("JSON_TYPE(settings, '$.fallbacks') = 'array'").
|
|
||||||
Where("EXISTS (SELECT * FROM json_each(settings, '$.fallbacks') WHERE json_extract(value, '$.dest') = ?)", dest).
|
|
||||||
Find(&inbound).Error
|
|
||||||
if err != nil {
|
|
||||||
return "", 0, "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
var stream map[string]interface{}
|
|
||||||
json.Unmarshal([]byte(streamSettings), &stream)
|
|
||||||
var masterStream map[string]interface{}
|
|
||||||
json.Unmarshal([]byte(inbound.StreamSettings), &masterStream)
|
|
||||||
stream["security"] = masterStream["security"]
|
|
||||||
stream["tlsSettings"] = masterStream["tlsSettings"]
|
|
||||||
stream["externalProxy"] = masterStream["externalProxy"]
|
|
||||||
modifiedStream, _ := json.MarshalIndent(stream, "", " ")
|
|
||||||
|
|
||||||
return inbound.Listen, inbound.Port, string(modifiedStream), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SubService) getLink(inbound *model.Inbound, email string) string {
|
|
||||||
switch inbound.Protocol {
|
|
||||||
case "vmess":
|
|
||||||
return s.genVmessLink(inbound, email)
|
|
||||||
case "vless":
|
|
||||||
return s.genVlessLink(inbound, email)
|
|
||||||
case "trojan":
|
|
||||||
return s.genTrojanLink(inbound, email)
|
|
||||||
case "shadowsocks":
|
|
||||||
return s.genShadowsocksLink(inbound, email)
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SubService) genVmessLink(inbound *model.Inbound, email string) string {
|
|
||||||
if inbound.Protocol != model.VMESS {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
obj := map[string]interface{}{
|
|
||||||
"v": "2",
|
|
||||||
"add": s.address,
|
|
||||||
"port": inbound.Port,
|
|
||||||
"type": "none",
|
|
||||||
}
|
|
||||||
var stream map[string]interface{}
|
|
||||||
json.Unmarshal([]byte(inbound.StreamSettings), &stream)
|
|
||||||
network, _ := stream["network"].(string)
|
|
||||||
obj["net"] = network
|
|
||||||
switch network {
|
|
||||||
case "tcp":
|
|
||||||
tcp, _ := stream["tcpSettings"].(map[string]interface{})
|
|
||||||
header, _ := tcp["header"].(map[string]interface{})
|
|
||||||
typeStr, _ := header["type"].(string)
|
|
||||||
obj["type"] = typeStr
|
|
||||||
if typeStr == "http" {
|
|
||||||
request := header["request"].(map[string]interface{})
|
|
||||||
requestPath, _ := request["path"].([]interface{})
|
|
||||||
obj["path"] = requestPath[0].(string)
|
|
||||||
headers, _ := request["headers"].(map[string]interface{})
|
|
||||||
obj["host"] = searchHost(headers)
|
|
||||||
}
|
|
||||||
case "kcp":
|
|
||||||
kcp, _ := stream["kcpSettings"].(map[string]interface{})
|
|
||||||
header, _ := kcp["header"].(map[string]interface{})
|
|
||||||
obj["type"], _ = header["type"].(string)
|
|
||||||
obj["path"], _ = kcp["seed"].(string)
|
|
||||||
case "ws":
|
|
||||||
ws, _ := stream["wsSettings"].(map[string]interface{})
|
|
||||||
obj["path"] = ws["path"].(string)
|
|
||||||
if host, ok := ws["host"].(string); ok && len(host) > 0 {
|
|
||||||
obj["host"] = host
|
|
||||||
} else {
|
|
||||||
headers, _ := ws["headers"].(map[string]interface{})
|
|
||||||
obj["host"] = searchHost(headers)
|
|
||||||
}
|
|
||||||
case "grpc":
|
|
||||||
grpc, _ := stream["grpcSettings"].(map[string]interface{})
|
|
||||||
obj["path"] = grpc["serviceName"].(string)
|
|
||||||
obj["authority"] = grpc["authority"].(string)
|
|
||||||
if grpc["multiMode"].(bool) {
|
|
||||||
obj["type"] = "multi"
|
|
||||||
}
|
|
||||||
case "httpupgrade":
|
|
||||||
httpupgrade, _ := stream["httpupgradeSettings"].(map[string]interface{})
|
|
||||||
obj["path"] = httpupgrade["path"].(string)
|
|
||||||
if host, ok := httpupgrade["host"].(string); ok && len(host) > 0 {
|
|
||||||
obj["host"] = host
|
|
||||||
} else {
|
|
||||||
headers, _ := httpupgrade["headers"].(map[string]interface{})
|
|
||||||
obj["host"] = searchHost(headers)
|
|
||||||
}
|
|
||||||
case "xhttp":
|
|
||||||
xhttp, _ := stream["xhttpSettings"].(map[string]interface{})
|
|
||||||
obj["path"] = xhttp["path"].(string)
|
|
||||||
if host, ok := xhttp["host"].(string); ok && len(host) > 0 {
|
|
||||||
obj["host"] = host
|
|
||||||
} else {
|
|
||||||
headers, _ := xhttp["headers"].(map[string]interface{})
|
|
||||||
obj["host"] = searchHost(headers)
|
|
||||||
}
|
|
||||||
obj["mode"] = xhttp["mode"].(string)
|
|
||||||
}
|
|
||||||
security, _ := stream["security"].(string)
|
|
||||||
obj["tls"] = security
|
|
||||||
if security == "tls" {
|
|
||||||
tlsSetting, _ := stream["tlsSettings"].(map[string]interface{})
|
|
||||||
alpns, _ := tlsSetting["alpn"].([]interface{})
|
|
||||||
if len(alpns) > 0 {
|
|
||||||
var alpn []string
|
|
||||||
for _, a := range alpns {
|
|
||||||
alpn = append(alpn, a.(string))
|
|
||||||
}
|
|
||||||
obj["alpn"] = strings.Join(alpn, ",")
|
|
||||||
}
|
|
||||||
if sniValue, ok := searchKey(tlsSetting, "serverName"); ok {
|
|
||||||
obj["sni"], _ = sniValue.(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
tlsSettings, _ := searchKey(tlsSetting, "settings")
|
|
||||||
if tlsSetting != nil {
|
|
||||||
if fpValue, ok := searchKey(tlsSettings, "fingerprint"); ok {
|
|
||||||
obj["fp"], _ = fpValue.(string)
|
|
||||||
}
|
|
||||||
if insecure, ok := searchKey(tlsSettings, "allowInsecure"); ok {
|
|
||||||
obj["allowInsecure"], _ = insecure.(bool)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clients, _ := s.inboundService.GetClients(inbound)
|
|
||||||
clientIndex := -1
|
|
||||||
for i, client := range clients {
|
|
||||||
if client.Email == email {
|
|
||||||
clientIndex = i
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
obj["id"] = clients[clientIndex].ID
|
|
||||||
obj["scy"] = clients[clientIndex].Security
|
|
||||||
|
|
||||||
externalProxies, _ := stream["externalProxy"].([]interface{})
|
|
||||||
|
|
||||||
if len(externalProxies) > 0 {
|
|
||||||
links := ""
|
|
||||||
for index, externalProxy := range externalProxies {
|
|
||||||
ep, _ := externalProxy.(map[string]interface{})
|
|
||||||
newSecurity, _ := ep["forceTls"].(string)
|
|
||||||
newObj := map[string]interface{}{}
|
|
||||||
for key, value := range obj {
|
|
||||||
if !(newSecurity == "none" && (key == "alpn" || key == "sni" || key == "fp" || key == "allowInsecure")) {
|
|
||||||
newObj[key] = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
newObj["ps"] = s.genRemark(inbound, email, ep["remark"].(string))
|
|
||||||
newObj["add"] = ep["dest"].(string)
|
|
||||||
newObj["port"] = int(ep["port"].(float64))
|
|
||||||
|
|
||||||
if newSecurity != "same" {
|
|
||||||
newObj["tls"] = newSecurity
|
|
||||||
}
|
|
||||||
if index > 0 {
|
|
||||||
links += "\n"
|
|
||||||
}
|
|
||||||
jsonStr, _ := json.MarshalIndent(newObj, "", " ")
|
|
||||||
links += "vmess://" + base64.StdEncoding.EncodeToString(jsonStr)
|
|
||||||
}
|
|
||||||
return links
|
|
||||||
}
|
|
||||||
|
|
||||||
obj["ps"] = s.genRemark(inbound, email, "")
|
|
||||||
|
|
||||||
jsonStr, _ := json.MarshalIndent(obj, "", " ")
|
|
||||||
return "vmess://" + base64.StdEncoding.EncodeToString(jsonStr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
|
|
||||||
address := s.address
|
|
||||||
if inbound.Protocol != model.VLESS {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
var stream map[string]interface{}
|
|
||||||
json.Unmarshal([]byte(inbound.StreamSettings), &stream)
|
|
||||||
clients, _ := s.inboundService.GetClients(inbound)
|
|
||||||
clientIndex := -1
|
|
||||||
for i, client := range clients {
|
|
||||||
if client.Email == email {
|
|
||||||
clientIndex = i
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
uuid := clients[clientIndex].ID
|
|
||||||
port := inbound.Port
|
|
||||||
streamNetwork := stream["network"].(string)
|
|
||||||
params := make(map[string]string)
|
|
||||||
params["type"] = streamNetwork
|
|
||||||
|
|
||||||
switch streamNetwork {
|
|
||||||
case "tcp":
|
|
||||||
tcp, _ := stream["tcpSettings"].(map[string]interface{})
|
|
||||||
header, _ := tcp["header"].(map[string]interface{})
|
|
||||||
typeStr, _ := header["type"].(string)
|
|
||||||
if typeStr == "http" {
|
|
||||||
request := header["request"].(map[string]interface{})
|
|
||||||
requestPath, _ := request["path"].([]interface{})
|
|
||||||
params["path"] = requestPath[0].(string)
|
|
||||||
headers, _ := request["headers"].(map[string]interface{})
|
|
||||||
params["host"] = searchHost(headers)
|
|
||||||
params["headerType"] = "http"
|
|
||||||
}
|
|
||||||
case "kcp":
|
|
||||||
kcp, _ := stream["kcpSettings"].(map[string]interface{})
|
|
||||||
header, _ := kcp["header"].(map[string]interface{})
|
|
||||||
params["headerType"] = header["type"].(string)
|
|
||||||
params["seed"] = kcp["seed"].(string)
|
|
||||||
case "ws":
|
|
||||||
ws, _ := stream["wsSettings"].(map[string]interface{})
|
|
||||||
params["path"] = ws["path"].(string)
|
|
||||||
if host, ok := ws["host"].(string); ok && len(host) > 0 {
|
|
||||||
params["host"] = host
|
|
||||||
} else {
|
|
||||||
headers, _ := ws["headers"].(map[string]interface{})
|
|
||||||
params["host"] = searchHost(headers)
|
|
||||||
}
|
|
||||||
case "grpc":
|
|
||||||
grpc, _ := stream["grpcSettings"].(map[string]interface{})
|
|
||||||
params["serviceName"] = grpc["serviceName"].(string)
|
|
||||||
params["authority"], _ = grpc["authority"].(string)
|
|
||||||
if grpc["multiMode"].(bool) {
|
|
||||||
params["mode"] = "multi"
|
|
||||||
}
|
|
||||||
case "httpupgrade":
|
|
||||||
httpupgrade, _ := stream["httpupgradeSettings"].(map[string]interface{})
|
|
||||||
params["path"] = httpupgrade["path"].(string)
|
|
||||||
if host, ok := httpupgrade["host"].(string); ok && len(host) > 0 {
|
|
||||||
params["host"] = host
|
|
||||||
} else {
|
|
||||||
headers, _ := httpupgrade["headers"].(map[string]interface{})
|
|
||||||
params["host"] = searchHost(headers)
|
|
||||||
}
|
|
||||||
case "xhttp":
|
|
||||||
xhttp, _ := stream["xhttpSettings"].(map[string]interface{})
|
|
||||||
params["path"] = xhttp["path"].(string)
|
|
||||||
if host, ok := xhttp["host"].(string); ok && len(host) > 0 {
|
|
||||||
params["host"] = host
|
|
||||||
} else {
|
|
||||||
headers, _ := xhttp["headers"].(map[string]interface{})
|
|
||||||
params["host"] = searchHost(headers)
|
|
||||||
}
|
|
||||||
params["mode"] = xhttp["mode"].(string)
|
|
||||||
}
|
|
||||||
security, _ := stream["security"].(string)
|
|
||||||
if security == "tls" {
|
|
||||||
params["security"] = "tls"
|
|
||||||
tlsSetting, _ := stream["tlsSettings"].(map[string]interface{})
|
|
||||||
alpns, _ := tlsSetting["alpn"].([]interface{})
|
|
||||||
var alpn []string
|
|
||||||
for _, a := range alpns {
|
|
||||||
alpn = append(alpn, a.(string))
|
|
||||||
}
|
|
||||||
if len(alpn) > 0 {
|
|
||||||
params["alpn"] = strings.Join(alpn, ",")
|
|
||||||
}
|
|
||||||
if sniValue, ok := searchKey(tlsSetting, "serverName"); ok {
|
|
||||||
params["sni"], _ = sniValue.(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
tlsSettings, _ := searchKey(tlsSetting, "settings")
|
|
||||||
if tlsSetting != nil {
|
|
||||||
if fpValue, ok := searchKey(tlsSettings, "fingerprint"); ok {
|
|
||||||
params["fp"], _ = fpValue.(string)
|
|
||||||
}
|
|
||||||
if insecure, ok := searchKey(tlsSettings, "allowInsecure"); ok {
|
|
||||||
if insecure.(bool) {
|
|
||||||
params["allowInsecure"] = "1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 {
|
|
||||||
params["flow"] = clients[clientIndex].Flow
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if security == "reality" {
|
|
||||||
params["security"] = "reality"
|
|
||||||
realitySetting, _ := stream["realitySettings"].(map[string]interface{})
|
|
||||||
realitySettings, _ := searchKey(realitySetting, "settings")
|
|
||||||
if realitySetting != nil {
|
|
||||||
if sniValue, ok := searchKey(realitySetting, "serverNames"); ok {
|
|
||||||
sNames, _ := sniValue.([]interface{})
|
|
||||||
params["sni"] = sNames[random.Num(len(sNames))].(string)
|
|
||||||
}
|
|
||||||
if pbkValue, ok := searchKey(realitySettings, "publicKey"); ok {
|
|
||||||
params["pbk"], _ = pbkValue.(string)
|
|
||||||
}
|
|
||||||
if sidValue, ok := searchKey(realitySetting, "shortIds"); ok {
|
|
||||||
shortIds, _ := sidValue.([]interface{})
|
|
||||||
params["sid"] = shortIds[random.Num(len(shortIds))].(string)
|
|
||||||
}
|
|
||||||
if fpValue, ok := searchKey(realitySettings, "fingerprint"); ok {
|
|
||||||
if fp, ok := fpValue.(string); ok && len(fp) > 0 {
|
|
||||||
params["fp"] = fp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
params["spx"] = "/" + random.Seq(15)
|
|
||||||
}
|
|
||||||
|
|
||||||
if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 {
|
|
||||||
params["flow"] = clients[clientIndex].Flow
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if security != "tls" && security != "reality" {
|
|
||||||
params["security"] = "none"
|
|
||||||
}
|
|
||||||
|
|
||||||
externalProxies, _ := stream["externalProxy"].([]interface{})
|
|
||||||
|
|
||||||
if len(externalProxies) > 0 {
|
|
||||||
links := ""
|
|
||||||
for index, externalProxy := range externalProxies {
|
|
||||||
ep, _ := externalProxy.(map[string]interface{})
|
|
||||||
newSecurity, _ := ep["forceTls"].(string)
|
|
||||||
dest, _ := ep["dest"].(string)
|
|
||||||
port := int(ep["port"].(float64))
|
|
||||||
link := fmt.Sprintf("vless://%s@%s:%d", uuid, dest, port)
|
|
||||||
|
|
||||||
if newSecurity != "same" {
|
|
||||||
params["security"] = newSecurity
|
|
||||||
} else {
|
|
||||||
params["security"] = security
|
|
||||||
}
|
|
||||||
url, _ := url.Parse(link)
|
|
||||||
q := url.Query()
|
|
||||||
|
|
||||||
for k, v := range params {
|
|
||||||
if !(newSecurity == "none" && (k == "alpn" || k == "sni" || k == "fp" || k == "allowInsecure")) {
|
|
||||||
q.Add(k, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the new query values on the URL
|
|
||||||
url.RawQuery = q.Encode()
|
|
||||||
|
|
||||||
url.Fragment = s.genRemark(inbound, email, ep["remark"].(string))
|
|
||||||
|
|
||||||
if index > 0 {
|
|
||||||
links += "\n"
|
|
||||||
}
|
|
||||||
links += url.String()
|
|
||||||
}
|
|
||||||
return links
|
|
||||||
}
|
|
||||||
|
|
||||||
link := fmt.Sprintf("vless://%s@%s:%d", uuid, address, port)
|
|
||||||
url, _ := url.Parse(link)
|
|
||||||
q := url.Query()
|
|
||||||
|
|
||||||
for k, v := range params {
|
|
||||||
q.Add(k, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the new query values on the URL
|
|
||||||
url.RawQuery = q.Encode()
|
|
||||||
|
|
||||||
url.Fragment = s.genRemark(inbound, email, "")
|
|
||||||
return url.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string {
|
|
||||||
address := s.address
|
|
||||||
if inbound.Protocol != model.Trojan {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
var stream map[string]interface{}
|
|
||||||
json.Unmarshal([]byte(inbound.StreamSettings), &stream)
|
|
||||||
clients, _ := s.inboundService.GetClients(inbound)
|
|
||||||
clientIndex := -1
|
|
||||||
for i, client := range clients {
|
|
||||||
if client.Email == email {
|
|
||||||
clientIndex = i
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
password := clients[clientIndex].Password
|
|
||||||
port := inbound.Port
|
|
||||||
streamNetwork := stream["network"].(string)
|
|
||||||
params := make(map[string]string)
|
|
||||||
params["type"] = streamNetwork
|
|
||||||
|
|
||||||
switch streamNetwork {
|
|
||||||
case "tcp":
|
|
||||||
tcp, _ := stream["tcpSettings"].(map[string]interface{})
|
|
||||||
header, _ := tcp["header"].(map[string]interface{})
|
|
||||||
typeStr, _ := header["type"].(string)
|
|
||||||
if typeStr == "http" {
|
|
||||||
request := header["request"].(map[string]interface{})
|
|
||||||
requestPath, _ := request["path"].([]interface{})
|
|
||||||
params["path"] = requestPath[0].(string)
|
|
||||||
headers, _ := request["headers"].(map[string]interface{})
|
|
||||||
params["host"] = searchHost(headers)
|
|
||||||
params["headerType"] = "http"
|
|
||||||
}
|
|
||||||
case "kcp":
|
|
||||||
kcp, _ := stream["kcpSettings"].(map[string]interface{})
|
|
||||||
header, _ := kcp["header"].(map[string]interface{})
|
|
||||||
params["headerType"] = header["type"].(string)
|
|
||||||
params["seed"] = kcp["seed"].(string)
|
|
||||||
case "ws":
|
|
||||||
ws, _ := stream["wsSettings"].(map[string]interface{})
|
|
||||||
params["path"] = ws["path"].(string)
|
|
||||||
if host, ok := ws["host"].(string); ok && len(host) > 0 {
|
|
||||||
params["host"] = host
|
|
||||||
} else {
|
|
||||||
headers, _ := ws["headers"].(map[string]interface{})
|
|
||||||
params["host"] = searchHost(headers)
|
|
||||||
}
|
|
||||||
case "grpc":
|
|
||||||
grpc, _ := stream["grpcSettings"].(map[string]interface{})
|
|
||||||
params["serviceName"] = grpc["serviceName"].(string)
|
|
||||||
params["authority"], _ = grpc["authority"].(string)
|
|
||||||
if grpc["multiMode"].(bool) {
|
|
||||||
params["mode"] = "multi"
|
|
||||||
}
|
|
||||||
case "httpupgrade":
|
|
||||||
httpupgrade, _ := stream["httpupgradeSettings"].(map[string]interface{})
|
|
||||||
params["path"] = httpupgrade["path"].(string)
|
|
||||||
if host, ok := httpupgrade["host"].(string); ok && len(host) > 0 {
|
|
||||||
params["host"] = host
|
|
||||||
} else {
|
|
||||||
headers, _ := httpupgrade["headers"].(map[string]interface{})
|
|
||||||
params["host"] = searchHost(headers)
|
|
||||||
}
|
|
||||||
case "xhttp":
|
|
||||||
xhttp, _ := stream["xhttpSettings"].(map[string]interface{})
|
|
||||||
params["path"] = xhttp["path"].(string)
|
|
||||||
if host, ok := xhttp["host"].(string); ok && len(host) > 0 {
|
|
||||||
params["host"] = host
|
|
||||||
} else {
|
|
||||||
headers, _ := xhttp["headers"].(map[string]interface{})
|
|
||||||
params["host"] = searchHost(headers)
|
|
||||||
}
|
|
||||||
params["mode"] = xhttp["mode"].(string)
|
|
||||||
}
|
|
||||||
security, _ := stream["security"].(string)
|
|
||||||
if security == "tls" {
|
|
||||||
params["security"] = "tls"
|
|
||||||
tlsSetting, _ := stream["tlsSettings"].(map[string]interface{})
|
|
||||||
alpns, _ := tlsSetting["alpn"].([]interface{})
|
|
||||||
var alpn []string
|
|
||||||
for _, a := range alpns {
|
|
||||||
alpn = append(alpn, a.(string))
|
|
||||||
}
|
|
||||||
if len(alpn) > 0 {
|
|
||||||
params["alpn"] = strings.Join(alpn, ",")
|
|
||||||
}
|
|
||||||
if sniValue, ok := searchKey(tlsSetting, "serverName"); ok {
|
|
||||||
params["sni"], _ = sniValue.(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
tlsSettings, _ := searchKey(tlsSetting, "settings")
|
|
||||||
if tlsSetting != nil {
|
|
||||||
if fpValue, ok := searchKey(tlsSettings, "fingerprint"); ok {
|
|
||||||
params["fp"], _ = fpValue.(string)
|
|
||||||
}
|
|
||||||
if insecure, ok := searchKey(tlsSettings, "allowInsecure"); ok {
|
|
||||||
if insecure.(bool) {
|
|
||||||
params["allowInsecure"] = "1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if security == "reality" {
|
|
||||||
params["security"] = "reality"
|
|
||||||
realitySetting, _ := stream["realitySettings"].(map[string]interface{})
|
|
||||||
realitySettings, _ := searchKey(realitySetting, "settings")
|
|
||||||
if realitySetting != nil {
|
|
||||||
if sniValue, ok := searchKey(realitySetting, "serverNames"); ok {
|
|
||||||
sNames, _ := sniValue.([]interface{})
|
|
||||||
params["sni"] = sNames[random.Num(len(sNames))].(string)
|
|
||||||
}
|
|
||||||
if pbkValue, ok := searchKey(realitySettings, "publicKey"); ok {
|
|
||||||
params["pbk"], _ = pbkValue.(string)
|
|
||||||
}
|
|
||||||
if sidValue, ok := searchKey(realitySetting, "shortIds"); ok {
|
|
||||||
shortIds, _ := sidValue.([]interface{})
|
|
||||||
params["sid"] = shortIds[random.Num(len(shortIds))].(string)
|
|
||||||
}
|
|
||||||
if fpValue, ok := searchKey(realitySettings, "fingerprint"); ok {
|
|
||||||
if fp, ok := fpValue.(string); ok && len(fp) > 0 {
|
|
||||||
params["fp"] = fp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
params["spx"] = "/" + random.Seq(15)
|
|
||||||
}
|
|
||||||
|
|
||||||
if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 {
|
|
||||||
params["flow"] = clients[clientIndex].Flow
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if security != "tls" && security != "reality" {
|
|
||||||
params["security"] = "none"
|
|
||||||
}
|
|
||||||
|
|
||||||
externalProxies, _ := stream["externalProxy"].([]interface{})
|
|
||||||
|
|
||||||
if len(externalProxies) > 0 {
|
|
||||||
links := ""
|
|
||||||
for index, externalProxy := range externalProxies {
|
|
||||||
ep, _ := externalProxy.(map[string]interface{})
|
|
||||||
newSecurity, _ := ep["forceTls"].(string)
|
|
||||||
dest, _ := ep["dest"].(string)
|
|
||||||
port := int(ep["port"].(float64))
|
|
||||||
link := fmt.Sprintf("trojan://%s@%s:%d", password, dest, port)
|
|
||||||
|
|
||||||
if newSecurity != "same" {
|
|
||||||
params["security"] = newSecurity
|
|
||||||
} else {
|
|
||||||
params["security"] = security
|
|
||||||
}
|
|
||||||
url, _ := url.Parse(link)
|
|
||||||
q := url.Query()
|
|
||||||
|
|
||||||
for k, v := range params {
|
|
||||||
if !(newSecurity == "none" && (k == "alpn" || k == "sni" || k == "fp" || k == "allowInsecure")) {
|
|
||||||
q.Add(k, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the new query values on the URL
|
|
||||||
url.RawQuery = q.Encode()
|
|
||||||
|
|
||||||
url.Fragment = s.genRemark(inbound, email, ep["remark"].(string))
|
|
||||||
|
|
||||||
if index > 0 {
|
|
||||||
links += "\n"
|
|
||||||
}
|
|
||||||
links += url.String()
|
|
||||||
}
|
|
||||||
return links
|
|
||||||
}
|
|
||||||
|
|
||||||
link := fmt.Sprintf("trojan://%s@%s:%d", password, address, port)
|
|
||||||
|
|
||||||
url, _ := url.Parse(link)
|
|
||||||
q := url.Query()
|
|
||||||
|
|
||||||
for k, v := range params {
|
|
||||||
q.Add(k, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the new query values on the URL
|
|
||||||
url.RawQuery = q.Encode()
|
|
||||||
|
|
||||||
url.Fragment = s.genRemark(inbound, email, "")
|
|
||||||
return url.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SubService) genShadowsocksLink(inbound *model.Inbound, email string) string {
|
|
||||||
address := s.address
|
|
||||||
if inbound.Protocol != model.Shadowsocks {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
var stream map[string]interface{}
|
|
||||||
json.Unmarshal([]byte(inbound.StreamSettings), &stream)
|
|
||||||
clients, _ := s.inboundService.GetClients(inbound)
|
|
||||||
|
|
||||||
var settings map[string]interface{}
|
|
||||||
json.Unmarshal([]byte(inbound.Settings), &settings)
|
|
||||||
inboundPassword := settings["password"].(string)
|
|
||||||
method := settings["method"].(string)
|
|
||||||
clientIndex := -1
|
|
||||||
for i, client := range clients {
|
|
||||||
if client.Email == email {
|
|
||||||
clientIndex = i
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
streamNetwork := stream["network"].(string)
|
|
||||||
params := make(map[string]string)
|
|
||||||
params["type"] = streamNetwork
|
|
||||||
|
|
||||||
switch streamNetwork {
|
|
||||||
case "tcp":
|
|
||||||
tcp, _ := stream["tcpSettings"].(map[string]interface{})
|
|
||||||
header, _ := tcp["header"].(map[string]interface{})
|
|
||||||
typeStr, _ := header["type"].(string)
|
|
||||||
if typeStr == "http" {
|
|
||||||
request := header["request"].(map[string]interface{})
|
|
||||||
requestPath, _ := request["path"].([]interface{})
|
|
||||||
params["path"] = requestPath[0].(string)
|
|
||||||
headers, _ := request["headers"].(map[string]interface{})
|
|
||||||
params["host"] = searchHost(headers)
|
|
||||||
params["headerType"] = "http"
|
|
||||||
}
|
|
||||||
case "kcp":
|
|
||||||
kcp, _ := stream["kcpSettings"].(map[string]interface{})
|
|
||||||
header, _ := kcp["header"].(map[string]interface{})
|
|
||||||
params["headerType"] = header["type"].(string)
|
|
||||||
params["seed"] = kcp["seed"].(string)
|
|
||||||
case "ws":
|
|
||||||
ws, _ := stream["wsSettings"].(map[string]interface{})
|
|
||||||
params["path"] = ws["path"].(string)
|
|
||||||
if host, ok := ws["host"].(string); ok && len(host) > 0 {
|
|
||||||
params["host"] = host
|
|
||||||
} else {
|
|
||||||
headers, _ := ws["headers"].(map[string]interface{})
|
|
||||||
params["host"] = searchHost(headers)
|
|
||||||
}
|
|
||||||
case "grpc":
|
|
||||||
grpc, _ := stream["grpcSettings"].(map[string]interface{})
|
|
||||||
params["serviceName"] = grpc["serviceName"].(string)
|
|
||||||
params["authority"], _ = grpc["authority"].(string)
|
|
||||||
if grpc["multiMode"].(bool) {
|
|
||||||
params["mode"] = "multi"
|
|
||||||
}
|
|
||||||
case "httpupgrade":
|
|
||||||
httpupgrade, _ := stream["httpupgradeSettings"].(map[string]interface{})
|
|
||||||
params["path"] = httpupgrade["path"].(string)
|
|
||||||
if host, ok := httpupgrade["host"].(string); ok && len(host) > 0 {
|
|
||||||
params["host"] = host
|
|
||||||
} else {
|
|
||||||
headers, _ := httpupgrade["headers"].(map[string]interface{})
|
|
||||||
params["host"] = searchHost(headers)
|
|
||||||
}
|
|
||||||
case "xhttp":
|
|
||||||
xhttp, _ := stream["xhttpSettings"].(map[string]interface{})
|
|
||||||
params["path"] = xhttp["path"].(string)
|
|
||||||
if host, ok := xhttp["host"].(string); ok && len(host) > 0 {
|
|
||||||
params["host"] = host
|
|
||||||
} else {
|
|
||||||
headers, _ := xhttp["headers"].(map[string]interface{})
|
|
||||||
params["host"] = searchHost(headers)
|
|
||||||
}
|
|
||||||
params["mode"] = xhttp["mode"].(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
security, _ := stream["security"].(string)
|
|
||||||
if security == "tls" {
|
|
||||||
params["security"] = "tls"
|
|
||||||
tlsSetting, _ := stream["tlsSettings"].(map[string]interface{})
|
|
||||||
alpns, _ := tlsSetting["alpn"].([]interface{})
|
|
||||||
var alpn []string
|
|
||||||
for _, a := range alpns {
|
|
||||||
alpn = append(alpn, a.(string))
|
|
||||||
}
|
|
||||||
if len(alpn) > 0 {
|
|
||||||
params["alpn"] = strings.Join(alpn, ",")
|
|
||||||
}
|
|
||||||
if sniValue, ok := searchKey(tlsSetting, "serverName"); ok {
|
|
||||||
params["sni"], _ = sniValue.(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
tlsSettings, _ := searchKey(tlsSetting, "settings")
|
|
||||||
if tlsSetting != nil {
|
|
||||||
if fpValue, ok := searchKey(tlsSettings, "fingerprint"); ok {
|
|
||||||
params["fp"], _ = fpValue.(string)
|
|
||||||
}
|
|
||||||
if insecure, ok := searchKey(tlsSettings, "allowInsecure"); ok {
|
|
||||||
if insecure.(bool) {
|
|
||||||
params["allowInsecure"] = "1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
encPart := fmt.Sprintf("%s:%s", method, clients[clientIndex].Password)
|
|
||||||
if method[0] == '2' {
|
|
||||||
encPart = fmt.Sprintf("%s:%s:%s", method, inboundPassword, clients[clientIndex].Password)
|
|
||||||
}
|
|
||||||
|
|
||||||
externalProxies, _ := stream["externalProxy"].([]interface{})
|
|
||||||
|
|
||||||
if len(externalProxies) > 0 {
|
|
||||||
links := ""
|
|
||||||
for index, externalProxy := range externalProxies {
|
|
||||||
ep, _ := externalProxy.(map[string]interface{})
|
|
||||||
newSecurity, _ := ep["forceTls"].(string)
|
|
||||||
dest, _ := ep["dest"].(string)
|
|
||||||
port := int(ep["port"].(float64))
|
|
||||||
link := fmt.Sprintf("ss://%s@%s:%d", base64.StdEncoding.EncodeToString([]byte(encPart)), dest, port)
|
|
||||||
|
|
||||||
if newSecurity != "same" {
|
|
||||||
params["security"] = newSecurity
|
|
||||||
} else {
|
|
||||||
params["security"] = security
|
|
||||||
}
|
|
||||||
url, _ := url.Parse(link)
|
|
||||||
q := url.Query()
|
|
||||||
|
|
||||||
for k, v := range params {
|
|
||||||
if !(newSecurity == "none" && (k == "alpn" || k == "sni" || k == "fp" || k == "allowInsecure")) {
|
|
||||||
q.Add(k, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the new query values on the URL
|
|
||||||
url.RawQuery = q.Encode()
|
|
||||||
|
|
||||||
url.Fragment = s.genRemark(inbound, email, ep["remark"].(string))
|
|
||||||
|
|
||||||
if index > 0 {
|
|
||||||
links += "\n"
|
|
||||||
}
|
|
||||||
links += url.String()
|
|
||||||
}
|
|
||||||
return links
|
|
||||||
}
|
|
||||||
|
|
||||||
link := fmt.Sprintf("ss://%s@%s:%d", base64.StdEncoding.EncodeToString([]byte(encPart)), address, inbound.Port)
|
|
||||||
url, _ := url.Parse(link)
|
|
||||||
q := url.Query()
|
|
||||||
|
|
||||||
for k, v := range params {
|
|
||||||
q.Add(k, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the new query values on the URL
|
|
||||||
url.RawQuery = q.Encode()
|
|
||||||
|
|
||||||
url.Fragment = s.genRemark(inbound, email, "")
|
|
||||||
return url.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SubService) genRemark(inbound *model.Inbound, email string, extra string) string {
|
|
||||||
separationChar := string(s.remarkModel[0])
|
|
||||||
orderChars := s.remarkModel[1:]
|
|
||||||
orders := map[byte]string{
|
|
||||||
'i': "",
|
|
||||||
'e': "",
|
|
||||||
'o': "",
|
|
||||||
}
|
|
||||||
if len(email) > 0 {
|
|
||||||
orders['e'] = email
|
|
||||||
}
|
|
||||||
if len(inbound.Remark) > 0 {
|
|
||||||
orders['i'] = inbound.Remark
|
|
||||||
}
|
|
||||||
if len(extra) > 0 {
|
|
||||||
orders['o'] = extra
|
|
||||||
}
|
|
||||||
|
|
||||||
var remark []string
|
|
||||||
for i := 0; i < len(orderChars); i++ {
|
|
||||||
char := orderChars[i]
|
|
||||||
order, exists := orders[char]
|
|
||||||
if exists && order != "" {
|
|
||||||
remark = append(remark, order)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.showInfo {
|
|
||||||
statsExist := false
|
|
||||||
var stats xray.ClientTraffic
|
|
||||||
for _, clientStat := range inbound.ClientStats {
|
|
||||||
if clientStat.Email == email {
|
|
||||||
stats = clientStat
|
|
||||||
statsExist = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get remained days
|
|
||||||
if statsExist {
|
|
||||||
if !stats.Enable {
|
|
||||||
return fmt.Sprintf("⛔️N/A%s%s", separationChar, strings.Join(remark, separationChar))
|
|
||||||
}
|
|
||||||
if vol := stats.Total - (stats.Up + stats.Down); vol > 0 {
|
|
||||||
remark = append(remark, fmt.Sprintf("%s%s", common.FormatTraffic(vol), "📊"))
|
|
||||||
}
|
|
||||||
now := time.Now().Unix()
|
|
||||||
switch exp := stats.ExpiryTime / 1000; {
|
|
||||||
case exp > 0:
|
|
||||||
remainingSeconds := exp - now
|
|
||||||
days := remainingSeconds / 86400
|
|
||||||
hours := (remainingSeconds % 86400) / 3600
|
|
||||||
minutes := (remainingSeconds % 3600) / 60
|
|
||||||
if days > 0 {
|
|
||||||
if hours > 0 {
|
|
||||||
remark = append(remark, fmt.Sprintf("%dD,%dH⏳", days, hours))
|
|
||||||
} else {
|
|
||||||
remark = append(remark, fmt.Sprintf("%dD⏳", days))
|
|
||||||
}
|
|
||||||
} else if hours > 0 {
|
|
||||||
remark = append(remark, fmt.Sprintf("%dH⏳", hours))
|
|
||||||
} else {
|
|
||||||
remark = append(remark, fmt.Sprintf("%dM⏳", minutes))
|
|
||||||
}
|
|
||||||
case exp < 0:
|
|
||||||
days := exp / -86400
|
|
||||||
hours := (exp % -86400) / 3600
|
|
||||||
minutes := (exp % -3600) / 60
|
|
||||||
if days > 0 {
|
|
||||||
if hours > 0 {
|
|
||||||
remark = append(remark, fmt.Sprintf("%dD,%dH⏳", days, hours))
|
|
||||||
} else {
|
|
||||||
remark = append(remark, fmt.Sprintf("%dD⏳", days))
|
|
||||||
}
|
|
||||||
} else if hours > 0 {
|
|
||||||
remark = append(remark, fmt.Sprintf("%dH⏳", hours))
|
|
||||||
} else {
|
|
||||||
remark = append(remark, fmt.Sprintf("%dM⏳", minutes))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return strings.Join(remark, separationChar)
|
|
||||||
}
|
|
||||||
|
|
||||||
func searchKey(data interface{}, key string) (interface{}, bool) {
|
|
||||||
switch val := data.(type) {
|
|
||||||
case map[string]interface{}:
|
|
||||||
for k, v := range val {
|
|
||||||
if k == key {
|
|
||||||
return v, true
|
|
||||||
}
|
|
||||||
if result, ok := searchKey(v, key); ok {
|
|
||||||
return result, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case []interface{}:
|
|
||||||
for _, v := range val {
|
|
||||||
if result, ok := searchKey(v, key); ok {
|
|
||||||
return result, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
func searchHost(headers interface{}) string {
|
|
||||||
data, _ := headers.(map[string]interface{})
|
|
||||||
for k, v := range data {
|
|
||||||
if strings.EqualFold(k, "host") {
|
|
||||||
switch v.(type) {
|
|
||||||
case []interface{}:
|
|
||||||
hosts, _ := v.([]interface{})
|
|
||||||
if len(hosts) > 0 {
|
|
||||||
return hosts[0].(string)
|
|
||||||
} else {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
case interface{}:
|
|
||||||
return v.(string)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ""
|
|
||||||
}
|
|
Loading…
Reference in a new issue