Files
zzyxyz_go_api/internal/vfs/vfs_impl.go
zzy a3033bbf67 feat(vfs): 增强虚拟文件系统删除和查询功能
- 新增递归删除目录及子项的功能,支持 recursive 和 force 操作模式
- 支持通过路径获取文件或目录内容,并完善参数说明和示例
- 完善 VFS API 文档,包括操作模式、权限描述和响应内容
- 优化 GetVFSNode 方法,改为通过 ID 查询节点信息
- 修复部分路径和权限检查的逻辑注释
2025-09-27 22:23:30 +08:00

528 lines
16 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package vfs
import (
"context"
"fmt"
"log"
"net/http"
api "git.zzyxyz.com/zzy/zzyxyz_go_api/gen/vfs_server"
"git.zzyxyz.com/zzy/zzyxyz_go_api/internal/vfs/models"
)
// TODO 重命名名称冲突以及合法名称检测以及JSONBody and TextBody的检测
// 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 = []byte(*request.Body)
// 创建节点 (可能需要传递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 {
// 回滚操作
delete_err := v.vfs.DeleteVFSNode(node)
log.Printf("service node: %s, err %s", node.Name, err.Error())
if delete_err != nil {
// FIXME: 需要解决这种原子性
return nil, fmt.Errorf("consistency error: %w", err)
}
return api.CreateVFSNode500JSONResponse{
ServerInternalErrorJSONResponse: api.ServerInternalErrorJSONResponse{
Errtype: api.ErrorErrtypeServiceProxyError,
Message: err.Error(),
},
}, nil
} else {
// 返回二进制数据响应
return api.CreateVFSNode201TextResponse(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:
if request.Params.Op != nil && *request.Params.Op == api.Recursive {
if sericeIDs, err := v.vfs.DeleteNodeRecursively(node.ID); err != nil {
return api.DeleteVFSNode500JSONResponse{
ServerInternalErrorJSONResponse: api.ServerInternalErrorJSONResponse{
Errtype: api.ErrorErrtypeInternalServerError,
Message: "Failed to delete node: " + err.Error(),
},
}, nil
} else {
for _, serviceID := range sericeIDs {
// 对于服务类型节点,通过代理删除
_, err := v.StrictProxy2Service(http.MethodDelete, nil, v.vfs.GetVFSNode(serviceID))
if err != nil {
return api.DeleteVFSNode500JSONResponse{
ServerInternalErrorJSONResponse: api.ServerInternalErrorJSONResponse{
Errtype: api.ErrorErrtypeServiceProxyError,
Message: err.Error(),
},
}, nil
}
}
return api.DeleteVFSNode204Response{}, nil
}
} else {
// 检查目录是否为空
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
}
}
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.GetVFSNode200TextResponse(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.JSONBody == nil {
return api.UpdateVFSNode400JSONResponse{
ParameterErrorJSONResponse: api.ParameterErrorJSONResponse{
Errtype: api.ErrorErrtypeParameterError,
Message: "Request body is required for rename operation",
},
}, nil
}
newName := string(*request.JSONBody)
if err := v.vfs.CheckNameValid(newName); err != nil {
return api.UpdateVFSNode400JSONResponse{
ParameterErrorJSONResponse: api.ParameterErrorJSONResponse{
Errtype: api.ErrorErrtypeParameterError,
Message: err.Error(),
},
}, 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: 使用stream可能更好
var content []byte = []byte(*request.TextBody)
// 对于服务节点,通过代理发送变更
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.JSONBody == nil {
return api.UpdateVFSNode400JSONResponse{
ParameterErrorJSONResponse: api.ParameterErrorJSONResponse{
Errtype: api.ErrorErrtypeParameterError,
Message: "Request body is required for move operation",
},
}, nil
}
targetPath := string(*request.JSONBody)
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)