002-secure-file-upload #9

Merged
faicel merged 11 commits from 002-secure-file-upload into dev 2026-05-11 20:47:32 +00:00
Owner

[1.0.0-rc.9] - 2026-05-11

Added

  • Phase 5 - Secure File Upload (MinIO): Full integration of MinIO object storage for document management.
    • POST /api/v1/applications/:applicationId/files — multipart upload endpoint (PDF/DOC/DOCX/TXT, max 10MB).
    • GET /api/v1/applications/:applicationId/files/:fileId/download — streamed download from MinIO.
    • DELETE /api/v1/applications/:applicationId/files/:fileId — delete document metadata + MinIO object (best-effort cleanup).
    • One document per type per application; uploading the same type replaces the previous file.
    • 404 anti-enumeration policy for unauthorized access to any document endpoint.
    • MinIO client provider (MinioClientService) with auto bucket creation on startup.
    • MinIO configuration loader registered in ConfigModule (minio.config.ts).
    • @fastify/multipart registered in Fastify for multipart form handling.
    • Object key format: applications/<applicationId>/<type>/<uuid>.<ext>.
    • Filename sanitization utility to prevent path traversal and dangerous characters.
    • MIME allowlist constants (ALLOWED_MIME_TYPES, ALLOWED_EXTENSIONS, MIME_TO_EXTENSION).
    • Swagger decorators for multipart upload, download, and delete endpoints.
    • Structured logging around all MinIO operations (no secrets logged).

Changed

  • FilesService now uses MinIO for file storage instead of accepting client-supplied filePath.
  • FilesController document download now returns NestJS StreamableFile instead of calling FastifyReply.send() with a raw stream, so headers and body are handled consistently by the Fastify adapter (including fastify.inject in E2E tests).
  • FilesController upload endpoint changed from JSON body to multipart form-data.
  • DocumentResponseDto still returns filePath but it now contains a MinIO object key.
  • CreateDocumentDto refactored: filePath, fileSize, mimeType are no longer client-supplied.
  • E2E tests updated to use multipart .attach() for file operations instead of JSON body.
  • Docker E2E script (run-e2e.sh) already provisions MinIO bucket jobs-tracker-e2e.

Dependencies

  • Added minio (MinIO JavaScript SDK) to runtime dependencies.
  • Added @fastify/multipart to runtime dependencies.

Fixed

  • E2E document download test: empty response body under fastify.inject when piping MinIO streams manually; resolved by using StreamableFile.
  • test/jest-e2e.json: set testTimeout to 120000 ms so slow bootstrap hooks (DB, MinIO) do not hit the default 5 s Jest hook limit.
  • Deleting an application now removes all MinIO objects under the applications/<appId>/ prefix via batch listObjects + removeObjects, instead of querying DB rows one-by-one.
## [1.0.0-rc.9] - 2026-05-11 ### Added - **Phase 5 - Secure File Upload (MinIO)**: Full integration of MinIO object storage for document management. - `POST /api/v1/applications/:applicationId/files` — multipart upload endpoint (PDF/DOC/DOCX/TXT, max 10MB). - `GET /api/v1/applications/:applicationId/files/:fileId/download` — streamed download from MinIO. - `DELETE /api/v1/applications/:applicationId/files/:fileId` — delete document metadata + MinIO object (best-effort cleanup). - One document per type per application; uploading the same type replaces the previous file. - 404 anti-enumeration policy for unauthorized access to any document endpoint. - MinIO client provider (`MinioClientService`) with auto bucket creation on startup. - MinIO configuration loader registered in `ConfigModule` (`minio.config.ts`). - `@fastify/multipart` registered in Fastify for multipart form handling. - Object key format: `applications/<applicationId>/<type>/<uuid>.<ext>`. - Filename sanitization utility to prevent path traversal and dangerous characters. - MIME allowlist constants (`ALLOWED_MIME_TYPES`, `ALLOWED_EXTENSIONS`, `MIME_TO_EXTENSION`). - Swagger decorators for multipart upload, download, and delete endpoints. - Structured logging around all MinIO operations (no secrets logged). ### Changed - `FilesService` now uses MinIO for file storage instead of accepting client-supplied `filePath`. - `FilesController` document download now returns NestJS `StreamableFile` instead of calling `FastifyReply.send()` with a raw stream, so headers and body are handled consistently by the Fastify adapter (including `fastify.inject` in E2E tests). - `FilesController` upload endpoint changed from JSON body to multipart form-data. - `DocumentResponseDto` still returns `filePath` but it now contains a MinIO object key. - `CreateDocumentDto` refactored: `filePath`, `fileSize`, `mimeType` are no longer client-supplied. - E2E tests updated to use multipart `.attach()` for file operations instead of JSON body. - Docker E2E script (`run-e2e.sh`) already provisions MinIO bucket `jobs-tracker-e2e`. ### Dependencies - Added `minio` (MinIO JavaScript SDK) to runtime dependencies. - Added `@fastify/multipart` to runtime dependencies. ### Fixed - E2E document download test: empty response body under `fastify.inject` when piping MinIO streams manually; resolved by using `StreamableFile`. - `test/jest-e2e.json`: set `testTimeout` to 120000 ms so slow bootstrap hooks (DB, MinIO) do not hit the default 5 s Jest hook limit. - Deleting an application now removes all MinIO objects under the `applications/<appId>/` prefix via batch `listObjects` + `removeObjects`, instead of querying DB rows one-by-one.
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
feat(files): integrate MinIO storage, E2E tests, and cascade delete
All checks were successful
Enforce branch flow / call-central-validation (pull_request) Successful in 1s
Enforce branch flow / lint-and-test (pull_request) Successful in 1m17s
830a3ae3a7
- Add MinIO client provider with auto bucket creation and removeObjectsByPrefix
- Add multipart upload/download via StreamableFile, delete endpoints
- Add filename sanitization, object key generation, MIME allowlist constants
- Add Swagger @ApiBody for multipart upload documentation
- E2E tests rewritten for Fastify inject (no supertest), all 59 tests passing
- Deleting an application now batch-removes MinIO objects by prefix
- forwardRef resolves circular dependency between Applications and Files modules
- test/jest-e2e.json: testTimeout 120s for slow bootstrap hooks

Co-authored-by: Cursor <cursoragent@cursor.com>
faicel deleted branch 002-secure-file-upload 2026-05-11 20:47:33 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
faicel/job_tracker_backend!9
No description provided.