package salty import ( "bytes" "context" "crypto/sha256" "fmt" "log" "net/url" "strings" "github.com/keys-pub/keys" "github.com/oklog/ulid/v2" "go.sour.is/ev/pkg/event" "go.sour.is/pkg/gql" ) type SaltyUser struct { pubkey *keys.EdX25519PublicKey inbox ulid.ULID event.IsAggregate } var _ event.Aggregate = (*SaltyUser)(nil) // ApplyEvent applies the event to the aggrigate state func (a *SaltyUser) ApplyEvent(lis ...event.Event) { for _, e := range lis { switch e := e.(type) { case *UserRegistered: // a.name = e.Name a.pubkey = e.Pubkey a.inbox = e.EventMeta().EventID // a.SetStreamID(a.streamID()) default: log.Printf("unknown event %T", e) } } } func (a *SaltyUser) OnUserRegister(pubkey *keys.EdX25519PublicKey) error { if err := event.NotExists(a); err != nil { return err } event.Raise(a, &UserRegistered{Pubkey: pubkey}) return nil } func (a *SaltyUser) Inbox() string { return a.inbox.String() } func (a *SaltyUser) Pubkey() string { return a.pubkey.String() } func (s *SaltyUser) Endpoint(ctx context.Context) (string, error) { svc := gql.FromContext[contextKey, *service](ctx, saltyKey) return url.JoinPath(svc.BaseURL(), s.inbox.String()) } type UserRegistered struct { Name string Pubkey *keys.EdX25519PublicKey event.IsEvent } var _ event.Event = (*UserRegistered)(nil) func (e *UserRegistered) MarshalBinary() (text []byte, err error) { var b bytes.Buffer b.WriteString(e.Name) b.WriteRune('\t') b.WriteString(e.Pubkey.String()) return b.Bytes(), nil } func (e *UserRegistered) UnmarshalBinary(b []byte) error { name, pub, ok := bytes.Cut(b, []byte{'\t'}) if !ok { return fmt.Errorf("parse error") } var err error e.Name = string(name) e.Pubkey, err = keys.NewEdX25519PublicKeyFromID(keys.ID(pub)) return err } func NickToStreamID(nick string) string { return fmt.Sprintf("saltyuser-%x", sha256.Sum256([]byte(strings.ToLower(nick)))) } func HashToStreamID(hash string) string { return fmt.Sprint("saltyuser-", hash) }