333 lines
8.4 KiB
Go
333 lines
8.4 KiB
Go
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"] = map[string]interface{}{"path": []interface{}{httpOpts["path"]}, "host": httpOpts["host"]}
|
|
}
|
|
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)
|
|
}
|