From 6e1fc063fa2ac6b563f8e0c25e5d057c74ba145f Mon Sep 17 00:00:00 2001 From: Levi Neely Date: Sat, 21 Mar 2026 22:10:37 +0100 Subject: [PATCH] Remove event stream and pubsub bus; add file rename on metadata change The pubsub event endpoint was only used for physical file sync, which is no longer needed now that reads/writes go directly through 9P. Removed the /event file, pubsub message bus, and all Publish calls. Added renameNote helper that renames the physical file and updates note.Path whenever title, keywords, signature, or body frontmatter changes, keeping the denote filename in sync with in-memory metadata. Co-Authored-By: Claude Sonnet 4.6 --- go.mod | 2 - go.sum | 8 ---- internal/p9/server/server.go | 92 ++++++++++++------------------------ 3 files changed, 31 insertions(+), 71 deletions(-) diff --git a/go.mod b/go.mod index f724fa0..75e083a 100644 --- a/go.mod +++ b/go.mod @@ -3,5 +3,3 @@ module denotesrv go 1.23.0 require 9fans.net/go v0.0.5 - -require github.com/simonfxr/pubsub v0.0.5 // indirect diff --git a/go.sum b/go.sum index e8c7e00..5089ea2 100644 --- a/go.sum +++ b/go.sum @@ -2,13 +2,7 @@ 9fans.net/go v0.0.5/go.mod h1:Rxvbbc1e+1TyGMjAvLthGTyO97t+6JMQ6ly+Lcs9Uf0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20201218220906-28db891af037/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/simonfxr/pubsub v0.0.5 h1:DJfvFoglqGvwJriIOC5NI5um34n2YX8KAU5+7jv768w= -github.com/simonfxr/pubsub v0.0.5/go.mod h1:bQ+B2NEEHZ08VY/0xVFGaGL1KMJ7cl3yLaXe8xvyC24= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -38,5 +32,3 @@ golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/p9/server/server.go b/internal/p9/server/server.go index 53124b5..1cbefd5 100644 --- a/internal/p9/server/server.go +++ b/internal/p9/server/server.go @@ -51,7 +51,6 @@ import ( "9fans.net/go/plan9" "9fans.net/go/plan9/client" - ps "github.com/simonfxr/pubsub" ) // File types in our filesystem @@ -69,7 +68,6 @@ const ( qidNoteBase = 1 qidFileBase = 1000001 qidIndex = 999999 - qidEvent = 999998 qidNew = 999997 qidNDir = 999996 qidCtl = 999995 @@ -93,7 +91,6 @@ type server struct { callbacks Callbacks filterQuery string filteredNotes []*metadata.Metadata - events *ps.Bus } type connState struct { @@ -106,9 +103,7 @@ type fid struct { path string offset int64 mode uint8 - writeBuf []byte // accumulates Twrite chunks, dispatched on Tclunk - eventCh chan string // for event subscribers - eventCancel func() // unsubscribe callback + writeBuf []byte // accumulates Twrite chunks, dispatched on Tclunk } var srv *server @@ -185,7 +180,6 @@ func NewServer(denoteDir string, callbacks Callbacks) (*Server, error) { notes: notes, denoteDir: denoteDir, callbacks: callbacks, - events: ps.NewBus(), } return &Server{s}, nil } @@ -204,7 +198,6 @@ func StartServer(initialData metadata.Results, denoteDir string, callbacks Callb notes: initialData, denoteDir: denoteDir, callbacks: callbacks, - events: ps.NewBus(), } // Get namespace and create Unix socket path @@ -409,14 +402,6 @@ func (s *server) walk(cs *connState, fc *plan9.Fcall) *plan9.Fcall { qids = append(qids, qid) path = "/dir" found = true - } else if name == "event" { - qid := plan9.Qid{ - Type: QTFile, - Path: uint64(qidEvent), - } - qids = append(qids, qid) - path = "/event" - found = true } else if name == "n" { qid := plan9.Qid{ Type: QTDir, @@ -509,24 +494,6 @@ func (s *server) read(cs *connState, fc *plan9.Fcall) *plan9.Fcall { return errorFcall(fc, "fid not found") } - // Handle event stream (blocking read with pubsub) - if f.path == "/event" { - if f.eventCh == nil { - ch := make(chan string, 64) - sub := s.events.SubscribeChan("events", ch, ps.CloseOnUnsubscribe) - f.eventCh = ch - f.eventCancel = func() { s.events.Unsubscribe(sub) } - } - cs.mu.Unlock() - select { - case msg := <-f.eventCh: - data := []byte(msg + "\n") - return &plan9.Fcall{Type: plan9.Rread, Tag: fc.Tag, Count: uint32(len(data)), Data: data} - case <-time.After(30 * time.Second): - return &plan9.Fcall{Type: plan9.Rread, Tag: fc.Tag, Count: 0, Data: []byte{}} - } - } - defer cs.mu.Unlock() var data []byte @@ -654,7 +621,6 @@ func (s *server) dispatchWrite(f *fid, tag uint16) *plan9.Fcall { if s.callbacks.OnNew != nil { go s.callbacks.OnNew(identifier) } - s.events.Publish("events", "n "+identifier) return &plan9.Fcall{Type: plan9.Rwrite, Tag: fc.Tag, Count: uint32(len(f.writeBuf))} } @@ -684,8 +650,9 @@ func (s *server) dispatchWrite(f *fid, tag uint16) *plan9.Fcall { if s.callbacks.OnRename != nil { s.callbacks.OnRename(noteID) } - s.events.Publish("events", "u "+noteID) - s.events.Publish("events", "r "+noteID) + if err := s.renameNote(note); err != nil { + return errorFcall(fc, err.Error()) + } case "keywords": if input == "" { note.Tags = []string{} @@ -702,8 +669,9 @@ func (s *server) dispatchWrite(f *fid, tag uint16) *plan9.Fcall { if s.callbacks.OnRename != nil { s.callbacks.OnRename(noteID) } - s.events.Publish("events", "u "+noteID) - s.events.Publish("events", "r "+noteID) + if err := s.renameNote(note); err != nil { + return errorFcall(fc, err.Error()) + } case "signature": note.Signature = input if s.callbacks.OnUpdate != nil { @@ -712,15 +680,15 @@ func (s *server) dispatchWrite(f *fid, tag uint16) *plan9.Fcall { if s.callbacks.OnRename != nil { s.callbacks.OnRename(noteID) } - s.events.Publish("events", "u "+noteID) - s.events.Publish("events", "r "+noteID) + if err := s.renameNote(note); err != nil { + return errorFcall(fc, err.Error()) + } case "ctl": switch input { case "d": if s.callbacks.OnDelete != nil { s.callbacks.OnDelete(noteID) } - s.events.Publish("events", "d "+noteID) s.mu.Lock() for i, n := range s.notes { if n.Identifier == noteID { @@ -739,7 +707,6 @@ func (s *server) dispatchWrite(f *fid, tag uint16) *plan9.Fcall { if s.callbacks.OnRename != nil { s.callbacks.OnRename(noteID) } - s.events.Publish("events", "r "+noteID) } case "body": if err := s.writeBody(note.Path, input, note); err != nil { @@ -785,10 +752,6 @@ func (s *server) clunk(cs *connState, fc *plan9.Fcall) *plan9.Fcall { } cs.mu.Lock() } - // Clean up event subscription if any - if ok && f.eventCancel != nil { - f.eventCancel() - } delete(cs.fids, fc.Fid) cs.mu.Unlock() @@ -854,19 +817,6 @@ func (s *server) readDir(path string, offset int64, count uint32) []byte { Muid: "denote", Length: uint64(len(s.denoteDir)), }) - // add event node - dirs = append(dirs, plan9.Dir{ - Qid: plan9.Qid{ - Type: QTFile, - Path: uint64(qidEvent), - }, - Mode: 0444, - Name: "event", - Uid: "denote", - Gid: "denote", - Muid: "denote", - Length: 0, - }) // add n directory dirs = append(dirs, plan9.Dir{ Qid: plan9.Qid{ @@ -1075,6 +1025,26 @@ func (s *server) readBody(path string) string { return string(data) } +// renameNote renames the physical file to match current note metadata and updates note.Path. +// It is a no-op when the filename is already correct. +func (s *server) renameNote(note *metadata.Metadata) error { + if note.Path == "" { + return nil + } + ext := filepath.Ext(note.Path) + fm := metadata.NewFrontMatter(note.Title, note.Signature, note.Tags, note.Identifier) + newName := metadata.BuildFilename(fm, ext) + newPath := filepath.Join(filepath.Dir(note.Path), newName) + if newPath == note.Path { + return nil + } + if err := os.Rename(note.Path, newPath); err != nil { + return err + } + note.Path = newPath + return nil +} + // writeBody writes content to file and syncs frontmatter to metadata func (s *server) writeBody(path, body string, note *metadata.Metadata) error { if path == "" { @@ -1091,7 +1061,7 @@ func (s *server) writeBody(path, body string, note *metadata.Metadata) error { note.Tags = fm.Tags note.Signature = fm.Signature } - return nil + return s.renameNote(note) } func errorFcall(fc *plan9.Fcall, msg string) *plan9.Fcall {