package pushnotify import ( "context" "github.com/jackc/pgx/v5/pgxpool" ) // Service is the full surface for per-device push token storage. HTTP handlers // use Register/Unregister; the worker's notifier uses ListForHumans and // DeleteByToken. Both consumers share the same underlying repository. type Service interface { // Register upserts a token for the given human. Returns ErrInvalidToken / // ErrInvalidPlatform on bad input. Register(ctx context.Context, humanID string, in RegisterInput) error // Unregister removes a token, scoped to the calling human so a user can't // delete another user's token. Returns ErrNotFound if the token doesn't // belong to humanID (or doesn't exist). Unregister(ctx context.Context, humanID, token string) error // ListForHumans returns every push token belonging to any of the given // human IDs. Returns an empty slice when nothing matches. ListForHumans(ctx context.Context, humanIDs []string) ([]*PushToken, error) // DeleteByToken removes a token regardless of owning human. Used by the // notifier to clean up after Expo returns DeviceNotRegistered. DeleteByToken(ctx context.Context, token string) error } type RegisterInput struct { Token string Platform Platform AppVersion string } type serviceImpl struct { repo repository } func NewService(pool *pgxpool.Pool) Service { return &serviceImpl{repo: newRepository(pool)} } func (s *serviceImpl) Register(ctx context.Context, humanID string, in RegisterInput) error { if !in.Platform.Valid() { return ErrInvalidPlatform } if !IsValidExpoToken(in.Token) { return ErrInvalidToken } return s.repo.upsert(ctx, &PushToken{ Token: in.Token, HumanID: humanID, Platform: in.Platform, AppVersion: in.AppVersion, }) } func (s *serviceImpl) Unregister(ctx context.Context, humanID, token string) error { if token == "" { return ErrInvalidToken } return s.repo.deleteForHuman(ctx, humanID, token) } func (s *serviceImpl) ListForHumans(ctx context.Context, humanIDs []string) ([]*PushToken, error) { return s.repo.listForHumans(ctx, humanIDs) } func (s *serviceImpl) DeleteByToken(ctx context.Context, token string) error { return s.repo.deleteByToken(ctx, token) }