Skip to content

feat: complete Cloudflare R2 storage integration #22

@GRACENOBLE

Description

@GRACENOBLE

Summary

The R2 infrastructure layer (internal/infrastructure/storage/r2/storage.go) exists but is unwired and incomplete. This issue covers finishing the integration end-to-end: ports interface, server wiring, upload/download handlers, and web/mobile client support.

Current state

internal/infrastructure/storage/r2/storage.go implements PresignUpload, Delete, and PublicURL using the AWS S3-compatible SDK. It references gigz/internal/application/ports.StorageService which does not yet exist.

Scope

Backend (Go)

  • Create internal/usecase/storage.go — define the StorageService port interface (PresignUpload, Delete, PublicURL)
  • Wire r2.New(...) in server.go using env vars R2_ACCOUNT_ID, R2_ACCESS_KEY, R2_SECRET_KEY, R2_BUCKET, R2_PUBLIC_URL
  • Add a storage_handler.go with two endpoints:
    • POST /storage/presign — returns a presigned PUT URL and the final public URL for a given filename + content type
    • DELETE /storage/:key — deletes an object by key
  • Protect both routes behind Firebase auth middleware (feat: add Firebase Authentication across all app layers #15)
  • Add env vars to backend/.env.example

Web (Next.js)

  • Add a lib/storage.ts utility: calls POST /storage/presign, then PUTs the file directly to R2 from the browser using the presigned URL
  • Expose a useUpload hook that manages progress state and returns the final public URL on success

Mobile (Android / Jetpack Compose)

  • Upload files directly to R2 via presigned URL using OkHttp
  • Add an UploadRepository that calls the backend for a presigned URL then streams the file to R2

Acceptance criteria

  • StorageService port interface exists in internal/usecase/
  • R2 client is wired in server.go and initialised from env vars
  • POST /storage/presign returns a valid presigned URL; browser upload to that URL succeeds
  • DELETE /storage/:key removes the object from R2
  • Both endpoints require a valid Firebase auth token
  • R2_ACCOUNT_ID, R2_ACCESS_KEY, R2_SECRET_KEY, R2_BUCKET, R2_PUBLIC_URL in backend/.env.example
  • Handler unit tests and at least one integration test against a real R2 bucket (or R2-compatible local mock)
  • Docs added to backend/docs/storage.md and web/docs/storage.md

Dependencies

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions