package neo4j import ( "encoding/json" "fmt" "time" "next.orly.dev/pkg/database" "git.mleku.dev/mleku/nostr/encoders/hex" ) // NIP-43 Invite-based ACL methods // Simplified implementation using marker-based storage via Badger // For production, these could use Neo4j nodes with relationships // AddNIP43Member adds a member using an invite code func (n *N) AddNIP43Member(pubkey []byte, inviteCode string) error { key := "nip43_" + hex.Enc(pubkey) member := database.NIP43Membership{ InviteCode: inviteCode, AddedAt: time.Now(), } copy(member.Pubkey[:], pubkey) data, err := json.Marshal(member) if err != nil { return fmt.Errorf("failed to marshal membership: %w", err) } // Also add to members list if err := n.addToMembersList(pubkey); err != nil { return err } return n.SetMarker(key, data) } // RemoveNIP43Member removes a member func (n *N) RemoveNIP43Member(pubkey []byte) error { key := "nip43_" + hex.Enc(pubkey) // Remove from members list if err := n.removeFromMembersList(pubkey); err != nil { return err } return n.DeleteMarker(key) } // IsNIP43Member checks if a pubkey is a member func (n *N) IsNIP43Member(pubkey []byte) (isMember bool, err error) { _, err = n.GetNIP43Membership(pubkey) return err == nil, nil } // GetNIP43Membership retrieves membership information func (n *N) GetNIP43Membership(pubkey []byte) (*database.NIP43Membership, error) { key := "nip43_" + hex.Enc(pubkey) data, err := n.GetMarker(key) if err != nil { return nil, err } var member database.NIP43Membership if err := json.Unmarshal(data, &member); err != nil { return nil, fmt.Errorf("failed to unmarshal membership: %w", err) } return &member, nil } // GetAllNIP43Members retrieves all member pubkeys func (n *N) GetAllNIP43Members() ([][]byte, error) { data, err := n.GetMarker("nip43_members_list") if err != nil { return nil, nil // No members = empty list } var members []string if err := json.Unmarshal(data, &members); err != nil { return nil, fmt.Errorf("failed to unmarshal members list: %w", err) } result := make([][]byte, 0, len(members)) for _, hexPubkey := range members { pubkey, err := hex.Dec(hexPubkey) if err != nil { continue } result = append(result, pubkey) } return result, nil } // StoreInviteCode stores an invite code with expiration func (n *N) StoreInviteCode(code string, expiresAt time.Time) error { key := "invite_" + code inviteData := map[string]interface{}{ "code": code, "expiresAt": expiresAt, } data, err := json.Marshal(inviteData) if err != nil { return fmt.Errorf("failed to marshal invite: %w", err) } return n.SetMarker(key, data) } // ValidateInviteCode checks if an invite code is valid func (n *N) ValidateInviteCode(code string) (valid bool, err error) { key := "invite_" + code data, err := n.GetMarker(key) if err != nil { return false, nil // Code doesn't exist } var inviteData map[string]interface{} if err := json.Unmarshal(data, &inviteData); err != nil { return false, fmt.Errorf("failed to unmarshal invite: %w", err) } // Check expiration if expiresStr, ok := inviteData["expiresAt"].(string); ok { expiresAt, err := time.Parse(time.RFC3339, expiresStr) if err == nil && time.Now().After(expiresAt) { return false, nil // Expired } } return true, nil } // DeleteInviteCode removes an invite code func (n *N) DeleteInviteCode(code string) error { key := "invite_" + code return n.DeleteMarker(key) } // PublishNIP43MembershipEvent publishes a membership event func (n *N) PublishNIP43MembershipEvent(kind int, pubkey []byte) error { // This would require publishing an actual Nostr event // For now, just log it n.Logger.Infof("would publish NIP-43 event kind %d for %s", kind, hex.Enc(pubkey)) return nil } // addToMembersList adds a pubkey to the members list func (n *N) addToMembersList(pubkey []byte) error { data, err := n.GetMarker("nip43_members_list") var members []string if err == nil { if err := json.Unmarshal(data, &members); err != nil { return fmt.Errorf("failed to unmarshal members list: %w", err) } } hexPubkey := hex.Enc(pubkey) // Check if already in list for _, member := range members { if member == hexPubkey { return nil // Already in list } } members = append(members, hexPubkey) data, err = json.Marshal(members) if err != nil { return fmt.Errorf("failed to marshal members list: %w", err) } return n.SetMarker("nip43_members_list", data) } // removeFromMembersList removes a pubkey from the members list func (n *N) removeFromMembersList(pubkey []byte) error { data, err := n.GetMarker("nip43_members_list") if err != nil { return nil // No list = nothing to remove } var members []string if err := json.Unmarshal(data, &members); err != nil { return fmt.Errorf("failed to unmarshal members list: %w", err) } hexPubkey := hex.Enc(pubkey) // Filter out the pubkey filtered := make([]string, 0, len(members)) for _, member := range members { if member != hexPubkey { filtered = append(filtered, member) } } data, err = json.Marshal(filtered) if err != nil { return fmt.Errorf("failed to marshal members list: %w", err) } return n.SetMarker("nip43_members_list", data) }