feat(email): Adding email options for registration
This commit is contained in:
@@ -11,6 +11,23 @@ export async function initUsersSchema() {
|
||||
password_hash TEXT NOT NULL,
|
||||
team TEXT NOT NULL CHECK (team IN ('blue', 'red')),
|
||||
role TEXT NOT NULL DEFAULT 'user' CHECK (role IN ('user', 'admin')),
|
||||
email_verified BOOLEAN NOT NULL DEFAULT false,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
`);
|
||||
|
||||
// Add email_verified to existing deployments that lack it
|
||||
await usersPool.query(`
|
||||
ALTER TABLE users ADD COLUMN IF NOT EXISTS email_verified BOOLEAN NOT NULL DEFAULT false;
|
||||
`);
|
||||
|
||||
await usersPool.query(`
|
||||
CREATE TABLE IF NOT EXISTS email_tokens (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
token TEXT NOT NULL UNIQUE,
|
||||
type TEXT NOT NULL CHECK (type IN ('confirm', 'reset')),
|
||||
expires_at TIMESTAMPTZ NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
`);
|
||||
@@ -22,7 +39,7 @@ export async function createUser(username, email, passwordHash, team) {
|
||||
const { rows } = await usersPool.query(
|
||||
`INSERT INTO users (username, email, password_hash, team)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
RETURNING id, username, email, team, role`,
|
||||
RETURNING id, username, email, team, role, email_verified`,
|
||||
[username, email, passwordHash, team]
|
||||
);
|
||||
return rows[0];
|
||||
@@ -30,16 +47,64 @@ export async function createUser(username, email, passwordHash, team) {
|
||||
|
||||
export async function getUserByUsername(username) {
|
||||
const { rows } = await usersPool.query(
|
||||
`SELECT id, username, email, team, role, password_hash FROM users WHERE username = $1`,
|
||||
`SELECT id, username, email, team, role, password_hash, email_verified FROM users WHERE username = $1`,
|
||||
[username]
|
||||
);
|
||||
return rows[0] ?? null;
|
||||
}
|
||||
|
||||
export async function getUserByEmail(email) {
|
||||
const { rows } = await usersPool.query(
|
||||
`SELECT id, username, email, team, role, email_verified FROM users WHERE email = $1`,
|
||||
[email]
|
||||
);
|
||||
return rows[0] ?? null;
|
||||
}
|
||||
|
||||
export async function getUserById(id) {
|
||||
const { rows } = await usersPool.query(
|
||||
`SELECT id, username, email, team, role FROM users WHERE id = $1`,
|
||||
`SELECT id, username, email, team, role, email_verified FROM users WHERE id = $1`,
|
||||
[id]
|
||||
);
|
||||
return rows[0] ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function createEmailToken(userId, token, type, expiresAt) {
|
||||
// Delete any existing token of the same type for this user
|
||||
await usersPool.query(
|
||||
`DELETE FROM email_tokens WHERE user_id = $1 AND type = $2`,
|
||||
[userId, type]
|
||||
);
|
||||
await usersPool.query(
|
||||
`INSERT INTO email_tokens (user_id, token, type, expires_at) VALUES ($1, $2, $3, $4)`,
|
||||
[userId, token, type, expiresAt]
|
||||
);
|
||||
}
|
||||
|
||||
export async function getEmailToken(token, type) {
|
||||
const { rows } = await usersPool.query(
|
||||
`SELECT et.*, u.email FROM email_tokens et
|
||||
JOIN users u ON u.id = et.user_id
|
||||
WHERE et.token = $1 AND et.type = $2`,
|
||||
[token, type]
|
||||
);
|
||||
return rows[0] ?? null;
|
||||
}
|
||||
|
||||
export async function deleteEmailToken(token) {
|
||||
await usersPool.query(`DELETE FROM email_tokens WHERE token = $1`, [token]);
|
||||
}
|
||||
|
||||
export async function markEmailVerified(userId) {
|
||||
await usersPool.query(
|
||||
`UPDATE users SET email_verified = true WHERE id = $1`,
|
||||
[userId]
|
||||
);
|
||||
}
|
||||
|
||||
export async function updatePassword(userId, passwordHash) {
|
||||
await usersPool.query(
|
||||
`UPDATE users SET password_hash = $1 WHERE id = $2`,
|
||||
[passwordHash, userId]
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user