feat(backend): implement file upload service for dispute evidence (#412) - #635
Open
Jaydams wants to merge 2 commits into
Open
feat(backend): implement file upload service for dispute evidence (#412)#635Jaydams wants to merge 2 commits into
Jaydams wants to merge 2 commits into
Conversation
…ayLitCodes#412) Add UploadModule providing a complete evidence file management system for disputes. The module is self-contained and uses abstract adapter interfaces so storage (local → S3) and virus scanning (noop → ClamAV) can be swapped without touching business logic. New endpoints: POST /disputes/:id/evidence upload a file GET /disputes/:id/evidence list evidence GET /disputes/:id/evidence/:evidenceId/download stream download DELETE /disputes/:id/evidence/:evidenceId admin-only soft delete Key implementation details: - Magic-bytes MIME validation (not extension trust) for PNG, JPEG, WebP, PDF, TXT, DOCX - Max 10 MB per file enforced at multer + service layers - Max 10 active files per dispute enforced at service layer - UUID filenames on disk prevent path-traversal attacks - Image thumbnails (200×200 JPEG) via sharp with graceful fallback - LocalStorageAdapter (default) + S3StorageAdapter stub ready to wire - VirusScanner interface with NoopVirusScannerAdapter; replace with ClamAV adapter by binding VIRUS_SCANNER token - UploadRateLimitGuard: 20 uploads / hour / user (in-memory window) - DisputeAccessGuard: checks caller is a party to the dispute's escrow - Admin-only DELETE with soft-delete + physical file removal - cleanOrphanedFiles() utility for scheduled cleanup - Migration 1780400000000-AddDisputeEvidence adds dispute_evidence table with FK cascade and indices on disputeId / uploadedByUserId - 12 unit tests covering all happy paths and error branches - E2E tests against in-memory SQLite covering upload, list, download, access control, and error cases
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #412
Implements a complete
UploadModulefor structured dispute evidence file management, closing issue #412. Evidence is now stored on the local filesystem (with an S3 adapter interface ready for production) instead of relying solely on the existing IPFS path. A dedicateddispute_evidencetable replaces the JSON column approach for new uploads.What's new
Module:
src/modules/upload/DisputeEvidenceentity with full metadata: UUID-based filename, magic-byte verified MIME type, SHA-256 checksum, ClamAV scan status, soft-delete fields, thumbnail pathPOST /v1/disputes/:id/evidence— upload up to 10 files, max 10 MB each, rate-limited to 20 uploads/hour/userGET /v1/disputes/:id/evidence— list evidence for a dispute (filterable by scan status)GET /v1/disputes/:id/evidence/:evidenceId/download— stream a file with correct Content-Type and Content-Disposition headersDELETE /v1/disputes/:id/evidence/:evidenceId— admin-only soft delete; removes physical fileSecurity & validation
VirusScanner) with aNoOpVirusScannerstub — swap in a realClamAvScannerServiceagainst a clamd socket without touching service logicStorage
LocalStorageAdapterwrites toUPLOAD_BASE_DIR(default./uploads)StorageAdapterinterface +S3StorageAdapterBaseabstract class ready for drop-in replacement via theSTORAGE_ADAPTER_TOKENinjection tokenImage thumbnails
sharpfor PNG, JPEG, and WebP uploadssharpbinary is unavailableMaintenance
@Cron(EVERY_DAY_AT_2AM)job removes orphaned files on disk not referenced by any DB row1780700000000-CreateDisputeEvidenceTablecreates thedispute_evidencetable with indexes ondisputeIdanduploadedByIdTests
application/pdf), and quota enforcementFiles changed
src/ ├── app.module.ts (UploadModule + DisputeEvidence registered) ├── data-source.ts (DisputeEvidence entity added) ├── migrations/ │ └── 1780700000000-CreateDisputeEvidenceTable.ts └── modules/upload/ ├── adapters/local-storage.adapter.ts ├── dto/upload.dto.ts ├── entities/dispute-evidence.entity.ts ├── guards/upload-rate-limit.guard.ts ├── interfaces/ │ ├── storage-adapter.interface.ts │ └── virus-scanner.interface.ts ├── utils/mime-magic.util.ts ├── upload.controller.ts ├── upload.module.ts ├── upload.scheduler.ts ├── upload.service.ts └── upload.service.spec.ts test/e2e/upload.e2e-spec.ts
Testing