Data Layer medium complexity mobile
0
Dependencies
1
Dependents
1
Entities
1
Integrations

Description

Manages file upload and retrieval operations against Supabase Storage using an org/user/claim-scoped path strategy. Enforces access boundaries so receipts are only accessible to the owning organization and authorized users. Provides signed URLs for secure display in the UI.

Feature: Receipt Capture and Attachment

receipt-storage-repository

Summaries

The Receipt Storage Repository ensures that expense receipts are stored securely in the cloud and accessible only to authorized personnel within the correct organization. By scoping file storage paths to org, user, and claim identifiers, the system prevents cross-organizational data exposure — a critical requirement for any multi-tenant financial product. Signed URL generation means receipt images are never exposed through persistent public links, reducing the risk of unauthorized access. Reliable upload progress streaming ensures employees are not left uncertain about whether their receipt was successfully attached, reducing support inquiries and resubmissions.

This component integrates directly with Supabase Storage, introducing an external service dependency that should be validated early in the project. Storage bucket configuration, access policy setup, and RLS (Row Level Security) rules in Supabase must be in place before integration testing can proceed — coordinate with whoever owns the Supabase project configuration. The signed URL expiry duration is a policy decision that should be confirmed with stakeholders before implementation. File size and type enforcement adds a validation surface that needs test cases covering oversized files, unsupported types, and boundary sizes.

Medium complexity overall, but the external dependency elevates delivery risk slightly. Ensure a test Supabase environment is available for the development team.

The Receipt Storage Repository wraps Supabase Storage with an org/user/claim-scoped path strategy built by `buildStoragePath(orgId, userId, claimId, fileName)`. Upload operations should use the Supabase client's resumable upload API where available to support large files and poor network conditions. `getUploadProgressStream()` should map Supabase's upload progress callbacks to a reactive stream for consumption by `receipt-attachment-service`. Signed URL generation via `getSignedUrl(storagePath, expiry)` must use Supabase's `createSignedUrl` method — never expose raw storage paths to the UI layer.

`deleteReceipt` must handle the case where the file no longer exists (e.g., already deleted) gracefully without throwing. `fileExists` is a cheap metadata check useful for idempotent retry logic. Enforce MIME type and file size constraints at this layer as a last-resort guard, even if compression has already run.

Responsibilities

  • Upload receipt files to Supabase Storage with org/user/claim path scoping
  • Generate signed download URLs for displaying receipts
  • Delete receipt files when claims are cancelled or attachments removed
  • Stream upload progress for non-blocking UI feedback
  • Enforce file size and type constraints before upload

Interfaces

uploadReceipt(orgId, userId, claimId, file)
getSignedUrl(storagePath, {Duration expiry})
deleteReceipt(storagePath)
getUploadProgressStream()
buildStoragePath(orgId, userId, claimId, fileName)
fileExists(storagePath)

Relationships

Dependents (1)

Components that depend on this component

Related Data Entities (1)

Data entities managed by this component

Used Integrations (1)

External integrations and APIs this component relies on

API Contract

View full contract →
REST /api/v1/receipt-storage 5 endpoints
GET /api/v1/receipt-storage List stored receipt objects for the authenticated user/org
GET /api/v1/receipt-storage/:storagePath/signed-url Get a time-limited signed URL to access or download a stored receipt
POST /api/v1/receipt-storage Upload a receipt file to storage
GET /api/v1/receipt-storage/upload-progress/:claimId Stream upload progress for a receipt upload (Server-Sent Events)
DELETE /api/v1/receipt-storage/:storagePath Delete a receipt from storage