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

579 lines
14 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.

// vfs.go
package models
import (
"crypto/rand"
"database/sql"
"errors"
"fmt"
"path"
"strings"
"time"
_ "github.com/mattn/go-sqlite3"
)
type Vfs struct {
DB *sql.DB
}
type VfsNodeType int
const (
VfsNodeTypeFile VfsNodeType = iota
VfsNodeTypeService
VfsNodeTypeDirectory
VfsNodeTypeSymlink
)
type VfsNode struct {
ID uint64
Name string
ParentID uint64
Type VfsNodeType
CreatedAt time.Time
UpdatedAt time.Time
}
type VfsUser struct {
Name string
Token string
}
type VfsDirEntry struct {
ID uint64
Name string
Type VfsNodeType
}
// NewVfs 创建新的 VFS 实例
func NewVfs(dbPath string) (*Vfs, error) {
db, err := sql.Open("sqlite3", dbPath)
if err != nil {
return nil, err
}
_, err = db.Exec(`
CREATE TABLE IF NOT EXISTS vfs_nodes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
parent_id INTEGER,
type INTEGER NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(parent_id, name)
)`)
if err != nil {
return nil, err
}
_, err = db.Exec(`
CREATE TABLE IF NOT EXISTS vfs_users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL UNIQUE,
token TEXT NOT NULL UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)`)
if err != nil {
return nil, err
}
err = createInitialDirectories(db)
if err != nil {
return nil, err
}
return &Vfs{DB: db}, nil
}
// 创建初始目录结构
func createInitialDirectories(db *sql.DB) error {
var err error
// 创建 /home 目录
_, err = db.Exec(`
INSERT OR IGNORE INTO vfs_nodes (name, parent_id, type, created_at, updated_at)
VALUES ('home', 0, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)`,
VfsNodeTypeDirectory)
if err != nil {
return err
}
// 创建 /public 目录
_, err = db.Exec(`
INSERT OR IGNORE INTO vfs_nodes (name, parent_id, type, created_at, updated_at)
VALUES ('public', 0, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)`,
VfsNodeTypeDirectory)
if err != nil {
return err
}
return nil
}
func generateToken() string {
bytes := make([]byte, 16)
if _, err := rand.Read(bytes); err != nil {
// fallback to time-based token
return fmt.Sprintf("%x", time.Now().UnixNano())
}
return fmt.Sprintf("%x", bytes)
}
// 添加用户相关操作
func (v *Vfs) CreateUser(username string) (string, error) {
// 生成随机token
token := generateToken()
// 检查用户是否已存在
_, err := v.GetUserByName(username)
if err == nil {
return "", errors.New("user already exists")
}
// 插入用户(不使用事务)
_, err = v.DB.Exec("INSERT INTO vfs_users (name, token) VALUES (?, ?)", username, token)
if err != nil {
return "", err
}
// 使用 defer 确保出错时能清理已创建的用户
defer func() {
if err != nil {
v.DB.Exec("DELETE FROM vfs_users WHERE name = ? AND token = ?", username, token)
}
}()
// 确保 /home 目录存在
homeDir, getHomeErr := v.GetNodeByPath("/home/")
if getHomeErr != nil {
// 如果 /home 不存在,创建它
homeDir = &VfsNode{
Name: "home",
ParentID: 0,
Type: VfsNodeTypeDirectory,
}
if createHomeErr := v.CreateVFSNode(homeDir); createHomeErr != nil {
err = createHomeErr
return "", err
}
}
// 创建用户目录 /home/username
userDir := &VfsNode{
Name: username,
ParentID: homeDir.ID,
Type: VfsNodeTypeDirectory,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
if createDirErr := v.CreateVFSNode(userDir); createDirErr != nil {
err = createDirErr
return "", err
}
return token, nil
}
func (v *Vfs) DeleteUser(username string) error {
// TODO: 递归删除用户相关文件
// 这里暂时只删除用户记录
_, err := v.DB.Exec("DELETE FROM vfs_users WHERE name = ?", username)
return err
}
func (v *Vfs) GetUserByToken(token string) (*VfsUser, error) {
user := &VfsUser{}
err := v.DB.QueryRow("SELECT name, token FROM vfs_users WHERE token = ?", token).
Scan(&user.Name, &user.Token)
if err != nil {
if err == sql.ErrNoRows {
return nil, errors.New("user not found")
}
return nil, err
}
return user, nil
}
func (v *Vfs) GetUserByName(username string) (*VfsUser, error) {
user := &VfsUser{}
err := v.DB.QueryRow("SELECT name, token FROM vfs_users WHERE name = ?", username).
Scan(&user.Name, &user.Token)
if err != nil {
if err == sql.ErrNoRows {
return nil, errors.New("user not found")
}
return nil, err
}
return user, nil
}
// GetChildrenID 获取目录下所有子项的 ID
func (v *Vfs) GetChildren(parentID uint64) ([]VfsDirEntry, error) {
rows, err := v.DB.Query("SELECT id, name, type FROM vfs_nodes WHERE parent_id = ?", parentID)
if err != nil {
return nil, err
}
defer rows.Close()
var dirEntrys []VfsDirEntry
for rows.Next() {
var entry VfsDirEntry
if err := rows.Scan(&entry.ID, &entry.Name, &entry.Type); err != nil {
return nil, err
}
dirEntrys = append(dirEntrys, entry)
}
return dirEntrys, nil
}
func (v *Vfs) GetChildrenID(parentID uint64, recursive bool) (ids []uint64, services []uint64, err error) {
if recursive {
// 递归获取所有子项ID
err := v.getChildrenIDRecursive(parentID, &ids, &services)
if err != nil {
return nil, nil, err
}
} else {
// 只获取直接子项ID
rows, err := v.DB.Query("SELECT id FROM vfs_nodes WHERE parent_id = ?", parentID)
if err != nil {
return nil, nil, err
}
defer rows.Close()
for rows.Next() {
var id uint64
if err := rows.Scan(&id); err != nil {
return nil, nil, err
}
ids = append(ids, id)
}
}
return ids, services, nil
}
// DeleteNodeRecursively 递归删除节点及其所有子项
func (v *Vfs) DeleteNodeRecursively(nodeID uint64) ([]uint64, error) {
// 获取所有需要删除的ID包括自己和所有子项
idsToDelete := []uint64{nodeID}
// 获取所有子项ID
childIDs, sericeIDs, err := v.GetChildrenID(nodeID, true)
if err != nil {
return nil, err
}
idsToDelete = append(idsToDelete, childIDs...)
// 构建删除语句
if len(idsToDelete) == 0 {
return nil, nil
}
// 创建占位符
placeholders := make([]string, len(idsToDelete))
args := make([]any, len(idsToDelete))
for i, id := range idsToDelete {
placeholders[i] = "?"
args[i] = id
}
query := fmt.Sprintf("DELETE FROM vfs_nodes WHERE id IN (%s)", strings.Join(placeholders, ","))
_, err = v.DB.Exec(query, args...)
return sericeIDs, err
}
// getChildrenIDRecursive 递归获取所有子项ID的辅助函数
func (v *Vfs) getChildrenIDRecursive(parentID uint64, ids *[]uint64, services *[]uint64) error {
// 获取当前层级的子项
rows, err := v.DB.Query("SELECT id, type FROM vfs_nodes WHERE parent_id = ?", parentID)
if err != nil {
return err
}
defer rows.Close()
for rows.Next() {
var id uint64
var nodeType VfsNodeType
if err := rows.Scan(&id, &nodeType); err != nil {
return err
}
// 将当前节点ID添加到列表中
*ids = append(*ids, id)
// 如果是目录,递归获取其子项
switch nodeType {
case VfsNodeTypeDirectory:
err := v.getChildrenIDRecursive(id, ids, services)
if err != nil {
return err
}
case VfsNodeTypeService:
*services = append(*services, id)
}
}
return nil
}
// GetParentID 根据父路径查找父节点 ID
// parentPath 应该是 ParsePathComponents 的第一个返回值
func (v *Vfs) GetParentID(parentPath string) (uint64, error) {
// 根目录特殊处理
if parentPath == "/" || parentPath == "" {
return 0, nil
}
// 递归查找父路径ID
// 先解析父路径的父路径和节点名
grandParentPath, parentName, _, err := ParsePathComponents(parentPath)
if err != nil {
return 0, err
}
// 递归获取祖父节点ID
grandParentID, err := v.GetParentID(grandParentPath)
if err != nil {
return 0, err
}
// 在祖父节点下查找父节点
var parentID uint64
err = v.DB.QueryRow("SELECT id FROM vfs_nodes WHERE parent_id = ? AND name = ?",
grandParentID, parentName).Scan(&parentID)
if err != nil {
if err == sql.ErrNoRows {
return 0, errors.New("parent path not found")
}
return 0, err
}
return parentID, nil
}
// GetNodeByPath 根据完整路径查找节点
func (v *Vfs) GetNodeByPath(fullPath string) (*VfsNode, error) {
// 根目录特殊处理
if path.Clean(fullPath) == "/" {
return &VfsNode{
ID: 0,
Name: "/",
ParentID: 0,
Type: VfsNodeTypeDirectory,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}, nil
}
// 使用 ParsePathComponents 解析路径
parentPath, nodeName, nodeType, err := ParsePathComponents(fullPath)
if err != nil {
return nil, err
}
// 获取父节点ID
parentID, err := v.GetParentID(parentPath)
if err != nil {
return nil, err
}
// 根据 parentID, nodeName, nodeType 查找或创建节点
node, err := v.GetNodeByParentIDAndName(parentID, nodeName)
if err != nil {
// 如果节点不存在,可以选择创建它或者返回错误
// 这里根据你的需求决定是返回错误还是创建节点
return nil, err
}
if node.Type != nodeType {
return nil, errors.New("node type mismatch")
}
return node, nil
}
// GetNodeByParentIDAndName 根据父ID和名称查找节点
func (v *Vfs) GetNodeByParentIDAndName(parentID uint64, name string) (*VfsNode, error) {
node := &VfsNode{}
err := v.DB.QueryRow(`
SELECT id, name, parent_id, type, created_at, updated_at
FROM vfs_nodes
WHERE parent_id = ? AND name = ?`, parentID, name).Scan(
&node.ID, &node.Name, &node.ParentID, &node.Type, &node.CreatedAt, &node.UpdatedAt)
if err != nil {
if err == sql.ErrNoRows {
return nil, errors.New("node not found")
}
return nil, err
}
return node, nil
}
// CreateNodeByComponents 根据路径组件创建节点
func (v *Vfs) CreateNodeByComponents(parentPath, nodeName string, nodeType VfsNodeType) (*VfsNode, error) {
// 获取父节点ID
parentID, err := v.GetParentID(parentPath)
if err != nil {
return nil, err
}
// 创建新节点
node := &VfsNode{
Name: nodeName,
ParentID: parentID,
Type: nodeType,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
// 保存到数据库
result, err := v.DB.Exec(`
INSERT INTO vfs_nodes (name, parent_id, type, created_at, updated_at)
VALUES (?, ?, ?, ?, ?)`,
node.Name, node.ParentID, node.Type, node.CreatedAt, node.UpdatedAt)
if err != nil {
return nil, err
}
// 获取插入的ID
id, err := result.LastInsertId()
if err != nil {
return nil, err
}
node.ID = uint64(id)
return node, nil
}
func ParsePathComponents(pathStr string) (parentPath, nodeName string, nodeType VfsNodeType, err error) {
// FIXME: 路径判断以及update的类型判断
abspath := path.Clean(pathStr)
if !path.IsAbs(abspath) {
return "", "", 0, errors.New("path must be absolute")
}
// 特殊处理根路径
if abspath == "/" {
return "", "", VfsNodeTypeDirectory, nil
}
// 判断是文件还是目录
nodeType = VfsNodeTypeFile
// 如果原始路径以 / 结尾,则为目录
if len(pathStr) > 1 && pathStr[len(pathStr)-1] == '/' {
nodeType = VfsNodeTypeDirectory
}
if nodeType == VfsNodeTypeFile && path.Ext(pathStr) == ".api" {
nodeType = VfsNodeTypeService
}
// 分割路径
parentPath, nodeName = path.Split(abspath)
// 清理父路径
if parentPath != "/" && parentPath != "" {
parentPath = path.Clean(parentPath)
}
// 处理特殊情况
if parentPath == "." {
parentPath = "/"
}
return parentPath, nodeName, nodeType, nil
}
// MoveToPath 将节点移动到指定路径
func (v *Vfs) MoveToPath(node *VfsNode, destPath string) error {
// FIXME: 路径和权限的检查
// 1. 解析目标路径
parentPath, nodeName, _, err := ParsePathComponents(destPath)
if err != nil {
return fmt.Errorf("invalid destination path: %w", err)
}
// 2. 查找目标父节点
parentID, err := v.GetParentID(parentPath)
if err != nil {
return fmt.Errorf("failed to find parent directory '%s': %w", parentPath, err)
}
// 3. 检查目标位置是否已存在同名节点
_, err = v.GetNodeByParentIDAndName(parentID, nodeName)
if err == nil {
return fmt.Errorf("destination path '%s' already exists", destPath)
}
if err.Error() != "node not found" {
return fmt.Errorf("error checking destination path: %w", err)
}
// 4. 更新节点的父节点ID和名称
node.ParentID = parentID
node.Name = nodeName
node.UpdatedAt = time.Now()
// 5. 更新数据库中的节点信息
_, err = v.DB.Exec(`
UPDATE vfs_nodes
SET name = ?, parent_id = ?, updated_at = ?
WHERE id = ?`,
node.Name, node.ParentID, node.UpdatedAt, node.ID)
if err != nil {
return fmt.Errorf("failed to update node: %w", err)
}
return nil
}
func (v *Vfs) CheckNameValid(name string) error {
if name == "" || strings.Contains(name, "/") {
return fmt.Errorf("invalid node name")
}
return nil
}
func (v *Vfs) CreateVFSNode(p *VfsNode) error {
_, err := v.DB.Exec(`
INSERT INTO vfs_nodes (name, parent_id, type, created_at, updated_at)
VALUES (?, ?, ?, ?, ?)`,
p.Name, p.ParentID, p.Type, time.Now(), time.Now())
return err
}
func (v *Vfs) DeleteVFSNode(p *VfsNode) error {
_, err := v.DB.Exec("DELETE FROM vfs_nodes WHERE id = ?", p.ID)
return err
}
func (v *Vfs) GetVFSNode(ID uint64) *VfsNode {
node := &VfsNode{}
err := v.DB.QueryRow(`
SELECT id, name, parent_id, type, created_at, updated_at
FROM vfs_nodes
WHERE id = ?`, ID).Scan(
&node.ID, &node.Name, &node.ParentID, &node.Type, &node.CreatedAt, &node.UpdatedAt)
if err != nil {
return nil
}
return node
}
func (v *Vfs) UpdateVFSNode(p *VfsNode) error {
_, err := v.DB.Exec(`
UPDATE vfs_nodes
SET name = ?, parent_id = ?, type = ?, updated_at = ?
WHERE id = ?`,
p.Name, p.ParentID, p.Type, time.Now(), p.ID)
return err
}