all repos — postern

@ c3d755b5d212b0d99538e0143bde5801275e4cd0

Modern mail management

clone
git clone https:///postern.git

internal/db/address.go (view raw)

  1package db
  2
  3import (
  4	"context"
  5	"database/sql"
  6	"errors"
  7	"fmt"
  8	"time"
  9
 10	"postern/internal/model"
 11)
 12
 13func (db *DB) InsertUserAddress(ctx context.Context, username, address string) error {
 14	tx, err := db.write.BeginTx(ctx, nil)
 15	if err != nil {
 16		return err
 17	}
 18	defer txRollback(tx)
 19
 20	// Get userID
 21	var userID int
 22	row := tx.QueryRowContext(ctx, `SELECT id FROM users WHERE name = ?`, username)
 23	if err = row.Scan(&userID); err != nil {
 24		if errors.Is(err, sql.ErrNoRows) {
 25			return errors.New("user not found")
 26		}
 27		return err
 28	}
 29
 30	// Insert address
 31	_, err = tx.ExecContext(ctx, `INSERT INTO addresses (user_id, name, created_at) VALUES (?, ?, ?)`, userID, address, time.Now().Unix())
 32	if err != nil {
 33		return err
 34	}
 35	return tx.Commit()
 36}
 37
 38// RemoveUserAddress removes a specific address from a user. It returns
 39// model.ErrNotFound wrapped error if the user or address does not exist.
 40func (db *DB) RemoveUserAddress(ctx context.Context, username, address string) error {
 41	tx, err := db.write.BeginTx(ctx, nil)
 42	if err != nil {
 43		return err
 44	}
 45	defer txRollback(tx)
 46
 47	// Get userID
 48	var userID int
 49	row := tx.QueryRowContext(ctx, `SELECT id FROM users WHERE name = ?`, username)
 50	if err = row.Scan(&userID); err != nil {
 51		if errors.Is(err, sql.ErrNoRows) {
 52			return fmt.Errorf("%w: user %q", model.ErrNotFound, username)
 53		}
 54		return err
 55	}
 56
 57	res, err := tx.ExecContext(ctx, `DELETE FROM addresses WHERE user_id = ? AND name = ?`, userID, address)
 58	if err != nil {
 59		return err
 60	}
 61
 62	n, err := res.RowsAffected()
 63	if err != nil {
 64		return err
 65	}
 66	if n == 0 {
 67		return fmt.Errorf("%w: address %q", model.ErrNotFound, address)
 68	}
 69	return tx.Commit()
 70}
 71
 72// ListUserAddresses returns all addresses for a user, ordered by name.
 73func (db *DB) ListUserAddresses(ctx context.Context, username string) ([]string, error) {
 74	rows, err := db.read.QueryContext(
 75		ctx, `SELECT addresses.name FROM addresses INNER JOIN users ON users.id = addresses.user_id WHERE users.name = ? ORDER BY addresses.name`, username)
 76	if err != nil {
 77		return nil, err
 78	}
 79	defer func(rows *sql.Rows) {
 80		_ = rows.Close()
 81	}(rows)
 82
 83	var addresses []string
 84	for rows.Next() {
 85		var name string
 86		if err := rows.Scan(&name); err != nil {
 87			return nil, err
 88		}
 89		addresses = append(addresses, name)
 90	}
 91	if err := rows.Err(); err != nil {
 92		return nil, err
 93	}
 94	return addresses, nil
 95}
 96
 97func (db *DB) GetUserForAddress(ctx context.Context, address string) (model.User, error) {
 98	res := db.read.QueryRowContext(ctx, `SELECT users.id, users.name, users.password_hash FROM users INNER JOIN addresses ON users.id = addresses.user_id WHERE addresses.name = ?`, address)
 99
100	var u model.User
101	var pw []byte
102	err := res.Scan(&u.ID, &u.Name, &pw)
103	u.SetPassword(pw)
104	return u, err
105}
106
107func (db *DB) GetGatekeepDecision(ctx context.Context, userID int, fromAddress string) (string, error) {
108	res := db.read.QueryRowContext(ctx, `SELECT destination FROM gatekeepers WHERE from_address = ? and user_id = ?`, fromAddress, userID)
109
110	var mailbox string
111	err := res.Scan(&mailbox)
112	if err != nil {
113		return "", err
114	}
115
116	return mailbox, nil
117}