1package admin
2
3import (
4 "context"
5 "errors"
6 "fmt"
7 "time"
8
9 "tug/internal/storage"
10)
11
12var (
13 ErrConfirmCode = errors.New("admin: the confirmation code does not match")
14 ErrConfirmSpent = errors.New("admin: too many wrong confirmation codes")
15)
16
17type PendingAccount struct {
18 Username string
19 Spent bool
20 WrongCodes int
21 ExpiresAt time.Time
22}
23
24func (s *Service) pending(ctx context.Context) (map[string]storage.Pending, error) {
25 pending, err := s.store.PendingUsers(ctx)
26 if err != nil {
27 return nil, failed("read the pending accounts", err)
28 }
29 return pending, nil
30}
31
32func (s *Service) PendingAccount(ctx context.Context, id string) (PendingAccount, error) {
33 pending, err := s.pending(ctx)
34 if err != nil {
35 return PendingAccount{}, err
36 }
37 p, ok := pending[id]
38 if !ok {
39 return PendingAccount{}, fmt.Errorf("%w: %s is not waiting for a confirmation", ErrNotFound, id)
40 }
41 u, err := s.store.UserByID(ctx, id)
42 if err != nil {
43 return PendingAccount{}, target("read user", err)
44 }
45 return PendingAccount{Username: u.Username, Spent: p.WrongCodes >= storage.ConfirmWrongLimit, WrongCodes: p.WrongCodes, ExpiresAt: p.ExpiresAt}, nil
46}
47
48func (s *Service) ConfirmAccount(ctx context.Context, actor, id, code string) error {
49 err := s.store.ConfirmUserAudited(ctx, id, code, actor)
50 switch {
51 case err == nil:
52 return nil
53 case errors.Is(err, storage.ErrConfirmCode):
54 return ErrConfirmCode
55 case errors.Is(err, storage.ErrConfirmSpent):
56 return ErrConfirmSpent
57 case errors.Is(err, storage.ErrNotPending):
58 return fmt.Errorf("%w: %w", ErrNotFound, err)
59 }
60 return failed("confirm account", err)
61}
62
63func (s *Service) RejectAccount(ctx context.Context, actor, id string) error {
64 _, err := s.store.RejectPendingUserAudited(ctx, id, actor)
65 switch {
66 case err == nil:
67 return nil
68 case errors.Is(err, storage.ErrNotPending), errors.Is(err, storage.ErrNotFound):
69 return fmt.Errorf("%w: %w", ErrNotFound, err)
70 }
71 return failed("reject account", err)
72}
73
74func markPending(members []Member, pending map[string]storage.Pending) []Member {
75 for i := range members {
76 _, members[i].Pending = pending[members[i].ID]
77 }
78 return members
79}
80
81func isPendingIn(pending map[string]storage.Pending, id string) bool {
82 _, ok := pending[id]
83 return ok
84}