Rust server (axum, yrs, sqlx/PostgreSQL): live shared models over the y-websocket protocol, versions, browser sign-in with PKCE, invites, single sign-on through OIDC, metrics. Docker image on distroless, compose file, and a Helm chart with one pod, optional bundled PostgreSQL and secrets from existing Secrets.
96 lines
3.7 KiB
SQL
96 lines
3.7 KiB
SQL
-- Organisations, accounts and sessions.
|
|
|
|
CREATE TABLE organisations (
|
|
id uuid PRIMARY KEY,
|
|
name text NOT NULL,
|
|
meta jsonb NOT NULL DEFAULT '{}',
|
|
created_at timestamptz NOT NULL DEFAULT now()
|
|
);
|
|
|
|
CREATE TABLE accounts (
|
|
id uuid PRIMARY KEY,
|
|
organisation_id uuid NOT NULL REFERENCES organisations (id) ON DELETE CASCADE,
|
|
name text NOT NULL, -- sign-in name, unique per server
|
|
display_name text NOT NULL,
|
|
role text NOT NULL CHECK (role IN ('admin', 'member')),
|
|
password_hash text, -- argon2; NULL for SSO-only accounts
|
|
meta jsonb NOT NULL DEFAULT '{}',
|
|
created_at timestamptz NOT NULL DEFAULT now()
|
|
);
|
|
CREATE UNIQUE INDEX accounts_name ON accounts (lower(name));
|
|
|
|
-- Refresh tokens rotate on every use; a reused token revokes its family.
|
|
CREATE TABLE refresh_tokens (
|
|
token_hash bytea PRIMARY KEY,
|
|
family uuid NOT NULL,
|
|
account_id uuid NOT NULL REFERENCES accounts (id) ON DELETE CASCADE,
|
|
expires_at timestamptz NOT NULL,
|
|
used_at timestamptz,
|
|
created_at timestamptz NOT NULL DEFAULT now()
|
|
);
|
|
CREATE INDEX refresh_tokens_family ON refresh_tokens (family);
|
|
|
|
-- Server-wide values: the signing key, whether setup is done.
|
|
CREATE TABLE settings (
|
|
key text PRIMARY KEY,
|
|
value jsonb NOT NULL
|
|
);
|
|
|
|
-- Spaces: one shared model each.
|
|
|
|
CREATE TABLE spaces (
|
|
id uuid PRIMARY KEY,
|
|
organisation_id uuid NOT NULL REFERENCES organisations (id) ON DELETE CASCADE,
|
|
name text NOT NULL,
|
|
created_by uuid REFERENCES accounts (id) ON DELETE SET NULL,
|
|
meta jsonb NOT NULL DEFAULT '{}',
|
|
created_at timestamptz NOT NULL DEFAULT now()
|
|
);
|
|
|
|
CREATE TABLE members (
|
|
space_id uuid NOT NULL REFERENCES spaces (id) ON DELETE CASCADE,
|
|
account_id uuid NOT NULL REFERENCES accounts (id) ON DELETE CASCADE,
|
|
role text NOT NULL CHECK (role IN ('owner', 'editor')),
|
|
joined_at timestamptz NOT NULL DEFAULT now(),
|
|
PRIMARY KEY (space_id, account_id)
|
|
);
|
|
CREATE INDEX members_account ON members (account_id);
|
|
|
|
CREATE TABLE invites (
|
|
token_hash bytea PRIMARY KEY,
|
|
space_id uuid NOT NULL REFERENCES spaces (id) ON DELETE CASCADE,
|
|
created_by uuid REFERENCES accounts (id) ON DELETE SET NULL,
|
|
expires_at timestamptz NOT NULL,
|
|
max_uses integer,
|
|
uses integer NOT NULL DEFAULT 0,
|
|
created_at timestamptz NOT NULL DEFAULT now()
|
|
);
|
|
|
|
-- The CRDT: an append-only log of Yjs updates since the last version, and
|
|
-- versions as full encoded documents. Taking a version compacts the log.
|
|
|
|
CREATE TABLE updates (
|
|
id bigserial PRIMARY KEY,
|
|
space_id uuid NOT NULL REFERENCES spaces (id) ON DELETE CASCADE,
|
|
data bytea NOT NULL,
|
|
account_id uuid REFERENCES accounts (id) ON DELETE SET NULL,
|
|
created_at timestamptz NOT NULL DEFAULT now()
|
|
);
|
|
CREATE INDEX updates_space ON updates (space_id, id);
|
|
|
|
CREATE TABLE versions (
|
|
id bigserial PRIMARY KEY,
|
|
space_id uuid NOT NULL REFERENCES spaces (id) ON DELETE CASCADE,
|
|
major integer NOT NULL,
|
|
minor integer NOT NULL,
|
|
kind text NOT NULL CHECK (kind IN ('snapshot', 'publish', 'restore')),
|
|
name text,
|
|
state bytea NOT NULL,
|
|
up_to bigint NOT NULL, -- last update id the state contains
|
|
restored_from bigint REFERENCES versions (id) ON DELETE SET NULL,
|
|
created_by uuid REFERENCES accounts (id) ON DELETE SET NULL,
|
|
created_at timestamptz NOT NULL DEFAULT now(),
|
|
UNIQUE (space_id, major, minor)
|
|
);
|
|
CREATE INDEX versions_space ON versions (space_id, id DESC);
|