nip43.go raw
1 package neo4j
2
3 import (
4 "encoding/json"
5 "fmt"
6 "time"
7
8 "next.orly.dev/pkg/database"
9 "next.orly.dev/pkg/nostr/encoders/hex"
10 )
11
12 // NIP-43 Invite-based ACL methods
13 // Simplified implementation using marker-based storage via Badger
14 // For production, these could use Neo4j nodes with relationships
15
16 // AddNIP43Member adds a member using an invite code
17 func (n *N) AddNIP43Member(pubkey []byte, inviteCode string) error {
18 key := "nip43_" + hex.Enc(pubkey)
19
20 member := database.NIP43Membership{
21 InviteCode: inviteCode,
22 AddedAt: time.Now(),
23 }
24 copy(member.Pubkey[:], pubkey)
25
26 data, err := json.Marshal(member)
27 if err != nil {
28 return fmt.Errorf("failed to marshal membership: %w", err)
29 }
30
31 // Also add to members list
32 if err := n.addToMembersList(pubkey); err != nil {
33 return err
34 }
35
36 return n.SetMarker(key, data)
37 }
38
39 // RemoveNIP43Member removes a member
40 func (n *N) RemoveNIP43Member(pubkey []byte) error {
41 key := "nip43_" + hex.Enc(pubkey)
42
43 // Remove from members list
44 if err := n.removeFromMembersList(pubkey); err != nil {
45 return err
46 }
47
48 return n.DeleteMarker(key)
49 }
50
51 // IsNIP43Member checks if a pubkey is a member
52 func (n *N) IsNIP43Member(pubkey []byte) (isMember bool, err error) {
53 _, err = n.GetNIP43Membership(pubkey)
54 return err == nil, nil
55 }
56
57 // GetNIP43Membership retrieves membership information
58 func (n *N) GetNIP43Membership(pubkey []byte) (*database.NIP43Membership, error) {
59 key := "nip43_" + hex.Enc(pubkey)
60
61 data, err := n.GetMarker(key)
62 if err != nil {
63 return nil, err
64 }
65
66 var member database.NIP43Membership
67 if err := json.Unmarshal(data, &member); err != nil {
68 return nil, fmt.Errorf("failed to unmarshal membership: %w", err)
69 }
70
71 return &member, nil
72 }
73
74 // GetAllNIP43Members retrieves all member pubkeys
75 func (n *N) GetAllNIP43Members() ([][]byte, error) {
76 data, err := n.GetMarker("nip43_members_list")
77 if err != nil {
78 return nil, nil // No members = empty list
79 }
80
81 var members []string
82 if err := json.Unmarshal(data, &members); err != nil {
83 return nil, fmt.Errorf("failed to unmarshal members list: %w", err)
84 }
85
86 result := make([][]byte, 0, len(members))
87 for _, hexPubkey := range members {
88 pubkey, err := hex.Dec(hexPubkey)
89 if err != nil {
90 continue
91 }
92 result = append(result, pubkey)
93 }
94
95 return result, nil
96 }
97
98 // StoreInviteCode stores an invite code with expiration
99 func (n *N) StoreInviteCode(code string, expiresAt time.Time) error {
100 key := "invite_" + code
101
102 inviteData := map[string]interface{}{
103 "code": code,
104 "expiresAt": expiresAt,
105 }
106
107 data, err := json.Marshal(inviteData)
108 if err != nil {
109 return fmt.Errorf("failed to marshal invite: %w", err)
110 }
111
112 return n.SetMarker(key, data)
113 }
114
115 // ValidateInviteCode checks if an invite code is valid
116 func (n *N) ValidateInviteCode(code string) (valid bool, err error) {
117 key := "invite_" + code
118
119 data, err := n.GetMarker(key)
120 if err != nil {
121 return false, nil // Code doesn't exist
122 }
123
124 var inviteData map[string]interface{}
125 if err := json.Unmarshal(data, &inviteData); err != nil {
126 return false, fmt.Errorf("failed to unmarshal invite: %w", err)
127 }
128
129 // Check expiration
130 if expiresStr, ok := inviteData["expiresAt"].(string); ok {
131 expiresAt, err := time.Parse(time.RFC3339, expiresStr)
132 if err == nil && time.Now().After(expiresAt) {
133 return false, nil // Expired
134 }
135 }
136
137 return true, nil
138 }
139
140 // DeleteInviteCode removes an invite code
141 func (n *N) DeleteInviteCode(code string) error {
142 key := "invite_" + code
143 return n.DeleteMarker(key)
144 }
145
146 // PublishNIP43MembershipEvent publishes a membership event
147 func (n *N) PublishNIP43MembershipEvent(kind int, pubkey []byte) error {
148 // This would require publishing an actual Nostr event
149 // For now, just log it
150 n.Logger.Infof("would publish NIP-43 event kind %d for %s", kind, hex.Enc(pubkey))
151 return nil
152 }
153
154 // addToMembersList adds a pubkey to the members list
155 func (n *N) addToMembersList(pubkey []byte) error {
156 data, err := n.GetMarker("nip43_members_list")
157
158 var members []string
159 if err == nil {
160 if err := json.Unmarshal(data, &members); err != nil {
161 return fmt.Errorf("failed to unmarshal members list: %w", err)
162 }
163 }
164
165 hexPubkey := hex.Enc(pubkey)
166
167 // Check if already in list
168 for _, member := range members {
169 if member == hexPubkey {
170 return nil // Already in list
171 }
172 }
173
174 members = append(members, hexPubkey)
175
176 data, err = json.Marshal(members)
177 if err != nil {
178 return fmt.Errorf("failed to marshal members list: %w", err)
179 }
180
181 return n.SetMarker("nip43_members_list", data)
182 }
183
184 // removeFromMembersList removes a pubkey from the members list
185 func (n *N) removeFromMembersList(pubkey []byte) error {
186 data, err := n.GetMarker("nip43_members_list")
187 if err != nil {
188 return nil // No list = nothing to remove
189 }
190
191 var members []string
192 if err := json.Unmarshal(data, &members); err != nil {
193 return fmt.Errorf("failed to unmarshal members list: %w", err)
194 }
195
196 hexPubkey := hex.Enc(pubkey)
197
198 // Filter out the pubkey
199 filtered := make([]string, 0, len(members))
200 for _, member := range members {
201 if member != hexPubkey {
202 filtered = append(filtered, member)
203 }
204 }
205
206 data, err = json.Marshal(filtered)
207 if err != nil {
208 return fmt.Errorf("failed to marshal members list: %w", err)
209 }
210
211 return n.SetMarker("nip43_members_list", data)
212 }
213