2026-06-08 17:24:55 +08:00
|
|
|
package services
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"platform/pkg/env"
|
|
|
|
|
"platform/pkg/u"
|
|
|
|
|
"platform/web/core"
|
|
|
|
|
g "platform/web/globals"
|
|
|
|
|
m "platform/web/models"
|
|
|
|
|
q "platform/web/queries"
|
|
|
|
|
"strings"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type channelGostProvider struct{}
|
|
|
|
|
|
2026-06-09 16:30:19 +08:00
|
|
|
func (s *channelGostProvider) prepareCreate(ctx *channelCreateContext) (*channelCreateResult, error) {
|
|
|
|
|
edges, err := s.selectEdge(ctx.Filter, ctx.Count)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
2026-06-08 17:24:55 +08:00
|
|
|
}
|
|
|
|
|
|
2026-06-09 16:30:19 +08:00
|
|
|
client, err := proxyGost(ctx.Proxy)
|
2026-06-08 17:24:55 +08:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-09 16:30:19 +08:00
|
|
|
admissions := make([]*g.GostAdmissionConfig, 0, ctx.Count)
|
|
|
|
|
authers := make([]*g.GostAutherConfig, 0, ctx.Count)
|
|
|
|
|
services := make([]*g.GostServiceConfig, len(ctx.Ports))
|
|
|
|
|
channels := make([]*m.Channel, len(ctx.Ports))
|
|
|
|
|
|
|
|
|
|
for i, portRef := range ctx.Ports {
|
|
|
|
|
edge := edges[i]
|
|
|
|
|
port := portRef.Port()
|
|
|
|
|
serviceName := gostServiceName(ctx.BatchNo, port)
|
|
|
|
|
channel := newBaseChannel(ctx, port)
|
|
|
|
|
channel.EdgeID = u.P(edge.ID)
|
|
|
|
|
channel.EdgeRef = u.P(serviceName)
|
|
|
|
|
channel.IP = u.P(edge.IP)
|
|
|
|
|
|
|
|
|
|
service := &g.GostServiceConfig{
|
|
|
|
|
Name: serviceName,
|
|
|
|
|
Addr: fmt.Sprintf(":%d", port),
|
|
|
|
|
Handler: g.GostHandlerConfig{
|
|
|
|
|
Type: "auto",
|
|
|
|
|
Chain: edge.Mac,
|
|
|
|
|
},
|
|
|
|
|
Listener: g.GostListenerConfig{
|
|
|
|
|
Type: "tcp",
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ctx.AuthWhitelist {
|
|
|
|
|
service.Admission = gostAdmissionName(ctx.BatchNo, port)
|
|
|
|
|
admissions = append(admissions, &g.GostAdmissionConfig{
|
|
|
|
|
Name: service.Admission,
|
|
|
|
|
Whitelist: true,
|
|
|
|
|
Matchers: ctx.Whitelists,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
if username, password, ok := applyChannelAuth(ctx, channel); ok {
|
|
|
|
|
service.Handler.Auther = gostAutherName(ctx.BatchNo, port)
|
|
|
|
|
authers = append(authers, &g.GostAutherConfig{
|
|
|
|
|
Name: service.Handler.Auther,
|
|
|
|
|
Auths: []g.GostAuthConfig{{
|
|
|
|
|
Username: username,
|
|
|
|
|
Password: password,
|
|
|
|
|
}},
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
services[i] = service
|
|
|
|
|
channels[i] = channel
|
|
|
|
|
}
|
2026-06-08 17:24:55 +08:00
|
|
|
|
2026-06-09 16:30:19 +08:00
|
|
|
return &channelCreateResult{
|
|
|
|
|
Channels: channels,
|
|
|
|
|
applyRemote: func() error {
|
|
|
|
|
for _, admission := range admissions {
|
|
|
|
|
if err := client.CreateAdmission(admission); err != nil {
|
|
|
|
|
return core.NewServErr(fmt.Sprintf("创建 GOST admission 失败: %s", admission.Name), err)
|
|
|
|
|
}
|
2026-06-08 17:24:55 +08:00
|
|
|
}
|
2026-06-09 16:30:19 +08:00
|
|
|
for _, auther := range authers {
|
|
|
|
|
if err := client.CreateAuther(auther); err != nil {
|
|
|
|
|
return core.NewServErr(fmt.Sprintf("创建 GOST auther 失败: %s", auther.Name), err)
|
|
|
|
|
}
|
2026-06-08 17:24:55 +08:00
|
|
|
}
|
2026-06-09 16:30:19 +08:00
|
|
|
for _, service := range services {
|
|
|
|
|
if err := client.CreateService(service); err != nil {
|
|
|
|
|
return core.NewServErr(fmt.Sprintf("创建 GOST service 失败: %s", service.Name), err)
|
|
|
|
|
}
|
2026-06-08 17:24:55 +08:00
|
|
|
}
|
2026-06-09 16:30:19 +08:00
|
|
|
return nil
|
|
|
|
|
},
|
|
|
|
|
}, nil
|
2026-06-08 17:24:55 +08:00
|
|
|
}
|
|
|
|
|
|
2026-06-09 16:30:19 +08:00
|
|
|
func (s *channelGostProvider) removeRemote(batchNo string, batch *usedChanBatch) error {
|
|
|
|
|
proxy, err := q.Proxy.Where(q.Proxy.ID.Eq(batch.ProxyID)).Take()
|
2026-06-08 17:24:55 +08:00
|
|
|
if err != nil {
|
2026-06-09 16:30:19 +08:00
|
|
|
return core.NewServErr("获取代理数据失败", err)
|
2026-06-08 17:24:55 +08:00
|
|
|
}
|
|
|
|
|
|
2026-06-09 16:30:19 +08:00
|
|
|
client, err := proxyGost(proxy)
|
2026-06-08 17:24:55 +08:00
|
|
|
if err != nil {
|
2026-06-09 16:30:19 +08:00
|
|
|
return core.NewServErr("创建 GOST 客户端失败", err)
|
2026-06-08 17:24:55 +08:00
|
|
|
}
|
2026-06-09 16:30:19 +08:00
|
|
|
var deleteErrs []error
|
|
|
|
|
for _, ch := range batch.Chans {
|
|
|
|
|
port := ch.Port()
|
|
|
|
|
serviceName := gostServiceName(batchNo, port)
|
|
|
|
|
deleteErrs = append(deleteErrs, deleteGostResource("service", serviceName, func() error {
|
|
|
|
|
return client.DeleteService(serviceName)
|
|
|
|
|
}))
|
|
|
|
|
|
|
|
|
|
autherName := gostAutherName(batchNo, port)
|
|
|
|
|
deleteErrs = append(deleteErrs, deleteGostResource("auther", autherName, func() error {
|
|
|
|
|
return client.DeleteAuther(autherName)
|
|
|
|
|
}))
|
|
|
|
|
|
|
|
|
|
admissionName := gostAdmissionName(batchNo, port)
|
|
|
|
|
deleteErrs = append(deleteErrs, deleteGostResource("admission", admissionName, func() error {
|
|
|
|
|
return client.DeleteAdmission(admissionName)
|
|
|
|
|
}))
|
2026-06-08 17:24:55 +08:00
|
|
|
}
|
2026-06-09 16:30:19 +08:00
|
|
|
return u.CombineErrors(deleteErrs)
|
2026-06-08 17:24:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *channelGostProvider) selectProxy(count int) (*m.Proxy, error) {
|
|
|
|
|
return selectProxyByType(m.ProxyTypeGost, count)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *channelGostProvider) selectEdge(filter *EdgeFilter, count int) ([]*m.Edge, error) {
|
|
|
|
|
if filter == nil {
|
|
|
|
|
filter = &EdgeFilter{}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
do := q.Edge.Where(
|
|
|
|
|
q.Edge.Type.Eq(int(m.EdgeTypeGostChain)),
|
|
|
|
|
q.Edge.Status.Eq(int(m.EdgeStatusNormal)),
|
|
|
|
|
)
|
|
|
|
|
if prov := u.N(filter.Prov); prov != nil {
|
|
|
|
|
do = do.Where(q.Edge.Prov.Eq(*prov))
|
|
|
|
|
}
|
|
|
|
|
if city := u.N(filter.City); city != nil {
|
|
|
|
|
do = do.Where(q.Edge.City.Eq(*city))
|
|
|
|
|
}
|
|
|
|
|
if isp := u.X(filter.Isp.String()); isp != nil {
|
|
|
|
|
do = do.Where(q.Edge.ISP.Eq(int(*filter.Isp)))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
edges, err := q.Edge.Where(do).Order(q.Edge.ID).Limit(count).Find()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, core.NewBizErr("查询可用节点失败", err)
|
|
|
|
|
}
|
|
|
|
|
return expandGostEdges(edges, count)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func expandGostEdges(edges []*m.Edge, count int) ([]*m.Edge, error) {
|
|
|
|
|
if len(edges) == 0 {
|
|
|
|
|
return nil, core.NewBizErr("地区可用节点数量不足")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result := make([]*m.Edge, count)
|
|
|
|
|
for i := range count {
|
|
|
|
|
result[i] = edges[i%len(edges)]
|
|
|
|
|
}
|
|
|
|
|
return result, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func proxyGost(proxy *m.Proxy) (g.GostClient, error) {
|
|
|
|
|
secret := strings.Split(u.Z(proxy.Secret), ":")
|
|
|
|
|
if len(secret) != 2 {
|
|
|
|
|
return nil, core.NewServErr(fmt.Sprintf("代理 %s 密钥格式错误", proxy.IP.String()), nil)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
host := u.Else(proxy.Host, proxy.IP.String())
|
|
|
|
|
return g.NewGost(host, env.GostApiPort, env.GostApiPathPrefix, secret[0], secret[1]), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func deleteGostResource(kind string, name string, deleteFn func() error) error {
|
|
|
|
|
if err := deleteFn(); err != nil && !g.IsGostNotFound(err) {
|
|
|
|
|
return core.NewServErr(fmt.Sprintf("删除 GOST %s 配置失败: %s", kind, name), err)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func gostServiceName(batchNo string, port uint16) string {
|
|
|
|
|
return fmt.Sprintf("gost-svc-%s-%d", batchNo, port)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func gostAutherName(batchNo string, port uint16) string {
|
|
|
|
|
return fmt.Sprintf("gost-auther-%s-%d", batchNo, port)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func gostAdmissionName(batchNo string, port uint16) string {
|
|
|
|
|
return fmt.Sprintf("gost-adm-%s-%d", batchNo, port)
|
|
|
|
|
}
|