Review round 1 on PR #189 (three tests stayed green with their guard removed): - TestEditRefusesANoteBehindASymlinkedDirectory had no .git, so Edit refused "not the sync source" before any path check. It now has one, plus a positive control. - AppendRegular's unclean-path case was refused by a final symlink, not cleanRel. New test where ".." after a symlinked dir resolves to a real file, for AppendRegular and PutRegular. - brainfs.Remove had no test; new one (refuses behind a symlinked dir, removes a final link but never its target, positive control). Also: promote/reject create with O_EXCL (new brainfs.CreateRegular), so a note that appears after the collision check is never overwritten; gofmt fixes in provenance.go and wiki/log.go; dead wingDir and putBrainFile removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
76 lines
2.2 KiB
Go
76 lines
2.2 KiB
Go
package api
|
|
|
|
import (
|
|
"fmt"
|
|
"path/filepath"
|
|
|
|
"github.com/mathiasbq/hyperguild/ingestion/internal/brainfs"
|
|
"github.com/mathiasbq/hyperguild/ingestion/internal/provenance"
|
|
)
|
|
|
|
// StampProvenance applies server-owned provenance (hyperguild#145) to note
|
|
// content. See package provenance for the rule; an error means the stamped
|
|
// note would not read back as stamped, and the write must be refused.
|
|
func StampProvenance(content, origin, writtenBy string) (string, error) {
|
|
return provenance.Apply(content, provenance.Stamp{Origin: origin, WrittenBy: writtenBy})
|
|
}
|
|
|
|
// StampProvenanceFile applies StampProvenance to a brain-relative note path,
|
|
// read and rewritten through the brain root (hyperguild#158).
|
|
func StampProvenanceFile(brainDir, relPath, origin, writtenBy string) error {
|
|
rel, _, err := resolveWithin(brainDir, relPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
bfs, err := brainfs.Open(brainDir)
|
|
if err != nil {
|
|
return fmt.Errorf("open brain: %w", err)
|
|
}
|
|
defer func() { _ = bfs.Close() }()
|
|
b, err := bfs.ReadRegular(rel)
|
|
if err != nil {
|
|
return fmt.Errorf("read for provenance: %w", err)
|
|
}
|
|
out, err := provenance.Apply(string(b), provenance.Stamp{Origin: origin, WrittenBy: writtenBy})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if out == string(b) {
|
|
return nil
|
|
}
|
|
if err := bfs.WriteRegular(rel, []byte(out)); err != nil {
|
|
return fmt.Errorf("write provenance: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// createBrainFile creates the file at abs (inside brainDir) through the
|
|
// brain root and fails if anything is already there.
|
|
func createBrainFile(brainDir, abs string, data []byte) error {
|
|
rel, err := filepath.Rel(brainDir, abs)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
bfs, err := brainfs.Open(brainDir)
|
|
if err != nil {
|
|
return fmt.Errorf("open brain: %w", err)
|
|
}
|
|
defer func() { _ = bfs.Close() }()
|
|
return bfs.CreateRegular(filepath.ToSlash(rel), data)
|
|
}
|
|
|
|
// removeBrainFile removes the file at abs (inside brainDir) through the
|
|
// brain root.
|
|
func removeBrainFile(brainDir, abs string) error {
|
|
rel, err := filepath.Rel(brainDir, abs)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
bfs, err := brainfs.Open(brainDir)
|
|
if err != nil {
|
|
return fmt.Errorf("open brain: %w", err)
|
|
}
|
|
defer func() { _ = bfs.Close() }()
|
|
return bfs.Remove(filepath.ToSlash(rel))
|
|
}
|