新增代理服务的离线接口,优化认证逻辑,代理服务表添加状态字段

This commit is contained in:
2025-05-13 15:26:40 +08:00
parent 60df1548f0
commit 0d40c5aa09
8 changed files with 88 additions and 17 deletions

View File

@@ -71,6 +71,11 @@ func main() {
return err return err
} }
proxySecret, err := bcrypt.GenerateFromPassword([]byte("proxy"), bcrypt.DefaultCost)
if err != nil {
return err
}
err = q.Client. err = q.Client.
Select( Select(
q.Client.ClientID, q.Client.ClientID,
@@ -96,6 +101,17 @@ func main() {
GrantClient: true, GrantClient: true,
Spec: int32(client2.SpecTrusted), Spec: int32(client2.SpecTrusted),
Name: "异步任务处理服务", Name: "异步任务处理服务",
}, &m.Client{
ClientID: "proxy",
ClientSecret: string(proxySecret),
GrantClient: true,
Spec: int32(client2.SpecTrusted),
Name: "代理转发服务",
}, &m.Client{
ClientID: "edge",
GrantClient: true,
Spec: int32(client2.SpecWeb),
Name: "代理边缘节点",
}) })
if err != nil { if err != nil {
return err return err

View File

@@ -516,14 +516,17 @@ create table proxy (
version int not null, version int not null,
name varchar(255) not null unique, name varchar(255) not null unique,
host varchar(255) not null, host varchar(255) not null,
type int not null default 0,
secret varchar(255), secret varchar(255),
created_at timestamp default current_timestamp, type int not null,
updated_at timestamp default current_timestamp, status int not null,
created_at timestamp default current_timestamp,
updated_at timestamp default current_timestamp,
deleted_at timestamp deleted_at timestamp
); );
create index proxy_name_index on proxy (name); create index proxy_name_index on proxy (name);
create index proxy_host_index on proxy (host); create index proxy_host_index on proxy (host);
create index proxy_type_index on proxy (type);
create index proxy_status_index on proxy (status);
create index proxy_deleted_at_index on proxy (deleted_at); create index proxy_deleted_at_index on proxy (deleted_at);
-- proxy表字段注释 -- proxy表字段注释
@@ -534,6 +537,7 @@ comment on column proxy.name is '代理服务名称';
comment on column proxy.host is '代理服务地址'; comment on column proxy.host is '代理服务地址';
comment on column proxy.type is '代理服务类型1-三方2-自有'; comment on column proxy.type is '代理服务类型1-三方2-自有';
comment on column proxy.secret is '代理服务密钥'; comment on column proxy.secret is '代理服务密钥';
comment on column proxy.status is '代理服务状态0-离线1-在线';
comment on column proxy.created_at is '创建时间'; comment on column proxy.created_at is '创建时间';
comment on column proxy.updated_at is '更新时间'; comment on column proxy.updated_at is '更新时间';
comment on column proxy.deleted_at is '删除时间'; comment on column proxy.deleted_at is '删除时间';

View File

@@ -43,11 +43,13 @@ func Protect(c *fiber.Ctx, types []PayloadType, permissions []string) (*Context,
var header = c.Get("Authorization") var header = c.Get("Authorization")
var split = strings.Split(header, " ") var split = strings.Split(header, " ")
if len(split) != 2 { if len(split) != 2 {
slog.Debug("Authorization 头格式不正确")
return nil, fiber.NewError(fiber.StatusUnauthorized, "无效的令牌") return nil, fiber.NewError(fiber.StatusUnauthorized, "无效的令牌")
} }
var token = split[1] var token = strings.TrimSpace(split[1])
if token == "" { if token == "" {
slog.Debug("提供的令牌为空")
return nil, fiber.NewError(fiber.StatusUnauthorized, "无效的令牌") return nil, fiber.NewError(fiber.StatusUnauthorized, "无效的令牌")
} }
@@ -63,7 +65,7 @@ func Protect(c *fiber.Ctx, types []PayloadType, permissions []string) (*Context,
} }
case "Basic": case "Basic":
if !slices.Contains(types, PayloadSecuredServer) { if !slices.Contains(types, PayloadInternalServer) {
slog.Debug("禁止使用 Basic 认证方式") slog.Debug("禁止使用 Basic 认证方式")
return nil, fiber.NewError(fiber.StatusUnauthorized, "无效的令牌") return nil, fiber.NewError(fiber.StatusUnauthorized, "无效的令牌")
} }
@@ -74,18 +76,18 @@ func Protect(c *fiber.Ctx, types []PayloadType, permissions []string) (*Context,
} }
default: default:
slog.Debug("无效的认证方式") slog.Debug("无效的认证方式", "method", split[0])
return nil, fiber.NewError(fiber.StatusUnauthorized, "无效的令牌") return nil, fiber.NewError(fiber.StatusUnauthorized, "无效的令牌")
} }
// 检查权限 // 检查权限
if !slices.Contains(types, auth.Payload.Type) { if !slices.Contains(types, auth.Payload.Type) {
slog.Debug("无效的认证主体") slog.Debug("无效的负载类型", "except", types, "actual", auth.Payload.Type)
return nil, fiber.NewError(fiber.StatusForbidden, "没有权限") return nil, fiber.NewError(fiber.StatusForbidden, "没有权限")
} }
if len(permissions) > 0 && !auth.AnyPermission(permissions...) { if len(permissions) > 0 && !auth.AnyPermission(permissions...) {
slog.Debug("无效的认证权限") slog.Debug("无效的认证权限", "except", permissions, "actual", auth.Permissions)
return nil, fiber.NewError(fiber.StatusForbidden, "没有权限") return nil, fiber.NewError(fiber.StatusForbidden, "没有权限")
} }
@@ -114,15 +116,12 @@ func authBasic(_ context.Context, token string) (*Context, error) {
// 解析 Basic 认证信息 // 解析 Basic 认证信息
var base, err = base64.RawURLEncoding.DecodeString(token) var base, err = base64.RawURLEncoding.DecodeString(token)
if err != nil { if err != nil {
slog.Debug(err.Error()) return nil, errors.New("令牌格式错误,无法解析令牌")
return nil, err
} }
var split = strings.Split(string(base), ":") var split = strings.Split(string(base), ":")
if len(split) != 2 { if len(split) != 2 {
msg := "无法解析 Basic 认证信息" return nil, errors.New("令牌格式错误,必须是 <client_id>:<client_secret> 格式")
slog.Debug(msg)
return nil, errors.New(msg)
} }
var clientID = split[0] var clientID = split[0]
@@ -151,7 +150,7 @@ func authBasic(_ context.Context, token string) (*Context, error) {
return &Context{ return &Context{
Payload: Payload{ Payload: Payload{
Id: client.ID, Id: client.ID,
Type: PayloadSecuredServer, Type: PayloadTypeFromClientSpec(client2.Spec(client.Spec)),
Name: client.Name, Name: client.Name,
Avatar: client.Icon, Avatar: client.Icon,
}, },

View File

@@ -65,6 +65,8 @@ func (t PayloadType) ToStr() string {
return "cpub" return "cpub"
case PayloadSecuredServer: case PayloadSecuredServer:
return "ccnf" return "ccnf"
case PayloadInternalServer:
return "inte"
default: default:
return "none" return "none"
} }
@@ -80,6 +82,8 @@ func PayloadTypeFromStr(name string) PayloadType {
return PayloadPublicServer return PayloadPublicServer
case "ccnf": case "ccnf":
return PayloadSecuredServer return PayloadSecuredServer
case "inte":
return PayloadInternalServer
default: default:
return PayloadNone return PayloadNone
} }

View File

@@ -3,6 +3,7 @@ package handlers
import ( import (
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"gorm.io/gorm/clause" "gorm.io/gorm/clause"
"log/slog"
auth2 "platform/web/auth" auth2 "platform/web/auth"
proxy2 "platform/web/domains/proxy" proxy2 "platform/web/domains/proxy"
g "platform/web/globals" g "platform/web/globals"
@@ -10,7 +11,9 @@ import (
q "platform/web/queries" q "platform/web/queries"
) )
type RegisterProxyReq struct { // region OnlineProxy
type OnlineProxyReq struct {
Name string `json:"name" validate:"required"` Name string `json:"name" validate:"required"`
Version int `json:"version" validate:"required"` Version int `json:"version" validate:"required"`
} }
@@ -26,13 +29,14 @@ func OnlineProxy(c *fiber.Ctx) (err error) {
} }
// 验证请求参数 // 验证请求参数
var req = new(RegisterProxyReq) var req = new(OnlineProxyReq)
err = g.Validator.Validate(c, req) err = g.Validator.Validate(c, req)
if err != nil { if err != nil {
return err return err
} }
// 创建代理 // 创建代理
slog.Debug("注册转发服务", "ip", c.IP())
err = q.Proxy. err = q.Proxy.
Clauses(clause.OnConflict{ Clauses(clause.OnConflict{
UpdateAll: true, UpdateAll: true,
@@ -45,6 +49,7 @@ func OnlineProxy(c *fiber.Ctx) (err error) {
Version: int32(req.Version), Version: int32(req.Version),
Host: c.IP(), Host: c.IP(),
Type: int32(proxy2.TypeSelfHosted), Type: int32(proxy2.TypeSelfHosted),
Status: 1,
}) })
if err != nil { if err != nil {
return err return err
@@ -52,3 +57,40 @@ func OnlineProxy(c *fiber.Ctx) (err error) {
return nil return nil
} }
// endregion
// region OfflineProxy
type OfflineProxyReq struct {
Name string `json:"name" validate:"required"`
}
func OfflineProxy(c *fiber.Ctx) (err error) {
// 检查接口权限
_, err = auth2.NewProtect(c).Payload(
auth2.PayloadInternalServer,
).Do()
if err != nil {
return err
}
// 验证请求参数
var req = new(OfflineProxyReq)
err = g.Validator.Validate(c, req)
if err != nil {
return err
}
// 下线转发服务
_, err = q.Proxy.
Where(q.Proxy.Name.Eq(req.Name)).
UpdateSimple(q.Proxy.Status.Value(0))
if err != nil {
return err
}
return nil
}
// endregion

View File

@@ -23,6 +23,7 @@ type Proxy struct {
CreatedAt orm.LocalDateTime `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间 CreatedAt orm.LocalDateTime `gorm:"column:created_at;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间
UpdatedAt orm.LocalDateTime `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间 UpdatedAt orm.LocalDateTime `gorm:"column:updated_at;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间 DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:删除时间" json:"deleted_at"` // 删除时间
Status int32 `gorm:"column:status;not null;comment:代理服务状态0-离线1-在线" json:"status"` // 代理服务状态0-离线1-在线
Edges []Edge `gorm:"foreignKey:ProxyID;references:ID" json:"edges"` Edges []Edge `gorm:"foreignKey:ProxyID;references:ID" json:"edges"`
} }

View File

@@ -36,6 +36,7 @@ func newProxy(db *gorm.DB, opts ...gen.DOOption) proxy {
_proxy.CreatedAt = field.NewField(tableName, "created_at") _proxy.CreatedAt = field.NewField(tableName, "created_at")
_proxy.UpdatedAt = field.NewField(tableName, "updated_at") _proxy.UpdatedAt = field.NewField(tableName, "updated_at")
_proxy.DeletedAt = field.NewField(tableName, "deleted_at") _proxy.DeletedAt = field.NewField(tableName, "deleted_at")
_proxy.Status = field.NewInt32(tableName, "status")
_proxy.Edges = proxyHasManyEdges{ _proxy.Edges = proxyHasManyEdges{
db: db.Session(&gorm.Session{}), db: db.Session(&gorm.Session{}),
@@ -60,6 +61,7 @@ type proxy struct {
CreatedAt field.Field // 创建时间 CreatedAt field.Field // 创建时间
UpdatedAt field.Field // 更新时间 UpdatedAt field.Field // 更新时间
DeletedAt field.Field // 删除时间 DeletedAt field.Field // 删除时间
Status field.Int32 // 代理服务状态0-离线1-在线
Edges proxyHasManyEdges Edges proxyHasManyEdges
fieldMap map[string]field.Expr fieldMap map[string]field.Expr
@@ -86,6 +88,7 @@ func (p *proxy) updateTableName(table string) *proxy {
p.CreatedAt = field.NewField(table, "created_at") p.CreatedAt = field.NewField(table, "created_at")
p.UpdatedAt = field.NewField(table, "updated_at") p.UpdatedAt = field.NewField(table, "updated_at")
p.DeletedAt = field.NewField(table, "deleted_at") p.DeletedAt = field.NewField(table, "deleted_at")
p.Status = field.NewInt32(table, "status")
p.fillFieldMap() p.fillFieldMap()
@@ -102,7 +105,7 @@ func (p *proxy) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
} }
func (p *proxy) fillFieldMap() { func (p *proxy) fillFieldMap() {
p.fieldMap = make(map[string]field.Expr, 10) p.fieldMap = make(map[string]field.Expr, 11)
p.fieldMap["id"] = p.ID p.fieldMap["id"] = p.ID
p.fieldMap["version"] = p.Version p.fieldMap["version"] = p.Version
p.fieldMap["name"] = p.Name p.fieldMap["name"] = p.Name
@@ -112,6 +115,7 @@ func (p *proxy) fillFieldMap() {
p.fieldMap["created_at"] = p.CreatedAt p.fieldMap["created_at"] = p.CreatedAt
p.fieldMap["updated_at"] = p.UpdatedAt p.fieldMap["updated_at"] = p.UpdatedAt
p.fieldMap["deleted_at"] = p.DeletedAt p.fieldMap["deleted_at"] = p.DeletedAt
p.fieldMap["status"] = p.Status
} }

View File

@@ -66,6 +66,7 @@ func ApplyRouters(app *fiber.App) {
// 网关 // 网关
proxy := api.Group("/proxy") proxy := api.Group("/proxy")
proxy.Post("/online", handlers.OnlineProxy) proxy.Post("/online", handlers.OnlineProxy)
proxy.Post("/offline", handlers.OfflineProxy)
// 节点 // 节点
edge := api.Group("/edge") edge := api.Group("/edge")