diff --git a/apps/backend/.env.prod.example b/apps/backend/.env.prod.example index 10cd85c..5efa82e 100644 --- a/apps/backend/.env.prod.example +++ b/apps/backend/.env.prod.example @@ -24,6 +24,14 @@ AZURE_CLIENT_ID= AZURE_CLIENT_SECRET= AZURE_TENANT_ID= +# File storage (Amazon S3, REQUIRED in production) +# Credentials of the IAM user that can read, write and delete objects in the bucket. +# Leave Storage__ServiceUrl unset: it is only for S3-compatible servers such as local MinIO. +S3_BUCKET_NAME= +S3_REGION=us-east-1 +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= + # Cache Settings CACHE_ENABLED=true diff --git a/apps/backend/README.md b/apps/backend/README.md index 1bee3c3..c0bc4f7 100644 --- a/apps/backend/README.md +++ b/apps/backend/README.md @@ -24,7 +24,7 @@ BitFinance is a finance platform for tracking bills, expenses, organizations, an - Durable in-app notifications and paid-plan bill reminder emails through Resend - PostgreSQL persistence through Entity Framework Core - Optional Redis caching -- Local object storage with MinIO and production S3-compatible storage +- Local object storage with MinIO and production storage on Amazon S3 - OpenAPI and Scalar API documentation ## Tech Stack @@ -122,7 +122,16 @@ The local `.env.example` includes safe development defaults. | `NOTIFICATIONS_FROM_ADDRESS` | Verified sender address | Development placeholder | | `NOTIFICATIONS_FRONTEND_BASE_URL` | Base URL used by notification email links | `http://localhost:5174` | -Production deployments should use `.env.prod.example` as a template and provide real values for database credentials, JWT settings, Azure Key Vault configuration, and image tags. +Production deployments should use `.env.prod.example` as a template and provide real values for database credentials, JWT settings, Azure Key Vault configuration, file storage, and image tags. + +### File storage + +Attachments are stored through the S3 API. `Storage:ServiceUrl` selects the backend: + +- **Set** (development): an S3-compatible server such as the local MinIO container, with path-style addressing and the `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` it is given. +- **Empty** (production): Amazon S3 in `Storage:Region`, with credentials from the default AWS chain (`AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` in the container). + +In production, set `S3_BUCKET_NAME`, `S3_REGION`, `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`. The IAM user needs `s3:GetObject`, `s3:PutObject` and `s3:DeleteObject` on the bucket's objects. The database stores each object's key in `attachments.storage_path`, so moving files between buckets only requires copying them with the same keys. ## API Overview diff --git a/apps/backend/docker-compose.prod.yml b/apps/backend/docker-compose.prod.yml index 9c524ee..ecfae32 100644 --- a/apps/backend/docker-compose.prod.yml +++ b/apps/backend/docker-compose.prod.yml @@ -14,6 +14,10 @@ services: - AZURE_CLIENT_ID=${AZURE_CLIENT_ID} - AZURE_CLIENT_SECRET=${AZURE_CLIENT_SECRET} - AZURE_TENANT_ID=${AZURE_TENANT_ID} + - Storage__BucketName=${S3_BUCKET_NAME} + - Storage__Region=${S3_REGION} + - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} + - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} - Notifications__EmailEnabled=${NOTIFICATIONS_EMAIL_ENABLED} - Notifications__ResendApiToken=${RESEND_API_TOKEN} - Notifications__ResendWebhookSecret=${RESEND_WEBHOOK_SECRET} diff --git a/docs/deployment.md b/docs/deployment.md index 48324a0..001fe77 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -125,6 +125,8 @@ GitHub secrets used by the publish workflows: Server-side requirements: - `DEPLOY_PATH` contains `docker-compose.yml`, `docker-compose.prod.yml` and `.env`. Use `apps/backend/.env.prod.example` as the template for `.env`. +- The server's `.env` sets the S3 storage variables (`S3_BUCKET_NAME`, `S3_REGION`, `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`). See "File storage" in `apps/backend/README.md`. +- The deploy does not copy the compose files. After changing `docker-compose.yml` or `docker-compose.prod.yml`, copy them to `DEPLOY_PATH` yourself. - The external Postgres volume (`POSTGRES_DATA_VOLUME`, default `bitfinance_postgres-data`) exists. See `apps/backend/README.md`. - The SSH user can run `docker compose` and can write to `/var/www/bitfinance` without `sudo`.