a509ec538b
Adds users.email (optional, lowercase-unique via partial index), smtp_config singleton, and password_resets tokens. Plus the queries U3-T2 / T3 / T4 use: - ChangeUserPassword: self-service password change. HTTP layer verifies the current password before this fires. - UpdateUserProfile: set display_name + email together. - RegenerateApiToken: invalidate old API token. - GetUserByEmail: case-insensitive lookup for forgot-password. - GetSMTPConfig + UpdateSMTPConfig: admin SMTP settings CRUD. - CreatePasswordReset / GetPasswordReset / UsePasswordReset (:execrows for atomic claim) / DeleteExpiredPasswordResets (cron-style cleanup, not yet wired). Email column is nullable; users without email have admin-reset as their only password-recovery path. The lower() unique index allows many NULLs and prevents case-insensitive duplicates. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
23 lines
812 B
SQL
23 lines
812 B
SQL
-- name: CreatePasswordReset :exec
|
|
INSERT INTO password_resets (token, user_id, expires_at)
|
|
VALUES ($1, $2, $3);
|
|
|
|
-- name: GetPasswordReset :one
|
|
SELECT * FROM password_resets WHERE token = $1;
|
|
|
|
-- name: UsePasswordReset :execrows
|
|
-- Atomic claim: marks the token used only if currently unused
|
|
-- and not expired. Returns rows-affected so the caller can
|
|
-- distinguish "successful claim" (rows=1) from "already used /
|
|
-- expired / nonexistent" (rows=0).
|
|
UPDATE password_resets
|
|
SET used_at = now()
|
|
WHERE token = $1
|
|
AND used_at IS NULL
|
|
AND expires_at > now();
|
|
|
|
-- name: DeleteExpiredPasswordResets :exec
|
|
-- Cleanup: drop tokens that have been expired more than 7 days.
|
|
-- Not wired in U3; useful to have for a future cron sweep.
|
|
DELETE FROM password_resets WHERE expires_at < now() - interval '7 days';
|