refactor(bookmark): 重构书签服务入口文件并整合用户权限功能
将 bookmark.go 重命名为 main.go,并调整包引用路径。将 bookmarks 和 user_np 两个模块的处理逻辑合并到同一个服务中,统一注册路由。同时更新了相关 API 的引用路径,确保生成代码与内部实现正确绑定。 此外,移除了独立的 user_np 服务入口文件,其功能已整合至 bookmark 服务中。 配置文件中调整了 user_np 和 vfs 服务的端口及部分接口定义,完善了用户 相关操作的路径参数和请求体结构。
This commit is contained in:
360
internal/vfs/vfs.go
Normal file
360
internal/vfs/vfs.go
Normal file
@ -0,0 +1,360 @@
|
||||
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 {
|
||||
vfs *models.Vfs
|
||||
enfocer *casbin.Enforcer
|
||||
config VFSConfig
|
||||
proxyTable []*ProxyEntry // 动态代理表
|
||||
proxyMutex sync.RWMutex // 保护代理表的读写锁
|
||||
}
|
||||
|
||||
func NewVfsHandler(config *Config) (*VfsImpl, error) {
|
||||
var err error
|
||||
|
||||
vfs, err := models.NewVfs(config.VFS.DbPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var policyPath = config.VFS.PolicyPath
|
||||
// 检查策略文件是否存在,如果不存在则创建
|
||||
if _, err := os.Stat(policyPath); os.IsNotExist(err) {
|
||||
// 确保目录存在
|
||||
dir := filepath.Dir(policyPath)
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
log.Fatalf("error: failed to create policy directory: %s", err)
|
||||
}
|
||||
|
||||
// 创建空的策略文件
|
||||
file, err := os.Create(policyPath)
|
||||
if err != nil {
|
||||
log.Fatalf("error: failed to create policy file: %s", err)
|
||||
}
|
||||
file.Close()
|
||||
|
||||
log.Printf("Created policy file: %s", policyPath)
|
||||
}
|
||||
|
||||
a := fileadapter.NewAdapter(policyPath)
|
||||
if a == nil {
|
||||
log.Fatalf("error: adapter: %s", err)
|
||||
}
|
||||
|
||||
m, err := model.NewModelFromString(CasbinModel)
|
||||
if err != nil {
|
||||
log.Fatalf("error: model: %s", err)
|
||||
}
|
||||
|
||||
e, err := casbin.NewEnforcer(m, a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Printf("Admin Token: %s", config.VFS.AdminToken)
|
||||
log.Printf("Register Token: %s", config.VFS.RegisterToken)
|
||||
|
||||
return &VfsImpl{
|
||||
vfs: vfs,
|
||||
enfocer: e,
|
||||
config: config.VFS,
|
||||
proxyTable: make([]*ProxyEntry, 0),
|
||||
proxyMutex: sync.RWMutex{},
|
||||
}, 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)
|
Reference in New Issue
Block a user