Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d82af6f9bd | |||
| 6b785c3404 | |||
| df1a271efa | |||
| bd9bd8590c | |||
| d186875ab7 | |||
| 3f7657c080 | |||
| a5f4c46066 |
@@ -0,0 +1,125 @@
|
|||||||
|
package migration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/url"
|
||||||
|
"s-ui/database/model"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func migrate_dns(db *gorm.DB) error {
|
||||||
|
var configStr string
|
||||||
|
err := db.Model(model.Setting{}).Select("value").Where("key = ?", "config").First(&configStr).Error
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if configStr == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var config map[string]interface{}
|
||||||
|
err = json.Unmarshal([]byte(configStr), &config)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if dnsConfig, ok := config["dns"].(map[string]interface{}); ok {
|
||||||
|
if dnsServers, ok := dnsConfig["servers"].([]interface{}); ok {
|
||||||
|
for index, dnsServer := range dnsServers {
|
||||||
|
if dnsServer, ok := dnsServer.(map[string]interface{}); ok {
|
||||||
|
if addr, ok := dnsServer["address"].(string); ok && addr != "" {
|
||||||
|
switch addr {
|
||||||
|
case "local":
|
||||||
|
delete(dnsServer, "address")
|
||||||
|
dnsServer["type"] = "local"
|
||||||
|
case "fakeip":
|
||||||
|
delete(dnsServer, "address")
|
||||||
|
dnsServer["type"] = "fakeip"
|
||||||
|
default:
|
||||||
|
addrParsed, err := url.Parse(addr)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch addrParsed.Scheme {
|
||||||
|
case "":
|
||||||
|
dnsServer["type"] = "udp"
|
||||||
|
dnsServer["server"] = addr
|
||||||
|
case "udp", "tcp", "tls", "quic", "https", "h3":
|
||||||
|
dnsServer["type"] = addrParsed.Scheme
|
||||||
|
dnsServer["server"] = addrParsed.Host
|
||||||
|
case "dhcp":
|
||||||
|
dnsServer["type"] = addrParsed.Scheme
|
||||||
|
if addrParsed.Host != "auto" && addrParsed.Host != "" {
|
||||||
|
dnsServer["interface"] = addrParsed.Host
|
||||||
|
}
|
||||||
|
case "rcode":
|
||||||
|
dnsServer["type"] = "predefined"
|
||||||
|
dnsServer["responses"] = []map[string]string{
|
||||||
|
{
|
||||||
|
"rcode": strings.ToUpper(addrParsed.Host),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete(dnsServer, "address")
|
||||||
|
if addrParsed.Port() != "" {
|
||||||
|
port, err := strconv.Atoi(addrParsed.Port())
|
||||||
|
if err == nil {
|
||||||
|
dnsServer["server_port"] = port
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if address_resolver, ok := dnsServer["address_resolver"].(string); ok && address_resolver != "" {
|
||||||
|
delete(dnsServer, "address_resolver")
|
||||||
|
dnsServer["domain_resolver"] = address_resolver
|
||||||
|
}
|
||||||
|
delete(dnsServer, "strategy")
|
||||||
|
}
|
||||||
|
dnsServers[index] = dnsServer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dnsConfig["servers"] = dnsServers
|
||||||
|
}
|
||||||
|
config["dns"] = dnsConfig
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// save changes
|
||||||
|
configs, err := json.MarshalIndent(config, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return db.Model(model.Setting{}).Where("key = ?", "config").Update("value", string(configs)).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func remove_outbound_strategy(db *gorm.DB) error {
|
||||||
|
var outbounds []model.Outbound
|
||||||
|
err := db.Find(&outbounds).Where("json_extract(options, '$.domain_strategy') IS NOT NULL").Error
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, outbound := range outbounds {
|
||||||
|
var restFields map[string]json.RawMessage
|
||||||
|
if err := json.Unmarshal(outbound.Options, &restFields); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
delete(restFields, "domain_strategy")
|
||||||
|
outbound.Options, _ = json.MarshalIndent(restFields, "", " ")
|
||||||
|
db.Save(&outbound)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func to1_3(db *gorm.DB) error {
|
||||||
|
err := migrate_dns(db)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = remove_outbound_strategy(db)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
+11
-1
@@ -56,10 +56,20 @@ func MigrateDb() {
|
|||||||
log.Fatal("Migration to 1.2 failed: ", err)
|
log.Fatal("Migration to 1.2 failed: ", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
dbVersion = "1.2"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Before 1.3
|
||||||
|
if dbVersion[0:3] == "1.2" {
|
||||||
|
err = to1_3(tx)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Migration to 1.3 failed: ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set version
|
// Set version
|
||||||
err = tx.Raw("UPDATE settings SET value = ? WHERE key = ?", currentVersion, "version").Error
|
err = tx.Exec("UPDATE settings SET value = ? WHERE key = ?", currentVersion, "version").Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Update version failed: ", err)
|
log.Fatal("Update version failed: ", err)
|
||||||
return
|
return
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
1.3.0-beta.0
|
1.3.0-beta.2
|
||||||
+1
-1
Submodule frontend updated: c4ac3ad924...2d7eb9d640
@@ -9,7 +9,7 @@ require (
|
|||||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
|
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
|
||||||
github.com/robfig/cron/v3 v3.0.1
|
github.com/robfig/cron/v3 v3.0.1
|
||||||
github.com/sagernet/sing v0.6.11-0.20250521033217-30d675ea099b
|
github.com/sagernet/sing v0.6.11-0.20250521033217-30d675ea099b
|
||||||
github.com/sagernet/sing-box v1.12.0-beta.19
|
github.com/sagernet/sing-box v1.12.0-beta.21
|
||||||
github.com/sagernet/sing-dns v0.4.5
|
github.com/sagernet/sing-dns v0.4.5
|
||||||
github.com/shirou/gopsutil/v4 v4.25.4
|
github.com/shirou/gopsutil/v4 v4.25.4
|
||||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10
|
||||||
@@ -107,12 +107,12 @@ require (
|
|||||||
github.com/sagernet/nftables v0.3.0-beta.4 // indirect
|
github.com/sagernet/nftables v0.3.0-beta.4 // indirect
|
||||||
github.com/sagernet/quic-go v0.52.0-beta.1 // indirect
|
github.com/sagernet/quic-go v0.52.0-beta.1 // indirect
|
||||||
github.com/sagernet/sing-mux v0.3.2 // indirect
|
github.com/sagernet/sing-mux v0.3.2 // indirect
|
||||||
github.com/sagernet/sing-quic v0.5.0-beta.1 // indirect
|
github.com/sagernet/sing-quic v0.5.0-beta.2 // indirect
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.7 // indirect
|
github.com/sagernet/sing-shadowsocks v0.2.8 // indirect
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.2.1 // indirect
|
github.com/sagernet/sing-shadowsocks2 v0.2.1 // indirect
|
||||||
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11 // indirect
|
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11 // indirect
|
||||||
github.com/sagernet/sing-tun v0.6.6-0.20250428031943-0686f8c4f210 // indirect
|
github.com/sagernet/sing-tun v0.6.6-0.20250428031943-0686f8c4f210 // indirect
|
||||||
github.com/sagernet/sing-vmess v0.2.2-0.20250503051933-9b4cf17393f8 // indirect
|
github.com/sagernet/sing-vmess v0.2.4-0.20250527060135-661c827800bc // indirect
|
||||||
github.com/sagernet/smux v1.5.34-mod.2 // indirect
|
github.com/sagernet/smux v1.5.34-mod.2 // indirect
|
||||||
github.com/sagernet/tailscale v1.80.3-mod.5 // indirect
|
github.com/sagernet/tailscale v1.80.3-mod.5 // indirect
|
||||||
github.com/sagernet/wireguard-go v0.0.1-beta.7 // indirect
|
github.com/sagernet/wireguard-go v0.0.1-beta.7 // indirect
|
||||||
|
|||||||
@@ -220,14 +220,20 @@ github.com/sagernet/sing v0.6.11-0.20250521033217-30d675ea099b h1:ZjTCYPb5f7aHdf
|
|||||||
github.com/sagernet/sing v0.6.11-0.20250521033217-30d675ea099b/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
github.com/sagernet/sing v0.6.11-0.20250521033217-30d675ea099b/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||||
github.com/sagernet/sing-box v1.12.0-beta.19 h1:UrEuYcewe9C68aGuQyE+dDRjtv+uZXXh5DK9hkQ0UTE=
|
github.com/sagernet/sing-box v1.12.0-beta.19 h1:UrEuYcewe9C68aGuQyE+dDRjtv+uZXXh5DK9hkQ0UTE=
|
||||||
github.com/sagernet/sing-box v1.12.0-beta.19/go.mod h1:zhoIuo39/5gsmJPIMK5P9Z0/SiRmFJsGtfl+8j+Cdcw=
|
github.com/sagernet/sing-box v1.12.0-beta.19/go.mod h1:zhoIuo39/5gsmJPIMK5P9Z0/SiRmFJsGtfl+8j+Cdcw=
|
||||||
|
github.com/sagernet/sing-box v1.12.0-beta.21 h1:4WKaD0NAfgMDrDJaoQs7QC1tF/zIQT2DRcALW+CSDFM=
|
||||||
|
github.com/sagernet/sing-box v1.12.0-beta.21/go.mod h1:0IuY97uJ8ydg+rA2xdy/Wn3n1182jlf7mEeBUYP7xWw=
|
||||||
github.com/sagernet/sing-dns v0.4.5 h1:D9REN14qx2FTrZRBrtFLL99f2CuFzQ9S7mIf8uV5hZI=
|
github.com/sagernet/sing-dns v0.4.5 h1:D9REN14qx2FTrZRBrtFLL99f2CuFzQ9S7mIf8uV5hZI=
|
||||||
github.com/sagernet/sing-dns v0.4.5/go.mod h1:dweQs54ng2YGzoJfz+F9dGuDNdP5pJ3PLeggnK5VWc8=
|
github.com/sagernet/sing-dns v0.4.5/go.mod h1:dweQs54ng2YGzoJfz+F9dGuDNdP5pJ3PLeggnK5VWc8=
|
||||||
github.com/sagernet/sing-mux v0.3.2 h1:meZVFiiStvHThb/trcpAkCrmtJOuItG5Dzl1RRP5/NE=
|
github.com/sagernet/sing-mux v0.3.2 h1:meZVFiiStvHThb/trcpAkCrmtJOuItG5Dzl1RRP5/NE=
|
||||||
github.com/sagernet/sing-mux v0.3.2/go.mod h1:pht8iFY4c9Xltj7rhVd208npkNaeCxzyXCgulDPLUDA=
|
github.com/sagernet/sing-mux v0.3.2/go.mod h1:pht8iFY4c9Xltj7rhVd208npkNaeCxzyXCgulDPLUDA=
|
||||||
github.com/sagernet/sing-quic v0.5.0-beta.1 h1:nC0i/s8LhlZB8ev6laZCXF/uiwAE4kRdT4PcDdE4rI4=
|
github.com/sagernet/sing-quic v0.5.0-beta.1 h1:nC0i/s8LhlZB8ev6laZCXF/uiwAE4kRdT4PcDdE4rI4=
|
||||||
github.com/sagernet/sing-quic v0.5.0-beta.1/go.mod h1:SAv/qdeDN+75msGG5U5ZIwG+3Ua50jVIKNrRSY8pkx0=
|
github.com/sagernet/sing-quic v0.5.0-beta.1/go.mod h1:SAv/qdeDN+75msGG5U5ZIwG+3Ua50jVIKNrRSY8pkx0=
|
||||||
|
github.com/sagernet/sing-quic v0.5.0-beta.2 h1:j7KAbBuGmsKwSxVAQL5soJ+wDqxim4/llK2kxB0hSKk=
|
||||||
|
github.com/sagernet/sing-quic v0.5.0-beta.2/go.mod h1:SAv/qdeDN+75msGG5U5ZIwG+3Ua50jVIKNrRSY8pkx0=
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
|
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE=
|
github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE=
|
||||||
|
github.com/sagernet/sing-shadowsocks v0.2.8 h1:PURj5PRoAkqeHh2ZW205RWzN9E9RtKCVCzByXruQWfE=
|
||||||
|
github.com/sagernet/sing-shadowsocks v0.2.8/go.mod h1:lo7TWEMDcN5/h5B8S0ew+r78ZODn6SwVaFhvB6H+PTI=
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.2.1 h1:dWV9OXCeFPuYGHb6IRqlSptVnSzOelnqqs2gQ2/Qioo=
|
github.com/sagernet/sing-shadowsocks2 v0.2.1 h1:dWV9OXCeFPuYGHb6IRqlSptVnSzOelnqqs2gQ2/Qioo=
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.2.1/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
|
github.com/sagernet/sing-shadowsocks2 v0.2.1/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
|
||||||
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11 h1:tK+75l64tm9WvEFrYRE1t0YxoFdWQqw/h7Uhzj0vJ+w=
|
github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11 h1:tK+75l64tm9WvEFrYRE1t0YxoFdWQqw/h7Uhzj0vJ+w=
|
||||||
@@ -236,6 +242,8 @@ github.com/sagernet/sing-tun v0.6.6-0.20250428031943-0686f8c4f210 h1:6H4BZaTqKI3
|
|||||||
github.com/sagernet/sing-tun v0.6.6-0.20250428031943-0686f8c4f210/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE=
|
github.com/sagernet/sing-tun v0.6.6-0.20250428031943-0686f8c4f210/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE=
|
||||||
github.com/sagernet/sing-vmess v0.2.2-0.20250503051933-9b4cf17393f8 h1:zW+zAOCxUIqBCgnZiPovt1uQ3S+zBS+w0NGp+1zITGA=
|
github.com/sagernet/sing-vmess v0.2.2-0.20250503051933-9b4cf17393f8 h1:zW+zAOCxUIqBCgnZiPovt1uQ3S+zBS+w0NGp+1zITGA=
|
||||||
github.com/sagernet/sing-vmess v0.2.2-0.20250503051933-9b4cf17393f8/go.mod h1:IL8Rr+EGwuqijszZkNrEFTQDKhilEpkqFqOlvdpS6/w=
|
github.com/sagernet/sing-vmess v0.2.2-0.20250503051933-9b4cf17393f8/go.mod h1:IL8Rr+EGwuqijszZkNrEFTQDKhilEpkqFqOlvdpS6/w=
|
||||||
|
github.com/sagernet/sing-vmess v0.2.4-0.20250527060135-661c827800bc h1:kd3olNfnf/1EAAHDQm0flN9eihyjpeQDKdGONlLtXfc=
|
||||||
|
github.com/sagernet/sing-vmess v0.2.4-0.20250527060135-661c827800bc/go.mod h1:IL8Rr+EGwuqijszZkNrEFTQDKhilEpkqFqOlvdpS6/w=
|
||||||
github.com/sagernet/smux v1.5.34-mod.2 h1:gkmBjIjlJ2zQKpLigOkFur5kBKdV6bNRoFu2WkltRQ4=
|
github.com/sagernet/smux v1.5.34-mod.2 h1:gkmBjIjlJ2zQKpLigOkFur5kBKdV6bNRoFu2WkltRQ4=
|
||||||
github.com/sagernet/smux v1.5.34-mod.2/go.mod h1:0KW0+R+ycvA2INW4gbsd7BNyg+HEfLIAxa5N02/28Zc=
|
github.com/sagernet/smux v1.5.34-mod.2/go.mod h1:0KW0+R+ycvA2INW4gbsd7BNyg+HEfLIAxa5N02/28Zc=
|
||||||
github.com/sagernet/tailscale v1.80.3-mod.5 h1:7V7z+p2C//TGtff20pPnDCt3qP6uFyY62peJoKF9z/A=
|
github.com/sagernet/tailscale v1.80.3-mod.5 h1:7V7z+p2C//TGtff20pPnDCt3qP6uFyY62peJoKF9z/A=
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ var defaultValueMap = map[string]string{
|
|||||||
"subShowInfo": "false",
|
"subShowInfo": "false",
|
||||||
"subURI": "",
|
"subURI": "",
|
||||||
"subJsonExt": "",
|
"subJsonExt": "",
|
||||||
|
"subClashExt": "",
|
||||||
"config": defaultConfig,
|
"config": defaultConfig,
|
||||||
"version": config.GetVersion(),
|
"version": config.GetVersion(),
|
||||||
}
|
}
|
||||||
@@ -392,6 +393,10 @@ func (s *SettingService) GetSubJsonExt() (string, error) {
|
|||||||
return s.getString("subJsonExt")
|
return s.getString("subJsonExt")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SettingService) GetSubClashExt() (string, error) {
|
||||||
|
return s.getString("subClashExt")
|
||||||
|
}
|
||||||
|
|
||||||
func (s *SettingService) fileExists(path string) error {
|
func (s *SettingService) fileExists(path string) error {
|
||||||
_, err := os.Stat(path)
|
_, err := os.Stat(path)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -0,0 +1,332 @@
|
|||||||
|
package sub
|
||||||
|
|
||||||
|
import (
|
||||||
|
"s-ui/logger"
|
||||||
|
"s-ui/service"
|
||||||
|
"s-ui/util"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ClashService struct {
|
||||||
|
service.SettingService
|
||||||
|
JsonService
|
||||||
|
LinkService
|
||||||
|
}
|
||||||
|
|
||||||
|
const basicClashConfig = `mixed-port: 7890
|
||||||
|
allow-lan: false
|
||||||
|
mode: rule
|
||||||
|
log-level: info
|
||||||
|
external-controller: 127.0.0.1:9090
|
||||||
|
tun:
|
||||||
|
enable: true
|
||||||
|
stack: system
|
||||||
|
auto-route: true
|
||||||
|
auto-detect-interface: true
|
||||||
|
dns-hijack:
|
||||||
|
- any:53
|
||||||
|
dns:
|
||||||
|
enable: true
|
||||||
|
ipv6: false
|
||||||
|
enhanced-mode: fake-ip
|
||||||
|
fake-ip-range: 198.18.0.1/16
|
||||||
|
default-nameserver:
|
||||||
|
- 8.8.8.8
|
||||||
|
- 1.1.1.1
|
||||||
|
nameserver:
|
||||||
|
- https://doh.pub/dns-query
|
||||||
|
- https://1.0.0.1/dns-query
|
||||||
|
fallback:
|
||||||
|
- tcp://9.9.9.9:53
|
||||||
|
fake-ip-filter:
|
||||||
|
- "*.lan"
|
||||||
|
- localhost
|
||||||
|
- "*.local"
|
||||||
|
rules:
|
||||||
|
- GEOIP,Private,DIRECT
|
||||||
|
- MATCH,Proxy
|
||||||
|
`
|
||||||
|
|
||||||
|
const ProxyGroups = `- name: Proxy
|
||||||
|
type: select
|
||||||
|
proxies: []
|
||||||
|
- name: Auto
|
||||||
|
type: url-test
|
||||||
|
proxies: []
|
||||||
|
url: http://www.gstatic.com/generate_204
|
||||||
|
interval: 300
|
||||||
|
tolerance: 50
|
||||||
|
`
|
||||||
|
|
||||||
|
func (s *ClashService) GetClash(subId string) (*string, []string, error) {
|
||||||
|
|
||||||
|
client, inDatas, err := s.getData(subId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
outbounds, outTags, err := s.getOutbounds(client.Config, inDatas)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
links := s.LinkService.GetLinks(&client.Links, "external", "")
|
||||||
|
for index, link := range links {
|
||||||
|
json, tag, err := util.GetOutbound(link, index)
|
||||||
|
if err == nil && len(tag) > 0 {
|
||||||
|
*outbounds = append(*outbounds, *json)
|
||||||
|
*outTags = append(*outTags, tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
othersStr, err := s.getClashConfig()
|
||||||
|
if err != nil || len(othersStr) == 0 {
|
||||||
|
othersStr = basicClashConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := s.ConvertToClashMeta(outbounds)
|
||||||
|
resultStr := othersStr + "\n" + string(result)
|
||||||
|
|
||||||
|
updateInterval, _ := s.SettingService.GetSubUpdates()
|
||||||
|
headers := util.GetHeaders(client, updateInterval)
|
||||||
|
|
||||||
|
return &resultStr, headers, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ClashService) getClashConfig() (string, error) {
|
||||||
|
subClashExt, err := s.SettingService.GetSubClashExt()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return subClashExt, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ClashService) ConvertToClashMeta(outbounds *[]map[string]interface{}) ([]byte, error) {
|
||||||
|
var proxies []interface{}
|
||||||
|
proxyTags := make([]string, 0)
|
||||||
|
for _, obMap := range *outbounds {
|
||||||
|
|
||||||
|
t, _ := obMap["type"].(string)
|
||||||
|
if t == "selector" || t == "urltest" || t == "direct" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
proxy := make(map[string]interface{})
|
||||||
|
proxy["name"] = obMap["tag"]
|
||||||
|
proxy["type"] = t
|
||||||
|
proxy["server"] = obMap["server"]
|
||||||
|
proxy["port"] = obMap["server_port"]
|
||||||
|
|
||||||
|
switch t {
|
||||||
|
case "vmess", "vless", "tuic":
|
||||||
|
proxy["uuid"] = obMap["uuid"]
|
||||||
|
if t == "vmess" {
|
||||||
|
proxy["alterId"] = obMap["alter_id"]
|
||||||
|
proxy["cipher"] = "auto"
|
||||||
|
}
|
||||||
|
if t == "vless" {
|
||||||
|
if flow, ok := obMap["flow"].(string); ok {
|
||||||
|
proxy["flow"] = flow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "trojan":
|
||||||
|
proxy["password"] = obMap["password"]
|
||||||
|
case "socks", "http":
|
||||||
|
if t == "socks" {
|
||||||
|
proxy["type"] = "socks5"
|
||||||
|
}
|
||||||
|
proxy["username"] = obMap["username"]
|
||||||
|
proxy["password"] = obMap["password"]
|
||||||
|
case "hysteria", "hysteria2":
|
||||||
|
if _, ok := obMap["up_mbps"].(float64); ok {
|
||||||
|
proxy["up"] = obMap["up_mbps"]
|
||||||
|
} else {
|
||||||
|
proxy["up"] = 1000
|
||||||
|
}
|
||||||
|
if _, ok := obMap["down_mbps"].(float64); ok {
|
||||||
|
proxy["down"] = obMap["down_mbps"]
|
||||||
|
} else {
|
||||||
|
proxy["down"] = 1000
|
||||||
|
}
|
||||||
|
if t == "hysteria" {
|
||||||
|
proxy["auth-str"] = obMap["auth_str"]
|
||||||
|
if obfs, ok := obMap["obfs"].(string); ok {
|
||||||
|
proxy["obfs"] = obfs
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
proxy["password"] = obMap["password"]
|
||||||
|
if obfs, ok := obMap["obfs"].(map[string]interface{}); ok {
|
||||||
|
proxy["obfs"] = obfs["type"]
|
||||||
|
proxy["obfs-password"] = obfs["password"]
|
||||||
|
}
|
||||||
|
if ports, ok := obMap["server_ports"].([]string); ok {
|
||||||
|
proxy["ports"] = strings.ReplaceAll(strings.Join(ports, ","), ":", "-")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "anytls":
|
||||||
|
proxy["password"] = obMap["password"]
|
||||||
|
if tls, ok := obMap["tls"].(map[string]interface{}); ok {
|
||||||
|
proxy["sni"] = tls["server_name"]
|
||||||
|
proxy["skip-cert-verify"] = tls["insecure"]
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// TLS params
|
||||||
|
tls, isTls := obMap["tls"].(map[string]interface{})
|
||||||
|
if isTls {
|
||||||
|
tlsEnabled, ok := tls["enabled"].(bool)
|
||||||
|
if ok && !tlsEnabled {
|
||||||
|
isTls = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if isTls {
|
||||||
|
// ignore ech outbounds
|
||||||
|
if _, ok := tls["ech"].(interface{}); ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
proxy["tls"] = tls["enabled"]
|
||||||
|
|
||||||
|
// ALPN if exists
|
||||||
|
if alpn, ok := tls["alpn"].([]interface{}); ok {
|
||||||
|
proxy["alpn"] = alpn
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add reality if exists
|
||||||
|
if reality, ok := tls["reality"].(map[string]interface{}); ok && reality["enabled"].(bool) {
|
||||||
|
reality_opts := make(map[string]interface{})
|
||||||
|
if pbk, ok := reality["public_key"].(string); ok {
|
||||||
|
reality_opts["public-key"] = pbk
|
||||||
|
}
|
||||||
|
if sid, ok := reality["short_id"].(string); ok {
|
||||||
|
reality_opts["short-id"] = sid
|
||||||
|
}
|
||||||
|
proxy["reality-opts"] = reality_opts
|
||||||
|
}
|
||||||
|
if utls, ok := tls["utls"].(map[string]interface{}); ok {
|
||||||
|
if enabled, ok := utls["enabled"].(bool); ok && enabled {
|
||||||
|
if fp, ok := utls["fingerprint"].(string); ok {
|
||||||
|
proxy["client-fingerprint"] = fp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if sni, ok := tls["server_name"].(string); ok {
|
||||||
|
if t == "http" {
|
||||||
|
proxy["sni"] = sni
|
||||||
|
} else {
|
||||||
|
proxy["servername"] = sni
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if insecure, ok := tls["insecure"].(bool); ok && insecure {
|
||||||
|
proxy["skip-cert-verify"] = insecure
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transport if exist
|
||||||
|
if transport, ok := obMap["transport"].(map[string]interface{}); ok {
|
||||||
|
tt, _ := transport["type"].(string)
|
||||||
|
switch tt {
|
||||||
|
case "http":
|
||||||
|
httpOpts := make(map[string]interface{})
|
||||||
|
if path, ok := transport["path"].([]interface{}); ok {
|
||||||
|
httpOpts["path"] = path[0]
|
||||||
|
} else if path, ok := transport["path"].(string); ok {
|
||||||
|
httpOpts["path"] = path
|
||||||
|
}
|
||||||
|
if host, ok := transport["host"].([]interface{}); ok {
|
||||||
|
httpOpts["host"] = host[0]
|
||||||
|
}
|
||||||
|
if isTls {
|
||||||
|
proxy["network"] = "h2"
|
||||||
|
proxy["h2-opts"] = httpOpts
|
||||||
|
} else {
|
||||||
|
proxy["network"] = "http"
|
||||||
|
proxy["http-opts"] = httpOpts
|
||||||
|
}
|
||||||
|
case "ws", "httpupgrade":
|
||||||
|
proxy["network"] = "ws"
|
||||||
|
wsOpts := make(map[string]interface{})
|
||||||
|
if path, ok := transport["path"].(string); ok {
|
||||||
|
wsOpts["path"] = path
|
||||||
|
}
|
||||||
|
if headers, ok := transport["headers"].([]interface{}); ok {
|
||||||
|
wsOpts["headers"] = headers
|
||||||
|
}
|
||||||
|
if ed, ok := transport["early_data_header_name"].(string); ok {
|
||||||
|
wsOpts["early-data-header-name"] = ed
|
||||||
|
}
|
||||||
|
if tt == "httpupgrade" {
|
||||||
|
wsOpts["v2ray-http-upgrade"] = true
|
||||||
|
}
|
||||||
|
proxy["ws-opts"] = wsOpts
|
||||||
|
case "grpc":
|
||||||
|
proxy["network"] = "grpc"
|
||||||
|
grpcOpts := make(map[string]interface{})
|
||||||
|
if service_name, ok := transport["service_name"].(string); ok {
|
||||||
|
grpcOpts["grpc-service-name"] = service_name
|
||||||
|
}
|
||||||
|
proxy["grpc-opts"] = grpcOpts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multiplex
|
||||||
|
if mux, ok := obMap["multiplex"].(map[string]interface{}); ok {
|
||||||
|
if enabled, ok := mux["enabled"].(bool); ok && enabled {
|
||||||
|
smux := make(map[string]interface{})
|
||||||
|
smux["enabled"] = true
|
||||||
|
if protocol, ok := mux["protocol"].(string); ok {
|
||||||
|
smux["protocol"] = protocol
|
||||||
|
}
|
||||||
|
if _, ok := mux["max_connections"].(float64); ok {
|
||||||
|
smux["max-connections"] = mux["max_connections"]
|
||||||
|
}
|
||||||
|
if _, ok := mux["min_streams"].(float64); ok {
|
||||||
|
smux["min-streams"] = mux["min_streams"]
|
||||||
|
}
|
||||||
|
if _, ok := mux["max_streams"].(float64); ok {
|
||||||
|
smux["max-streams"] = mux["max_streams"]
|
||||||
|
}
|
||||||
|
if _, ok := mux["padding"].(bool); ok {
|
||||||
|
smux["padding"] = mux["padding"]
|
||||||
|
}
|
||||||
|
if brutal, ok := mux["brutal"].(map[string]interface{}); ok {
|
||||||
|
if enabled, ok := brutal["enabled"].(bool); ok && enabled {
|
||||||
|
brutalOpts := make(map[string]interface{})
|
||||||
|
brutalOpts["enabled"] = true
|
||||||
|
if _, ok := brutal["up_mbps"].(float64); ok {
|
||||||
|
brutalOpts["up"] = brutal["up_mbps"]
|
||||||
|
}
|
||||||
|
if _, ok := brutal["down_mbps"].(float64); ok {
|
||||||
|
brutalOpts["down"] = brutal["down_mbps"]
|
||||||
|
}
|
||||||
|
smux["brutal-opts"] = brutalOpts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
proxy["smux"] = smux
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
proxies = append(proxies, proxy)
|
||||||
|
proxyTags = append(proxyTags, obMap["tag"].(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
var proxyGroups []map[string]interface{}
|
||||||
|
err := yaml.Unmarshal([]byte(ProxyGroups), &proxyGroups)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
proxyGroups[1]["proxies"] = proxyTags
|
||||||
|
proxyGroups[0]["proxies"] = append([]string{proxyGroups[1]["name"].(string)}, proxyTags...)
|
||||||
|
|
||||||
|
output := map[string]interface{}{
|
||||||
|
"proxies": proxies,
|
||||||
|
"proxy-groups": proxyGroups,
|
||||||
|
}
|
||||||
|
|
||||||
|
return yaml.Marshal(output)
|
||||||
|
}
|
||||||
+18
-8
@@ -46,17 +46,17 @@ type JsonService struct {
|
|||||||
LinkService
|
LinkService
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *JsonService) GetJson(subId string, format string) (*string, error) {
|
func (j *JsonService) GetJson(subId string, format string) (*string, []string, error) {
|
||||||
var jsonConfig map[string]interface{}
|
var jsonConfig map[string]interface{}
|
||||||
|
|
||||||
client, inDatas, err := j.getData(subId)
|
client, inDatas, err := j.getData(subId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
outbounds, outTags, err := j.getOutbounds(client.Config, inDatas)
|
outbounds, outTags, err := j.getOutbounds(client.Config, inDatas)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
links := j.LinkService.GetLinks(&client.Links, "external", "")
|
links := j.LinkService.GetLinks(&client.Links, "external", "")
|
||||||
@@ -72,7 +72,7 @@ func (j *JsonService) GetJson(subId string, format string) (*string, error) {
|
|||||||
|
|
||||||
err = json.Unmarshal([]byte(defaultJson), &jsonConfig)
|
err = json.Unmarshal([]byte(defaultJson), &jsonConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonConfig["outbounds"] = outbounds
|
jsonConfig["outbounds"] = outbounds
|
||||||
@@ -82,7 +82,11 @@ func (j *JsonService) GetJson(subId string, format string) (*string, error) {
|
|||||||
|
|
||||||
result, _ := json.MarshalIndent(jsonConfig, "", " ")
|
result, _ := json.MarshalIndent(jsonConfig, "", " ")
|
||||||
resultStr := string(result)
|
resultStr := string(result)
|
||||||
return &resultStr, nil
|
|
||||||
|
updateInterval, _ := j.SettingService.GetSubUpdates()
|
||||||
|
headers := util.GetHeaders(client, updateInterval)
|
||||||
|
|
||||||
|
return &resultStr, headers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *JsonService) getData(subId string) (*model.Client, []*model.Inbound, error) {
|
func (j *JsonService) getData(subId string) (*model.Client, []*model.Inbound, error) {
|
||||||
@@ -211,7 +215,7 @@ func (j *JsonService) addDefaultOutbounds(outbounds *[]map[string]interface{}, o
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (j *JsonService) addOthers(jsonConfig *map[string]interface{}) error {
|
func (j *JsonService) addOthers(jsonConfig *map[string]interface{}) error {
|
||||||
rules := []interface{}{
|
rules_start := []interface{}{
|
||||||
map[string]interface{}{
|
map[string]interface{}{
|
||||||
"action": "sniff",
|
"action": "sniff",
|
||||||
},
|
},
|
||||||
@@ -220,6 +224,8 @@ func (j *JsonService) addOthers(jsonConfig *map[string]interface{}) error {
|
|||||||
"action": "route",
|
"action": "route",
|
||||||
"outbound": "direct",
|
"outbound": "direct",
|
||||||
},
|
},
|
||||||
|
}
|
||||||
|
rules_end := []interface{}{
|
||||||
map[string]interface{}{
|
map[string]interface{}{
|
||||||
"clash_mode": "Global",
|
"clash_mode": "Global",
|
||||||
"action": "route",
|
"action": "route",
|
||||||
@@ -229,7 +235,7 @@ func (j *JsonService) addOthers(jsonConfig *map[string]interface{}) error {
|
|||||||
route := map[string]interface{}{
|
route := map[string]interface{}{
|
||||||
"auto_detect_interface": true,
|
"auto_detect_interface": true,
|
||||||
"final": "proxy",
|
"final": "proxy",
|
||||||
"rules": rules,
|
"rules": rules_start,
|
||||||
}
|
}
|
||||||
|
|
||||||
othersStr, err := j.SettingService.GetSubJsonExt()
|
othersStr, err := j.SettingService.GetSubJsonExt()
|
||||||
@@ -261,7 +267,11 @@ func (j *JsonService) addOthers(jsonConfig *map[string]interface{}) error {
|
|||||||
route["rule_set"] = othersJson["rule_set"]
|
route["rule_set"] = othersJson["rule_set"]
|
||||||
}
|
}
|
||||||
if settingRules, ok := othersJson["rules"].([]interface{}); ok {
|
if settingRules, ok := othersJson["rules"].([]interface{}); ok {
|
||||||
route["rules"] = append(rules, settingRules...)
|
rules := append(rules_start, settingRules...)
|
||||||
|
route["rules"] = append(rules, rules_end...)
|
||||||
|
}
|
||||||
|
if defaultDomainResolver, ok := othersJson["default_domain_resolver"].(string); ok {
|
||||||
|
route["default_domain_resolver"] = defaultDomainResolver
|
||||||
}
|
}
|
||||||
(*jsonConfig)["route"] = route
|
(*jsonConfig)["route"] = route
|
||||||
|
|
||||||
|
|||||||
+19
-12
@@ -11,6 +11,7 @@ type SubHandler struct {
|
|||||||
service.SettingService
|
service.SettingService
|
||||||
SubService
|
SubService
|
||||||
JsonService
|
JsonService
|
||||||
|
ClashService
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSubHandler(g *gin.RouterGroup) {
|
func NewSubHandler(g *gin.RouterGroup) {
|
||||||
@@ -23,29 +24,35 @@ func (s *SubHandler) initRouter(g *gin.RouterGroup) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *SubHandler) subs(c *gin.Context) {
|
func (s *SubHandler) subs(c *gin.Context) {
|
||||||
|
var headers []string
|
||||||
|
var result *string
|
||||||
|
var err error
|
||||||
subId := c.Param("subid")
|
subId := c.Param("subid")
|
||||||
format, isFormat := c.GetQuery("format")
|
format, isFormat := c.GetQuery("format")
|
||||||
if isFormat {
|
if isFormat {
|
||||||
result, err := s.JsonService.GetJson(subId, format)
|
switch format {
|
||||||
|
case "json":
|
||||||
|
result, headers, err = s.JsonService.GetJson(subId, format)
|
||||||
|
case "clash":
|
||||||
|
result, headers, err = s.ClashService.GetClash(subId)
|
||||||
|
}
|
||||||
if err != nil || result == nil {
|
if err != nil || result == nil {
|
||||||
logger.Error(err)
|
logger.Error(err)
|
||||||
c.String(400, "Error!")
|
c.String(400, "Error!")
|
||||||
} else {
|
return
|
||||||
c.String(200, *result)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
result, headers, err := s.SubService.GetSubs(subId)
|
result, headers, err = s.SubService.GetSubs(subId)
|
||||||
if err != nil || result == nil {
|
if err != nil || result == nil {
|
||||||
logger.Error(err)
|
logger.Error(err)
|
||||||
c.String(400, "Error!")
|
c.String(400, "Error!")
|
||||||
} else {
|
return
|
||||||
|
|
||||||
// Add headers
|
|
||||||
c.Writer.Header().Set("Subscription-Userinfo", headers[0])
|
|
||||||
c.Writer.Header().Set("Profile-Update-Interval", headers[1])
|
|
||||||
c.Writer.Header().Set("Profile-Title", headers[2])
|
|
||||||
|
|
||||||
c.String(200, *result)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Add headers
|
||||||
|
c.Writer.Header().Set("Subscription-Userinfo", headers[0])
|
||||||
|
c.Writer.Header().Set("Profile-Update-Interval", headers[1])
|
||||||
|
c.Writer.Header().Set("Profile-Title", headers[2])
|
||||||
|
|
||||||
|
c.String(200, *result)
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-4
@@ -6,6 +6,7 @@ import (
|
|||||||
"s-ui/database"
|
"s-ui/database"
|
||||||
"s-ui/database/model"
|
"s-ui/database/model"
|
||||||
"s-ui/service"
|
"s-ui/service"
|
||||||
|
"s-ui/util"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -34,11 +35,8 @@ func (s *SubService) GetSubs(subId string) (*string, []string, error) {
|
|||||||
linksArray := s.LinkService.GetLinks(&client.Links, "all", clientInfo)
|
linksArray := s.LinkService.GetLinks(&client.Links, "all", clientInfo)
|
||||||
result := strings.Join(linksArray, "\n")
|
result := strings.Join(linksArray, "\n")
|
||||||
|
|
||||||
var headers []string
|
|
||||||
updateInterval, _ := s.SettingService.GetSubUpdates()
|
updateInterval, _ := s.SettingService.GetSubUpdates()
|
||||||
headers = append(headers, fmt.Sprintf("upload=%d; download=%d; total=%d; expire=%d", client.Up, client.Down, client.Volume, client.Expiry))
|
headers := util.GetHeaders(client, updateInterval)
|
||||||
headers = append(headers, fmt.Sprintf("%d", updateInterval))
|
|
||||||
headers = append(headers, subId)
|
|
||||||
|
|
||||||
subEncode, _ := s.SettingService.GetSubEncode()
|
subEncode, _ := s.SettingService.GetSubEncode()
|
||||||
if subEncode {
|
if subEncode {
|
||||||
|
|||||||
+37
-1
@@ -10,7 +10,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
var InboundTypeWithLink = []string{"shadowsocks", "naive", "hysteria", "hysteria2", "tuic", "vless", "trojan", "vmess"}
|
var InboundTypeWithLink = []string{"shadowsocks", "naive", "hysteria", "hysteria2", "anytls", "tuic", "vless", "trojan", "vmess"}
|
||||||
|
|
||||||
func LinkGenerator(clientConfig json.RawMessage, i *model.Inbound, hostname string) []string {
|
func LinkGenerator(clientConfig json.RawMessage, i *model.Inbound, hostname string) []string {
|
||||||
inbound, err := i.MarshalFull()
|
inbound, err := i.MarshalFull()
|
||||||
@@ -73,6 +73,8 @@ func LinkGenerator(clientConfig json.RawMessage, i *model.Inbound, hostname stri
|
|||||||
return tuicLink(userConfig["tuic"], *inbound, Addrs)
|
return tuicLink(userConfig["tuic"], *inbound, Addrs)
|
||||||
case "vless":
|
case "vless":
|
||||||
return vlessLink(userConfig["vless"], *inbound, Addrs)
|
return vlessLink(userConfig["vless"], *inbound, Addrs)
|
||||||
|
case "anytls":
|
||||||
|
return anytlsLink(userConfig["anytls"], Addrs)
|
||||||
case "trojan":
|
case "trojan":
|
||||||
return trojanLink(userConfig["trojan"], *inbound, Addrs)
|
return trojanLink(userConfig["trojan"], *inbound, Addrs)
|
||||||
case "vmess":
|
case "vmess":
|
||||||
@@ -280,6 +282,40 @@ func hysteria2Link(
|
|||||||
return links
|
return links
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func anytlsLink(
|
||||||
|
userConfig map[string]interface{},
|
||||||
|
addrs []map[string]interface{}) []string {
|
||||||
|
|
||||||
|
password, _ := userConfig["password"].(string)
|
||||||
|
baseUri := fmt.Sprintf("%s%s@", "anytls://", password)
|
||||||
|
var links []string
|
||||||
|
|
||||||
|
for _, addr := range addrs {
|
||||||
|
params := map[string]string{}
|
||||||
|
if tls, ok := addr["tls"].(map[string]interface{}); ok {
|
||||||
|
if sni, ok := tls["server_name"].(string); ok {
|
||||||
|
params["sni"] = sni
|
||||||
|
}
|
||||||
|
if alpn, ok := tls["alpn"].([]interface{}); ok {
|
||||||
|
alpnList := make([]string, len(alpn))
|
||||||
|
for i, v := range alpn {
|
||||||
|
alpnList[i] = v.(string)
|
||||||
|
}
|
||||||
|
params["alpn"] = strings.Join(alpnList, ",")
|
||||||
|
}
|
||||||
|
if insecure, ok := tls["insecure"].(bool); ok && insecure {
|
||||||
|
params["insecure"] = "1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
port, _ := addr["server_port"].(float64)
|
||||||
|
uri := fmt.Sprintf("%s%s:%d", baseUri, addr["server"].(string), uint(port))
|
||||||
|
links = append(links, addParams(uri, params, addr["remark"].(string)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return links
|
||||||
|
}
|
||||||
|
|
||||||
func tuicLink(
|
func tuicLink(
|
||||||
userConfig map[string]interface{},
|
userConfig map[string]interface{},
|
||||||
inbound map[string]interface{},
|
inbound map[string]interface{},
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ func GetOutbound(uri string, i int) (*map[string]interface{}, string, error) {
|
|||||||
return hy(u, i)
|
return hy(u, i)
|
||||||
case "hy2", "hysteria2":
|
case "hy2", "hysteria2":
|
||||||
return hy2(u, i)
|
return hy2(u, i)
|
||||||
|
case "anytls":
|
||||||
|
return anytls(u, i)
|
||||||
case "tuic":
|
case "tuic":
|
||||||
return tuic(u, i)
|
return tuic(u, i)
|
||||||
case "ss", "shadowsocks":
|
case "ss", "shadowsocks":
|
||||||
@@ -293,6 +295,42 @@ func hy2(u *url.URL, i int) (*map[string]interface{}, string, error) {
|
|||||||
return &hy2, tag, nil
|
return &hy2, tag, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func anytls(u *url.URL, i int) (*map[string]interface{}, string, error) {
|
||||||
|
query, _ := url.ParseQuery(u.RawQuery)
|
||||||
|
host, portStr, _ := net.SplitHostPort(u.Host)
|
||||||
|
port := 443
|
||||||
|
if len(portStr) > 0 {
|
||||||
|
port, _ = strconv.Atoi(portStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
tls := map[string]interface{}{
|
||||||
|
"enabled": true,
|
||||||
|
"server_name": query.Get("sni"),
|
||||||
|
}
|
||||||
|
alpn := query.Get("alpn")
|
||||||
|
insecure := query.Get("insecure")
|
||||||
|
if len(alpn) > 0 {
|
||||||
|
tls["alpn"] = strings.Split(alpn, ",")
|
||||||
|
}
|
||||||
|
if insecure == "1" || insecure == "true" {
|
||||||
|
tls["insecure"] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
tag := u.Fragment
|
||||||
|
if i > 0 {
|
||||||
|
tag = fmt.Sprintf("%d.%s", i, u.Fragment)
|
||||||
|
}
|
||||||
|
anytls := map[string]interface{}{
|
||||||
|
"type": "anytls",
|
||||||
|
"tag": tag,
|
||||||
|
"server": host,
|
||||||
|
"server_port": port,
|
||||||
|
"password": u.User.Username(),
|
||||||
|
"tls": tls,
|
||||||
|
}
|
||||||
|
return &anytls, tag, nil
|
||||||
|
}
|
||||||
|
|
||||||
func tuic(u *url.URL, i int) (*map[string]interface{}, string, error) {
|
func tuic(u *url.URL, i int) (*map[string]interface{}, string, error) {
|
||||||
query, _ := url.ParseQuery(u.RawQuery)
|
query, _ := url.ParseQuery(u.RawQuery)
|
||||||
host, portStr, _ := net.SplitHostPort(u.Host)
|
host, portStr, _ := net.SplitHostPort(u.Host)
|
||||||
|
|||||||
@@ -209,6 +209,7 @@ func trojanOut(out *map[string]interface{}, inbound map[string]interface{}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func vmessOut(out *map[string]interface{}, inbound map[string]interface{}) {
|
func vmessOut(out *map[string]interface{}, inbound map[string]interface{}) {
|
||||||
|
(*out)["alter_id"] = 0
|
||||||
delete(*out, "transport")
|
delete(*out, "transport")
|
||||||
if transport, ok := inbound["transport"]; ok {
|
if transport, ok := inbound["transport"]; ok {
|
||||||
(*out)["transport"] = transport
|
(*out)["transport"] = transport
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"s-ui/database/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetHeaders(client *model.Client, updateInterval int) []string {
|
||||||
|
var headers []string
|
||||||
|
headers = append(headers, fmt.Sprintf("upload=%d; download=%d; total=%d; expire=%d", client.Up, client.Down, client.Volume, client.Expiry))
|
||||||
|
headers = append(headers, fmt.Sprintf("%d", updateInterval))
|
||||||
|
headers = append(headers, client.Name)
|
||||||
|
return headers
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user