package handlers import ( "crypto/rand" "encoding/base32" "fmt" "log/slog" "platform/pkg/u" auth2 "platform/web/auth" "platform/web/core" edge2 "platform/web/domains/edge" proxy2 "platform/web/domains/proxy" g "platform/web/globals" "platform/web/globals/orm" m "platform/web/models" q "platform/web/queries" "strings" "time" "github.com/gofiber/fiber/v2" "gorm.io/gen/field" "gorm.io/gorm/clause" ) // region 报告上线 type ProxyReportOnlineReq struct { Name string `json:"name" validate:"required"` Version int `json:"version" validate:"required"` } type ProxyReportOnlineResp struct { Id int32 `json:"id"` Secret string `json:"secret"` Permits []*ProxyPermit `json:"permits"` Edges []*ProxyEdge `json:"edges"` } func ProxyReportOnline(c *fiber.Ctx) (err error) { // 检查接口权限 _, err = auth2.NewProtect(c).Payload( auth2.PayloadInternalServer, ).Do() if err != nil { return err } // 验证请求参数 var req = new(ProxyReportOnlineReq) err = g.Validator.Validate(c, req) if err != nil { return err } // 创建代理 var ip = c.Context().RemoteIP() var secretBytes = make([]byte, 16) if _, err := rand.Read(secretBytes); err != nil { return err } var secret = base32.StdEncoding. WithPadding(base32.NoPadding). EncodeToString(secretBytes) slog.Debug("生成随机密钥", "ip", ip, "secret", secret) var proxy = &m.Proxy{ Name: req.Name, Version: int32(req.Version), Type: int32(proxy2.TypeSelfHosted), Host: ip.String(), Secret: &secret, Status: 1, } err = q.Proxy. Clauses(clause.OnConflict{ UpdateAll: true, Columns: []clause.Column{ {Name: q.Proxy.Name.ColumnName().String()}, }, }). Create(proxy) if err != nil { return err } // 获取边缘节点信息 data, err := q.Edge.Where( q.Edge.ProxyID.Eq(proxy.ID), ).Find() if err != nil { return err } edges := make([]*ProxyEdge, len(data)) for i, edge := range data { edges[i] = &ProxyEdge{ Id: edge.ID, Port: edge.ProxyPort, Prov: &edge.Prov, City: &edge.City, Isp: u.P(edge2.ISP(edge.Isp).String()), Status: &edge.Status, Loss: edge.Loss, Rtt: edge.Rtt, } } // 获取许可配置 channels, err := q.Channel.Where( q.Channel.ProxyID.Eq(proxy.ID), q.Channel.Expiration.Gt(orm.LocalDateTime(time.Now())), ).Find() if err != nil { return err } var permits = make([]*ProxyPermit, len(channels)) for i, channel := range channels { if channel.EdgeID == nil { return core.NewBizErr(fmt.Sprintf("权限解析异常,通道缺少边缘节点ID %d", channel.ID)) } permits[i] = &ProxyPermit{ Id: *channel.EdgeID, Expire: time.Time(channel.Expiration), Whitelists: u.P(strings.Split(u.Z(channel.Whitelists), ",")), Username: channel.Username, Password: channel.Password, } } slog.Debug("注册转发服务", "ip", ip, "id", proxy.ID) return c.JSON(&ProxyReportOnlineResp{ Id: proxy.ID, Secret: secret, Edges: edges, Permits: permits, }) } // endregion // region 报告下线 type ProxyReportOfflineReq struct { Id int32 `json:"id" validate:"required"` } func ProxyReportOffline(c *fiber.Ctx) (err error) { // 检查接口权限 _, err = auth2.NewProtect(c).Payload( auth2.PayloadInternalServer, ).Do() if err != nil { return err } // 验证请求参数 var req = new(ProxyReportOfflineReq) err = g.Validator.Validate(c, req) if err != nil { return err } // 下线转发服务 _, err = q.Proxy. Where(q.Proxy.ID.Eq(req.Id)). UpdateSimple(q.Proxy.Status.Value(0)) if err != nil { return err } // 下线所有相关的边缘节点 _, err = q.Edge. Where(q.Edge.ProxyID.Eq(req.Id)). UpdateSimple(q.Edge.Status.Value(0)) if err != nil { return err } return nil } // endregion // region 报告更新 type ProxyReportUpdateReq struct { Id int32 `json:"id" validate:"required"` Edges []*ProxyEdge `json:"edges" validate:"required"` } func ProxyReportUpdate(c *fiber.Ctx) (err error) { // 检查接口权限 _, err = auth2.NewProtect(c).Payload( auth2.PayloadInternalServer, ).Do() if err != nil { return err } // 验证请求参数 var req = new(ProxyReportUpdateReq) err = g.Validator.Validate(c, req) if err != nil { return err } // 更新节点信息 var idsActive = make([]int32, 0, len(req.Edges)) var idsInactive = make([]int32, 0, len(req.Edges)) var idsIspUnknown = make([]int32, 0, len(req.Edges)) var idsIspTelecom = make([]int32, 0, len(req.Edges)) var idsIspUnicom = make([]int32, 0, len(req.Edges)) var idsIspMobile = make([]int32, 0, len(req.Edges)) var otherEdges = make([]*ProxyEdge, 0, len(req.Edges)) for _, edge := range req.Edges { // 检查更新ISP if edge.Isp != nil { switch edge2.ISPFromStr(*edge.Isp) { case edge2.IspUnknown: idsIspUnknown = append(idsIspUnknown, edge.Id) case edge2.IspChinaTelecom: idsIspTelecom = append(idsIspTelecom, edge.Id) case edge2.IspChinaUnicom: idsIspUnicom = append(idsIspUnicom, edge.Id) case edge2.IspChinaMobile: idsIspMobile = append(idsIspMobile, edge.Id) } } // 检查更新状态 if edge.Status != nil { if *edge.Status == 1 { idsActive = append(idsActive, edge.Id) } else { idsInactive = append(idsInactive, edge.Id) } } // 无法分类更新 if edge.Host != nil || edge.Port != nil || edge.Prov != nil || edge.City != nil { otherEdges = append(otherEdges, edge) continue } } slog.Debug("更新边缘节点信息", "active", len(idsActive), "inactive", len(idsInactive), "isp_unknown", len(idsIspUnknown), "isp_telecom", len(idsIspTelecom), "isp_unicom", len(idsIspUnicom), "isp_mobile", len(idsIspMobile), "other_edges", len(otherEdges), ) err = q.Q.Transaction(func(q *q.Query) error { // 更新边缘节点状态 if len(idsActive) > 0 { _, err = q.Edge.Debug(). Where(q.Edge.ID.In(idsActive...)). UpdateSimple(q.Edge.Status.Value(1)) if err != nil { return err } } if len(idsInactive) > 0 { _, err = q.Edge.Debug(). Where(q.Edge.ID.In(idsInactive...)). UpdateSimple(q.Edge.Status.Value(0)) if err != nil { return err } } // 更新边缘节点ISP if len(idsIspUnknown) > 0 { _, err = q.Edge.Debug(). Where(q.Edge.ID.In(idsIspUnknown...)). UpdateSimple(q.Edge.Isp.Value(int32(edge2.IspUnknown))) if err != nil { return err } } if len(idsIspTelecom) > 0 { _, err = q.Edge.Debug(). Where(q.Edge.ID.In(idsIspTelecom...)). UpdateSimple(q.Edge.Isp.Value(int32(edge2.IspChinaTelecom))) if err != nil { return err } } if len(idsIspUnicom) > 0 { _, err = q.Edge.Debug(). Where(q.Edge.ID.In(idsIspUnicom...)). UpdateSimple(q.Edge.Isp.Value(int32(edge2.IspChinaUnicom))) if err != nil { return err } } if len(idsIspMobile) > 0 { _, err = q.Edge.Debug(). Where(q.Edge.ID.In(idsIspMobile...)). UpdateSimple(q.Edge.Isp.Value(int32(edge2.IspChinaMobile))) if err != nil { return err } } // 更新其他边缘节点信息 for _, edge := range otherEdges { do := q.Edge.Debug().Where(q.Edge.ID.Eq(edge.Id)) var assigns = make([]field.AssignExpr, 0, 5) if edge.Host != nil { assigns = append(assigns, q.Edge.Host.Value(*edge.Host)) } if edge.Port != nil { assigns = append(assigns, q.Edge.ProxyPort.Value(*edge.Port)) } if edge.Prov != nil { assigns = append(assigns, q.Edge.Prov.Value(*edge.Prov)) } if edge.City != nil { assigns = append(assigns, q.Edge.City.Value(*edge.City)) } // 更新边缘节点 _, err := do.UpdateSimple(assigns...) if err != nil { return fmt.Errorf("更新边缘节点 %d 失败: %w", edge.Id, err) } } return nil }) if err != nil { return err } return nil } // endregion type ProxyPermit struct { Id int32 `json:"id"` Expire time.Time `json:"expire"` Whitelists *[]string `json:"whitelists"` Username *string `json:"username"` Password *string `json:"password"` } type ProxyEdge struct { Id int32 `json:"id"` Host *string `json:"host,omitempty"` // 边缘节点地址 Port *int32 `json:"port,omitempty"` // 边缘节点代理端口 Prov *string `json:"prov,omitempty"` City *string `json:"city,omitempty"` Isp *string `json:"isp,omitempty"` Status *int32 `json:"status,omitempty"` Loss *int32 `json:"loss,omitempty"` // 丢包率 Rtt *int32 `json:"latency,omitempty"` // 延迟 }