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" "gorm.io/gen" ) type channelGostProvider struct{} func (s *channelGostProvider) prepareCreate(ctx *channelCreateContext) (*channelCreateResult, error) { edges, err := s.selectEdge(ctx.Filter, ctx.Area, ctx.Count) if err != nil { return nil, err } client, err := proxyGost(ctx.Proxy) if err != nil { return nil, err } 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", }, Recorders: []g.GostRecorderConfig{ {Name: "record-file", Record: "recorder.service.handler"}, }, } 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 } 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) } } for _, auther := range authers { if err := client.CreateAuther(auther); err != nil { return core.NewServErr(fmt.Sprintf("创建 GOST auther 失败: %s", auther.Name), err) } } for _, service := range services { if err := client.CreateService(service); err != nil { return core.NewServErr(fmt.Sprintf("创建 GOST service 失败: %s", service.Name), err) } } return nil }, }, nil } func (s *channelGostProvider) removeRemote(batchNo string, batch *usedChanBatch) error { proxy, err := q.Proxy.Where(q.Proxy.ID.Eq(batch.ProxyID)).Take() if err != nil { return core.NewServErr("获取代理数据失败", err) } client, err := proxyGost(proxy) if err != nil { return core.NewServErr("创建 GOST 客户端失败", err) } 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) })) } return u.CombineErrors(deleteErrs) } func (s *channelGostProvider) selectProxy(count int) (*m.Proxy, error) { return selectProxyByType(m.ProxyTypeGost, count) } func (s *channelGostProvider) selectEdge(filter *EdgeFilter, area *m.Area, count int) ([]*m.Edge, error) { if filter == nil { filter = &EdgeFilter{} } conds := []gen.Condition{ q.Edge.Type.Eq(int(m.EdgeTypeGostChain)), q.Edge.Status.Eq(int(m.EdgeStatusNormal)), } if isp := u.X(filter.Isp.String()); isp != nil { conds = append(conds, q.Edge.ISP.Eq(int(*filter.Isp))) } query := q.Edge.Where(conds...) if area != nil { switch area.Level { case m.AreaLevelProvince: edgeArea := q.Area.As("EdgeArea") query = query. Join(edgeArea, edgeArea.ID.EqCol(q.Edge.AreaID)). Where(edgeArea.ParentID.Eq(area.ID)) case m.AreaLevelCity: query = query.Where(q.Edge.AreaID.Eq(area.ID)) default: return nil, core.NewBizErr("地区层级不支持") } } edges, err := query. 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) }