Files
audioblume/crates/audioblume-data/migrations/0002_accounts.sql
rob thijssen 4f49a1d74b feat: scaffold audioblume audiobook backend foundation
Cargo workspace with the entities/core/data/api/cli crate split:

- entities: content-addressing (Sha256), ISBN-13, ids, domain types, DTOs
- core: dedup upload orchestration, access-decision gating, Argon2id auth,
  catalog browse, and the data-access port traits
- data: sqlx (Postgres, compile-time-checked, offline cache), aws-sdk-s3
  (MinIO), epub parsing; implements the core ports
- api: Axum /v1 (auth, upload, catalog, gated download, cover)
- cli: operator commands (migrate, user, copyright, work, blob, session)

Three-layer catalog (blob -> edition -> work) with content-addressed
deduplication, copyright gating (public-domain open; in-copyright/unknown
gated by ownership), and bookstore-style metadata visibility. Forward-compatible
schema for later translation, narration, voice marketplace, and sales phases.

Includes deployment assets (systemd, firewalld, nginx, sysusers, config
template), Postgres + MinIO bootstrap, and an idempotent deploy.sh.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 15:13:08 +03:00

30 lines
1.1 KiB
SQL

-- User accounts and sessions.
CREATE TABLE users (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
email text NOT NULL,
display_name text,
password_hash text NOT NULL, -- Argon2id PHC string
is_admin boolean NOT NULL DEFAULT false,
status text NOT NULL DEFAULT 'active', -- active | disabled
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
-- Case-insensitive uniqueness on email.
CREATE UNIQUE INDEX users_email_lower_uq ON users (lower(email));
-- Only the SHA-256 hash of an opaque token is stored, never the token itself.
CREATE TABLE sessions (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
token_hash bytea NOT NULL UNIQUE,
created_at timestamptz NOT NULL DEFAULT now(),
last_seen_at timestamptz NOT NULL DEFAULT now(),
expires_at timestamptz NOT NULL,
revoked_at timestamptz
);
CREATE INDEX sessions_user_idx ON sessions(user_id);
CREATE INDEX sessions_active_idx ON sessions(expires_at) WHERE revoked_at IS NULL;