From a30a4840761db6ab88bfeefbf068ade4e6666ca6 Mon Sep 17 00:00:00 2001 From: vegard Date: Tue, 17 Mar 2026 11:53:31 +0100 Subject: [PATCH] Opprett PostgreSQL-skjema for Synops (oppgave 1.1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Oppretter database `synops` på serveren med kjerneskjemaet: - Enums: visibility (hidden/discoverable/readable/open), access_level (reader/member/admin/owner) - Tabeller: nodes, edges, node_access, auth_identities - Funksjon: recompute_access for tilgangsmatrise-oppdatering - Indekser iht. docs/primitiver/nodes.md og edges.md Migrasjonen er kjørt og verifisert på produksjonsserver (sidelinja-postgres-1, database: synops). Co-Authored-By: Claude Opus 4.6 --- migrations/001_initial_schema.sql | 118 ++++++++++++++++++++++++++++++ tasks.md | 3 +- 2 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 migrations/001_initial_schema.sql diff --git a/migrations/001_initial_schema.sql b/migrations/001_initial_schema.sql new file mode 100644 index 0000000..6ff4ee1 --- /dev/null +++ b/migrations/001_initial_schema.sql @@ -0,0 +1,118 @@ +-- 001_initial_schema.sql +-- Oppretter kjerneskjema for Synops: enums, nodes, edges, node_access, auth_identities. +-- Ref: docs/primitiver/nodes.md, docs/primitiver/edges.md, docs/retninger/bruker_ikke_workspace.md + +BEGIN; + +-- ============================================================================= +-- Enums +-- ============================================================================= + +CREATE TYPE visibility AS ENUM ('hidden', 'discoverable', 'readable', 'open'); +CREATE TYPE access_level AS ENUM ('reader', 'member', 'admin', 'owner'); + +-- ============================================================================= +-- Nodes +-- ============================================================================= + +CREATE TABLE nodes ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + node_kind TEXT NOT NULL DEFAULT 'content', + title TEXT, + content TEXT, + visibility visibility NOT NULL DEFAULT 'hidden', + metadata JSONB NOT NULL DEFAULT '{}', + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + created_by UUID REFERENCES nodes(id) +); + +CREATE INDEX idx_nodes_kind ON nodes (node_kind); +CREATE INDEX idx_nodes_created_by ON nodes (created_by); +CREATE INDEX idx_nodes_visibility ON nodes (visibility); + +-- ============================================================================= +-- Edges +-- ============================================================================= + +CREATE TABLE edges ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + source_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE, + target_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE, + edge_type TEXT NOT NULL, + metadata JSONB NOT NULL DEFAULT '{}', + system BOOLEAN NOT NULL DEFAULT false, + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + created_by UUID REFERENCES nodes(id), + + UNIQUE (source_id, target_id, edge_type) +); + +CREATE INDEX idx_edges_source ON edges (source_id); +CREATE INDEX idx_edges_target ON edges (target_id); +CREATE INDEX idx_edges_type ON edges (edge_type); + +-- ============================================================================= +-- Tilgangsmatrise (node_access) +-- ============================================================================= + +CREATE TABLE node_access ( + subject_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE, + object_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE, + access access_level NOT NULL, + via_edge UUID REFERENCES edges(id) ON DELETE CASCADE, + PRIMARY KEY (subject_id, object_id) +); + +CREATE INDEX idx_na_subject ON node_access (subject_id); + +-- ============================================================================= +-- Auth-identiteter (bro mellom HTTP-sesjon og graf) +-- ============================================================================= + +CREATE TABLE auth_identities ( + node_id UUID PRIMARY KEY REFERENCES nodes(id) ON DELETE CASCADE, + authentik_sub TEXT UNIQUE NOT NULL, + email TEXT UNIQUE NOT NULL +); + +-- ============================================================================= +-- Tilgangsberegning (recompute_access) +-- Oppdaterer node_access-matrisen når edges endres. +-- Ref: docs/retninger/bruker_ikke_workspace.md +-- ============================================================================= + +CREATE OR REPLACE FUNCTION recompute_access( + p_subject_id UUID, + p_root_node_id UUID, + p_access access_level, + p_via_edge UUID +) RETURNS void AS $$ +BEGIN + -- Direkte tilgang til roten + INSERT INTO node_access (subject_id, object_id, access, via_edge) + VALUES (p_subject_id, p_root_node_id, p_access, p_via_edge) + ON CONFLICT (subject_id, object_id) + DO UPDATE SET access = GREATEST(node_access.access, p_access); + + -- Transitiv: noder som tilhører roten + INSERT INTO node_access (subject_id, object_id, access, via_edge) + SELECT p_subject_id, e.source_id, p_access, p_via_edge + FROM edges e + WHERE e.target_id = p_root_node_id + AND e.edge_type = 'belongs_to' + ON CONFLICT (subject_id, object_id) + DO UPDATE SET access = GREATEST(node_access.access, p_access); + + -- Hvis subject er et team: propager til alle teammedlemmer + INSERT INTO node_access (subject_id, object_id, access, via_edge) + SELECT e.source_id, na.object_id, na.access, na.via_edge + FROM node_access na + JOIN edges e ON e.target_id = p_subject_id + AND e.edge_type = 'member_of' + WHERE na.subject_id = p_subject_id + ON CONFLICT (subject_id, object_id) + DO UPDATE SET access = GREATEST(node_access.access, EXCLUDED.access); +END; +$$ LANGUAGE plpgsql; + +COMMIT; diff --git a/tasks.md b/tasks.md index 47e7aff..03820cc 100644 --- a/tasks.md +++ b/tasks.md @@ -43,8 +43,7 @@ Uavhengige faser kan fortsatt plukkes. ## Fase 1: Infrastruktur-fundament -- [~] 1.1 PostgreSQL schema: opprett database `synops`, enums (`visibility`, `access_level`), tabeller (`nodes`, `edges`, `node_access`, `auth_identities`) med indekser. Kjør på server via SSH. Ref: `docs/primitiver/nodes.md`, `docs/primitiver/edges.md`, `docs/retninger/bruker_ikke_workspace.md`. - > Påbegynt: 2026-03-17T11:51 +- [x] 1.1 PostgreSQL schema: opprett database `synops`, enums (`visibility`, `access_level`), tabeller (`nodes`, `edges`, `node_access`, `auth_identities`) med indekser. Kjør på server via SSH. Ref: `docs/primitiver/nodes.md`, `docs/primitiver/edges.md`, `docs/retninger/bruker_ikke_workspace.md`. - [ ] 1.2 Seed-data: opprett Vegards brukernode (`node_kind='person'`, `title='Vegard'`) og `auth_identities`-rad. Opprett Sidelinja samlings-node og `owner`-edge fra Vegard. - [ ] 1.3 SpacetimeDB modul: opprett Rust-modul med `nodes` og `edges`-tabeller som speiler PG-skjema. Grunnleggende reducers for CRUD. Deploy til server. Ref: `docs/retninger/datalaget.md`, `docs/erfaringer/spacetimedb_integrasjon.md`. - [ ] 1.4 Caddy-config: reverse proxy for maskinrommet (api.sidelinja.org), SpacetimeDB, og SvelteKit. Auto-TLS. Ref: `docs/setup/produksjon.md`.