Merge pull request 'feat: Document feature enhancements (#31)' (#32) from issue-31-document-enhancements into main
All checks were successful
Deploy to Staging / Build Images (push) Successful in 4m43s
Deploy to Staging / Deploy to Staging (push) Successful in 38s
Deploy to Staging / Verify Staging (push) Successful in 7s
Deploy to Staging / Notify Staging Ready (push) Successful in 6s
Deploy to Staging / Notify Staging Failure (push) Has been skipped

Reviewed-on: #32
This commit was merged in pull request #32.
This commit is contained in:
2026-01-15 02:35:55 +00:00
20 changed files with 1137 additions and 93 deletions

View File

@@ -421,6 +421,165 @@ export class DocumentsController {
const stream = await storage.getObjectStream(doc.storageBucket, doc.storageKey);
return reply.send(stream);
}
async listByVehicle(request: FastifyRequest<{ Params: { vehicleId: string } }>, reply: FastifyReply) {
const userId = (request as any).user?.sub as string;
const vehicleId = request.params.vehicleId;
logger.info('Documents by vehicle requested', {
operation: 'documents.listByVehicle',
userId,
vehicleId,
});
try {
const docs = await this.service.getDocumentsByVehicle(userId, vehicleId);
logger.info('Documents by vehicle retrieved', {
operation: 'documents.listByVehicle.success',
userId,
vehicleId,
documentCount: docs.length,
});
return reply.code(200).send(docs);
} catch (e: any) {
if (e.statusCode === 403) {
logger.warn('Vehicle not found or not owned', {
operation: 'documents.listByVehicle.forbidden',
userId,
vehicleId,
});
return reply.code(403).send({ error: 'Forbidden', message: e.message });
}
throw e;
}
}
async addVehicle(request: FastifyRequest<{ Params: { id: string; vehicleId: string } }>, reply: FastifyReply) {
const userId = (request as any).user?.sub as string;
const { id: documentId, vehicleId } = request.params;
logger.info('Add vehicle to document requested', {
operation: 'documents.addVehicle',
userId,
documentId,
vehicleId,
});
try {
const updated = await this.service.addVehicleToDocument(userId, documentId, vehicleId);
if (!updated) {
logger.warn('Document not updated (possibly duplicate vehicle)', {
operation: 'documents.addVehicle.not_updated',
userId,
documentId,
vehicleId,
});
return reply.code(400).send({ error: 'Bad Request', message: 'Vehicle could not be added' });
}
logger.info('Vehicle added to document', {
operation: 'documents.addVehicle.success',
userId,
documentId,
vehicleId,
sharedVehicleCount: updated.sharedVehicleIds.length,
});
return reply.code(200).send(updated);
} catch (e: any) {
if (e.statusCode === 404) {
logger.warn('Document not found for adding vehicle', {
operation: 'documents.addVehicle.not_found',
userId,
documentId,
vehicleId,
});
return reply.code(404).send({ error: 'Not Found', message: e.message });
}
if (e.statusCode === 400) {
logger.warn('Bad request for adding vehicle', {
operation: 'documents.addVehicle.bad_request',
userId,
documentId,
vehicleId,
reason: e.message,
});
return reply.code(400).send({ error: 'Bad Request', message: e.message });
}
if (e.statusCode === 403) {
logger.warn('Forbidden - vehicle not owned', {
operation: 'documents.addVehicle.forbidden',
userId,
documentId,
vehicleId,
});
return reply.code(403).send({ error: 'Forbidden', message: e.message });
}
throw e;
}
}
async removeVehicle(request: FastifyRequest<{ Params: { id: string; vehicleId: string } }>, reply: FastifyReply) {
const userId = (request as any).user?.sub as string;
const { id: documentId, vehicleId } = request.params;
logger.info('Remove vehicle from document requested', {
operation: 'documents.removeVehicle',
userId,
documentId,
vehicleId,
});
try {
const updated = await this.service.removeVehicleFromDocument(userId, documentId, vehicleId);
if (!updated) {
// Document was soft deleted
logger.info('Document soft deleted (primary vehicle removed, no shared vehicles)', {
operation: 'documents.removeVehicle.deleted',
userId,
documentId,
vehicleId,
});
return reply.code(204).send();
}
logger.info('Vehicle removed from document', {
operation: 'documents.removeVehicle.success',
userId,
documentId,
vehicleId,
sharedVehicleCount: updated.sharedVehicleIds.length,
primaryVehicleId: updated.vehicleId,
});
return reply.code(200).send(updated);
} catch (e: any) {
if (e.statusCode === 404) {
logger.warn('Document not found for removing vehicle', {
operation: 'documents.removeVehicle.not_found',
userId,
documentId,
vehicleId,
});
return reply.code(404).send({ error: 'Not Found', message: e.message });
}
if (e.statusCode === 400) {
logger.warn('Bad request for removing vehicle', {
operation: 'documents.removeVehicle.bad_request',
userId,
documentId,
vehicleId,
reason: e.message,
});
return reply.code(400).send({ error: 'Bad Request', message: e.message });
}
throw e;
}
}
}
function cryptoRandom(): string {