import { json, error } from '@sveltejs/kit'; import type { RequestHandler } from './$types.js'; import { boardSchema } from '$lib/server/validation.js'; import { withTenant } from '$lib/server/rls.js'; import { requireAuth } from '$lib/server/guards.js'; import { prisma } from '$lib/server/db.js'; import { generatePositions } from '$lib/utils/fractional-index.js'; // List boards for current tenant export const GET: RequestHandler = async ({ locals }) => { const tenant = locals.tenant; if (!tenant) throw error(400, 'Unknown tenant'); const boards = await withTenant(tenant.id, async (tx) => { const where: Record = { tenantId: tenant.id, archived: false }; // If not logged in, only show public boards if (!locals.user) { where.visibility = 'PUBLIC'; } else { // Show boards user is a member of + public boards where.OR = [ { visibility: 'PUBLIC' }, { members: { some: { userId: locals.user.id } } } ]; } return tx.board.findMany({ where, include: { members: { include: { user: { select: { id: true, name: true, avatarUrl: true } } } }, _count: { select: { columns: true } } }, orderBy: { updatedAt: 'desc' } }); }); return json({ boards }); }; // Create a new board export const POST: RequestHandler = async ({ request, locals }) => { const tenant = locals.tenant; if (!tenant) throw error(400, 'Unknown tenant'); requireAuth(locals.user); const body = await request.json(); const result = boardSchema.safeParse(body); if (!result.success) { return json({ error: result.error.issues[0].message }, { status: 400 }); } const { title, description, visibility, categoryId, templateId } = result.data; const cardPrefix = title.replace(/[^a-zA-Z]/g, '').toUpperCase().slice(0, 4) || 'CARD'; // Load template columns if templateId provided let templateColumns: { title: string }[] = []; if (templateId) { // System templates have null tenantId, so query without RLS const template = await prisma.boardTemplate.findFirst({ where: { id: templateId, OR: [{ tenantId: null }, { tenantId: tenant.id }] } }); if (template) { const tpl = template.template as { columns?: { title: string }[] }; templateColumns = tpl.columns || []; } } const board = await withTenant(tenant.id, async (tx) => { const newBoard = await tx.board.create({ data: { tenantId: tenant.id, title, description, visibility, cardPrefix, ...(categoryId ? { categoryId } : {}), members: { create: { userId: locals.user!.id, role: 'OWNER' } } }, include: { members: { include: { user: { select: { id: true, name: true, avatarUrl: true } } } } } }); // Create columns from template if (templateColumns.length > 0) { const positions = generatePositions(templateColumns.length); for (let i = 0; i < templateColumns.length; i++) { await tx.column.create({ data: { boardId: newBoard.id, title: templateColumns[i].title, position: positions[i] } }); } } return newBoard; }); return json({ board }, { status: 201 }); };