From b97854c8ec2665887121d773ab994a09ca768adb Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Sat, 11 May 2024 18:53:16 +0300 Subject: also return the checksums for each element of the repository --- cmd/gosd/main.go | 4 ++ internal/server/handle/handle.go | 18 ++++---- internal/server/repository/repository.go | 56 +++++++++++++++++++++---- internal/types/entry.go | 71 +++++++++++++++++++++++++++----- internal/types/entry_test.go | 22 ++++++++++ 5 files changed, 143 insertions(+), 28 deletions(-) create mode 100644 internal/types/entry_test.go diff --git a/cmd/gosd/main.go b/cmd/gosd/main.go index eb018c3..06a0353 100644 --- a/cmd/gosd/main.go +++ b/cmd/gosd/main.go @@ -38,6 +38,10 @@ func main() { return handle.Get(w, r, conf.DataDir) }) + serv.Handle("merge", func(w http.ResponseWriter, r *http.Request) error { + return handle.Merge(w, r, conf.DataDir) + }) + log.Println("Server is starting on", conf.ListenAddr) if err := http.ListenAndServe(conf.ListenAddr, nil); err != err { log.Fatal("error starting server:", err) diff --git a/internal/server/handle/handle.go b/internal/server/handle/handle.go index d1f7748..55f7b3e 100644 --- a/internal/server/handle/handle.go +++ b/internal/server/handle/handle.go @@ -1,7 +1,6 @@ package handle import ( - "encoding/json" "fmt" "io" "net/http" @@ -49,19 +48,13 @@ func List(w http.ResponseWriter, r *http.Request, dataDir string) error { return fmt.Errorf("expexted GET request") } - repository := repository.New(dataDir) - ids, err := repository.List() + list, err := repository.New(dataDir).List() if err != nil { return err } - jsonData, err := json.Marshal(ids) - if err != nil { - return err - } - - fmt.Fprint(w, string(jsonData)) - return nil + _, err = w.Write(list) + return err } func Get(w http.ResponseWriter, r *http.Request, dataDir string) error { @@ -78,3 +71,8 @@ func Get(w http.ResponseWriter, r *http.Request, dataDir string) error { fmt.Fprint(w, string(data)) return nil } + +func Merge(w http.ResponseWriter, r *http.Request, dataDir string) error { + + return nil +} diff --git a/internal/server/repository/repository.go b/internal/server/repository/repository.go index 5fe79fd..7eb7122 100644 --- a/internal/server/repository/repository.go +++ b/internal/server/repository/repository.go @@ -1,36 +1,76 @@ package repository import ( + "encoding/json" "log" "os" "path/filepath" "strings" + "sync" + + "codeberg.org/snonux/gos/internal/types" ) type Repository struct { dataDir string + entries map[string]types.Entry + mu *sync.Mutex } func New(dataDir string) Repository { - return Repository{dataDir} + return Repository{ + dataDir: dataDir, + entries: make(map[string]types.Entry), + mu: &sync.Mutex{}, + } } -func (r Repository) List() ([]string, error) { - var ids []string +func (r Repository) store(entry types.Entry) { + r.mu.Lock() + defer r.mu.Unlock() + r.entries[entry.ID] = entry +} - visit := func(files *[]string) filepath.WalkFunc { +// Load repository into memory +func (r Repository) load() error { + visit := func() filepath.WalkFunc { return func(path string, info os.FileInfo, err error) error { if err != nil { log.Println(err) return nil } - if !info.IsDir() && strings.HasSuffix(path, ".json") { - *files = append(*files, strings.TrimPrefix(path, r.dataDir)) + if info.IsDir() || !strings.HasSuffix(path, ".json") { + return nil + } + + entry, err := types.NewEntryFromFile(path) + if err != err { + return err } + r.store(entry) return nil } } - err := filepath.Walk(r.dataDir, visit(&ids)) - return ids, err + return filepath.Walk(r.dataDir, visit()) +} + +func (r Repository) List() ([]byte, error) { + if err := r.load(); err != nil { + return []byte{}, err + } + + type pair struct { + ID, Checksum string + } + + var pairs []pair + r.mu.Lock() + defer r.mu.Unlock() + + for _, entry := range r.entries { + pairs = append(pairs, pair{entry.ID, entry.Checksum()}) + } + + return json.Marshal(pairs) } diff --git a/internal/types/entry.go b/internal/types/entry.go index a274e2c..067d849 100644 --- a/internal/types/entry.go +++ b/internal/types/entry.go @@ -4,31 +4,82 @@ import ( "crypto/sha256" "encoding/json" "fmt" + "os" + "strings" + "sync" ) +// Tells me whether the entry was shared to the sm platform named Name type Shared struct { Name string `json:"id"` Is bool `json:"is,omitempty"` } +func (s Shared) String() string { + return fmt.Sprintf("Name:%s\nIs:%v\n", s.Name, s.Is) +} + type Entry struct { - Body string `json:"body"` - Shared []Shared `json:"shared,omitempty"` - Epoch int `json:"epoch,omitempty"` - ID string `json:"id,omitempty"` + Body string `json:"body"` + Shared []Shared `json:"shared,omitempty"` + Epoch int `json:"epoch,omitempty"` + ID string `json:"id,omitempty"` + mu *sync.Mutex + dirty bool + checksum string } func NewEntry(bytes []byte) (Entry, error) { - var entry Entry - if err := json.Unmarshal(bytes, &entry); err != nil { - return entry, fmt.Errorf("unable to deserialise payload: %w", err) + var ent Entry + if err := json.Unmarshal(bytes, &ent); err != nil { + return ent, fmt.Errorf("unable to deserialise payload: %w", err) } - if entry.ID == "" { - entry.ID = fmt.Sprintf("%x", sha256.Sum256(bytes)) + ent.mu = &sync.Mutex{} + ent.dirty = true + if ent.ID == "" { + ent.ID = fmt.Sprintf("%x", sha256.Sum256(bytes)) + } + return ent, nil +} + +func NewEntryFromFile(filePath string) (Entry, error) { + bytes, err := os.ReadFile(filePath) + if err != err { + return Entry{}, err } - return entry, nil + return NewEntry(bytes) } func (e Entry) Serialize() ([]byte, error) { return json.Marshal(e) } + +func (e Entry) String() string { + var sb strings.Builder + + sb.WriteString("ID:") + sb.WriteString(e.ID) + sb.WriteString("\n") + sb.WriteString(fmt.Sprintf("Epoch:%d\n", e.Epoch)) + for _, shared := range e.Shared { + sb.WriteString(shared.String()) + } + sb.WriteString("Body:") + sb.WriteString(e.Body) + sb.WriteString("\n") + + return sb.String() +} + +func (e *Entry) Checksum() string { + e.mu.Lock() + defer e.mu.Unlock() + + if !e.dirty { + return e.checksum + } + + e.checksum = fmt.Sprintf("%x", sha256.Sum256([]byte(e.String()))) + e.dirty = false + return e.checksum +} diff --git a/internal/types/entry_test.go b/internal/types/entry_test.go new file mode 100644 index 0000000..65e74a3 --- /dev/null +++ b/internal/types/entry_test.go @@ -0,0 +1,22 @@ +package types + +import "testing" + +func TestEntryChecksum(t *testing.T) { + t.Parallel() + + ent, err := NewEntry([]byte(`{"Body": "Body text here"}`)) + if err != nil { + t.Error(err) + return + } + + expected := "8618a63380fe6d365422cae6ef143a88bb6bd78df567fea3822074cc748f52f8" + got := ent.Checksum() + + if expected != got { + t.Errorf("expected checksum '%s' but got '%s'", expected, got) + return + } + t.Log(ent.Checksum()) +} -- cgit v1.2.3