1package inviteui
2
3import (
4 "context"
5 "errors"
6 "fmt"
7
8 "tug/internal/service/invite"
9 "tug/internal/service/password"
10 "tug/internal/transport/web"
11)
12
13type Redeemer struct {
14 service *invite.Service
15}
16
17func New(service *invite.Service) Redeemer {
18 if service == nil {
19 panic("inviteui: the redeemer needs an invite service")
20 }
21 return Redeemer{service: service}
22}
23
24var inviteProblems = []struct {
25 err error
26 problem web.InviteProblem
27}{
28 {invite.ErrUsernameGrammar, web.InviteUsernameGrammar},
29 {invite.ErrUsernameTaken, web.InviteUsernameTaken},
30 {invite.ErrDisplayNameInvalid, web.InviteDisplayNameInvalid},
31 {password.ErrTooShort, web.InvitePasswordShort},
32 {password.ErrTooLong, web.InvitePasswordLong},
33 {invite.ErrPasswordMismatch, web.InvitePasswordMismatch},
34}
35
36func inviteError(err error) error {
37 if errors.Is(err, invite.ErrInvalidToken) {
38 return web.ErrInviteInvalid
39 }
40 if errors.Is(err, password.ErrBusy) {
41 return fmt.Errorf("%w: %w", web.ErrInviteBusy, err)
42 }
43 for _, p := range inviteProblems {
44 if errors.Is(err, p.err) {
45 return p.problem
46 }
47 }
48 return err
49}
50
51func (r Redeemer) Check(ctx context.Context, token string) error {
52 if err := r.service.Check(ctx, token); err != nil {
53 return inviteError(err)
54 }
55 return nil
56}
57
58func (r Redeemer) Redeem(ctx context.Context, token string, f web.InviteForm) (web.InviteRedeemed, error) {
59 redeemed, err := r.service.RedeemAccount(ctx, token, invite.Form{
60 Username: f.Username,
61 DisplayName: f.DisplayName,
62 Password: f.Password,
63 Confirm: f.Confirm,
64 })
65 if err != nil {
66 return web.InviteRedeemed{}, inviteError(err)
67 }
68 return web.InviteRedeemed{Username: redeemed.Username, Code: redeemed.Code}, nil
69}