ConusAI vendor v0.1.0-dev.20261013+85463d22
This commit is contained in:
parent
c3c6526952
commit
0da8a2a81b
@@ -4,7 +4,7 @@ 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, recordUsage, serverErrors } from '@/shared/api'
|
||||
import { audit, blob, db, logger, recordUsage, serverErrors } from '@/shared/api'
|
||||
import { consumeUploadQuota } from '@/shared/lib/optional'
|
||||
import { ensureVisitor } from '@/shared/lib/visitor-session'
|
||||
|
||||
@@ -13,45 +13,52 @@ import { ensureVisitor } from '@/shared/lib/visitor-session'
|
||||
* upload, so a refused file never reaches Blob Storage.
|
||||
*/
|
||||
export async function uploadFile(note: string, form: FormData): Promise<void> {
|
||||
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,
|
||||
})
|
||||
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()
|
||||
// With KV linked, each visitor has a per-minute upload allowance; without it, none.
|
||||
const quota = await consumeUploadQuota(actor)
|
||||
if (quota && !quota.allowed) throw new Error('the upload limit for this minute is reached')
|
||||
const owner = await noteRepository(db()).get(key)
|
||||
if (!owner || owner.authorId !== actor) throw new Error('that note is not yours to attach to')
|
||||
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 } })
|
||||
// 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('/')
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user