import { Injectable, BadRequestException, NotFoundException } from '@nestjs/common'; import { TenantService } from '../../database/tenant.service'; const ALLOWED_MIMES = [ 'application/pdf', 'image/jpeg', 'image/png', 'image/webp', 'image/gif', 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', ]; const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB @Injectable() export class AttachmentsService { constructor(private tenant: TenantService) {} async upload(journalEntryId: string, file: Express.Multer.File, userId: string) { if (!file) { throw new BadRequestException('No file provided'); } if (file.size > MAX_FILE_SIZE) { throw new BadRequestException('File size exceeds 10MB limit'); } if (!ALLOWED_MIMES.includes(file.mimetype)) { throw new BadRequestException( `File type "${file.mimetype}" not allowed. Allowed: PDF, JPEG, PNG, WebP, GIF, XLS, XLSX`, ); } // Verify journal entry exists const jeRows = await this.tenant.query( `SELECT id FROM journal_entries WHERE id = $1`, [journalEntryId], ); if (!jeRows.length) { throw new NotFoundException('Journal entry not found'); } const rows = await this.tenant.query( `INSERT INTO attachments (journal_entry_id, filename, mime_type, file_size, file_data, uploaded_by) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id, filename, mime_type, file_size, created_at`, [journalEntryId, file.originalname, file.mimetype, file.size, file.buffer, userId], ); return rows[0]; } async findByJournalEntry(journalEntryId: string) { return this.tenant.query( `SELECT id, journal_entry_id, filename, mime_type, file_size, created_at FROM attachments WHERE journal_entry_id = $1 ORDER BY created_at`, [journalEntryId], ); } async download(id: string) { const rows = await this.tenant.query( `SELECT filename, mime_type, file_data FROM attachments WHERE id = $1`, [id], ); if (!rows.length) throw new NotFoundException('Attachment not found'); return rows[0]; } async delete(id: string) { const result = await this.tenant.query( `DELETE FROM attachments WHERE id = $1 RETURNING id`, [id], ); if (!result.length) throw new NotFoundException('Attachment not found'); return { deleted: true }; } }