···11+SERVER_HOST=127.0.0.1
22+SERVER_PORT=3000
33+44+DATABASE_URL=postgres://postgres:postgres@localhost:5432/pds
55+66+OBJECT_STORAGE_ENDPOINT=
77+OBJECT_STORAGE_REGION=us-east-1
88+OBJECT_STORAGE_BUCKET=pds-blobs
99+OBJECT_STORAGE_ACCESS_KEY=
1010+OBJECT_STORAGE_SECRET_KEY=
1111+1212+# Set to 'true' for MinIO or other services that need path-style addressing
1313+OBJECT_STORAGE_FORCE_PATH_STYLE=false
1414+1515+JWT_SECRET=your-super-secret-jwt-key-please-change-me
1616+PDS_HOSTNAME=localhost:3000 # The public-facing hostname of the PDS
1717+PLC_URL=plc.directory
···11+-- A very basic schema to get started.
22+-- TODO: PRODUCTIONIZE BABY
33+44+CREATE TABLE IF NOT EXISTS users (
55+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
66+ handle TEXT NOT NULL UNIQUE,
77+ email TEXT NOT NULL UNIQUE,
88+ did TEXT NOT NULL UNIQUE,
99+ password_hash TEXT NOT NULL,
1010+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
1111+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
1212+);
1313+1414+CREATE TABLE IF NOT EXISTS invite_codes (
1515+ code TEXT PRIMARY KEY,
1616+ available_uses INT NOT NULL DEFAULT 1,
1717+ created_by_user UUID NOT NULL REFERENCES users(id),
1818+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
1919+);
2020+2121+CREATE TABLE IF NOT EXISTS invite_code_uses (
2222+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
2323+ code TEXT NOT NULL REFERENCES invite_codes(code),
2424+ used_by_user UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
2525+ used_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
2626+ UNIQUE(code, used_by_user)
2727+);
2828+2929+-- OIII THIS TABLE CONTAINS PLAINTEXT PRIVATE KEYS, TODO: encrypt at rest!
3030+CREATE TABLE IF NOT EXISTS user_keys (
3131+ user_id UUID PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE,
3232+ -- Storing as raw bytes
3333+ -- secp256k1 is 32 bytes
3434+ key_bytes BYTEA NOT NULL,
3535+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
3636+);
3737+3838+CREATE TABLE IF NOT EXISTS repos (
3939+ user_id UUID PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE,
4040+ repo_root_cid TEXT NOT NULL,
4141+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
4242+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
4343+);
4444+4545+CREATE TABLE IF NOT EXISTS blocks (
4646+ cid BYTEA PRIMARY KEY,
4747+ data BYTEA NOT NULL,
4848+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
4949+);
5050+5151+-- A denormalized table to quickly query for records
5252+-- TODO: Do I actually need this?
5353+CREATE TABLE IF NOT EXISTS records (
5454+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
5555+ repo_id UUID NOT NULL REFERENCES repos(user_id) ON DELETE CASCADE,
5656+ collection TEXT NOT NULL,
5757+ rkey TEXT NOT NULL,
5858+ record_cid TEXT NOT NULL,
5959+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
6060+ UNIQUE(repo_id, collection, rkey)
6161+);
6262+6363+CREATE TABLE IF NOT EXISTS blobs (
6464+ cid TEXT PRIMARY KEY,
6565+ mime_type TEXT NOT NULL,
6666+ size_bytes BIGINT NOT NULL,
6767+ created_by_user UUID NOT NULL REFERENCES users(id),
6868+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
6969+7070+ -- The key/path in the S3 bucket
7171+ storage_key TEXT NOT NULL
7272+);
7373+7474+CREATE TABLE IF NOT EXISTS sessions (
7575+ access_jwt TEXT PRIMARY KEY,
7676+ refresh_jwt TEXT NOT NULL UNIQUE,
7777+ did TEXT NOT NULL REFERENCES users(did) ON DELETE CASCADE,
7878+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
7979+);
8080+