'use server' import { revalidatePath } from 'next/cache' import { fileRepository, uploadLimits } from '@/entities/file' import { noteId, noteRepository } from '@/entities/note' import { getEnv } from '@/shared/config' import { audit, blob, db, logger, recordUsage, serverErrors } from '@/shared/api' import { consumeUploadQuota } from '@/shared/lib/optional' import { ensureVisitor } from '@/shared/lib/visitor-session' /** * Attach a file to a note the visitor wrote. The limits are checked before any * upload, so a refused file never reaches Blob Storage. */ export async function uploadFile(note: string, form: FormData): Promise { try { const key = noteId.parse(note) const upload = form.get('file') if (!(upload instanceof File)) throw new Error('choose a file to upload') const limits = uploadLimits.parse({ name: upload.name, size: upload.size, contentType: upload.type, }) const actor = await ensureVisitor() const owner = await noteRepository(db()).get(key) if (!owner || owner.authorId !== actor) throw new Error('that note is not yours to attach to') // With KV linked, each visitor has a per-minute upload allowance; without it, none. // Checked after ownership: an attempt on someone else's note spends no quota. const quota = await consumeUploadQuota(actor) if (quota && !quota.allowed) throw new Error('the upload limit for this minute is reached') const stored = await blob().put(`notes/${key}/${limits.name}`, Buffer.from(await upload.arrayBuffer()), { contentType: limits.contentType, }) const row = await fileRepository(db()).create({ noteId: key, authorId: actor, name: limits.name, contentType: limits.contentType, size: limits.size, url: stored.url, }) await audit().log('file.uploaded', { actor: { kind: 'human', id: actor }, target: { id: row.id } }) logger()?.info('file.uploaded', { 'file.id': row.id, 'note.id': key, outcome: 'ok' }) // One upload, one usage event. The key is the attachment's id, so a resend stores nothing twice. // A failed send is reported, not raised: the upload itself succeeded. const env = getEnv() await recordUsage({ baseUrl: env.CONUSAI_API_URL, token: env.CONUSAI_API_TOKEN }, [ { idempotency_key: `upload:${row.id}`, customer: { external_id: 'showcase' }, meter: 'uploads', quantity: '1', properties: { bytes: limits.size, content_type: limits.contentType }, }, ]).catch((error: unknown) => serverErrors().captureException(error)) revalidatePath('/') } catch (error) { logger()?.recordException(error, { 'conusai.action': 'file.uploaded', 'note.id': note }) throw error } }