vor temaplte

This commit is contained in:
2025-09-29 23:04:46 +02:00
parent 36d1c1512a
commit 9122ebf2f1
6 changed files with 251 additions and 40 deletions

View File

@@ -11,15 +11,13 @@ import (
type MemStore struct {
mu sync.Mutex
items map[ID]File
self string
// optionales Eventing
subs []chan ChangeEvent
}
func NewMemStore() *MemStore {
return &MemStore{
items: make(map[ID]File),
}
func NewMemStore(self string) *MemStore {
return &MemStore{self: strings.TrimSpace(self), items: make(map[ID]File)}
}
/*** Store ***/
@@ -102,7 +100,7 @@ func (m *MemStore) Create(_ context.Context, name string) (File, error) {
if err != nil {
return File{}, err
}
it := File{ID: uid, Name: name, UpdatedAt: now}
it := File{ID: uid, Name: name, UpdatedAt: now, Owner: m.self}
m.items[it.ID] = it
m.emit(it)
return it, nil
@@ -120,7 +118,10 @@ func (m *MemStore) Rename(_ context.Context, id ID, newName string) (File, error
if !ok || it.Deleted {
return File{}, ErrNotFound
}
it.Name = newName
if it.Owner != "" && it.Owner != m.self { // ← nur Owner
return File{}, ErrForbidden
}
it.Name = strings.TrimSpace(newName)
it.UpdatedAt = time.Now().UnixNano()
m.items[id] = it
m.emit(it)
@@ -134,6 +135,9 @@ func (m *MemStore) Delete(_ context.Context, id ID) (File, error) {
if !ok {
return File{}, ErrNotFound
}
if it.Owner != "" && it.Owner != m.self { // ← nur Owner
return File{}, ErrForbidden
}
if it.Deleted {
return it, nil
}
@@ -144,6 +148,31 @@ func (m *MemStore) Delete(_ context.Context, id ID) (File, error) {
return it, nil
}
func (m *MemStore) TakeoverOwner(_ context.Context, id ID, newOwner string) (File, error) {
m.mu.Lock()
defer m.mu.Unlock()
it, ok := m.items[id]
if !ok || it.Deleted {
return File{}, ErrNotFound
}
newOwner = strings.TrimSpace(newOwner)
if newOwner == "" {
return File{}, ErrBadInput
}
// Sicherheit: nur für sich selbst übernehmen
if newOwner != m.self {
return File{}, ErrForbidden
}
if it.Owner == newOwner {
return it, nil
}
it.Owner = newOwner
it.UpdatedAt = time.Now().UnixNano()
m.items[id] = it
m.emitLocked(it)
return it, nil
}
/*** Replicable ***/
func (m *MemStore) Snapshot(_ context.Context) (Snapshot, error) {
@@ -162,6 +191,10 @@ func (m *MemStore) ApplyRemote(_ context.Context, s Snapshot) error {
for _, ri := range s.Items {
li, ok := m.items[ri.ID]
if !ok || ri.UpdatedAt > li.UpdatedAt {
// Owner nie überschreiben, außer er ist leer
if ok && li.Owner != "" && ri.Owner != "" && ri.Owner != li.Owner {
ri.Owner = li.Owner
}
m.items[ri.ID] = ri
m.emitLocked(ri)
}

View File

@@ -13,11 +13,11 @@ import (
type ID = string
type File struct {
ID ID `json:"id"`
Name string `json:"name"`
// weitere Metadaten optional: Size, Hash, Owner, Tags, ...
UpdatedAt int64 `json:"updatedAt"` // UnixNano für LWW
Deleted bool `json:"deleted"` // Tombstone für Mesh-Delete
ID ID `json:"id"`
Name string `json:"name"`
UpdatedAt int64 `json:"updatedAt"` // UnixNano für LWW
Deleted bool `json:"deleted"` // Tombstone für Mesh-Delete
Owner string `json:"owner"` //AdvertURL/NodeID des Erzeugers
}
/*** Fehler ***/
@@ -36,11 +36,10 @@ type Store interface {
// Lesen & Auflisten
Get(ctx context.Context, id ID) (File, error)
List(ctx context.Context, next ID, limit int) (items []File, nextOut ID, err error)
// Mutationen mit LWW-Semantik (UpdatedAt wird intern gesetzt, außer bei ApplyRemote)
Create(ctx context.Context, name string) (File, error)
Rename(ctx context.Context, id ID, newName string) (File, error)
Delete(ctx context.Context, id ID) (File, error)
Rename(ctx context.Context, id ID, newName string) (File, error) // nur Owner darf
Delete(ctx context.Context, id ID) (File, error) // nur Owner darf
TakeoverOwner(ctx context.Context, id ID, newOwner string) (File, error)
}
/*** Mesh-Replikation ***/