import { error } from '@sveltejs/kit'; import type { PageServerLoad } from './$types.js'; import * as boardService from '$lib/server/services/boardService.js'; import * as appService from '$lib/server/services/appService.js'; import * as permissionService from '$lib/server/services/permissionService.js'; import * as userService from '$lib/server/services/userService.js'; import * as groupService from '$lib/server/services/groupService.js'; import { EntityType, PermissionLevel, UserRole } from '$lib/utils/constants.js'; import { isBoardGuestAccessible } from '$lib/server/middleware/guestAccess.js'; export const load: PageServerLoad = async ({ params, locals }) => { const { boardId } = params; const user = locals.user; // Permission check if (!user) { const isGuest = await isBoardGuestAccessible(boardId); if (!isGuest) { throw error(401, { message: 'Authentication required' }); } } else if (user.role !== UserRole.ADMIN) { const result = await permissionService.checkPermission( EntityType.BOARD, boardId, user.id, PermissionLevel.VIEW ); if (!result.hasPermission) { const isGuest = await isBoardGuestAccessible(boardId); if (!isGuest) { throw error(403, { message: 'Insufficient permissions' }); } } } try { // findBoardById includes sections -> widgets -> app -> statuses const [board, allApps] = await Promise.all([ boardService.findBoardById(boardId), appService.findAll() ]); // Determine if user can edit this board let canEdit = false; if (user) { if (user.role === UserRole.ADMIN) { canEdit = true; } else { const editResult = await permissionService.checkPermission( EntityType.BOARD, boardId, user.id, PermissionLevel.EDIT ); canEdit = editResult.hasPermission; } } // Load users and groups for the share dialog (only if user can edit) let users: { id: string; name: string }[] = []; let groups: { id: string; name: string }[] = []; if (canEdit) { const [allUsers, allGroups] = await Promise.all([ userService.findAll(), groupService.findAll() ]); users = allUsers.map((u) => ({ id: u.id, name: u.displayName || u.email })); groups = allGroups.map((g) => ({ id: g.id, name: g.name })); } // Batch-load sparkline history for all apps on this board (single query) const appIdsOnBoard = board.sections .flatMap((s: { widgets: { appId: string | null }[] }) => s.widgets) .map((w: { appId: string | null }) => w.appId) .filter((id: string | null): id is string => id !== null); const historyMap = appIdsOnBoard.length > 0 ? await appService.getBatchStatusHistory(appIdsOnBoard) : new Map(); // Serialize the Map to a plain object for the client const appHistories: Record = {}; for (const [appId, data] of historyMap) { appHistories[appId] = { history: data.history.map((h) => ({ ...h, checkedAt: h.checkedAt.toISOString() })), uptimePercent: data.uptimePercent }; } return { board, canEdit, allApps, users, groups, appHistories }; } catch (err) { const message = err instanceof Error ? err.message : 'Board not found'; if (message.includes('not found')) { throw error(404, { message: 'Board not found' }); } throw error(500, { message }); } };