Skip to content

Commit

Permalink
1.4
Browse files Browse the repository at this point in the history
  • Loading branch information
gooaclok819 committed Apr 14, 2024
1 parent 3031af0 commit bdbb711
Show file tree
Hide file tree
Showing 75 changed files with 445 additions and 91 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,12 @@

二进制编译无需Docker容器

目前仅支持客户端:v2ray clash
目前仅支持客户端:v2ray clash surge

v2ray为base64通用格式

clash支持协议:ss ssr trojan vmess vless hy hy2 tuic
surge支持协议:ss trojan vmess hy2 tuic

## [项目预览]

Expand Down
77 changes: 71 additions & 6 deletions api/clients.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"io"
"log"
"net/http"
"net/url"
"strings"
"sublink/models"
"sublink/node"
Expand All @@ -29,9 +30,6 @@ func GetV2ray(c *gin.Context) {
return
}
baselist := ""
// for _, v := range sub.Nodes {
// baselist += v.Link + "\n"
// }
for _, v := range sub.Nodes {
switch {
// 如果包含多条节点
Expand All @@ -55,9 +53,10 @@ func GetV2ray(c *gin.Context) {
baselist += v.Link + "\n"
}
}
Content_Disposition := fmt.Sprintf("inline; filename=%s.txt", subname)
c.Set("subname", subname)
c.Writer.Header().Set("Content-Disposition", Content_Disposition)
filename := fmt.Sprintf("%s.txt", subname)
encodedFilename := url.QueryEscape(filename)
c.Writer.Header().Set("Content-Disposition", "inline; filename*=utf-8''"+encodedFilename)
c.Writer.Header().Set("Content-Type", "text/html; charset=utf-8")
c.Writer.WriteString(node.Base64Encode(baselist))
}
Expand Down Expand Up @@ -114,6 +113,72 @@ func GetClash(c *gin.Context) {
return
}
c.Set("subname", subname)
c.Writer.Header().Set("Content-Type", "text/yaml; charset=utf-8")
filename := fmt.Sprintf("%s.yaml", subname)
encodedFilename := url.QueryEscape(filename)
c.Writer.Header().Set("Content-Disposition", "inline; filename*=utf-8''"+encodedFilename)
c.Writer.Header().Set("Content-Type", "text/plain; charset=utf-8")
c.Writer.WriteString(string(DecodeClash))
}
func GetSurge(c *gin.Context) {
var sub models.Subcription
subname := c.Param("subname")
subname = node.Base64Decode(subname)
sub.Name = subname
err := sub.Find()
if err != nil {
c.Writer.WriteString("找不到这个订阅:" + subname)
return
}
err = sub.GetSub()
if err != nil {
c.Writer.WriteString("读取错误")
return
}
urls := []string{}
for _, v := range sub.Nodes {
switch {
// 如果包含多条节点
case strings.Contains(v.Link, ","):
links := strings.Split(v.Link, ",")
urls = append(urls, links...)
continue
//如果是订阅转换
case strings.Contains(v.Link, "http://") || strings.Contains(v.Link, "https://"):
resp, err := http.Get(v.Link)
if err != nil {
log.Println(err)
return
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
nodes := node.Base64Decode(string(body))
links := strings.Split(nodes, "\n")
urls = append(urls, links...)
// 默认
default:
urls = append(urls, v.Link)
}
}

var configs node.SqlConfig
err = json.Unmarshal([]byte(sub.Config), &configs)
if err != nil {
c.Writer.WriteString("配置读取错误")
return
}
// log.Println("surge路径:", configs)
DecodeClash, err := node.EncodeSurge(urls, configs)
if err != nil {
c.Writer.WriteString(err.Error())
return
}
c.Set("subname", subname)
filename := fmt.Sprintf("%s.conf", subname)
encodedFilename := url.QueryEscape(filename)
c.Writer.Header().Set("Content-Disposition", "inline; filename*=utf-8''"+encodedFilename)
c.Writer.Header().Set("Content-Type", "text/plain; charset=utf-8")
host := c.Request.Host
url := c.Request.URL.String()
interval := fmt.Sprintf("#!MANAGED-CONFIG %s interval=86400 strict=false", host+url)
c.Writer.WriteString(string(interval + "\n" + DecodeClash))
}
47 changes: 35 additions & 12 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,27 +20,50 @@ import (
//go:embed static/index.html
var embeddedFiles embed.FS

//go:embed template/clash.yaml
var clashTemplate []byte
//go:embed template
var Template embed.FS

func Templateinit() {
// 设置template路径

// 检查目录是否创建
_, err := os.Stat("./template/clash.yaml")
Template, err := fs.Sub(embeddedFiles, "template")
if err != nil {
if os.IsNotExist(err) {
os.MkdirAll("./template", os.ModePerm)
}
log.Println(err)
}
_, err = os.Stat("./template/clash.yaml")
if os.IsNotExist(err) {
err = os.WriteFile("./template/clash.yaml", clashTemplate, 0666)
if err != nil {
log.Println(err)
return
Templates, err := fs.ReadDir(Template, ".")
if err != nil {
log.Println(err)
}
for _, v := range Templates {
_, err := os.Stat("./template/" + v.Name())
//如果文件不存在则写入默认模板
if os.IsNotExist(err) {
data, err := fs.ReadFile(Template, v.Name())
if err != nil {
log.Println(err)
continue
}
err = os.WriteFile("./template/"+v.Name(), data, 0666)
if err != nil {
log.Println(err)
}
}
}
// _, err := os.Stat("./template/clash.yaml")
// if err != nil {
// if os.IsNotExist(err) {
// os.MkdirAll("./template", os.ModePerm)
// }
// }
// _, err = os.Stat("./template/clash.yaml")
// if os.IsNotExist(err) {
// err = os.WriteFile("./template/clash.yaml", clashTemplate, 0666)
// if err != nil {
// log.Println(err)
// return
// }
// }

}
func main() {
Expand Down
5 changes: 0 additions & 5 deletions node/clash.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,6 @@ type Config struct {
Proxies []Proxy `yaml:"proxies"`
Proxy_groups []ProxyGroup `yaml:"proxy-groups"`
}
type SqlConfig struct {
Clash string `json:"clash"`
Udp bool `json:"udp"`
Cert bool `json:"cert"`
}

// 删除opts中的空值
func DeleteOpts(opts map[string]interface{}) {
Expand Down
7 changes: 7 additions & 0 deletions node/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ import (
"strings"
)

type SqlConfig struct {
Clash string `json:"clash"`
Surge string `json:"surge"`
Udp bool `json:"udp"`
Cert bool `json:"cert"`
}

// ipv6地址匹配规则
func ValRetIPv6Addr(s string) string {
pattern := `\[([0-9a-fA-F:]+)\]`
Expand Down
161 changes: 161 additions & 0 deletions node/surge.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
package node

import (
"fmt"
"log"
"os"
"regexp"
"strings"
)

func EncodeSurge(urls []string, sqlconfig SqlConfig) (string, error) {
var proxys, groups []string
for _, link := range urls {
Scheme := strings.Split(link, "://")[0]
switch {
case Scheme == "ss":
ss, err := DecodeSSURL(link)
if err != nil {
log.Println(err)
continue
}
proxy := map[string]interface{}{
"name": ss.Name,
"server": ss.Server,
"port": ss.Port,
"cipher": ss.Param.Cipher,
"password": ss.Param.Password,
"udp": sqlconfig.Udp,
}
ssproxy := fmt.Sprintf("%s = ss, %s, %d, encrypt-method=%s, password=%s, udp-relay=%t",
proxy["name"], proxy["server"], proxy["port"], proxy["cipher"], proxy["password"], proxy["udp"])
groups = append(groups, ss.Name)
proxys = append(proxys, ssproxy)
case Scheme == "vmess":
vmess, err := DecodeVMESSURL(link)
if err != nil {
log.Println(err)
continue
}
tls := false
if vmess.Tls != "none" && vmess.Tls != "" {
tls = true
}
proxy := map[string]interface{}{
"name": vmess.Ps,
"server": vmess.Add,
"port": vmess.Port,
"uuid": vmess.Id,
"tls": tls,
"network": vmess.Net,
"ws-path": vmess.Path,
"ws-host": vmess.Host,
"udp": sqlconfig.Udp,
"skip-cert-verify": sqlconfig.Cert,
}

vmessproxy := fmt.Sprintf("%s = vmess, %s, %d, username=%s , tls=%s, vmess-aead=true, udp-relay=%t , skip-cert-verify=%t",
proxy["name"], proxy["server"], proxy["port"], proxy["uuid"], proxy["tls"], proxy["udp"], proxy["skip-cert-verify"])
if vmess.Net == "ws" {
vmessproxy = fmt.Sprintf("%s, ws=true,ws-path=%s", vmessproxy, proxy["ws-path"])
if vmess.Host != "" && vmess.Host != "none" {
vmessproxy = fmt.Sprintf("%s, ws-headers=Host:%s", vmessproxy, proxy["ws-host"])
}
}
if vmess.Sni != "" {
vmessproxy = fmt.Sprintf("%s, sni=%s", vmessproxy, vmess.Sni)
}
groups = append(groups, vmess.Ps)
proxys = append(proxys, vmessproxy)
case Scheme == "trojan":
trojan, err := DecodeTrojanURL(link)
if err != nil {
log.Println(err)
continue
}
proxy := map[string]interface{}{
"name": trojan.Name,
"server": trojan.Hostname,
"port": trojan.Port,
"password": trojan.Password,
"udp": sqlconfig.Udp,
"skip-cert-verify": sqlconfig.Cert,
}
trojanproxy := fmt.Sprintf("%s = trojan, %s, %d, password=%s, udp-relay=%t, skip-cert-verify=%t",
proxy["name"], proxy["server"], proxy["port"], proxy["password"], proxy["udp"], proxy["skip-cert-verify"])
if trojan.Query.Sni != "" {
trojanproxy = fmt.Sprintf("%s, sni=%s", trojanproxy, trojan.Query.Sni)

}
groups = append(groups, trojan.Name)
proxys = append(proxys, trojanproxy)
case Scheme == "hysteria2" || Scheme == "hy2":
hy2, err := DecodeHY2URL(link)
if err != nil {
log.Println(err)
continue
}
proxy := map[string]interface{}{
"name": hy2.Name,
"server": hy2.Host,
"port": hy2.Port,
"password": hy2.Password,
"udp": sqlconfig.Udp,
"skip-cert-verify": sqlconfig.Cert,
}
hy2proxy := fmt.Sprintf("%s = hy2, %s, %d, password=%s, udp-relay=%t, skip-cert-verify=%t",
proxy["name"], proxy["server"], proxy["port"], proxy["password"], proxy["udp"], proxy["skip-cert-verify"])
if hy2.Sni != "" {
hy2proxy = fmt.Sprintf("%s, sni=%s", hy2proxy, hy2.Sni)

}
groups = append(groups, hy2.Name)
proxys = append(proxys, hy2proxy)
case Scheme == "tuic":
tuic, err := DecodeTuicURL(link)
if err != nil {
log.Println(err)
continue
}
proxy := map[string]interface{}{
"name": tuic.Name,
"server": tuic.Host,
"port": tuic.Port,
"password": tuic.Password,
"udp": sqlconfig.Udp,
"skip-cert-verify": sqlconfig.Cert,
}
tuicproxy := fmt.Sprintf("%s = tuic, %s, %d, token=%s, udp-relay=%t, skip-cert-verify=%t",
proxy["name"], proxy["server"], proxy["port"], proxy["password"], proxy["udp"], proxy["skip-cert-verify"])
groups = append(groups, tuic.Name)
proxys = append(proxys, tuicproxy)
}
}
return DecodeSurge(proxys, groups, sqlconfig.Surge)
}
func DecodeSurge(proxys, groups []string, file string) (string, error) {
surge, err := os.ReadFile(file)
if err != nil {
log.Println(err)
return "", err
}
proxyReg := regexp.MustCompile(`(?s)\[Proxy\](.*?)\[*]`)
groupReg := regexp.MustCompile(`(?s)\[Proxy Group\](.*?)\[*]`)

proxyPart := proxyReg.ReplaceAllStringFunc(string(surge), func(s string) string {

text := strings.Join(proxys, "\n")
return "[Proxy]\n" + text + s[len("[Proxy]"):]
})
groupPart := groupReg.ReplaceAllStringFunc(proxyPart, func(s string) string {
lines := strings.Split(s, "\n")
grouplist := strings.Join(groups, ",")
for i, line := range lines {
if strings.Contains(line, "=") {
lines[i] = line + "," + grouplist
}
}
return strings.Join(lines, "\n") + s[len("[Proxy Group]"):]
})
return groupPart, nil
}
2 changes: 1 addition & 1 deletion node/trojan.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ func DecodeTrojanURL(s string) (Trojan, error) {
if u.Scheme != "trojan" {
return Trojan{}, fmt.Errorf("非trojan协议: %s", s)
}
password := Base64Decode2(u.User.Username())
password := u.User.Username()
hostname := u.Hostname()
port, _ := strconv.Atoi(u.Port())
peer := u.Query().Get("peer")
Expand Down
1 change: 1 addition & 0 deletions routers/clients.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ func Clients(r *gin.Engine) {
{
ClientsGroup.GET("/v2ray/:subname", api.GetV2ray)
ClientsGroup.GET("/clash/:subname", api.GetClash)
ClientsGroup.GET("/surge/:subname", api.GetSurge)
}

}
1 change: 1 addition & 0 deletions static/css/subs.DCunInJu.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.el-checkbox-group{font-size:0;line-height:0}.el-card[data-v-4bd1fff0]{margin:10px}.el-input[data-v-4bd1fff0]{margin-bottom:10px}.el-tag[data-v-4bd1fff0]{margin:5px}
1 change: 0 additions & 1 deletion static/css/subs.DwtIKdSy.css

This file was deleted.

2 changes: 1 addition & 1 deletion static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<link rel="icon" href="/static/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>sublinkX节点订阅管理系统</title>
<script type="module" crossorigin src="/static/js/index.CBDW5MZN.js"></script>
<script type="module" crossorigin src="/static/js/index.BrsIWfQX.js"></script>
<link rel="stylesheet" crossorigin href="/static/css/index.Diqaf-Cs.css">
</head>

Expand Down
Loading

0 comments on commit bdbb711

Please sign in to comment.