converters.go raw
1 // Package orlydbv1 provides type converters between proto messages and Go types.
2 package orlydbv1
3
4 import (
5 "time"
6
7 "next.orly.dev/pkg/nostr/encoders/event"
8 "next.orly.dev/pkg/nostr/encoders/filter"
9 "next.orly.dev/pkg/nostr/encoders/kind"
10 "next.orly.dev/pkg/nostr/encoders/tag"
11 "next.orly.dev/pkg/nostr/encoders/timestamp"
12 ntypes "next.orly.dev/pkg/nostr/types"
13 "next.orly.dev/pkg/database"
14 indextypes "next.orly.dev/pkg/database/indexes/types"
15 "next.orly.dev/pkg/interfaces/store"
16 )
17
18 // EventToProto converts a nostr event.E to a proto Event.
19 // Binary fields (ID, Pubkey, Sig) are copied directly.
20 // Tags are converted preserving binary values for e/p tags.
21 func EventToProto(ev *event.E) *Event {
22 if ev == nil {
23 return nil
24 }
25
26 pb := &Event{
27 Id: ev.ID,
28 Pubkey: ev.Pubkey,
29 CreatedAt: ev.CreatedAt,
30 Kind: uint32(ev.Kind),
31 Content: ev.Content,
32 Sig: ev.Sig,
33 }
34
35 if ev.Tags != nil {
36 pb.Tags = make([]*Tag, 0, len(*ev.Tags))
37 for _, t := range *ev.Tags {
38 pbTag := &Tag{
39 Values: make([][]byte, 0, len(t.T)),
40 }
41 for _, v := range t.T {
42 // Copy bytes directly to preserve binary data
43 val := make([]byte, len(v))
44 copy(val, v)
45 pbTag.Values = append(pbTag.Values, val)
46 }
47 pb.Tags = append(pb.Tags, pbTag)
48 }
49 }
50
51 return pb
52 }
53
54 // ProtoToEvent converts a proto Event to a nostr event.E.
55 func ProtoToEvent(pb *Event) *event.E {
56 if pb == nil {
57 return nil
58 }
59
60 ev := event.New()
61 ev.ID = pb.Id
62 ev.Pubkey = pb.Pubkey
63 ev.CreatedAt = pb.CreatedAt
64 ev.Kind = uint16(pb.Kind)
65 ev.Content = pb.Content
66 ev.Sig = pb.Sig
67
68 if len(pb.Tags) > 0 {
69 tags := tag.NewSWithCap(len(pb.Tags))
70 for _, pbTag := range pb.Tags {
71 t := tag.NewWithCap(len(pbTag.Values))
72 for _, v := range pbTag.Values {
73 t.T = append(t.T, v)
74 }
75 *tags = append(*tags, t)
76 }
77 ev.Tags = tags
78 }
79
80 return ev
81 }
82
83 // FilterToProto converts a nostr filter.F to a proto Filter.
84 func FilterToProto(f *filter.F) *Filter {
85 if f == nil {
86 return nil
87 }
88
89 pb := &Filter{}
90
91 // IDs
92 if f.Ids != nil && len(f.Ids.T) > 0 {
93 pb.Ids = make([][]byte, 0, len(f.Ids.T))
94 for _, id := range f.Ids.T {
95 pb.Ids = append(pb.Ids, id)
96 }
97 }
98
99 // Kinds
100 if f.Kinds != nil && f.Kinds.Len() > 0 {
101 pb.Kinds = make([]uint32, 0, f.Kinds.Len())
102 for _, k := range f.Kinds.K {
103 pb.Kinds = append(pb.Kinds, uint32(k.K))
104 }
105 }
106
107 // Authors
108 if f.Authors != nil && len(f.Authors.T) > 0 {
109 pb.Authors = make([][]byte, 0, len(f.Authors.T))
110 for _, a := range f.Authors.T {
111 pb.Authors = append(pb.Authors, a)
112 }
113 }
114
115 // Tags (e.g., #e, #p, #t)
116 if f.Tags != nil && len(*f.Tags) > 0 {
117 pb.Tags = make(map[string]*TagSet)
118 for _, t := range *f.Tags {
119 if len(t.T) >= 2 {
120 key := string(t.T[0])
121 ts, exists := pb.Tags[key]
122 if !exists {
123 ts = &TagSet{}
124 pb.Tags[key] = ts
125 }
126 // Add all values after the key
127 for _, v := range t.T[1:] {
128 ts.Values = append(ts.Values, v)
129 }
130 }
131 }
132 }
133
134 // Since
135 if f.Since != nil {
136 since := f.Since.I64()
137 pb.Since = &since
138 }
139
140 // Until
141 if f.Until != nil {
142 until := f.Until.I64()
143 pb.Until = &until
144 }
145
146 // Search
147 if len(f.Search) > 0 {
148 pb.Search = f.Search
149 }
150
151 // Limit
152 if f.Limit != nil {
153 limit := uint32(*f.Limit)
154 pb.Limit = &limit
155 }
156
157 return pb
158 }
159
160 // ProtoToFilter converts a proto Filter to a nostr filter.F.
161 func ProtoToFilter(pb *Filter) *filter.F {
162 if pb == nil {
163 return nil
164 }
165
166 f := filter.New()
167
168 // IDs
169 if len(pb.Ids) > 0 {
170 f.Ids = tag.NewWithCap(len(pb.Ids))
171 for _, id := range pb.Ids {
172 f.Ids.T = append(f.Ids.T, id)
173 }
174 }
175
176 // Kinds
177 if len(pb.Kinds) > 0 {
178 kinds := kind.NewWithCap(len(pb.Kinds))
179 for _, k := range pb.Kinds {
180 kinds.K = append(kinds.K, kind.New(uint16(k)))
181 }
182 f.Kinds = kinds
183 }
184
185 // Authors
186 if len(pb.Authors) > 0 {
187 f.Authors = tag.NewWithCap(len(pb.Authors))
188 for _, a := range pb.Authors {
189 f.Authors.T = append(f.Authors.T, a)
190 }
191 }
192
193 // Tags
194 if len(pb.Tags) > 0 {
195 tags := tag.NewSWithCap(len(pb.Tags))
196 for key, ts := range pb.Tags {
197 for _, v := range ts.Values {
198 t := tag.NewWithCap(2)
199 t.T = append(t.T, []byte(key), v)
200 *tags = append(*tags, t)
201 }
202 }
203 f.Tags = tags
204 }
205
206 // Since
207 if pb.Since != nil {
208 f.Since = timestamp.FromUnix(*pb.Since)
209 }
210
211 // Until
212 if pb.Until != nil {
213 f.Until = timestamp.FromUnix(*pb.Until)
214 }
215
216 // Search
217 if len(pb.Search) > 0 {
218 f.Search = pb.Search
219 }
220
221 // Limit
222 if pb.Limit != nil {
223 limit := uint(*pb.Limit)
224 f.Limit = &limit
225 }
226
227 return f
228 }
229
230 // Uint40ToProto converts a indextypes.Uint40 to a proto Uint40.
231 func Uint40ToProto(u *indextypes.Uint40) *Uint40 {
232 if u == nil {
233 return nil
234 }
235 return &Uint40{Value: u.Get()}
236 }
237
238 // ProtoToUint40 converts a proto Uint40 to a indextypes.Uint40.
239 func ProtoToUint40(pb *Uint40) *indextypes.Uint40 {
240 if pb == nil {
241 return nil
242 }
243 return newUint40(pb.Value)
244 }
245
246 // Uint40sToProto converts a slice of Uint40s to a SerialList.
247 func Uint40sToProto(serials indextypes.Uint40s) *SerialList {
248 result := &SerialList{
249 Serials: make([]uint64, 0, len(serials)),
250 }
251 for _, s := range serials {
252 result.Serials = append(result.Serials, s.Get())
253 }
254 return result
255 }
256
257 // ProtoToUint40s converts a SerialList to a slice of Uint40 pointers.
258 func ProtoToUint40s(pb *SerialList) []*indextypes.Uint40 {
259 if pb == nil {
260 return nil
261 }
262 result := make([]*indextypes.Uint40, 0, len(pb.Serials))
263 for _, s := range pb.Serials {
264 result = append(result, newUint40(s))
265 }
266 return result
267 }
268
269 // IdPkTsToProto converts a store.IdPkTs to a proto IdPkTs.
270 func IdPkTsToProto(i *store.IdPkTs) *IdPkTs {
271 if i == nil {
272 return nil
273 }
274 return &IdPkTs{
275 Id: i.Id[:],
276 Pubkey: i.Pub[:],
277 Timestamp: i.Ts,
278 Serial: i.Ser,
279 }
280 }
281
282 // ProtoToIdPkTs converts a proto IdPkTs to a store.IdPkTs.
283 func ProtoToIdPkTs(pb *IdPkTs) *store.IdPkTs {
284 if pb == nil {
285 return nil
286 }
287 return &store.IdPkTs{
288 Id: ntypes.EventIDFromBytes(pb.Id),
289 Pub: ntypes.PubkeyFromBytes(pb.Pubkey),
290 Ts: pb.Timestamp,
291 Ser: pb.Serial,
292 }
293 }
294
295 // IdPkTsListToProto converts a slice of IdPkTs to a proto IdPkTsList.
296 func IdPkTsListToProto(items []*store.IdPkTs) *IdPkTsList {
297 result := &IdPkTsList{
298 Items: make([]*IdPkTs, 0, len(items)),
299 }
300 for _, item := range items {
301 result.Items = append(result.Items, IdPkTsToProto(item))
302 }
303 return result
304 }
305
306 // ProtoToIdPkTsList converts a proto IdPkTsList to a slice of IdPkTs.
307 func ProtoToIdPkTsList(pb *IdPkTsList) []*store.IdPkTs {
308 if pb == nil {
309 return nil
310 }
311 result := make([]*store.IdPkTs, 0, len(pb.Items))
312 for _, item := range pb.Items {
313 result = append(result, ProtoToIdPkTs(item))
314 }
315 return result
316 }
317
318 // SubscriptionToProto converts a database.Subscription to a proto Subscription.
319 func SubscriptionToProto(s *database.Subscription, pubkey []byte) *Subscription {
320 if s == nil {
321 return nil
322 }
323 return &Subscription{
324 Pubkey: pubkey,
325 TrialEnd: s.TrialEnd.Unix(),
326 PaidUntil: s.PaidUntil.Unix(),
327 BlossomLevel: s.BlossomLevel,
328 BlossomStorageMb: s.BlossomStorage,
329 }
330 }
331
332 // ProtoToSubscription converts a proto Subscription to a database.Subscription.
333 func ProtoToSubscription(pb *Subscription) *database.Subscription {
334 if pb == nil {
335 return nil
336 }
337 return &database.Subscription{
338 TrialEnd: timeFromUnix(pb.TrialEnd),
339 PaidUntil: timeFromUnix(pb.PaidUntil),
340 BlossomLevel: pb.BlossomLevel,
341 BlossomStorage: pb.BlossomStorageMb,
342 }
343 }
344
345 // PaymentToProto converts a database.Payment to a proto Payment.
346 func PaymentToProto(p *database.Payment) *Payment {
347 return &Payment{
348 Amount: p.Amount,
349 Timestamp: p.Timestamp.Unix(),
350 Invoice: p.Invoice,
351 Preimage: p.Preimage,
352 }
353 }
354
355 // ProtoToPayment converts a proto Payment to a database.Payment.
356 func ProtoToPayment(pb *Payment) *database.Payment {
357 return &database.Payment{
358 Amount: pb.Amount,
359 Timestamp: timeFromUnix(pb.Timestamp),
360 Invoice: pb.Invoice,
361 Preimage: pb.Preimage,
362 }
363 }
364
365 // PaymentListToProto converts a slice of payments to a proto PaymentList.
366 func PaymentListToProto(payments []database.Payment) *PaymentList {
367 result := &PaymentList{
368 Payments: make([]*Payment, 0, len(payments)),
369 }
370 for _, p := range payments {
371 result.Payments = append(result.Payments, PaymentToProto(&p))
372 }
373 return result
374 }
375
376 // ProtoToPaymentList converts a proto PaymentList to a slice of payments.
377 func ProtoToPaymentList(pb *PaymentList) []database.Payment {
378 if pb == nil {
379 return nil
380 }
381 result := make([]database.Payment, 0, len(pb.Payments))
382 for _, p := range pb.Payments {
383 result = append(result, *ProtoToPayment(p))
384 }
385 return result
386 }
387
388 // NIP43MembershipToProto converts a database.NIP43Membership to a proto NIP43Membership.
389 func NIP43MembershipToProto(m *database.NIP43Membership) *NIP43Membership {
390 if m == nil {
391 return nil
392 }
393 return &NIP43Membership{
394 Pubkey: m.Pubkey,
395 AddedAt: m.AddedAt.Unix(),
396 InviteCode: m.InviteCode,
397 }
398 }
399
400 // ProtoToNIP43Membership converts a proto NIP43Membership to a database.NIP43Membership.
401 func ProtoToNIP43Membership(pb *NIP43Membership) *database.NIP43Membership {
402 if pb == nil {
403 return nil
404 }
405 return &database.NIP43Membership{
406 Pubkey: pb.Pubkey,
407 AddedAt: timeFromUnix(pb.AddedAt),
408 InviteCode: pb.InviteCode,
409 }
410 }
411
412 // RangeToProto converts a database.Range to a proto Range.
413 func RangeToProto(r database.Range) *Range {
414 return &Range{
415 Prefix: r.Start,
416 Start: 0,
417 End: 0,
418 }
419 }
420
421 // ProtoToRange converts a proto Range to a database.Range.
422 func ProtoToRange(pb *Range) database.Range {
423 if pb == nil {
424 return database.Range{}
425 }
426 return database.Range{
427 Start: pb.Prefix,
428 End: nil,
429 }
430 }
431
432 // EventMapToProto converts a map of serial->event to a proto EventMap.
433 func EventMapToProto(events map[uint64]*event.E) *EventMap {
434 result := &EventMap{
435 Events: make(map[uint64]*Event),
436 }
437 for serial, ev := range events {
438 result.Events[serial] = EventToProto(ev)
439 }
440 return result
441 }
442
443 // ProtoToEventMap converts a proto EventMap to a map of serial->event.
444 func ProtoToEventMap(pb *EventMap) map[uint64]*event.E {
445 if pb == nil {
446 return nil
447 }
448 result := make(map[uint64]*event.E)
449 for serial, ev := range pb.Events {
450 result[serial] = ProtoToEvent(ev)
451 }
452 return result
453 }
454
455 // EventsToProto converts a slice of events to a slice of proto Events.
456 func EventsToProto(events event.S) []*Event {
457 result := make([]*Event, 0, len(events))
458 for _, ev := range events {
459 result = append(result, EventToProto(ev))
460 }
461 return result
462 }
463
464 // ProtoToEvents converts a slice of proto Events to a slice of events.
465 func ProtoToEvents(pbs []*Event) event.S {
466 result := make(event.S, 0, len(pbs))
467 for _, pb := range pbs {
468 result = append(result, ProtoToEvent(pb))
469 }
470 return result
471 }
472
473 // timeFromUnix converts a unix timestamp to time.Time (unexported).
474 func timeFromUnix(unix int64) time.Time {
475 if unix == 0 {
476 return time.Time{}
477 }
478 return time.Unix(unix, 0)
479 }
480
481 // TimeFromUnix converts a unix timestamp to time.Time (exported).
482 func TimeFromUnix(unix int64) time.Time {
483 return timeFromUnix(unix)
484 }
485
486 // newUint40 creates a new Uint40 with the given value.
487 func newUint40(value uint64) *indextypes.Uint40 {
488 u := &indextypes.Uint40{}
489 _ = u.Set(value)
490 return u
491 }
492
493 // BytesToTag converts a slice of byte slices to a tag.T.
494 func BytesToTag(ids [][]byte) *tag.T {
495 t := tag.NewWithCap(len(ids))
496 for _, id := range ids {
497 t.T = append(t.T, id)
498 }
499 return t
500 }
501
502 // === Blob Storage Converters ===
503
504 // BlobMetadataToProto converts a database.BlobMetadata to a proto BlobMetadata.
505 func BlobMetadataToProto(m *database.BlobMetadata) *BlobMetadata {
506 if m == nil {
507 return nil
508 }
509 return &BlobMetadata{
510 Pubkey: m.Pubkey,
511 MimeType: m.MimeType,
512 Size: m.Size,
513 Uploaded: m.Uploaded,
514 Extension: m.Extension,
515 }
516 }
517
518 // ProtoToBlobMetadata converts a proto BlobMetadata to a database.BlobMetadata.
519 func ProtoToBlobMetadata(pb *BlobMetadata) *database.BlobMetadata {
520 if pb == nil {
521 return nil
522 }
523 return &database.BlobMetadata{
524 Pubkey: pb.Pubkey,
525 MimeType: pb.MimeType,
526 Size: pb.Size,
527 Uploaded: pb.Uploaded,
528 Extension: pb.Extension,
529 }
530 }
531
532 // BlobDescriptorToProto converts a database.BlobDescriptor to a proto BlobDescriptor.
533 // Note: NIP94 field is not transported via gRPC as it's generated at response time.
534 func BlobDescriptorToProto(d *database.BlobDescriptor) *BlobDescriptor {
535 if d == nil {
536 return nil
537 }
538 return &BlobDescriptor{
539 Url: d.URL,
540 Sha256: d.SHA256,
541 Size: d.Size,
542 Type: d.Type,
543 Uploaded: d.Uploaded,
544 }
545 }
546
547 // ProtoToBlobDescriptor converts a proto BlobDescriptor to a database.BlobDescriptor.
548 // Note: NIP94 field is not populated from proto as it's generated at response time.
549 func ProtoToBlobDescriptor(pb *BlobDescriptor) *database.BlobDescriptor {
550 if pb == nil {
551 return nil
552 }
553 return &database.BlobDescriptor{
554 URL: pb.Url,
555 SHA256: pb.Sha256,
556 Size: pb.Size,
557 Type: pb.Type,
558 Uploaded: pb.Uploaded,
559 }
560 }
561
562 // BlobDescriptorListToProto converts a slice of BlobDescriptors to proto.
563 func BlobDescriptorListToProto(descriptors []*database.BlobDescriptor) []*BlobDescriptor {
564 result := make([]*BlobDescriptor, 0, len(descriptors))
565 for _, d := range descriptors {
566 result = append(result, BlobDescriptorToProto(d))
567 }
568 return result
569 }
570
571 // ProtoToBlobDescriptorList converts proto BlobDescriptors to a slice.
572 func ProtoToBlobDescriptorList(pbs []*BlobDescriptor) []*database.BlobDescriptor {
573 result := make([]*database.BlobDescriptor, 0, len(pbs))
574 for _, pb := range pbs {
575 result = append(result, ProtoToBlobDescriptor(pb))
576 }
577 return result
578 }
579
580 // UserBlobStatsToProto converts a database.UserBlobStats to a proto UserBlobStats.
581 func UserBlobStatsToProto(s *database.UserBlobStats) *UserBlobStats {
582 if s == nil {
583 return nil
584 }
585 return &UserBlobStats{
586 PubkeyHex: s.PubkeyHex,
587 BlobCount: s.BlobCount,
588 TotalSizeBytes: s.TotalSizeBytes,
589 }
590 }
591
592 // ProtoToUserBlobStats converts a proto UserBlobStats to a database.UserBlobStats.
593 func ProtoToUserBlobStats(pb *UserBlobStats) *database.UserBlobStats {
594 if pb == nil {
595 return nil
596 }
597 return &database.UserBlobStats{
598 PubkeyHex: pb.PubkeyHex,
599 BlobCount: pb.BlobCount,
600 TotalSizeBytes: pb.TotalSizeBytes,
601 }
602 }
603
604 // UserBlobStatsListToProto converts a slice of UserBlobStats to proto.
605 func UserBlobStatsListToProto(stats []*database.UserBlobStats) []*UserBlobStats {
606 result := make([]*UserBlobStats, 0, len(stats))
607 for _, s := range stats {
608 result = append(result, UserBlobStatsToProto(s))
609 }
610 return result
611 }
612
613 // ProtoToUserBlobStatsList converts proto UserBlobStats to a slice.
614 func ProtoToUserBlobStatsList(pbs []*UserBlobStats) []*database.UserBlobStats {
615 result := make([]*database.UserBlobStats, 0, len(pbs))
616 for _, pb := range pbs {
617 result = append(result, ProtoToUserBlobStats(pb))
618 }
619 return result
620 }
621
622 // === Paid ACL Converters ===
623
624 // PaidSubscriptionToProto converts a database.PaidSubscription to proto.
625 func PaidSubscriptionToProto(s *database.PaidSubscription) *PaidSubscriptionMsg {
626 if s == nil {
627 return nil
628 }
629 return &PaidSubscriptionMsg{
630 PubkeyHex: s.PubkeyHex,
631 Alias: s.Alias,
632 ExpiresAt: s.ExpiresAt.Unix(),
633 CreatedAt: s.CreatedAt.Unix(),
634 InvoiceHash: s.InvoiceHash,
635 }
636 }
637
638 // ProtoToPaidSubscription converts a proto PaidSubscriptionMsg to database type.
639 func ProtoToPaidSubscription(pb *PaidSubscriptionMsg) *database.PaidSubscription {
640 if pb == nil {
641 return nil
642 }
643 return &database.PaidSubscription{
644 PubkeyHex: pb.PubkeyHex,
645 Alias: pb.Alias,
646 ExpiresAt: timeFromUnix(pb.ExpiresAt),
647 CreatedAt: timeFromUnix(pb.CreatedAt),
648 InvoiceHash: pb.InvoiceHash,
649 }
650 }
651
652 // PaidSubscriptionListToProto converts a slice of PaidSubscriptions to proto.
653 func PaidSubscriptionListToProto(subs []*database.PaidSubscription) *PaidSubscriptionList {
654 result := &PaidSubscriptionList{
655 Subscriptions: make([]*PaidSubscriptionMsg, 0, len(subs)),
656 }
657 for _, s := range subs {
658 result.Subscriptions = append(result.Subscriptions, PaidSubscriptionToProto(s))
659 }
660 return result
661 }
662
663 // ProtoToPaidSubscriptionList converts a proto PaidSubscriptionList to a slice.
664 func ProtoToPaidSubscriptionList(pb *PaidSubscriptionList) []*database.PaidSubscription {
665 if pb == nil {
666 return nil
667 }
668 result := make([]*database.PaidSubscription, 0, len(pb.Subscriptions))
669 for _, s := range pb.Subscriptions {
670 result = append(result, ProtoToPaidSubscription(s))
671 }
672 return result
673 }
674