import { json } from '@sveltejs/kit'; import type { RequestHandler } from './$types'; /** * GET: Retrieve the Matrix Space ID for an org */ export const GET: RequestHandler = async ({ url, locals }) => { const session = await locals.safeGetSession(); if (!session.user) { return json({ error: 'Unauthorized' }, { status: 401 }); } const orgId = url.searchParams.get('org_id'); if (!orgId) { return json({ error: 'org_id is required' }, { status: 400 }); } const { data, error } = await locals.supabase .from('organizations') .select('matrix_space_id') .eq('id', orgId) .single(); if (error) { return json({ error: error.message }, { status: 500 }); } return json({ spaceId: data?.matrix_space_id ?? null }); }; /** * POST: Create a Matrix Space for an org, or link an existing one. * * Body options: * - { org_id, action: "create", homeserver_url, access_token, org_name } * Creates a new Space on the homeserver and stores the ID. * - { org_id, action: "link", space_id } * Links an existing Matrix Space ID to the org. */ export const POST: RequestHandler = async ({ request, locals }) => { const session = await locals.safeGetSession(); if (!session.user) { return json({ error: 'Unauthorized' }, { status: 401 }); } const body = await request.json(); const { org_id, action } = body; if (!org_id || !action) { return json({ error: 'org_id and action are required' }, { status: 400 }); } if (action === 'create') { const { homeserver_url, access_token, org_name } = body; if (!homeserver_url || !access_token || !org_name) { return json({ error: 'homeserver_url, access_token, and org_name are required for create' }, { status: 400 }); } try { // Create a Matrix Space via the Client-Server API const createRes = await fetch(`${homeserver_url}/_matrix/client/v3/createRoom`, { method: 'POST', headers: { 'Authorization': `Bearer ${access_token}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ name: org_name, topic: `Organization space for ${org_name}`, visibility: 'private', creation_content: { type: 'm.space', }, initial_state: [ { type: 'm.room.guest_access', state_key: '', content: { guest_access: 'can_join' }, }, ], power_level_content_override: { invite: 50, kick: 50, ban: 50, events_default: 0, state_default: 50, }, }), }); if (!createRes.ok) { const err = await createRes.json().catch(() => ({})); return json({ error: err.error || 'Failed to create Matrix Space' }, { status: 500 }); } const { room_id: spaceId } = await createRes.json(); // Also create default #general room inside the space const generalRes = await fetch(`${homeserver_url}/_matrix/client/v3/createRoom`, { method: 'POST', headers: { 'Authorization': `Bearer ${access_token}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ name: 'General', topic: 'General discussion', visibility: 'private', preset: 'private_chat', }), }); if (generalRes.ok) { const { room_id: generalRoomId } = await generalRes.json(); // Add #general as a child of the space await fetch( `${homeserver_url}/_matrix/client/v3/rooms/${encodeURIComponent(spaceId)}/state/m.space.child/${encodeURIComponent(generalRoomId)}`, { method: 'PUT', headers: { 'Authorization': `Bearer ${access_token}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ via: [new URL(homeserver_url).hostname], }), } ); } // Store space ID in org record const { error: updateError } = await locals.supabase .from('organizations') .update({ matrix_space_id: spaceId }) .eq('id', org_id); if (updateError) { return json({ error: updateError.message }, { status: 500 }); } return json({ spaceId, created: true }); } catch (e: any) { console.error('Failed to create Matrix Space:', e); return json({ error: e.message || 'Failed to create Matrix Space' }, { status: 500 }); } } if (action === 'link') { const { space_id } = body; if (!space_id) { return json({ error: 'space_id is required for link action' }, { status: 400 }); } const { error: updateError } = await locals.supabase .from('organizations') .update({ matrix_space_id: space_id }) .eq('id', org_id); if (updateError) { return json({ error: updateError.message }, { status: 500 }); } return json({ spaceId: space_id, linked: true }); } return json({ error: 'Invalid action. Use "create" or "link".' }, { status: 400 }); };