Files
zzyxyz_go_api/internal/vfs/vfs_impl.go
zzy 429a863b76 feat(bookmark): 添加 Authorization 请求头支持
为 CORS 配置添加 `Authorization` 请求头,以支持携带认证信息的跨域请求。

feat(user-np): 改进错误信息和用户注册流程

- 在注册 VFS 服务失败时返回更详细的错误信息,包括状态码和响应体内容。
- 用户创建失败时返回具体的错误详情,并确保数据库记录被正确回滚。
- 注销用户时先尝试删除 VFS 服务中的相关资源,再执行数据库删除操作。
- 使用 `Unscoped().Delete()` 确保物理删除用户数据。

feat(vfs): 完善用户目录结构及节点创建逻辑

- 创建用户主目录 `/home/username` 及其子目录 `.Recycle_Bin`。
- 调整 `CreateVFSNode` 方法参数类型为值传递。
- 修复创建用户目录时传参不一致的问题。
- 删除用户时递归清理其在 VFS 中的所有节点,并通过代理删除关联的服务节点。

feat(mage): 新增生成 TypeScript 类型定义的任务

新增 `Gen_TS` Mage 任务用于将 OpenAPI 配置文件转换为 TypeScript 类型定义文件,
支持 `vfs`、`bookmark` 和 `user_np` 模块。
2025-09-28 12:28:22 +08:00

534 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, 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
}
if userDir, err := v.vfs.GetNodeByPath("/home/" + request.Username + "/"); err != nil {
panic(err)
} else {
if serviceIDs, err := v.vfs.DeleteNodeRecursively(userDir.ID); err != nil {
panic(err)
} else {
for _, serviceID := range serviceIDs {
// 对于服务类型节点,通过代理删除
_, err := v.StrictProxy2Service(http.MethodDelete, nil, v.vfs.GetVFSNode(serviceID))
if err != nil {
return api.DeleteUser500JSONResponse{
ServerInternalErrorJSONResponse: api.ServerInternalErrorJSONResponse{
Errtype: api.ErrorErrtypeServiceProxyError,
Message: 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)