-- +goose Up -- Initial schema for the Scrabble backend service: durable accounts, their -- platform/email identities, and opaque server sessions. -- -- Every backend table lives in the `backend` schema. The schema is created here -- so a fresh database can apply this migration, and search_path is pinned for -- the rest of the migration so the CREATE statements land in `backend` without -- qualifying every object. Production also pins search_path via -- BACKEND_POSTGRES_DSN. CREATE SCHEMA IF NOT EXISTS backend; SET search_path = backend, pg_catalog; -- Durable internal accounts. Guests are session-only and never reach this table. CREATE TABLE accounts ( account_id uuid PRIMARY KEY, display_name text NOT NULL DEFAULT '', preferred_language text NOT NULL DEFAULT 'en', time_zone text NOT NULL DEFAULT 'UTC', block_chat boolean NOT NULL DEFAULT false, block_friend_requests boolean NOT NULL DEFAULT false, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), CONSTRAINT accounts_preferred_language_chk CHECK (preferred_language IN ('en', 'ru')) ); -- Platform and email identities attached to an account. external_id is the -- platform user id (kind='telegram') or the email address (kind='email'); -- confirmed flips true once an email confirm-code is verified (later stages). CREATE TABLE identities ( identity_id uuid PRIMARY KEY, account_id uuid NOT NULL REFERENCES accounts (account_id) ON DELETE CASCADE, kind text NOT NULL, external_id text NOT NULL, confirmed boolean NOT NULL DEFAULT false, created_at timestamptz NOT NULL DEFAULT now(), CONSTRAINT identities_kind_chk CHECK (kind IN ('telegram', 'email')), CONSTRAINT identities_kind_external_id_key UNIQUE (kind, external_id) ); CREATE INDEX identities_account_idx ON identities (account_id); -- Opaque server sessions. token_hash is the hex-encoded SHA-256 of the bearer -- token; the plaintext token is never stored. Sessions are revoke-only (no -- TTL): status moves active -> revoked and revoked_at is stamped. CREATE TABLE sessions ( session_id uuid PRIMARY KEY, account_id uuid NOT NULL REFERENCES accounts (account_id) ON DELETE CASCADE, token_hash text NOT NULL, status text NOT NULL DEFAULT 'active', created_at timestamptz NOT NULL DEFAULT now(), last_seen_at timestamptz, revoked_at timestamptz, CONSTRAINT sessions_status_chk CHECK (status IN ('active', 'revoked')), CONSTRAINT sessions_token_hash_key UNIQUE (token_hash) ); CREATE INDEX sessions_account_idx ON sessions (account_id); -- +goose Down DROP TABLE sessions; DROP TABLE identities; DROP TABLE accounts;