package pushnotify import ( "context" "github.com/jackc/pgx/v5/pgxpool" ) // Service stores per-device Expo push tokens and exposes the operations // needed by both the HTTP handlers and the worker-side notifier. type Service interface { // Register returns ErrInvalidToken / ErrInvalidPlatform on bad input. Register(ctx context.Context, humanID string, in RegisterInput) error // Unregister is scoped to humanID so a user can't delete another user's // token. Returns ErrNotFound if the token isn't owned by humanID. Unregister(ctx context.Context, humanID, token string) error // ListForHumans returns an empty slice when nothing matches. ListForHumans(ctx context.Context, humanIDs []string) ([]*PushToken, error) // DeleteByToken removes a token regardless of owner — used to prune after // Expo reports 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) }