d262f734f0
* mobile: wire notification registration and listener
* implement backend components for push notifications
* refactor: agentic comment cleanup
* docs: use proper module name for particle processor
* set required env variables for push notifications
* bump version
* fix: always upsert push token on mobile start
* Revert "fix: always upsert push token on mobile start"
This reverts commit 90ff18a788.
* send push notifications regardless of online status
101 lines
3.3 KiB
Go
101 lines
3.3 KiB
Go
package network
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/flowy-live/llink/internal/utils"
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
)
|
|
|
|
type Reader interface {
|
|
// GetByID returns ErrNotFound if network doesn't exist.
|
|
GetByID(ctx context.Context, id string) (*Network, error)
|
|
// ListForHuman returns ErrInvalidHumanId if humanId is empty.
|
|
ListForHuman(ctx context.Context, humanId string) ([]*Network, error)
|
|
// IsMember returns ErrInvalidHumanId if humanId is empty.
|
|
IsMember(ctx context.Context, networkID, humanId string) (bool, error)
|
|
// ListMembers returns an empty slice if the network doesn't exist.
|
|
ListMembers(ctx context.Context, networkID string) ([]string, error)
|
|
ListAll(ctx context.Context) ([]*Network, error)
|
|
// ListAllMemberships returns humanId -> networkIds for every human with at
|
|
// least one membership. Humans with zero memberships are absent from the map.
|
|
ListAllMemberships(ctx context.Context) (map[string][]string, error)
|
|
|
|
CountSeats(ctx context.Context, networkID string) (int, error)
|
|
// ListInvitationsForEmail returns ErrInvalidEmail if normalization fails.
|
|
ListInvitationsForEmail(ctx context.Context, email string) ([]*Invitation, error)
|
|
ListInvitationsForNetwork(ctx context.Context, networkID string) ([]*Invitation, error)
|
|
}
|
|
|
|
type readerImpl struct {
|
|
pool *pgxpool.Pool
|
|
repo repository
|
|
}
|
|
|
|
// newReader exposes the concrete type so the service can embed it without
|
|
// hiding pool/repo behind the Reader interface.
|
|
func newReader(pool *pgxpool.Pool) *readerImpl {
|
|
return &readerImpl{
|
|
pool: pool,
|
|
repo: newRepository(pool),
|
|
}
|
|
}
|
|
|
|
func NewReader(pool *pgxpool.Pool) Reader {
|
|
return newReader(pool)
|
|
}
|
|
|
|
func (r *readerImpl) GetByID(ctx context.Context, id string) (*Network, error) {
|
|
n, err := r.repo.getByID(ctx, id)
|
|
if errors.Is(err, errNotFound) {
|
|
return nil, ErrNotFound
|
|
}
|
|
return n, err
|
|
}
|
|
|
|
func (r *readerImpl) ListForHuman(ctx context.Context, humanId string) ([]*Network, error) {
|
|
if humanId == "" {
|
|
return nil, ErrInvalidHumanId
|
|
}
|
|
return r.repo.getNetworksForHuman(ctx, humanId)
|
|
}
|
|
|
|
func (r *readerImpl) IsMember(ctx context.Context, networkID, humanId string) (bool, error) {
|
|
if humanId == "" {
|
|
return false, ErrInvalidHumanId
|
|
}
|
|
return r.repo.isMember(ctx, networkID, humanId)
|
|
}
|
|
|
|
func (r *readerImpl) ListMembers(ctx context.Context, networkID string) ([]string, error) {
|
|
// Admin is guaranteed to be in network_members: Create() calls AddMembers
|
|
// for the admin, and RemoveMemberFromNetwork rejects admin removal.
|
|
return r.repo.getMemberHumanIds(ctx, networkID)
|
|
}
|
|
|
|
func (r *readerImpl) ListAll(ctx context.Context) ([]*Network, error) {
|
|
return r.repo.listAll(ctx)
|
|
}
|
|
|
|
func (r *readerImpl) ListAllMemberships(ctx context.Context) (map[string][]string, error) {
|
|
return r.repo.listAllMemberships(ctx)
|
|
}
|
|
|
|
func (r *readerImpl) CountSeats(ctx context.Context, networkID string) (int, error) {
|
|
return r.repo.countSeats(ctx, r.pool, networkID)
|
|
}
|
|
|
|
func (r *readerImpl) ListInvitationsForEmail(ctx context.Context, email string) ([]*Invitation, error) {
|
|
normalized, err := utils.NormalizeEmail(email)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: %w", ErrInvalidEmail, err)
|
|
}
|
|
return r.repo.getInvitationsByEmail(ctx, normalized)
|
|
}
|
|
|
|
func (r *readerImpl) ListInvitationsForNetwork(ctx context.Context, networkID string) ([]*Invitation, error) {
|
|
return r.repo.getInvitationsByNetwork(ctx, networkID)
|
|
}
|