summaryrefslogtreecommitdiffstats
path: root/internal/handler
diff options
context:
space:
mode:
authorbndw <ben@bdw.to>2026-02-13 20:38:59 -0800
committerbndw <ben@bdw.to>2026-02-13 20:38:59 -0800
commitdfa19ff0776be0850ad7b86ca579601431349593 (patch)
treeb56a3af23dda020bd6fd6709a7d1fc3c2d9f625f /internal/handler
parent89b8948195f24df127b7ae656ab3f60bd1b49ac7 (diff)
feat: implement NIP-09 with hard delete
Implement event deletion (NIP-09) using hard delete approach: - Kind 5 events trigger deletion but are not stored themselves - ProcessDeletion hard deletes referenced events (DELETE FROM events) - Only authors can delete their own events (pubkey verification) - Support multiple event IDs in single deletion request - No deletions table needed (simpler schema) - Added 4 deletion tests covering various scenarios - All 45 tests passing
Diffstat (limited to 'internal/handler')
-rw-r--r--internal/handler/grpc/server.go17
-rw-r--r--internal/handler/websocket/handler.go11
2 files changed, 28 insertions, 0 deletions
diff --git a/internal/handler/grpc/server.go b/internal/handler/grpc/server.go
index b65b527..4d6e700 100644
--- a/internal/handler/grpc/server.go
+++ b/internal/handler/grpc/server.go
@@ -13,6 +13,7 @@ import (
13type EventStore interface { 13type EventStore interface {
14 StoreEvent(context.Context, *storage.EventData) error 14 StoreEvent(context.Context, *storage.EventData) error
15 QueryEvents(context.Context, []*pb.Filter, *storage.QueryOptions) ([]*pb.Event, error) 15 QueryEvents(context.Context, []*pb.Filter, *storage.QueryOptions) ([]*pb.Event, error)
16 ProcessDeletion(context.Context, *pb.Event) error
16} 17}
17 18
18type Server struct { 19type Server struct {
@@ -58,6 +59,22 @@ func (s *Server) PublishEvent(ctx context.Context, req *pb.PublishEventRequest)
58 59
59 canonicalJSON := nostrEvent.Serialize() 60 canonicalJSON := nostrEvent.Serialize()
60 61
62 // Handle deletion events (kind 5) - process but don't store
63 if req.Event.Kind == 5 {
64 if err := s.store.ProcessDeletion(ctx, req.Event); err != nil {
65 return &pb.PublishEventResponse{
66 Accepted: false,
67 Message: fmt.Sprintf("deletion failed: %v", err),
68 CanonicalJson: canonicalJSON,
69 }, nil
70 }
71 return &pb.PublishEventResponse{
72 Accepted: true,
73 Message: "deleted",
74 CanonicalJson: canonicalJSON,
75 }, nil
76 }
77
61 eventData := &storage.EventData{ 78 eventData := &storage.EventData{
62 Event: req.Event, 79 Event: req.Event,
63 CanonicalJSON: canonicalJSON, 80 CanonicalJSON: canonicalJSON,
diff --git a/internal/handler/websocket/handler.go b/internal/handler/websocket/handler.go
index 38d4fa6..224a2f8 100644
--- a/internal/handler/websocket/handler.go
+++ b/internal/handler/websocket/handler.go
@@ -17,6 +17,7 @@ import (
17type EventStore interface { 17type EventStore interface {
18 StoreEvent(context.Context, *storage.EventData) error 18 StoreEvent(context.Context, *storage.EventData) error
19 QueryEvents(context.Context, []*pb.Filter, *storage.QueryOptions) ([]*pb.Event, error) 19 QueryEvents(context.Context, []*pb.Filter, *storage.QueryOptions) ([]*pb.Event, error)
20 ProcessDeletion(context.Context, *pb.Event) error
20} 21}
21 22
22type Handler struct { 23type Handler struct {
@@ -115,6 +116,16 @@ func (h *Handler) handleEvent(ctx context.Context, conn *websocket.Conn, raw []j
115 pbEvent := NostrToPB(&event) 116 pbEvent := NostrToPB(&event)
116 canonicalJSON := event.Serialize() 117 canonicalJSON := event.Serialize()
117 118
119 // Handle deletion events (kind 5) - process but don't store
120 if pbEvent.Kind == 5 {
121 if err := h.store.ProcessDeletion(ctx, pbEvent); err != nil {
122 h.sendOK(ctx, conn, event.ID, false, fmt.Sprintf("deletion failed: %v", err))
123 return nil
124 }
125 h.sendOK(ctx, conn, event.ID, true, "deleted")
126 return nil
127 }
128
118 eventData := &storage.EventData{ 129 eventData := &storage.EventData{
119 Event: pbEvent, 130 Event: pbEvent,
120 CanonicalJSON: canonicalJSON, 131 CanonicalJSON: canonicalJSON,