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>
30 lines
1.1 KiB
SQL
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;
|