krz/gitbay

A CLI-first git forge.

clone: git clone https://gitbay.org/krz/gitbay.git

main: internal/store/registration.go · raw

 1package store
 2
 3import (
 4	"errors"
 5	"time"
 6)
 7
 8// CreateInvite stores an invite code hash bound to an email address.
 9func (s *Store) CreateInvite(codeHash, email string) error {
10	_, err := s.DB.Exec("INSERT INTO invites (code_hash, email) VALUES (?, ?)", codeHash, email)
11	return err
12}
13
14// ConsumeInvite redeems an invite exactly once, returning the address it was
15// issued for. Used and unknown codes fail identically.
16func (s *Store) ConsumeInvite(codeHash string) (string, error) {
17	res, err := s.DB.Exec(
18		"UPDATE invites SET used_at = strftime('%Y-%m-%dT%H:%M:%fZ','now') WHERE code_hash = ? AND used_at IS NULL",
19		codeHash)
20	if err != nil {
21		return "", err
22	}
23	if n, _ := res.RowsAffected(); n == 0 {
24		return "", ErrNotFound
25	}
26	var email string
27	err = s.DB.QueryRow("SELECT email FROM invites WHERE code_hash = ?", codeHash).Scan(&email)
28	return email, err
29}
30
31// CreateEmailToken stores a verification code hash for one address.
32func (s *Store) CreateEmailToken(userID int64, address, tokenHash string, ttl time.Duration) error {
33	_, err := s.DB.Exec(
34		"INSERT INTO email_tokens (token_hash, user_id, address, expires_at) VALUES (?, ?, ?, ?)",
35		tokenHash, userID, address, fmtTime(time.Now().Add(ttl)))
36	return err
37}
38
39// ConsumeEmailToken redeems a verification code for the given user.
40func (s *Store) ConsumeEmailToken(userID int64, tokenHash string) (string, error) {
41	res, err := s.DB.Exec(`
42		UPDATE email_tokens SET used_at = strftime('%Y-%m-%dT%H:%M:%fZ','now')
43		WHERE token_hash = ? AND user_id = ? AND used_at IS NULL AND expires_at > ?`,
44		tokenHash, userID, fmtTime(time.Now()))
45	if err != nil {
46		return "", err
47	}
48	if n, _ := res.RowsAffected(); n == 0 {
49		return "", ErrNotFound
50	}
51	var address string
52	err = s.DB.QueryRow("SELECT address FROM email_tokens WHERE token_hash = ?", tokenHash).Scan(&address)
53	return address, err
54}
55
56// CreateRegisteredUser makes a self-registered account, pending until its
57// email is verified.
58func (s *Store) CreateRegisteredUser(username string, pending bool) (int64, error) {
59	if taken, err := ownerNameTaken(s.DB, username); err != nil {
60		return 0, err
61	} else if taken {
62		return 0, errors.New("that username is taken")
63	}
64	res, err := s.DB.Exec("INSERT INTO users (username, pending) VALUES (?, ?)", username, boolInt(pending))
65	if err != nil {
66		if isUniqueErr(err) {
67			return 0, errors.New("that username is taken")
68		}
69		return 0, err
70	}
71	return res.LastInsertId()
72}
73
74// ClearPending activates a pending account.
75func (s *Store) ClearPending(userID int64) error {
76	_, err := s.DB.Exec("UPDATE users SET pending = 0 WHERE id = ?", userID)
77	return err
78}