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` 模块。
This commit is contained in:
@ -18,7 +18,7 @@ func main() {
|
|||||||
config := cors.Config{
|
config := cors.Config{
|
||||||
AllowAllOrigins: true,
|
AllowAllOrigins: true,
|
||||||
AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"},
|
AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"},
|
||||||
AllowHeaders: []string{"Origin", "Content-Length", "Content-Type"},
|
AllowHeaders: []string{"Origin", "Content-Length", "Content-Type", "Authorization"},
|
||||||
}
|
}
|
||||||
router.Use(cors.New(config))
|
router.Use(cors.New(config))
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ func (u *UserNPImpl) RegisterVFSService(username, token string) (*string, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if reqs.StatusCode() != http.StatusCreated {
|
if reqs.StatusCode() != http.StatusCreated {
|
||||||
return nil, fmt.Errorf("failed to register vfs service: %s", reqs.Status())
|
return nil, fmt.Errorf("failed to register vfs service: %s %s", reqs.Status(), string(reqs.Body))
|
||||||
}
|
}
|
||||||
|
|
||||||
tokenHeader := reqs.HTTPResponse.Header.Get("X-VFS-Token")
|
tokenHeader := reqs.HTTPResponse.Header.Get("X-VFS-Token")
|
||||||
@ -148,12 +148,13 @@ func (u *UserNPImpl) UserRegister(c *gin.Context, username string) {
|
|||||||
|
|
||||||
// 保存到数据库
|
// 保存到数据库
|
||||||
if err := u.db.Create(&user).Error; err != nil {
|
if err := u.db.Create(&user).Error; err != nil {
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "用户创建失败"})
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "用户创建失败", "detail": err.Error()})
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if token, err := u.RegisterVFSService(username, u.vfsToken); err != nil {
|
if token, err := u.RegisterVFSService(username, u.vfsToken); err != nil {
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "无法生成访问令牌"})
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "无法生成访问令牌", "detail": err.Error()})
|
||||||
u.db.Delete(&user)
|
u.db.Unscoped().Delete(&user)
|
||||||
} else {
|
} else {
|
||||||
user.Token = token
|
user.Token = token
|
||||||
u.db.Save(&user)
|
u.db.Save(&user)
|
||||||
@ -182,7 +183,12 @@ func (u *UserNPImpl) DeleteUser(c *gin.Context, username string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
u.db.Delete(&user)
|
if err := u.UnregisterVFSService(username, u.vfsToken); err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "无法注销访问令牌", "detail": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
u.db.Unscoped().Delete(&user)
|
||||||
c.JSON(http.StatusNoContent, nil)
|
c.JSON(http.StatusNoContent, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,14 +152,14 @@ func (v *Vfs) CreateUser(username string) (string, error) {
|
|||||||
ParentID: 0,
|
ParentID: 0,
|
||||||
Type: VfsNodeTypeDirectory,
|
Type: VfsNodeTypeDirectory,
|
||||||
}
|
}
|
||||||
if createHomeErr := v.CreateVFSNode(homeDir); createHomeErr != nil {
|
if createHomeErr := v.CreateVFSNode(*homeDir); createHomeErr != nil {
|
||||||
err = createHomeErr
|
err = createHomeErr
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建用户目录 /home/username
|
// 创建用户目录 /home/username
|
||||||
userDir := &VfsNode{
|
userDir := VfsNode{
|
||||||
Name: username,
|
Name: username,
|
||||||
ParentID: homeDir.ID,
|
ParentID: homeDir.ID,
|
||||||
Type: VfsNodeTypeDirectory,
|
Type: VfsNodeTypeDirectory,
|
||||||
@ -171,6 +171,22 @@ func (v *Vfs) CreateUser(username string) (string, error) {
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if userDir, getUserErr := v.GetNodeByParentIDAndName(homeDir.ID, username); getUserErr != nil {
|
||||||
|
panic(getUserErr)
|
||||||
|
} else {
|
||||||
|
dotDir := VfsNode{
|
||||||
|
Name: ".Recycle_Bin",
|
||||||
|
ParentID: userDir.ID,
|
||||||
|
Type: VfsNodeTypeDirectory,
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
UpdatedAt: time.Now(),
|
||||||
|
}
|
||||||
|
if createDirErr := v.CreateVFSNode(dotDir); createDirErr != nil {
|
||||||
|
err = createDirErr
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return token, nil
|
return token, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -540,7 +556,7 @@ func (v *Vfs) CheckNameValid(name string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Vfs) CreateVFSNode(p *VfsNode) error {
|
func (v *Vfs) CreateVFSNode(p VfsNode) error {
|
||||||
_, err := v.DB.Exec(`
|
_, err := v.DB.Exec(`
|
||||||
INSERT INTO vfs_nodes (name, parent_id, type, created_at, updated_at)
|
INSERT INTO vfs_nodes (name, parent_id, type, created_at, updated_at)
|
||||||
VALUES (?, ?, ?, ?, ?)`,
|
VALUES (?, ?, ?, ?, ?)`,
|
||||||
|
@ -429,21 +429,6 @@ func ModelType2ResponseType(nodeType models.VfsNodeType) api.VFSNodeType {
|
|||||||
|
|
||||||
// CreateUser implements server.StrictServerInterface.
|
// CreateUser implements server.StrictServerInterface.
|
||||||
func (v *VfsImpl) CreateUser(ctx context.Context, request api.CreateUserRequestObject) (api.CreateUserResponseObject, error) {
|
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)
|
token, err := v.vfs.CreateUser(request.Username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -506,6 +491,27 @@ func (v *VfsImpl) DeleteUser(ctx context.Context, request api.DeleteUserRequestO
|
|||||||
}, nil
|
}, 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)
|
_, err = v.enfocer.DeleteRolesForUser(request.Username)
|
||||||
|
1
internal/vfs/vfs_user.go
Normal file
1
internal/vfs/vfs_user.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package vfs
|
30
magefile.go
30
magefile.go
@ -71,3 +71,33 @@ func Build_All() error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Gen_TS() error {
|
||||||
|
// 创建输出目录
|
||||||
|
if err := os.MkdirAll("./bin/ts", 0755); err != nil {
|
||||||
|
return fmt.Errorf("failed to create output directory: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义需要生成 TypeScript 类型的配置
|
||||||
|
configs := []struct {
|
||||||
|
Input string
|
||||||
|
Output string
|
||||||
|
}{
|
||||||
|
{"config/vfs/vfs.yaml", "bin/ts/vfs.ts"},
|
||||||
|
{"config/bookmark/bookmark.yaml", "bin/ts/bookmark.ts"},
|
||||||
|
{"config/user_np/user_np.yaml", "bin/ts/user_np.ts"},
|
||||||
|
}
|
||||||
|
|
||||||
|
// 为每个配置生成 TypeScript 类型文件
|
||||||
|
for _, cfg := range configs {
|
||||||
|
fmt.Printf("Generating TypeScript types for %s -> %s\n", cfg.Input, cfg.Output)
|
||||||
|
|
||||||
|
cmd := exec.Command("openapi-typescript",
|
||||||
|
cfg.Input, "--output", cfg.Output)
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
return fmt.Errorf("failed to generate TypeScript types for %s: %w", cfg.Input, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user