32 lines
1.5 KiB
SQL
32 lines
1.5 KiB
SQL
-- Required Postgres extensions:
|
|
-- pgcrypto → gen_random_uuid() for primary keys
|
|
-- citext → case-insensitive emails (so "Foo@x.com" == "foo@x.com")
|
|
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
|
CREATE EXTENSION IF NOT EXISTS citext;
|
|
|
|
-- Users table. Stores all auth + profile data.
|
|
--
|
|
-- Fields:
|
|
-- email unique, citext (case-insensitive)
|
|
-- password_hash bcrypt hash; never logged or returned in API responses
|
|
-- email_verified_at NULL until the user clicks the verification link
|
|
-- avatar_url absolute URL to a file in the R2 bucket (or NULL)
|
|
-- token_version incremented whenever issued JWTs should be invalidated
|
|
-- (password change, forced logout). JWTs carry this value
|
|
-- as a "tv" claim; mismatch ⇒ middleware rejects the token.
|
|
-- role "user" | "admin" | "moderator"
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
email CITEXT NOT NULL UNIQUE,
|
|
password_hash TEXT NOT NULL,
|
|
name VARCHAR(120) NOT NULL,
|
|
avatar_url TEXT,
|
|
role VARCHAR(32) NOT NULL DEFAULT 'user',
|
|
email_verified_at TIMESTAMPTZ,
|
|
token_version INTEGER NOT NULL DEFAULT 1,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS users_created_at_idx ON users (created_at DESC);
|