server/migrations/0005_meldingsboks.sql
vegard 1faef972dd Meldingsboks-migrasjon: universell diskusjonsprimitiv + entities
Migrering 0005 samler kanban-kort, kalenderhendelser, faktoider og
notater til én felles messages-tabell med view-config-tabeller.
Actors og topics erstattes av unified entities-tabell.

- 0005_meldingsboks.sql: messages utvides med title/pinned/visibility,
  kanban_card_view + calendar_event_view + message_reactions opprettes,
  entities erstatter actors+topics, gamle tabeller droppes
- seed_dev.sql: oppdatert til meldingsboks-modell + 5 test-entiteter
  med graf-relasjoner
- API-ruter: kanban/kalender/notater bruker messages + view-config
- Dokumentasjon: meldingsboks feature-spec, oppdatert arkitektur,
  kunnskapsgraf, jobbkø, konseptdokumenter og proposals

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 15:32:15 +01:00

258 lines
8.5 KiB
PL/PgSQL

-- Meldingsboks: Universell diskusjonsprimitiv
-- Erstatter separate tabeller for kanban-kort, kalenderhendelser, faktoider og notater.
-- Alle disse blir meldingsbokser (messages) med view-config-tabeller.
-- Se docs/features/meldingsboks.md for full spec.
--
-- Avhenger av: 0001, 0002, 0003, 0004
BEGIN;
-- ============================================================
-- 1. ALTER messages-tabellen
-- ============================================================
-- channel_id nullable (notater, standalone-bokser trenger ikke channel)
ALTER TABLE messages ALTER COLUMN channel_id DROP NOT NULL;
-- Nye kolonner
ALTER TABLE messages ADD COLUMN title TEXT;
ALTER TABLE messages ADD COLUMN pinned BOOLEAN NOT NULL DEFAULT false;
ALTER TABLE messages ADD COLUMN visibility TEXT NOT NULL DEFAULT 'workspace';
ALTER TABLE messages ADD COLUMN updated_at TIMESTAMPTZ NOT NULL DEFAULT now();
-- Indeks for reply_to (tråd-oppslag)
CREATE INDEX IF NOT EXISTS idx_messages_reply ON messages(reply_to) WHERE reply_to IS NOT NULL;
-- updated_at trigger
CREATE TRIGGER trg_messages_updated_at BEFORE UPDATE ON messages
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
-- ============================================================
-- 2. View-config-tabeller
-- ============================================================
-- Kanban: view-metadata for meldingsbokser som vises som kort
CREATE TABLE kanban_card_view (
message_id UUID PRIMARY KEY REFERENCES messages(id) ON DELETE CASCADE,
column_id UUID NOT NULL REFERENCES kanban_columns(id) ON DELETE CASCADE,
position REAL NOT NULL DEFAULT 0,
color TEXT,
assignee_id TEXT REFERENCES users(authentik_id) ON DELETE SET NULL
);
CREATE INDEX idx_kanban_card_view_column ON kanban_card_view(column_id, position);
-- Kalender: view-metadata for meldingsbokser som vises som hendelser
CREATE TABLE calendar_event_view (
message_id UUID PRIMARY KEY REFERENCES messages(id) ON DELETE CASCADE,
calendar_id UUID NOT NULL REFERENCES calendars(id) ON DELETE CASCADE,
starts_at TIMESTAMPTZ NOT NULL,
ends_at TIMESTAMPTZ,
all_day BOOLEAN NOT NULL DEFAULT false,
color TEXT
);
CREATE INDEX idx_calendar_event_view_calendar ON calendar_event_view(calendar_id, starts_at);
-- ============================================================
-- 3. Migrer kanban_cards → messages + kanban_card_view
-- ============================================================
-- Oppdater node_type til 'melding'
UPDATE nodes SET node_type = 'melding'
WHERE id IN (SELECT id FROM kanban_cards);
-- Opprett meldingsbokser fra kanban-kort
INSERT INTO messages (id, channel_id, author_id, message_type, title, body, created_at, updated_at)
SELECT
kc.id,
NULL,
kc.created_by,
'text',
kc.title,
COALESCE(kc.description, ''),
kc.created_at,
kc.updated_at
FROM kanban_cards kc;
-- Opprett kanban view-config
INSERT INTO kanban_card_view (message_id, column_id, position, assignee_id)
SELECT
kc.id,
kc.column_id,
kc.position,
kc.assignee_id
FROM kanban_cards kc;
-- ============================================================
-- 4. Migrer calendar_events → messages + calendar_event_view
-- ============================================================
UPDATE nodes SET node_type = 'melding'
WHERE id IN (SELECT id FROM calendar_events);
INSERT INTO messages (id, channel_id, author_id, message_type, title, body, created_at, updated_at)
SELECT
ce.id,
NULL,
ce.created_by,
'text',
ce.title,
COALESCE(ce.description, ''),
ce.created_at,
ce.created_at
FROM calendar_events ce;
INSERT INTO calendar_event_view (message_id, calendar_id, starts_at, ends_at, all_day, color)
SELECT
ce.id,
ce.calendar_id,
ce.starts_at,
ce.ends_at,
ce.all_day,
ce.color
FROM calendar_events ce;
-- ============================================================
-- 5. Reaksjoner (erstatter message_votes og factoid_votes)
-- ============================================================
CREATE TABLE message_reactions (
message_id UUID NOT NULL REFERENCES messages(id) ON DELETE CASCADE,
user_id TEXT NOT NULL REFERENCES users(authentik_id) ON DELETE CASCADE,
reaction TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
PRIMARY KEY (message_id, user_id, reaction)
);
-- Migrer eksisterende message_votes til reactions
INSERT INTO message_reactions (message_id, user_id, reaction, created_at)
SELECT
mv.message_id,
mv.user_id,
CASE WHEN mv.vote = 1 THEN 'upvote' ELSE 'downvote' END,
mv.created_at
FROM message_votes mv;
-- ============================================================
-- 6. Migrer factoids → messages
-- ============================================================
UPDATE nodes SET node_type = 'melding'
WHERE id IN (SELECT id FROM factoids);
INSERT INTO messages (id, channel_id, author_id, message_type, body, created_at, updated_at)
SELECT
f.id,
NULL,
f.created_by,
'factoid',
f.body,
f.created_at,
f.created_at
FROM factoids f;
-- Flytt factoid_votes til reactions
INSERT INTO message_reactions (message_id, user_id, reaction, created_at)
SELECT
fv.factoid_id,
fv.user_id,
CASE WHEN fv.vote = 1 THEN 'upvote' ELSE 'downvote' END,
fv.created_at
FROM factoid_votes fv
ON CONFLICT (message_id, user_id, reaction) DO NOTHING;
-- ============================================================
-- 7. Migrer notes → messages
-- ============================================================
UPDATE nodes SET node_type = 'melding'
WHERE id IN (SELECT id FROM notes);
INSERT INTO messages (id, channel_id, author_id, message_type, title, body, created_at, updated_at)
SELECT
n.id,
NULL,
n.created_by,
'text',
n.title,
COALESCE(n.content, ''),
n.created_at,
n.updated_at
FROM notes n;
-- ============================================================
-- 8. Drop gamle tabeller
-- ============================================================
-- Triggers først
DROP TRIGGER IF EXISTS trg_kanban_cards_updated_at ON kanban_cards;
DROP TRIGGER IF EXISTS trg_calendar_events_updated_at ON calendar_events;
DROP TRIGGER IF EXISTS trg_notes_updated_at ON notes;
-- Tabeller (rekkefølge: FK-avhengigheter)
DROP TABLE factoid_votes;
DROP TABLE factoids;
DROP TABLE message_votes;
DROP TABLE kanban_cards;
DROP TABLE calendar_events;
DROP TABLE notes;
-- ============================================================
-- RLS: Ikke nødvendig på messages/view-config-tabeller.
-- Workspace-isolasjon arves via FK til nodes (samme mønster
-- som episodes, segments, entities).
-- ============================================================
COMMIT;
-- ============================================================
-- 9. Entities (erstatter actors + topics)
-- ============================================================
-- Alt som kan nevnes med # er en entitet: personer, organisasjoner,
-- steder, temaer, konsepter. Én tabell, én autocomplete.
--
-- ALTER TYPE ADD VALUE kan ikke kjøres i en transaksjon (PG < 16),
-- derfor utenfor BEGIN/COMMIT-blokken.
ALTER TYPE node_type ADD VALUE IF NOT EXISTS 'entitet';
BEGIN;
CREATE TABLE entities (
id UUID PRIMARY KEY REFERENCES nodes(id) ON DELETE CASCADE,
name TEXT NOT NULL,
type TEXT NOT NULL, -- 'person', 'organisasjon', 'sted', 'tema', 'konsept'
aliases TEXT[] DEFAULT '{}', -- Forkortelser, kallenavn: {'JGS', 'Støre'}
avatar_url TEXT
);
CREATE INDEX idx_entities_name ON entities(name);
CREATE INDEX idx_entities_aliases ON entities USING GIN(aliases);
-- Migrer actors → entities
INSERT INTO entities (id, name, type)
SELECT id, name, COALESCE(type, 'person')
FROM actors;
UPDATE nodes SET node_type = 'entitet'
WHERE id IN (SELECT id FROM actors);
-- Migrer topics → entities
INSERT INTO entities (id, name, type)
SELECT id, name, 'tema'
FROM topics;
UPDATE nodes SET node_type = 'entitet'
WHERE id IN (SELECT id FROM topics);
DROP TABLE actors;
DROP TABLE topics;
-- ============================================================
-- RLS: Ikke nødvendig på messages/view-config/entities.
-- Workspace-isolasjon arves via FK til nodes (samme mønster
-- som episodes, segments).
-- ============================================================
COMMIT;