import { json, error } from '@sveltejs/kit'; import type { RequestHandler } from './$types'; import { sql } from '$lib/server/db'; export const GET: RequestHandler = async ({ params, url, locals }) => { if (!locals.workspace || !locals.user) error(401); const channelId = params.id; const after = url.searchParams.get('after'); const limit = Math.min(Number(url.searchParams.get('limit') ?? 50), 100); // Verifiser at kanalen tilhører workspace const [channel] = await sql` SELECT c.id FROM channels c JOIN nodes n ON n.id = c.id WHERE c.id = ${channelId} AND n.workspace_id = ${locals.workspace.id} `; if (!channel) error(404, 'Kanal ikke funnet'); const currentUserId = locals.user.id; const messages = after ? await sql` SELECT m.id, m.channel_id, m.body, m.message_type, m.title, m.pinned, m.visibility, m.created_at, m.updated_at, m.reply_to, u.display_name as author_name, u.authentik_id as author_id, COALESCE(r.reactions, '[]'::jsonb) as reactions FROM messages m LEFT JOIN users u ON u.authentik_id = m.author_id LEFT JOIN LATERAL ( SELECT jsonb_agg(jsonb_build_object( 'reaction', sub.reaction, 'count', sub.cnt, 'user_reacted', sub.user_reacted )) as reactions FROM ( SELECT mr.reaction, count(*)::int as cnt, bool_or(mr.user_id = ${currentUserId}) as user_reacted FROM message_reactions mr WHERE mr.message_id = m.id GROUP BY mr.reaction ) sub ) r ON true WHERE m.channel_id = ${channelId} AND m.created_at > ${after} ORDER BY m.created_at ASC LIMIT ${limit} ` : await sql` SELECT m.id, m.channel_id, m.body, m.message_type, m.title, m.pinned, m.visibility, m.created_at, m.updated_at, m.reply_to, u.display_name as author_name, u.authentik_id as author_id, COALESCE(r.reactions, '[]'::jsonb) as reactions FROM messages m LEFT JOIN users u ON u.authentik_id = m.author_id LEFT JOIN LATERAL ( SELECT jsonb_agg(jsonb_build_object( 'reaction', sub.reaction, 'count', sub.cnt, 'user_reacted', sub.user_reacted )) as reactions FROM ( SELECT mr.reaction, count(*)::int as cnt, bool_or(mr.user_id = ${currentUserId}) as user_reacted FROM message_reactions mr WHERE mr.message_id = m.id GROUP BY mr.reaction ) sub ) r ON true WHERE m.channel_id = ${channelId} ORDER BY m.created_at DESC LIMIT ${limit} `.then((rows) => rows.reverse()); return json(messages); }; export const POST: RequestHandler = async ({ params, request, locals }) => { if (!locals.workspace || !locals.user) error(401); const workspace = locals.workspace; const user = locals.user; const channelId = params.id; const { body, replyTo, mentions } = await request.json(); if (!body || typeof body !== 'string' || body.trim().length === 0) { error(400, 'Melding kan ikke være tom'); } // Verifiser at kanalen tilhører workspace const [channel] = await sql` SELECT c.id FROM channels c JOIN nodes n ON n.id = c.id WHERE c.id = ${channelId} AND n.workspace_id = ${workspace.id} `; if (!channel) error(404, 'Kanal ikke funnet'); // Opprett node + melding i PG const [message] = await sql` WITH new_node AS ( INSERT INTO nodes (workspace_id, node_type) VALUES (${workspace.id}, 'melding') RETURNING id ) INSERT INTO messages (id, channel_id, author_id, body, reply_to) SELECT new_node.id, ${channelId}, ${user.id}, ${body.trim()}, ${replyTo ?? null} FROM new_node RETURNING id, body, message_type, created_at, reply_to `; // Opprett MENTIONS-edges for hver #-mention if (Array.isArray(mentions) && mentions.length > 0) { const entityIds = mentions .map((m: { id?: string }) => m.id) .filter((id): id is string => typeof id === 'string' && id !== message.id); if (entityIds.length > 0) { // Verifiser at alle nevnte entiteter tilhører workspace const validEntities = await sql` SELECT id FROM nodes WHERE id = ANY(${entityIds}) AND workspace_id = ${workspace.id} `; const validIds = new Set(validEntities.map((e) => (e as { id: string }).id)); for (const entityId of entityIds) { if (!validIds.has(entityId)) continue; await sql` INSERT INTO graph_edges (workspace_id, source_id, target_id, relation_type, created_by, origin) VALUES (${workspace.id}, ${message.id}, ${entityId}, 'MENTIONS', ${user.id}, 'user') ON CONFLICT (source_id, target_id, relation_type) DO NOTHING `; } } } return json({ ...message, author_name: user.name, author_id: user.id }, { status: 201 }); };