refactor(bookmark): 重构 bookmark 服务生成配置和内部引用
将 bookmark 和 user_np 服务的生成配置分离为 server 和 client 包, 并更新了相应的导入路径。同时更新了 OpenAPI 配置文件中的包名、输出路径及 启用 strict-server 模式以增强类型安全。 此外,同步更新了 VFS 服务的客户端和服务端生成配置,并完善了其 OpenAPI 错误响应定义与二进制内容支持,增强了 API 的规范性和健壮性。 修复了部分权限校验逻辑,并调整了中间件注册方式以适配新的严格模式接口。
This commit is contained in:
@ -5,7 +5,7 @@ package bookmarks
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
api "git.zzyxyz.com/zzy/zzyxyz_go_api/gen/bookmarks"
|
||||
api "git.zzyxyz.com/zzy/zzyxyz_go_api/gen/bookmarks_server"
|
||||
"git.zzyxyz.com/zzy/zzyxyz_go_api/internal/bookmarks/models"
|
||||
"github.com/gin-gonic/gin"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
|
@ -7,8 +7,8 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
api "git.zzyxyz.com/zzy/zzyxyz_go_api/gen/user_np"
|
||||
client "git.zzyxyz.com/zzy/zzyxyz_go_api/gen/vfs"
|
||||
api "git.zzyxyz.com/zzy/zzyxyz_go_api/gen/user_np_server"
|
||||
client "git.zzyxyz.com/zzy/zzyxyz_go_api/gen/vfs_client"
|
||||
"git.zzyxyz.com/zzy/zzyxyz_go_api/internal/bookmarks/models"
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/driver/sqlite"
|
||||
|
@ -360,6 +360,7 @@ func (v *Vfs) CreateNodeByComponents(parentPath, nodeName string, nodeType VfsNo
|
||||
}
|
||||
|
||||
func ParsePathComponents(pathStr string) (parentPath, nodeName string, nodeType VfsNodeType, err error) {
|
||||
// FIXME: 路径判断以及update的类型判断
|
||||
abspath := path.Clean(pathStr)
|
||||
if !path.IsAbs(abspath) {
|
||||
return "", "", 0, errors.New("path must be absolute")
|
||||
|
@ -2,17 +2,14 @@ package vfs
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
api "git.zzyxyz.com/zzy/zzyxyz_go_api/gen/vfs"
|
||||
"git.zzyxyz.com/zzy/zzyxyz_go_api/internal/vfs/models"
|
||||
"github.com/casbin/casbin/v2"
|
||||
"github.com/casbin/casbin/v2/model"
|
||||
fileadapter "github.com/casbin/casbin/v2/persist/file-adapter"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type VfsImpl struct {
|
||||
@ -23,6 +20,7 @@ type VfsImpl struct {
|
||||
proxyMutex sync.RWMutex // 保护代理表的读写锁
|
||||
}
|
||||
|
||||
// 在NewVfsHandler中注册中间件
|
||||
func NewVfsHandler(config *Config) (*VfsImpl, error) {
|
||||
var err error
|
||||
|
||||
@ -68,293 +66,15 @@ func NewVfsHandler(config *Config) (*VfsImpl, error) {
|
||||
log.Printf("Admin Token: %s", config.VFS.AdminToken)
|
||||
log.Printf("Register Token: %s", config.VFS.RegisterToken)
|
||||
|
||||
return &VfsImpl{
|
||||
impl := &VfsImpl{
|
||||
vfs: vfs,
|
||||
enfocer: e,
|
||||
config: config.VFS,
|
||||
proxyTable: make([]*ProxyEntry, 0),
|
||||
proxyMutex: sync.RWMutex{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 注册中间件
|
||||
// 注意:在注册handler时需要传入中间件
|
||||
return impl, nil
|
||||
}
|
||||
|
||||
func ModelType2ResponseType(nodeType models.VfsNodeType) api.VFSNodeType {
|
||||
var reponseType api.VFSNodeType
|
||||
switch nodeType {
|
||||
case models.VfsNodeTypeFile:
|
||||
reponseType = api.File
|
||||
case models.VfsNodeTypeDirectory:
|
||||
reponseType = api.Directory
|
||||
case models.VfsNodeTypeService:
|
||||
reponseType = api.Service
|
||||
}
|
||||
return reponseType
|
||||
}
|
||||
|
||||
// CreateVFSNode implements api.ServerInterface.
|
||||
func (v *VfsImpl) CreateVFSNode(c *gin.Context, params api.CreateVFSNodeParams) {
|
||||
if !v.CheckPermissionMiddleware(c, params.Path) {
|
||||
return
|
||||
}
|
||||
|
||||
// 解析路径组件
|
||||
parentPath, nodeName, nodeType, err := models.ParsePathComponents(params.Path)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, api.Error{
|
||||
Errtype: "error",
|
||||
Message: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 创建节点
|
||||
node, err := v.vfs.CreateNodeByComponents(parentPath, nodeName, nodeType)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, api.Error{
|
||||
Errtype: "CreateNodeByComponents",
|
||||
Message: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if nodeType == models.VfsNodeTypeService {
|
||||
if !v.Proxy2Service(c, node) {
|
||||
// Rollback
|
||||
err := v.vfs.DeleteVFSNode(node)
|
||||
if err != nil {
|
||||
// FIXME: 需要解决这种原子性
|
||||
panic("Maybe Consistency Error")
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 返回创建成功的节点
|
||||
c.JSON(http.StatusCreated, api.VFSNodeResponse{
|
||||
Name: node.Name,
|
||||
Type: ModelType2ResponseType(node.Type),
|
||||
CreatedAt: node.CreatedAt,
|
||||
UpdatedAt: node.UpdatedAt,
|
||||
})
|
||||
}
|
||||
|
||||
// GetVFSNode implements api.ServerInterface.
|
||||
func (v *VfsImpl) GetVFSNode(c *gin.Context, params api.GetVFSNodeParams) {
|
||||
if !v.CheckPermissionMiddleware(c, params.Path) {
|
||||
return
|
||||
}
|
||||
|
||||
node, err := v.vfs.GetNodeByPath(params.Path)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, api.Error{
|
||||
Errtype: "error",
|
||||
Message: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
switch node.Type {
|
||||
case models.VfsNodeTypeDirectory:
|
||||
if entries, err := v.vfs.GetChildren(node.ID); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, api.Error{
|
||||
Errtype: "error",
|
||||
Message: err.Error(),
|
||||
})
|
||||
return
|
||||
} else {
|
||||
var responseEntries []api.VFSDirectoryEntry
|
||||
for _, entry := range entries {
|
||||
responseEntries = append(responseEntries, api.VFSDirectoryEntry{
|
||||
Name: entry.Name,
|
||||
Type: ModelType2ResponseType(entry.Type),
|
||||
})
|
||||
}
|
||||
c.JSON(http.StatusOK, responseEntries)
|
||||
return
|
||||
}
|
||||
case models.VfsNodeTypeService:
|
||||
v.Proxy2Service(c, node)
|
||||
default:
|
||||
c.JSON(http.StatusBadRequest, api.Error{
|
||||
Errtype: "error",
|
||||
Message: "Not a valid node type",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteVFSNode implements api.ServerInterface.
|
||||
func (v *VfsImpl) DeleteVFSNode(c *gin.Context, params api.DeleteVFSNodeParams) {
|
||||
if !v.CheckPermissionMiddleware(c, params.Path) {
|
||||
return
|
||||
}
|
||||
|
||||
node, err := v.vfs.GetNodeByPath(params.Path)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, api.Error{
|
||||
Errtype: "error",
|
||||
Message: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
switch node.Type {
|
||||
case models.VfsNodeTypeService:
|
||||
if !v.Proxy2Service(c, node) {
|
||||
return
|
||||
}
|
||||
case models.VfsNodeTypeDirectory:
|
||||
if children, err := v.vfs.GetChildren(node.ID); err != nil || len(children) != 0 {
|
||||
c.JSON(http.StatusInternalServerError, api.Error{
|
||||
Errtype: "error",
|
||||
Message: "the folder is not empty",
|
||||
})
|
||||
return
|
||||
}
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, api.Error{
|
||||
Errtype: "error",
|
||||
Message: "node type not supported",
|
||||
})
|
||||
return
|
||||
}
|
||||
if err := v.vfs.DeleteVFSNode(node); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, api.Error{
|
||||
Errtype: "error",
|
||||
Message: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusNoContent, nil)
|
||||
}
|
||||
|
||||
// UpdateVFSNode implements api.ServerInterface.
|
||||
func (v *VfsImpl) UpdateVFSNode(c *gin.Context, params api.UpdateVFSNodeParams) {
|
||||
if !v.CheckPermissionMiddleware(c, params.Path) {
|
||||
return
|
||||
}
|
||||
|
||||
var req api.UpdateVFSNodeJSONRequestBody
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, api.Error{
|
||||
Errtype: "ParameterError",
|
||||
Message: "Invalid request parameters",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
node, err := v.vfs.GetNodeByPath(params.Path)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, api.Error{
|
||||
Errtype: "error",
|
||||
Message: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
switch params.Op {
|
||||
case api.Rename:
|
||||
if req == "" {
|
||||
c.JSON(http.StatusBadRequest, api.Error{
|
||||
Errtype: "ParameterError",
|
||||
Message: "Invalid request parameters",
|
||||
})
|
||||
return
|
||||
}
|
||||
// FIXME: 对于service,后缀属性需要强制保留
|
||||
if err := v.vfs.UpdateVFSNode(node); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, api.Error{
|
||||
Errtype: "error",
|
||||
Message: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
case api.Change:
|
||||
if node.Type != models.VfsNodeTypeFile {
|
||||
c.JSON(http.StatusBadRequest, api.Error{
|
||||
Errtype: "ParameterError",
|
||||
Message: "Invalid request parameters, node type must be a service",
|
||||
})
|
||||
return
|
||||
}
|
||||
if !v.Proxy2Service(c, node) {
|
||||
return
|
||||
}
|
||||
case api.Move:
|
||||
// FIXME: 需要添加权限控制
|
||||
v.vfs.MoveToPath(node, req)
|
||||
|
||||
case api.Copy:
|
||||
fallthrough
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, api.Error{
|
||||
Errtype: "error",
|
||||
Message: "op type not supported",
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// CreateUser implements api.ServerInterface.
|
||||
func (v *VfsImpl) CreateUser(c *gin.Context, username string) {
|
||||
token := c.GetHeader("X-VFS-Token")
|
||||
if token != v.config.RegisterToken || token != v.config.AdminToken {
|
||||
c.JSON(http.StatusForbidden, api.Error{
|
||||
Errtype: "AccessDenied",
|
||||
Message: "Access denied",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
token, err := v.vfs.CreateUser(username)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, api.Error{
|
||||
Errtype: "CreateUserError",
|
||||
Message: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
_, err = v.enfocer.AddRoleForUser(username, "user")
|
||||
if err != nil {
|
||||
log.Printf("Failed to add role for user %s: %v", username, err)
|
||||
}
|
||||
v.enfocer.SavePolicy()
|
||||
|
||||
// 根据API文档,token应该通过响应头返回
|
||||
c.Header("X-VFS-Token", token)
|
||||
c.Status(http.StatusCreated)
|
||||
}
|
||||
|
||||
// DeleteUser implements api.ServerInterface.
|
||||
func (v *VfsImpl) DeleteUser(c *gin.Context, username string) {
|
||||
token := c.GetHeader("X-VFS-Token")
|
||||
user, err := v.vfs.GetUserByToken(token)
|
||||
if err != nil || user.Name != username {
|
||||
c.JSON(http.StatusForbidden, api.Error{
|
||||
Errtype: "AccessDenied",
|
||||
Message: "Access denied",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
err = v.vfs.DeleteUser(username)
|
||||
if err != nil {
|
||||
if err.Error() == "user not found" {
|
||||
c.JSON(http.StatusNotFound, api.Error{
|
||||
Errtype: "UserNotFoundError",
|
||||
Message: "User not found",
|
||||
})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, api.Error{
|
||||
Errtype: "DeleteUserError",
|
||||
Message: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 根据API文档,删除成功返回204状态码
|
||||
c.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// Make sure we conform to ServerInterface
|
||||
var _ api.ServerInterface = (*VfsImpl)(nil)
|
||||
|
@ -2,10 +2,9 @@ package vfs
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
api "git.zzyxyz.com/zzy/zzyxyz_go_api/gen/vfs"
|
||||
api "git.zzyxyz.com/zzy/zzyxyz_go_api/gen/vfs_server"
|
||||
"git.zzyxyz.com/zzy/zzyxyz_go_api/internal/vfs/models"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@ -13,13 +12,153 @@ import (
|
||||
//go:embed vfs_model.conf
|
||||
var CasbinModel string
|
||||
|
||||
func NewVfsPermission() (*api.GinServerOptions, error) {
|
||||
return &api.GinServerOptions{
|
||||
Middlewares: []api.MiddlewareFunc{VfsMiddleware()},
|
||||
}, nil
|
||||
// 使用示例:在main.go或路由注册处
|
||||
func RegisterVFSRoutes(router gin.IRouter, vfsHandler *VfsImpl) {
|
||||
// 创建严格模式handler并添加权限中间件
|
||||
strictHandler := api.NewStrictHandler(vfsHandler, []api.StrictMiddlewareFunc{
|
||||
vfsHandler.PermissionMiddleware,
|
||||
})
|
||||
|
||||
// 注册handler
|
||||
api.RegisterHandlers(router, strictHandler)
|
||||
}
|
||||
|
||||
func (v *VfsImpl) CheckPermission(token, path, action string) (bool, error) {
|
||||
// PermissionMiddleware 是一个权限验证中间件
|
||||
func (v *VfsImpl) PermissionMiddleware(handler api.StrictHandlerFunc, operation string) api.StrictHandlerFunc {
|
||||
return func(ctx *gin.Context, request interface{}) (interface{}, error) {
|
||||
// 从请求头获取token
|
||||
token := ctx.GetHeader("X-VFS-Token")
|
||||
|
||||
// Admin token 拥有所有权限
|
||||
if token == v.config.AdminToken && len(token) != 0 {
|
||||
return handler(ctx, request)
|
||||
}
|
||||
|
||||
// 根据操作类型进行不同的权限检查
|
||||
switch operation {
|
||||
case "CreateUser":
|
||||
// 只有admin或register token可以创建用户
|
||||
if token != v.config.RegisterToken {
|
||||
return api.CreateUser403JSONResponse{
|
||||
ForbiddenErrorJSONResponse: api.ForbiddenErrorJSONResponse{
|
||||
Errtype: api.ErrorErrtypeForbidden,
|
||||
Message: "Access denied",
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
return handler(ctx, request)
|
||||
|
||||
// 如果是删除用户操作,检查是否具有管理员权限
|
||||
case "DeleteUser":
|
||||
// 获取要删除的用户名
|
||||
var username string
|
||||
if req, ok := request.(api.DeleteUserRequestObject); ok {
|
||||
username = req.Username
|
||||
}
|
||||
|
||||
// 验证token对应的用户是否存在
|
||||
user, err := v.vfs.GetUserByToken(token)
|
||||
if err != nil || (user.Name != username && token != v.config.AdminToken) {
|
||||
return api.DeleteUser403JSONResponse{
|
||||
ForbiddenErrorJSONResponse: api.ForbiddenErrorJSONResponse{
|
||||
Errtype: api.ErrorErrtypeForbidden,
|
||||
Message: "Access denied",
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
// 如果验证通过,继续执行原处理函数
|
||||
return handler(ctx, request)
|
||||
case "GetVFSNode", "CreateVFSNode", "UpdateVFSNode", "DeleteVFSNode":
|
||||
// VFS节点操作的权限检查
|
||||
var path string
|
||||
var method string
|
||||
|
||||
// 根据操作类型获取路径和HTTP方法
|
||||
switch operation {
|
||||
case "GetVFSNode":
|
||||
if req, ok := request.(api.GetVFSNodeRequestObject); ok {
|
||||
path = req.Params.Path
|
||||
}
|
||||
method = "GET"
|
||||
case "CreateVFSNode":
|
||||
if req, ok := request.(api.CreateVFSNodeRequestObject); ok {
|
||||
path = req.Params.Path
|
||||
}
|
||||
method = "POST"
|
||||
case "UpdateVFSNode":
|
||||
if req, ok := request.(api.UpdateVFSNodeRequestObject); ok {
|
||||
path = req.Params.Path
|
||||
}
|
||||
method = "PATCH"
|
||||
case "DeleteVFSNode":
|
||||
if req, ok := request.(api.DeleteVFSNodeRequestObject); ok {
|
||||
path = req.Params.Path
|
||||
}
|
||||
method = "DELETE"
|
||||
}
|
||||
|
||||
// 检查权限
|
||||
allowed, err := v.CheckPermission(token, path, method)
|
||||
if err != nil {
|
||||
return api.GetVFSNode500JSONResponse{
|
||||
ServerInternalErrorJSONResponse: api.ServerInternalErrorJSONResponse{
|
||||
Errtype: api.ErrorErrtypeInternalServerError,
|
||||
Message: "Failed to check permission: " + err.Error(),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
if !allowed {
|
||||
// 根据操作类型返回相应的错误响应
|
||||
switch operation {
|
||||
case "GetVFSNode":
|
||||
return api.GetVFSNode403JSONResponse{
|
||||
ForbiddenErrorJSONResponse: api.ForbiddenErrorJSONResponse{
|
||||
Errtype: api.ErrorErrtypeForbidden,
|
||||
Message: "Access denied",
|
||||
},
|
||||
}, nil
|
||||
case "CreateVFSNode":
|
||||
return api.CreateVFSNode403JSONResponse{
|
||||
ForbiddenErrorJSONResponse: api.ForbiddenErrorJSONResponse{
|
||||
Errtype: api.ErrorErrtypeForbidden,
|
||||
Message: "Access denied",
|
||||
},
|
||||
}, nil
|
||||
case "UpdateVFSNode":
|
||||
return api.UpdateVFSNode403JSONResponse{
|
||||
ForbiddenErrorJSONResponse: api.ForbiddenErrorJSONResponse{
|
||||
Errtype: api.ErrorErrtypeForbidden,
|
||||
Message: "Access denied",
|
||||
},
|
||||
}, nil
|
||||
case "DeleteVFSNode":
|
||||
return api.DeleteVFSNode403JSONResponse{
|
||||
ForbiddenErrorJSONResponse: api.ForbiddenErrorJSONResponse{
|
||||
Errtype: api.ErrorErrtypeForbidden,
|
||||
Message: "Access denied",
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
return handler(ctx, request)
|
||||
default:
|
||||
return api.Error{
|
||||
Errtype: api.ErrorErrtypeForbidden,
|
||||
Message: "not supported function it's a bug in backend",
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CheckPermission 检查用户对指定路径的权限
|
||||
// FIXME: using casbin to check it
|
||||
func (v *VfsImpl) CheckPermission(token, path, method string) (bool, error) {
|
||||
// Admin token 拥有所有权限
|
||||
if token == v.config.AdminToken && len(token) != 0 {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// 根据 token 获取用户信息
|
||||
user, err := v.vfs.GetUserByToken(token)
|
||||
if err != nil {
|
||||
@ -27,13 +166,8 @@ func (v *VfsImpl) CheckPermission(token, path, action string) (bool, error) {
|
||||
user = &models.VfsUser{Name: "", Token: ""}
|
||||
}
|
||||
|
||||
// 特殊处理:admin 用户拥有所有权限
|
||||
if token == v.config.AdminToken && len(token) != 0 { // admin 用户拥有所有权限
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// 允许任何人读取 public 目录
|
||||
if strings.HasPrefix(path, "/public") && action == "GET" {
|
||||
if method == "GET" && strings.HasPrefix(path, "/public") {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
@ -42,48 +176,14 @@ func (v *VfsImpl) CheckPermission(token, path, action string) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// 构造 Casbin 请求
|
||||
// 对于普通用户,需要将策略中的 {{username}} 替换为实际用户名
|
||||
obj := path
|
||||
sub := user.Name
|
||||
// 使用 Casbin 检查权限
|
||||
allowed, err := v.enfocer.Enforce(sub, obj, action)
|
||||
// sub: 用户名 (匿名用户为空字符串)
|
||||
// obj: 路径
|
||||
// act: HTTP方法
|
||||
allowed, err := v.enfocer.Enforce(user.Name, path, method)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return allowed, nil
|
||||
}
|
||||
|
||||
func (v *VfsImpl) CheckPermissionMiddleware(c *gin.Context, path string) bool {
|
||||
token := c.GetHeader("X-VFS-Token")
|
||||
allowed, err := v.CheckPermission(token, path, c.Request.Method)
|
||||
// log.Println("CheckPermission:", allowed, err)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, api.Error{
|
||||
Errtype: "PermissionCheckError",
|
||||
Message: "Failed to check permission: " + err.Error(),
|
||||
})
|
||||
return false
|
||||
}
|
||||
if !allowed {
|
||||
c.JSON(http.StatusForbidden, api.Error{
|
||||
Errtype: "AccessDenied",
|
||||
Message: "Access denied",
|
||||
})
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func VfsMiddleware() api.MiddlewareFunc {
|
||||
return func(c *gin.Context) {
|
||||
// 检查当前请求是否需要认证
|
||||
if _, exists := c.Get(api.ApiKeyAuthScopes); exists {
|
||||
// 提取 API Key
|
||||
apiKey := c.GetHeader("X-VFS-Token")
|
||||
c.Set(api.ApiKeyAuthScopes, apiKey)
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
518
internal/vfs/vfs_impl.go
Normal file
518
internal/vfs/vfs_impl.go
Normal file
@ -0,0 +1,518 @@
|
||||
package vfs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
api "git.zzyxyz.com/zzy/zzyxyz_go_api/gen/vfs_server"
|
||||
"git.zzyxyz.com/zzy/zzyxyz_go_api/internal/vfs/models"
|
||||
)
|
||||
|
||||
// CreateVFSNode implements server.StrictServerInterface.
|
||||
func (v *VfsImpl) CreateVFSNode(ctx context.Context, request api.CreateVFSNodeRequestObject) (api.CreateVFSNodeResponseObject, error) {
|
||||
// 解析路径组件
|
||||
parentPath, nodeName, nodeType, err := models.ParsePathComponents(request.Params.Path)
|
||||
if err != nil {
|
||||
return api.CreateVFSNode400JSONResponse{
|
||||
ParameterErrorJSONResponse: api.ParameterErrorJSONResponse{
|
||||
Errtype: api.ErrorErrtypeParameterError,
|
||||
Message: err.Error(),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 读取请求体数据
|
||||
// FIXME: 使用stream可能更好
|
||||
var content []byte
|
||||
if request.Body != nil {
|
||||
content, err = io.ReadAll(request.Body)
|
||||
if err != nil {
|
||||
return api.CreateVFSNode500JSONResponse{
|
||||
ServerInternalErrorJSONResponse: api.ServerInternalErrorJSONResponse{
|
||||
Errtype: api.ErrorErrtypeInternalServerError,
|
||||
Message: "Failed to read request body: " + err.Error(),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// 创建节点 (可能需要传递content数据到vfs层)
|
||||
node, err := v.vfs.CreateNodeByComponents(parentPath, nodeName, nodeType)
|
||||
if err != nil {
|
||||
return api.CreateVFSNode500JSONResponse{
|
||||
ServerInternalErrorJSONResponse: api.ServerInternalErrorJSONResponse{
|
||||
Errtype: api.ErrorErrtypeInternalServerError,
|
||||
Message: err.Error(),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 处理服务类型节点
|
||||
if nodeType == models.VfsNodeTypeService {
|
||||
// 这里可能需要将content传递给服务处理
|
||||
if result, err := v.StrictProxy2Service(http.MethodPost, content, node); err != nil {
|
||||
// 回滚操作
|
||||
err := v.vfs.DeleteVFSNode(node)
|
||||
if err != nil {
|
||||
// FIXME: 需要解决这种原子性
|
||||
return nil, fmt.Errorf("consistency error: %w", err)
|
||||
}
|
||||
return nil, err
|
||||
} else {
|
||||
// 返回二进制数据响应
|
||||
return api.CreateVFSNode201ApplicationoctetStreamResponse{
|
||||
Body: bytes.NewReader(result),
|
||||
ContentLength: int64(len(result)),
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// 返回JSON响应
|
||||
return api.CreateVFSNode201JSONResponse{
|
||||
Name: node.Name,
|
||||
Type: ModelType2ResponseType(node.Type),
|
||||
CreatedAt: node.CreatedAt,
|
||||
UpdatedAt: node.UpdatedAt,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DeleteVFSNode implements server.StrictServerInterface.
|
||||
func (v *VfsImpl) DeleteVFSNode(ctx context.Context, request api.DeleteVFSNodeRequestObject) (api.DeleteVFSNodeResponseObject, error) {
|
||||
// 获取节点
|
||||
node, err := v.vfs.GetNodeByPath(request.Params.Path)
|
||||
if err != nil {
|
||||
return api.DeleteVFSNode404JSONResponse{
|
||||
PathNotFoundErrorJSONResponse: api.PathNotFoundErrorJSONResponse{
|
||||
Errtype: api.ErrorErrtypePathNotFound,
|
||||
Message: err.Error(),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 根据节点类型进行不同处理
|
||||
switch node.Type {
|
||||
case models.VfsNodeTypeService:
|
||||
// 对于服务类型节点,通过代理删除
|
||||
_, err := v.StrictProxy2Service(http.MethodDelete, nil, node)
|
||||
if err != nil {
|
||||
return api.DeleteVFSNode500JSONResponse{
|
||||
ServerInternalErrorJSONResponse: api.ServerInternalErrorJSONResponse{
|
||||
Errtype: api.ErrorErrtypeServiceProxyError,
|
||||
Message: err.Error(),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
case models.VfsNodeTypeDirectory:
|
||||
// 检查目录是否为空
|
||||
children, err := v.vfs.GetChildren(node.ID)
|
||||
if err != nil {
|
||||
return api.DeleteVFSNode500JSONResponse{
|
||||
ServerInternalErrorJSONResponse: api.ServerInternalErrorJSONResponse{
|
||||
Errtype: api.ErrorErrtypeInternalServerError,
|
||||
Message: "Failed to get directory children: " + err.Error(),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
if len(children) != 0 {
|
||||
return api.DeleteVFSNode400JSONResponse{
|
||||
ParameterErrorJSONResponse: api.ParameterErrorJSONResponse{
|
||||
Errtype: api.ErrorErrtypeConflictError,
|
||||
Message: "Directory is not empty",
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
case models.VfsNodeTypeFile:
|
||||
// TODO
|
||||
// 文件类型节点可以直接删除
|
||||
fallthrough
|
||||
default:
|
||||
return api.DeleteVFSNode400JSONResponse{
|
||||
ParameterErrorJSONResponse: api.ParameterErrorJSONResponse{
|
||||
Errtype: api.ErrorErrtypeParameterError,
|
||||
Message: "Node type not supported for deletion",
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 执行实际的删除操作
|
||||
if err := v.vfs.DeleteVFSNode(node); err != nil {
|
||||
return api.DeleteVFSNode500JSONResponse{
|
||||
ServerInternalErrorJSONResponse: api.ServerInternalErrorJSONResponse{
|
||||
Errtype: api.ErrorErrtypeInternalServerError,
|
||||
Message: "Failed to delete node: " + err.Error(),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 成功删除返回204状态
|
||||
return api.DeleteVFSNode204Response{}, nil
|
||||
}
|
||||
|
||||
// GetVFSNode implements server.StrictServerInterface.
|
||||
func (v *VfsImpl) GetVFSNode(ctx context.Context, request api.GetVFSNodeRequestObject) (api.GetVFSNodeResponseObject, error) {
|
||||
// 获取节点
|
||||
node, err := v.vfs.GetNodeByPath(request.Params.Path)
|
||||
if err != nil {
|
||||
return api.GetVFSNode404JSONResponse{
|
||||
PathNotFoundErrorJSONResponse: api.PathNotFoundErrorJSONResponse{
|
||||
Errtype: api.ErrorErrtypePathNotFound,
|
||||
Message: err.Error(),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
switch node.Type {
|
||||
case models.VfsNodeTypeDirectory:
|
||||
// 处理目录类型
|
||||
entries, err := v.vfs.GetChildren(node.ID)
|
||||
if err != nil {
|
||||
return api.GetVFSNode500JSONResponse{
|
||||
ServerInternalErrorJSONResponse: api.ServerInternalErrorJSONResponse{
|
||||
Errtype: api.ErrorErrtypeInternalServerError,
|
||||
Message: err.Error(),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
var responseEntries []api.VFSDirectoryEntry
|
||||
for _, entry := range entries {
|
||||
responseEntries = append(responseEntries, api.VFSDirectoryEntry{
|
||||
Name: entry.Name,
|
||||
Type: ModelType2ResponseType(entry.Type),
|
||||
})
|
||||
}
|
||||
return api.GetVFSNode200JSONResponse(responseEntries), nil
|
||||
|
||||
// case models.VfsNodeTypeFile:
|
||||
// // 处理文件类型,返回二进制数据
|
||||
// // 这里需要从vfs中获取文件内容
|
||||
// fileContent, err := v.vfs.GetFileContent(node.ID) // 假设有此方法
|
||||
// if err != nil {
|
||||
// return api.GetVFSNode500JSONResponse{
|
||||
// ServerInternalErrorJSONResponse: api.ServerInternalErrorJSONResponse{
|
||||
// Errtype: api.ErrorErrtypeInternalServerError,
|
||||
// Message: err.Error(),
|
||||
// },
|
||||
// }, nil
|
||||
// }
|
||||
// return api.GetVFSNode200ApplicationoctetStreamResponse{
|
||||
// Body: bytes.NewReader(fileContent),
|
||||
// ContentLength: int64(len(fileContent)),
|
||||
// }, nil
|
||||
|
||||
case models.VfsNodeTypeService:
|
||||
// 处理服务类型
|
||||
result, err := v.StrictProxy2Service(http.MethodGet, nil, node)
|
||||
if err != nil {
|
||||
return api.GetVFSNode500JSONResponse{
|
||||
ServerInternalErrorJSONResponse: api.ServerInternalErrorJSONResponse{
|
||||
Errtype: api.ErrorErrtypeServiceProxyError,
|
||||
Message: err.Error(),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
return api.GetVFSNode200ApplicationoctetStreamResponse{
|
||||
Body: bytes.NewReader(result),
|
||||
ContentLength: int64(len(result)),
|
||||
}, nil
|
||||
|
||||
default:
|
||||
return api.GetVFSNode400JSONResponse{
|
||||
ParameterErrorJSONResponse: api.ParameterErrorJSONResponse{
|
||||
Errtype: api.ErrorErrtypeParameterError,
|
||||
Message: "Not a valid node type",
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateVFSNode implements server.StrictServerInterface.
|
||||
func (v *VfsImpl) UpdateVFSNode(ctx context.Context, request api.UpdateVFSNodeRequestObject) (api.UpdateVFSNodeResponseObject, error) {
|
||||
// 获取节点
|
||||
node, err := v.vfs.GetNodeByPath(request.Params.Path)
|
||||
if err != nil {
|
||||
return api.UpdateVFSNode404JSONResponse{
|
||||
PathNotFoundErrorJSONResponse: api.PathNotFoundErrorJSONResponse{
|
||||
Errtype: api.ErrorErrtypePathNotFound,
|
||||
Message: err.Error(),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 根据操作类型进行不同处理
|
||||
switch request.Params.Op {
|
||||
case api.Rename:
|
||||
// 检查请求体
|
||||
if request.Body == nil {
|
||||
return api.UpdateVFSNode400JSONResponse{
|
||||
ParameterErrorJSONResponse: api.ParameterErrorJSONResponse{
|
||||
Errtype: api.ErrorErrtypeParameterError,
|
||||
Message: "Request body is required for rename operation",
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
newName := string(*request.Body)
|
||||
if newName == "" {
|
||||
return api.UpdateVFSNode400JSONResponse{
|
||||
ParameterErrorJSONResponse: api.ParameterErrorJSONResponse{
|
||||
Errtype: api.ErrorErrtypeParameterError,
|
||||
Message: "New name cannot be empty",
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// FIXME: 检查新名称是否合法
|
||||
// 对于服务类型节点,可能需要保留特定后缀
|
||||
if node.Type == models.VfsNodeTypeService {
|
||||
// 可以添加对服务节点重命名的特殊处理
|
||||
// 例如确保保留.service后缀
|
||||
}
|
||||
|
||||
// 更新节点名称
|
||||
node.Name = newName
|
||||
if err := v.vfs.UpdateVFSNode(node); err != nil {
|
||||
return api.UpdateVFSNode500JSONResponse{
|
||||
ServerInternalErrorJSONResponse: api.ServerInternalErrorJSONResponse{
|
||||
Errtype: api.ErrorErrtypeInternalServerError,
|
||||
Message: "Failed to rename node: " + err.Error(),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
case api.Change:
|
||||
// Change操作仅适用于文件和服务类型节点
|
||||
if node.Type != models.VfsNodeTypeFile && node.Type != models.VfsNodeTypeService {
|
||||
return api.UpdateVFSNode400JSONResponse{
|
||||
ParameterErrorJSONResponse: api.ParameterErrorJSONResponse{
|
||||
Errtype: api.ErrorErrtypeParameterError,
|
||||
Message: "Change operation is only supported for file and service nodes",
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// FIXME: 性能问题
|
||||
var content []byte
|
||||
if request.Body != nil {
|
||||
content = []byte(*request.Body)
|
||||
}
|
||||
|
||||
// 对于服务节点,通过代理发送变更
|
||||
if node.Type == models.VfsNodeTypeService {
|
||||
_, err := v.StrictProxy2Service(http.MethodPatch, content, node)
|
||||
if err != nil {
|
||||
return api.UpdateVFSNode500JSONResponse{
|
||||
ServerInternalErrorJSONResponse: api.ServerInternalErrorJSONResponse{
|
||||
Errtype: api.ErrorErrtypeServiceProxyError,
|
||||
Message: "Failed to proxy change to service: " + err.Error(),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
} else {
|
||||
// 对于文件节点,可能需要更新文件内容
|
||||
// 这里可以根据需要实现文件内容更新逻辑
|
||||
return api.UpdateVFSNode500JSONResponse{
|
||||
ServerInternalErrorJSONResponse: api.ServerInternalErrorJSONResponse{
|
||||
Errtype: api.ErrorErrtypeInternalServerError,
|
||||
Message: "File Opreation Not Supported",
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
case api.Move:
|
||||
// FIXME: 需要添加权限控制
|
||||
if request.Body == nil {
|
||||
return api.UpdateVFSNode400JSONResponse{
|
||||
ParameterErrorJSONResponse: api.ParameterErrorJSONResponse{
|
||||
Errtype: api.ErrorErrtypeParameterError,
|
||||
Message: "Request body is required for move operation",
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
targetPath := string(*request.Body)
|
||||
if targetPath == "" {
|
||||
return api.UpdateVFSNode400JSONResponse{
|
||||
ParameterErrorJSONResponse: api.ParameterErrorJSONResponse{
|
||||
Errtype: api.ErrorErrtypeParameterError,
|
||||
Message: "Target path cannot be empty",
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 移动节点到新路径
|
||||
if err := v.vfs.MoveToPath(node, targetPath); err != nil {
|
||||
return api.UpdateVFSNode500JSONResponse{
|
||||
ServerInternalErrorJSONResponse: api.ServerInternalErrorJSONResponse{
|
||||
Errtype: api.ErrorErrtypeInternalServerError,
|
||||
Message: "Failed to move node: " + err.Error(),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
case api.Copy:
|
||||
fallthrough
|
||||
// if request.Body == nil {
|
||||
// return api.UpdateVFSNode400JSONResponse{
|
||||
// ParameterErrorJSONResponse: api.ParameterErrorJSONResponse{
|
||||
// Errtype: api.ErrorErrtypeParameterError,
|
||||
// Message: "Request body is required for copy operation",
|
||||
// },
|
||||
// }, nil
|
||||
// }
|
||||
|
||||
// targetPath := string(*request.Body)
|
||||
// if targetPath == "" {
|
||||
// return api.UpdateVFSNode400JSONResponse{
|
||||
// ParameterErrorJSONResponse: api.ParameterErrorJSONResponse{
|
||||
// Errtype: api.ErrorErrtypeParameterError,
|
||||
// Message: "Target path cannot be empty",
|
||||
// },
|
||||
// }, nil
|
||||
// }
|
||||
|
||||
// // 复制节点到新路径
|
||||
// if err := v.vfs.CopyToPath(node, targetPath); err != nil {
|
||||
// return api.UpdateVFSNode500JSONResponse{
|
||||
// ServerInternalErrorJSONResponse: api.ServerInternalErrorJSONResponse{
|
||||
// Errtype: api.ErrorErrtypeInternalServerError,
|
||||
// Message: "Failed to copy node: " + err.Error(),
|
||||
// },
|
||||
// }, nil
|
||||
// }
|
||||
|
||||
default:
|
||||
return api.UpdateVFSNode400JSONResponse{
|
||||
ParameterErrorJSONResponse: api.ParameterErrorJSONResponse{
|
||||
Errtype: api.ErrorErrtypeParameterError,
|
||||
Message: "Unsupported operation type",
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 返回更新后的节点信息
|
||||
return api.UpdateVFSNode200JSONResponse{
|
||||
Name: node.Name,
|
||||
Type: ModelType2ResponseType(node.Type),
|
||||
CreatedAt: node.CreatedAt,
|
||||
UpdatedAt: node.UpdatedAt,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func ModelType2ResponseType(nodeType models.VfsNodeType) api.VFSNodeType {
|
||||
var reponseType api.VFSNodeType
|
||||
switch nodeType {
|
||||
case models.VfsNodeTypeFile:
|
||||
reponseType = api.File
|
||||
case models.VfsNodeTypeDirectory:
|
||||
reponseType = api.Directory
|
||||
case models.VfsNodeTypeService:
|
||||
reponseType = api.Service
|
||||
}
|
||||
return reponseType
|
||||
}
|
||||
|
||||
// CreateUser implements server.StrictServerInterface.
|
||||
func (v *VfsImpl) CreateUser(ctx context.Context, request api.CreateUserRequestObject) (api.CreateUserResponseObject, error) {
|
||||
// 从上下文中获取请求头信息
|
||||
// 注意:在严格模式下,我们需要从上下文获取请求信息
|
||||
// 但这里我们无法直接访问请求头,因此需要在中间件中处理或使用其他方式传递
|
||||
|
||||
// 由于严格模式接口限制,我们无法直接访问请求头
|
||||
// 这里假设我们通过其他方式验证了权限(比如中间件)
|
||||
// token := c.GetHeader("X-VFS-Token")
|
||||
// if token != v.config.RegisterToken || token != v.config.AdminToken {
|
||||
// c.JSON(http.StatusForbidden, api.Error{
|
||||
// Errtype: "AccessDenied",
|
||||
// Message: "Access denied",
|
||||
// })
|
||||
// return
|
||||
// }
|
||||
|
||||
// 创建用户
|
||||
token, err := v.vfs.CreateUser(request.Username)
|
||||
if err != nil {
|
||||
// 检查是否是用户已存在的错误
|
||||
if err.Error() == "user already exists" {
|
||||
return api.CreateUser400JSONResponse{
|
||||
ParameterErrorJSONResponse: api.ParameterErrorJSONResponse{
|
||||
Errtype: api.ErrorErrtypeConflictError,
|
||||
Message: "User already exists",
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
return api.CreateUser500JSONResponse{
|
||||
ServerInternalErrorJSONResponse: api.ServerInternalErrorJSONResponse{
|
||||
Errtype: api.ErrorErrtypeInternalServerError,
|
||||
Message: "Failed to create user: " + err.Error(),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 为新用户添加角色
|
||||
_, err = v.enfocer.AddRoleForUser(request.Username, "user")
|
||||
if err != nil {
|
||||
log.Printf("Failed to add role for user %s: %v", request.Username, err)
|
||||
// 注意:这里即使添加角色失败,我们也不会回滚用户创建
|
||||
// 因为用户创建已经成功,且这是两个独立的系统
|
||||
// FIXME: unknown
|
||||
}
|
||||
|
||||
// 保存策略
|
||||
v.enfocer.SavePolicy()
|
||||
|
||||
// 返回带有token的响应
|
||||
return api.CreateUser201Response{
|
||||
Headers: api.CreateUser201ResponseHeaders{
|
||||
XVFSToken: token,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DeleteUser implements server.StrictServerInterface.
|
||||
func (v *VfsImpl) DeleteUser(ctx context.Context, request api.DeleteUserRequestObject) (api.DeleteUserResponseObject, error) {
|
||||
// 删除用户
|
||||
err := v.vfs.DeleteUser(request.Username)
|
||||
if err != nil {
|
||||
// 检查是否是用户未找到的错误
|
||||
if err.Error() == "user not found" {
|
||||
return api.DeleteUser404JSONResponse{
|
||||
Errtype: api.ErrorErrtypeNotFoundError,
|
||||
Message: "User not found",
|
||||
}, nil
|
||||
}
|
||||
|
||||
return api.DeleteUser500JSONResponse{
|
||||
ServerInternalErrorJSONResponse: api.ServerInternalErrorJSONResponse{
|
||||
Errtype: api.ErrorErrtypeInternalServerError,
|
||||
Message: "Failed to delete user: " + err.Error(),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 从权限系统中移除用户
|
||||
// 移除用户的所有角色
|
||||
_, err = v.enfocer.DeleteRolesForUser(request.Username)
|
||||
if err != nil {
|
||||
log.Printf("Failed to delete roles for user %s: %v", request.Username, err)
|
||||
// 注意:这里即使删除角色失败,我们也不会回滚用户删除
|
||||
// 因为数据库中的用户已经删除,且这是两个独立的系统
|
||||
// FIXME: unknown
|
||||
}
|
||||
|
||||
// 保存策略
|
||||
v.enfocer.SavePolicy()
|
||||
|
||||
// 成功删除返回204状态
|
||||
return api.DeleteUser204Response{}, nil
|
||||
}
|
||||
|
||||
// Make sure we conform to ServerInterface
|
||||
var _ api.StrictServerInterface = (*VfsImpl)(nil)
|
@ -1,27 +1,26 @@
|
||||
package vfs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
api "git.zzyxyz.com/zzy/zzyxyz_go_api/gen/vfs"
|
||||
"git.zzyxyz.com/zzy/zzyxyz_go_api/internal/vfs/models"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// ServiceProxy 服务代理接口
|
||||
type ServiceProxy interface {
|
||||
// Get 从后端服务获取数据
|
||||
Get(c *gin.Context, servicePath string, node *models.VfsNode) (any, error)
|
||||
Get(servicePath string, node *models.VfsNode) ([]byte, error)
|
||||
|
||||
// Create 在后端服务创建资源
|
||||
Create(c *gin.Context, servicePath string, node *models.VfsNode, data []byte) (string, error) // 返回创建的资源ID
|
||||
Create(servicePath string, node *models.VfsNode, data []byte) ([]byte, error) // 返回创建的资源ID
|
||||
|
||||
// Update 更新后端服务资源
|
||||
Update(c *gin.Context, servicePath string, node *models.VfsNode, data []byte) error
|
||||
Update(servicePath string, node *models.VfsNode, data []byte) error
|
||||
|
||||
// Delete 删除后端服务资源
|
||||
Delete(c *gin.Context, servicePath string, node *models.VfsNode) error
|
||||
Delete(servicePath string, node *models.VfsNode) error
|
||||
|
||||
// GetName 获取代理名称
|
||||
GetName() string
|
||||
@ -60,96 +59,45 @@ func (v *VfsImpl) RegisterProxy(entry *ProxyEntry) {
|
||||
v.proxyTable = append(v.proxyTable, entry)
|
||||
}
|
||||
|
||||
// Proxy2Service 通用服务代理处理函数
|
||||
func (v *VfsImpl) Proxy2Service(c *gin.Context, node *models.VfsNode) bool {
|
||||
func (v *VfsImpl) StrictProxy2Service(method string, data []byte, node *models.VfsNode) ([]byte, error) {
|
||||
exts := strings.Split(node.Name, ".")
|
||||
var serviceName = exts[1]
|
||||
// log.Println("Proxy2Service: ", serviceName)
|
||||
// 查找对应的代理
|
||||
proxy := v.FindProxyByServiceName(serviceName)
|
||||
if proxy == nil {
|
||||
c.JSON(http.StatusNotImplemented, api.Error{
|
||||
Errtype: "error",
|
||||
Message: "Service proxy not found for: " + serviceName,
|
||||
})
|
||||
return false
|
||||
return nil, fmt.Errorf("service proxy not found for: %s", serviceName)
|
||||
}
|
||||
|
||||
// 根据HTTP方法调用相应的代理方法
|
||||
switch c.Request.Method {
|
||||
switch method {
|
||||
case http.MethodGet:
|
||||
result, err := proxy.Get(c, serviceName, node)
|
||||
result, err := proxy.Get(serviceName, node)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, api.Error{
|
||||
Errtype: "error",
|
||||
Message: "Failed to get service data: " + err.Error(),
|
||||
})
|
||||
return false
|
||||
return nil, fmt.Errorf("failed to get service data: %v", err)
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, result)
|
||||
return true
|
||||
return result, nil
|
||||
case http.MethodPost:
|
||||
// 读取请求体数据
|
||||
data, err := c.GetRawData()
|
||||
result, err := proxy.Create(serviceName, node, data)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, api.Error{
|
||||
Errtype: "error",
|
||||
Message: "Failed to read request data: " + err.Error(),
|
||||
})
|
||||
return false
|
||||
return nil, fmt.Errorf("failed to create service resource: %v", err)
|
||||
}
|
||||
|
||||
resourceID, err := proxy.Create(c, serviceName, node, data)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, api.Error{
|
||||
Errtype: "error",
|
||||
Message: "Failed to create service resource: " + err.Error(),
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, gin.H{"resource_id": resourceID})
|
||||
return true
|
||||
return result, nil
|
||||
case http.MethodPut, http.MethodPatch:
|
||||
// 读取请求体数据
|
||||
data, err := c.GetRawData()
|
||||
err := proxy.Update(serviceName, node, data)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, api.Error{
|
||||
Errtype: "error",
|
||||
Message: "Failed to read request data: " + err.Error(),
|
||||
})
|
||||
return false
|
||||
return nil, fmt.Errorf("failed to update service resource: %v", err)
|
||||
}
|
||||
|
||||
err = proxy.Update(c, serviceName, node, data)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, api.Error{
|
||||
Errtype: "error",
|
||||
Message: "Failed to update service resource: " + err.Error(),
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Updated successfully"})
|
||||
return true
|
||||
return nil, nil
|
||||
case http.MethodDelete:
|
||||
err := proxy.Delete(c, serviceName, node)
|
||||
err := proxy.Delete(serviceName, node)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, api.Error{
|
||||
Errtype: "error",
|
||||
Message: "Failed to delete service resource: " + err.Error(),
|
||||
})
|
||||
return false
|
||||
return nil, fmt.Errorf("failed to delete service resource: %v", err)
|
||||
}
|
||||
|
||||
c.JSON(http.StatusNoContent, nil)
|
||||
return true
|
||||
return nil, nil
|
||||
default:
|
||||
c.JSON(http.StatusMethodNotAllowed, api.Error{
|
||||
Errtype: "error",
|
||||
Message: "Method not allowed",
|
||||
})
|
||||
return false
|
||||
return nil, fmt.Errorf("method not allowed")
|
||||
}
|
||||
}
|
||||
|
@ -7,10 +7,9 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
api "git.zzyxyz.com/zzy/zzyxyz_go_api/gen/bookmarks"
|
||||
api "git.zzyxyz.com/zzy/zzyxyz_go_api/gen/bookmarks_client"
|
||||
"git.zzyxyz.com/zzy/zzyxyz_go_api/internal/vfs"
|
||||
"git.zzyxyz.com/zzy/zzyxyz_go_api/internal/vfs/models"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type VfsBookMarkService struct {
|
||||
@ -32,44 +31,44 @@ func NewVfsBookMarkService(serverURL string) (*vfs.ProxyEntry, error) {
|
||||
}
|
||||
|
||||
// Create implements ServiceProxy.
|
||||
func (v *VfsBookMarkService) Create(c *gin.Context, servicePath string, node *models.VfsNode, data []byte) (string, error) {
|
||||
func (v *VfsBookMarkService) Create(servicePath string, node *models.VfsNode, data []byte) ([]byte, error) {
|
||||
ctx := context.Background()
|
||||
|
||||
// 解析传入的数据为 BookmarkRequest
|
||||
var req api.BookmarkRequest
|
||||
if err := json.Unmarshal(data, &req); err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 调用 bookmark 服务创建书签
|
||||
resp, err := v.client.CreateBookmarkWithResponse(ctx, int64(node.ID), req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 处理响应
|
||||
if resp.JSON201 != nil {
|
||||
result, err := json.Marshal(resp.JSON201)
|
||||
data, err := json.Marshal(resp.JSON201)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, fmt.Errorf("marshal response error: %w", err)
|
||||
}
|
||||
return string(result), nil
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// 处理错误情况
|
||||
if resp.JSON400 != nil {
|
||||
return "", fmt.Errorf("bad request: %s", resp.JSON400.Message)
|
||||
return nil, fmt.Errorf("bad request: %s", resp.JSON400.Message)
|
||||
}
|
||||
|
||||
if resp.JSON500 != nil {
|
||||
return "", fmt.Errorf("server error: %s", resp.JSON500.Message)
|
||||
return nil, fmt.Errorf("server error: %s", resp.JSON500.Message)
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("unknown error")
|
||||
return nil, fmt.Errorf("unknown error")
|
||||
}
|
||||
|
||||
// Delete implements ServiceProxy.
|
||||
func (v *VfsBookMarkService) Delete(c *gin.Context, servicePath string, node *models.VfsNode) error {
|
||||
func (v *VfsBookMarkService) Delete(servicePath string, node *models.VfsNode) error {
|
||||
ctx := context.Background()
|
||||
|
||||
// 调用 bookmark 服务删除书签
|
||||
@ -96,7 +95,7 @@ func (v *VfsBookMarkService) Delete(c *gin.Context, servicePath string, node *mo
|
||||
}
|
||||
|
||||
// Get implements ServiceProxy.
|
||||
func (v *VfsBookMarkService) Get(c *gin.Context, servicePath string, node *models.VfsNode) (any, error) {
|
||||
func (v *VfsBookMarkService) Get(servicePath string, node *models.VfsNode) ([]byte, error) {
|
||||
ctx := context.Background()
|
||||
|
||||
// 调用 bookmark 服务获取书签
|
||||
@ -107,7 +106,11 @@ func (v *VfsBookMarkService) Get(c *gin.Context, servicePath string, node *model
|
||||
|
||||
// 处理响应
|
||||
if resp.JSON200 != nil {
|
||||
return resp.JSON200, nil
|
||||
data, err := json.Marshal(resp.JSON200)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("marshal response error: %w", err)
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// 处理错误情况
|
||||
@ -124,7 +127,7 @@ func (v *VfsBookMarkService) GetName() string {
|
||||
}
|
||||
|
||||
// Update implements ServiceProxy.
|
||||
func (v *VfsBookMarkService) Update(c *gin.Context, servicePath string, node *models.VfsNode, data []byte) error {
|
||||
func (v *VfsBookMarkService) Update(servicePath string, node *models.VfsNode, data []byte) error {
|
||||
ctx := context.Background()
|
||||
|
||||
// 解析传入的数据为 BookmarkRequest
|
||||
|
Reference in New Issue
Block a user