143 lines
3.7 KiB
Go
143 lines
3.7 KiB
Go
package disk
|
|
|
|
import (
|
|
p9client "denote/internal/p9/client"
|
|
"denote/pkg/metadata"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"9fans.net/go/plan9/client"
|
|
)
|
|
|
|
// HandleUpdateEvent handles 'u' events from the 9P server.
|
|
// When metadata is updated in 9P, sync it to the filesystem.
|
|
func HandleUpdateEvent(f *client.Fsys, identifier, denoteDir string) error {
|
|
fields, err := p9client.ReadFields(f, identifier, "title", "keywords", "signature", "path")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
title := fields["title"]
|
|
keywords := fields["keywords"]
|
|
signature := fields["signature"]
|
|
path := fields["path"]
|
|
|
|
var tags []string
|
|
if keywords != "" {
|
|
for _, tag := range strings.Split(keywords, ",") {
|
|
tags = append(tags, strings.TrimSpace(tag))
|
|
}
|
|
}
|
|
|
|
// Only update if file exists (skip if not yet created via Put)
|
|
if _, err := os.Stat(path); err != nil {
|
|
return nil
|
|
}
|
|
|
|
// Only process files with frontmatter
|
|
if !SupportsFrontMatter(path) {
|
|
return nil
|
|
}
|
|
|
|
// Parse existing frontmatter to get FileType and current metadata
|
|
existing, fileType, err := ExtractFrontMatter(path)
|
|
if err != nil {
|
|
log.Printf("failed to extract frontmatter for %s: %v", identifier, err)
|
|
return nil
|
|
}
|
|
|
|
// Update fields from 9P metadata
|
|
existing.Title = title
|
|
existing.Tags = tags
|
|
existing.Signature = signature
|
|
|
|
// Build new filename using updated metadata
|
|
dir := filepath.Dir(path)
|
|
if dir == "." {
|
|
dir = denoteDir
|
|
}
|
|
|
|
ext := metadata.GetExtension(fileType)
|
|
filename := metadata.BuildFilename(existing, ext)
|
|
newPath := filepath.Join(dir, filename)
|
|
|
|
// If filename changed, update path in 9P (which might trigger rename)
|
|
if newPath != path {
|
|
newPath = filepath.Clean(newPath)
|
|
absNew, err := filepath.Abs(newPath)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to resolve new path: %w", err)
|
|
}
|
|
absBase, err := filepath.Abs(denoteDir)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to resolve base directory: %w", err)
|
|
}
|
|
if !strings.HasPrefix(absNew, absBase) {
|
|
return fmt.Errorf("path traversal attempt: path outside base")
|
|
}
|
|
|
|
if err := p9client.WriteFile(f, "n/"+identifier+"/path", newPath); err != nil {
|
|
return fmt.Errorf("failed to update path in 9p: %w", err)
|
|
}
|
|
}
|
|
|
|
// Apply updated frontmatter to file content
|
|
if err := UpdateFrontMatter(path, existing, fileType); err != nil {
|
|
log.Printf("failed to update front matter for %s: %v", identifier, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// HandleRenameEvent handles 'r' events from the 9P server.
|
|
// Delegates to Drename binary to perform the actual rename.
|
|
func HandleRenameEvent(f *client.Fsys, identifier, denoteDir string) error {
|
|
cmd := exec.Command("Drename", "--id="+identifier)
|
|
cmd.Stderr = os.Stderr
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("rename failed: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// HandleDeleteEvent handles 'd' events from the 9P server.
|
|
// When a note is deleted in 9P, delete the file from the filesystem.
|
|
func HandleDeleteEvent(f *client.Fsys, identifier, denoteDir string) error {
|
|
// Get path from metadata to find correct directory
|
|
path, err := p9client.ReadFile(f, "n/"+identifier+"/path")
|
|
if err != nil || path == "" {
|
|
// Fallback to denoteDir if path not set
|
|
path = denoteDir
|
|
}
|
|
|
|
// Use directory from metadata path
|
|
dir := filepath.Dir(path)
|
|
if dir == "." {
|
|
dir = denoteDir
|
|
}
|
|
|
|
pattern := filepath.Join(dir, identifier+"*")
|
|
matches, err := filepath.Glob(pattern)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to find file: %w", err)
|
|
}
|
|
|
|
if len(matches) == 0 {
|
|
return nil
|
|
}
|
|
|
|
if len(matches) > 1 {
|
|
log.Printf("warning: multiple files match identifier %s, deleting first: %s", identifier, matches[0])
|
|
}
|
|
|
|
if err := os.Remove(matches[0]); err != nil {
|
|
return fmt.Errorf("failed to delete file: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|