将 bookmark.go 重命名为 main.go,并调整包引用路径。将 bookmarks 和 user_np 两个模块的处理逻辑合并到同一个服务中,统一注册路由。同时更新了相关 API 的引用路径,确保生成代码与内部实现正确绑定。 此外,移除了独立的 user_np 服务入口文件,其功能已整合至 bookmark 服务中。 配置文件中调整了 user_np 和 vfs 服务的端口及部分接口定义,完善了用户 相关操作的路径参数和请求体结构。
361 lines
8.4 KiB
Go
361 lines
8.4 KiB
Go
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)
|