Files
motovaultpro/backend/src/features/documents/domain/documents.service.ts
2025-12-25 12:54:00 -06:00

58 lines
2.0 KiB
TypeScript

import { randomUUID } from 'crypto';
import type { CreateDocumentBody, DocumentRecord, DocumentType, UpdateDocumentBody } from './documents.types';
import { DocumentsRepository } from '../data/documents.repository';
import pool from '../../../core/config/database';
export class DocumentsService {
private readonly repo = new DocumentsRepository(pool);
async createDocument(userId: string, body: CreateDocumentBody): Promise<DocumentRecord> {
await this.assertVehicleOwnership(userId, body.vehicleId);
const id = randomUUID();
return this.repo.insert({
id,
userId,
vehicleId: body.vehicleId,
documentType: body.documentType as DocumentType,
title: body.title,
notes: body.notes ?? null,
details: body.details ?? null,
issuedDate: body.issuedDate ?? null,
expirationDate: body.expirationDate ?? null,
emailNotifications: body.emailNotifications ?? false,
scanForMaintenance: body.scanForMaintenance ?? false,
});
}
async getDocument(userId: string, id: string): Promise<DocumentRecord | null> {
return this.repo.findById(id, userId);
}
async listDocuments(userId: string, filters?: { vehicleId?: string; type?: DocumentType; expiresBefore?: string }) {
return this.repo.listByUser(userId, filters);
}
async updateDocument(userId: string, id: string, patch: UpdateDocumentBody) {
const existing = await this.repo.findById(id, userId);
if (!existing) return null;
if (patch && typeof patch === 'object') {
return this.repo.updateMetadata(id, userId, patch as any);
}
return existing;
}
async deleteDocument(userId: string, id: string): Promise<void> {
await this.repo.softDelete(id, userId);
}
private async assertVehicleOwnership(userId: string, vehicleId: string) {
const res = await pool.query('SELECT id FROM vehicles WHERE id = $1 AND user_id = $2', [vehicleId, userId]);
if (!res.rows[0]) {
const err: any = new Error('Vehicle not found or not owned by user');
err.statusCode = 403;
throw err;
}
}
}