Files
zzyxyz_go_api/internal/vfs/models/vfs.go
zzy b2cc27b8f5 refactor(bookmark): 重构 bookmark 服务生成配置和内部引用
将 bookmark 和 user_np 服务的生成配置分离为 server 和 client 包,
并更新了相应的导入路径。同时更新了 OpenAPI 配置文件中的包名、输出路径及
启用 strict-server 模式以增强类型安全。

此外,同步更新了 VFS 服务的客户端和服务端生成配置,并完善了其 OpenAPI
错误响应定义与二进制内容支持,增强了 API 的规范性和健壮性。

修复了部分权限校验逻辑,并调整了中间件注册方式以适配新的严格模式接口。
2025-09-25 16:15:34 +08:00

477 lines
11 KiB
Go

// vfs.go
package models
import (
"crypto/rand"
"database/sql"
"errors"
"fmt"
"path"
"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
}
// 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 {
// 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) 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(p *VfsNode) *VfsNode {
node := &VfsNode{}
err := v.DB.QueryRow(`
SELECT id, name, parent_id, type, created_at, updated_at
FROM vfs_nodes
WHERE id = ?`, p.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
}