import { getDelegate, parseId } from './prisma-delegate'; import { extractFormData } from './form-data'; import type { AdminModelConfig } from './types'; import { superValidate } from 'sveltekit-superforms/server'; import { zod4 } from 'sveltekit-superforms/adapters'; import * as Schemas from './schemas'; import type { z } from 'zod'; const modelSchemas: Record> = { User: Schemas.User, Profile: Schemas.Profile, UserRole: Schemas.UserRole, Proclamation: Schemas.Proclamation, ProclamationCategory: Schemas.ProclamationCategory, SolfeggioFrequency: Schemas.SolfeggioFrequency, SubscriptionLevel: Schemas.SubscriptionLevel, LicenseKey: Schemas.LicenseKey }; export function superFormErrorsToFieldErrors(superErrors: Record): { error: string; fieldErrors: Record; } { const fieldErrors: Record = {}; let formError = ''; for (const [key, value] of Object.entries(superErrors)) { if (key === '_errors' && Array.isArray(value)) { formError = (value[0] as string) ?? ''; } else if ( value && typeof value === 'object' && '_errors' in value && Array.isArray((value as Record)._errors) ) { const errMsg = ((value as Record)._errors as string[])[0]; fieldErrors[key] = errMsg; } } return { error: formError, fieldErrors }; } export async function loadRecords(locals: App.Locals, config: AdminModelConfig) { const delegate = getDelegate(config); const viewerRoleId = (locals.user as { roleId?: number } | null)?.roleId ?? 4; const where = config.accessFilter?.(viewerRoleId); const records = await delegate.findMany({ where, include: config.include, orderBy: { id: 'asc' } }); const relationOptionsData: Record = {}; for (const [field, relConfig] of Object.entries(config.relationOptions ?? {})) { const relDelegate = getDelegate({ ...config, modelName: relConfig.model }); const selectFields: Record = { [relConfig.valueField]: true, [relConfig.labelField]: true }; const options = await relDelegate.findMany({ select: selectFields, orderBy: { [relConfig.labelField]: 'asc' } }); const mapped = (options as Record[]).map((opt: Record) => ({ label: String(opt[relConfig.labelField]), value: opt[relConfig.valueField] as string | number })); relationOptionsData[field] = mapped; } return { records: records as Record[], relationOptionsData }; } export async function upsertRecord( requestOrFormData: Request | FormData, config: AdminModelConfig, locals: App.Locals ) { const formData = requestOrFormData instanceof FormData ? requestOrFormData : await requestOrFormData.formData(); const schema = modelSchemas[config.modelName]; const form = schema ? await superValidate(formData, zod4(schema)) : null; if (form && !form.valid) { const { error, fieldErrors } = superFormErrorsToFieldErrors( form.errors as Record ); return { ...form, success: false, error, fieldErrors }; } const rawId = formData.get('id')?.toString(); const data = form?.data ?? (extractFormData(formData, config.formFields, config.fieldTypes) as Record); // Strip virtual/UI-only fields from Prisma payload for (const f of config.dataOnlyFields ?? []) { delete data[f]; } const delegate = getDelegate(config); const viewerRoleId = (locals.user as { roleId?: number } | null)?.roleId ?? 4; const where = config.accessFilter?.(viewerRoleId); if (rawId) { const id = parseId(config, rawId); const existing = await delegate.findUnique({ where: { id }, ...((config.include ? { include: config.include } : {}) as object) }); if (!existing) { return { success: false, error: 'Record not found' }; } if (where) { const match = await delegate.findFirst({ where: { id, ...where } }); if (!match) { return { success: false, error: 'Access denied' }; } } await delegate.update({ where: { id }, data }); return { success: true }; } else { try { await delegate.create({ data }); } catch (e: unknown) { if (e && typeof e === 'object' && 'code' in e && (e as { code: string }).code === 'P2002') { const meta = (e as { meta?: { target?: string[]; modelName?: string } }).meta; const fields = meta?.target ?? []; console.error( `[crud] P2002 on ${meta?.modelName ?? 'unknown'} create. data=`, data, `meta.target=`, meta?.target ); if (fields.length === 0) { return { success: false, error: 'A database constraint violation occurred. The record could not be created.' }; } return { success: false, error: `A record with this ${fields.join(', ')} already exists.`, fieldErrors: Object.fromEntries(fields.map((f) => [f, 'Already taken'])) }; } throw e; } return { success: true }; } } export async function deleteRecord( requestOrFormData: Request | FormData, config: AdminModelConfig, locals: App.Locals ) { const delegate = getDelegate(config); const formData = requestOrFormData instanceof FormData ? requestOrFormData : await requestOrFormData.formData(); const rawId = formData.get('id')?.toString(); if (!rawId) return { success: false, error: 'Missing id' }; const id = parseId(config, rawId); const viewerRoleId = (locals.user as { roleId?: number } | null)?.roleId ?? 4; const where = config.accessFilter?.(viewerRoleId); if (where) { const match = await delegate.findFirst({ where: { id, ...where } }); if (!match) return { success: false, error: 'Access denied' }; } await delegate.delete({ where: { id } }); return { success: true }; }