fix: complete vault layout transition — fs_path everywhere, no more spaces/

- notes.Create(): .md files stored in parent node's fs_path folder
- files.CopyIntoVault/CreateEmptyFile/Duplicate: use parent fs_path
- files.AddPathCopy/AddPathLink: use parent fs_path, set folder fs_path
- files.DeleteNodeAndChildren: move physical folder to .verstak/trash
- UpdateFsPathRecursive: use SafeDisplayNameToPathSegment(child.Title)
- sync_apply.go note ops: use fs_path instead of spaces/
- internal/gui/server.go file upload: use n.FsPath instead of nodeSlug
- VaultCheck diagnostic: walk nodes/files, verify paths on disk
- Tests: create/rename/move/delete/name-conflict/vault-check all pass
This commit is contained in:
2026-06-02 15:43:40 +08:00
parent 0b26f7e5b3
commit 4f01f2de2e
11 changed files with 599 additions and 33 deletions
+21 -10
View File
@@ -9,6 +9,7 @@ import (
"verstak/internal/core/files"
"verstak/internal/core/nodes"
"verstak/internal/core/storage"
"verstak/internal/core/templates"
"verstak/internal/core/util"
)
@@ -40,33 +41,43 @@ func (s *Service) Create(parentID, title, section string) (*nodes.Node, *files.R
return nil, nil, fmt.Errorf("create node: %w", err)
}
slug := node.Slug
if slug == "" {
slug = "note"
seg := templates.SafeDisplayNameToPathSegment(title)
if seg == "" {
seg = "note"
}
filename := seg + ".md"
var destDir string
if parentID != "" {
parent, err := s.nodes.GetActive(parentID)
if err == nil && parent.FsPath != "" {
destDir = filepath.Join(s.vaultRoot, parent.FsPath)
}
}
if destDir == "" {
destDir = s.vaultRoot
}
if err := os.MkdirAll(destDir, 0o750); err != nil {
return nil, nil, fmt.Errorf("mkdir: %w", err)
}
filename := slug + ".md"
destDir := filepath.Join(s.vaultRoot, "spaces")
os.MkdirAll(destDir, 0o750)
dest := filepath.Join(destDir, filename)
if _, err := os.Stat(dest); err == nil {
filename = fmt.Sprintf("%s_%s.md", slug, node.ID[:8])
filename = fmt.Sprintf("%s_%s.md", seg, node.ID[:8])
dest = filepath.Join(destDir, filename)
}
// Write initial content.
if err := os.WriteFile(dest, []byte("# "+title+"\n\n"), 0o640); err != nil {
return nil, nil, fmt.Errorf("write: %w", err)
}
// Register file record.
relPath, _ := filepath.Rel(s.vaultRoot, dest)
fileRec, err := insertFileRecord(s.db, node.ID, filename, relPath, "vault", 0)
if err != nil {
return nil, nil, fmt.Errorf("insert file: %w", err)
}
// Link.
_, err = s.db.Exec(
`INSERT INTO notes (node_id, file_id, format) VALUES (?,?,?)`,
node.ID, fileRec.ID, "markdown")
+10 -5
View File
@@ -49,11 +49,16 @@ func TestCreateAndRead(t *testing.T) {
t.Errorf("content = %q", content)
}
// Verify file on disk.
spacesDir := filepath.Join(vaultRoot, "spaces")
entries, _ := os.ReadDir(spacesDir)
if len(entries) == 0 {
t.Error("expected file in spaces/")
// Verify file on disk (in vault root for parentless notes).
entries, _ := os.ReadDir(vaultRoot)
var mdFiles int
for _, e := range entries {
if !e.IsDir() && filepath.Ext(e.Name()) == ".md" {
mdFiles++
}
}
if mdFiles == 0 {
t.Error("expected .md file in vault root")
}
}