Prepare production and Coolify deployment with original exports and datetime pickers
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
# Production deployment
|
||||
|
||||
## Coolify on one VM
|
||||
|
||||
Use the repository Docker Compose build pack and `/compose.coolify.yml`.
|
||||
This adds isolated Postgres and Garage volumes, migrations, and a one-shot
|
||||
storage initializer (exact-origin CORS, one-day export expiry and abandoned
|
||||
multipart cleanup). It does not import local demo data or create accounts.
|
||||
Coolify generates `SERVICE_URL_WEB` and `SERVICE_URL_GARAGE`; these are passed
|
||||
to the app/auth and browser storage endpoint respectively. Route web to port
|
||||
3000 and Garage to 3900. Both generated domains must use HTTPS. Only these
|
||||
services are public; do not expose Postgres or Garage's admin/RPC ports.
|
||||
Set `EMAIL_FROM`, `RESEND_API_KEY`, and `RESEND_WEBHOOK_SECRET` in Coolify before
|
||||
starting. Generated credentials belong to this stack, not other applications.
|
||||
Keep worker concurrency at one initially on a shared VM. Back up all three
|
||||
data volumes off-host and verify recovery before collecting real wedding photos.
|
||||
|
||||
## Standalone Compose
|
||||
|
||||
Requires Docker Compose, an HTTPS reverse proxy, and a private S3-compatible
|
||||
bucket. Compose includes persistent Postgres, a one-shot migration service,
|
||||
the standalone web app, and a persistent Bun image/email/export worker.
|
||||
Mailpit and demo credentials are not included. Production never auto-creates
|
||||
the bucket or changes its CORS configuration.
|
||||
|
||||
1. Copy `.env.production.example` to `.env.production` and fill every required
|
||||
secret. Use random values, not development credentials. Keep auth and public
|
||||
URLs identical to the final HTTPS origin. The public URL is also a build arg;
|
||||
rebuild the web image when changing it.
|
||||
2. Provision the private bucket and configure CORS: allow your exact public app
|
||||
origin, GET/PUT/HEAD, Content-Type and necessary S3 checksum headers; expose
|
||||
ETag and Content-Length. Guests must reach `S3_PUBLIC_ENDPOINT` over HTTPS.
|
||||
The worker needs list/get/put/delete and multipart-upload permissions.
|
||||
Set a one-day lifecycle expiry for the `exports/` prefix and abort incomplete
|
||||
multipart uploads after one day. Do not expire originals. The worker also
|
||||
removes expired ZIPs, but lifecycle handles archives belonging to deleted events.
|
||||
3. Verify the Resend sender domain. Configure `/api/webhooks/resend` as described
|
||||
in `email-delivery.md`. Set Authentik's callback if using it (see README).
|
||||
4. Run:
|
||||
|
||||
```sh
|
||||
docker compose --env-file .env.production -f compose.production.yml build
|
||||
docker compose --env-file .env.production -f compose.production.yml up -d
|
||||
docker compose --env-file .env.production -f compose.production.yml ps
|
||||
```
|
||||
|
||||
The web service binds only `127.0.0.1:3000`. Configure your host reverse proxy to
|
||||
forward HTTPS to that address, preserve Host and X-Forwarded-Proto, and support
|
||||
normal streaming responses. Postgres is not published to the host. If your proxy
|
||||
runs in Docker, attach it to the Compose network and use `web:3000` instead.
|
||||
|
||||
## First administrator
|
||||
|
||||
Register your real account in the app and verify its email (or sign in through
|
||||
Authentik). Obtain its ID from your database administrator, then run:
|
||||
|
||||
```sh
|
||||
docker compose --env-file .env.production -f compose.production.yml exec worker bun packages/database/src/bootstrap-admin.ts USER_ID
|
||||
```
|
||||
|
||||
This promotes only an existing verified account and refuses once any platform
|
||||
administrator exists. No passwords are created or logged. Never run demo seeds.
|
||||
|
||||
## Updates and operations
|
||||
|
||||
Back up Postgres and the original-image bucket before updates. Build updated
|
||||
images, then run `docker compose ... run --rm migrate` before `up -d` (replace
|
||||
`...` with the same env-file/file arguments above). Do not use `down -v` on a
|
||||
live installation. Schema rollback requires a tested backup/restore strategy.
|
||||
Monitor web health at `/api/health` (process liveness only), worker logs, failed
|
||||
jobs, disk usage, database backups and storage lifecycle rules. Test restores.
|
||||
Pin image digests in your deployment pipeline after verifying them.
|
||||
|
||||
## Originals exports
|
||||
|
||||
Event → Photos → Export originals. Owners/managers with settings and private-photo
|
||||
access can choose approved or all processed photos. Exports are requester-only;
|
||||
permissions are rechecked before issuing each five-minute signed download URL.
|
||||
Already-issued URLs remain usable until expiry. ZIP files contain unchanged
|
||||
original bytes (including EXIF/GPS), named by photo ID to prevent unsafe or
|
||||
duplicate filenames. Standalone banners and unprocessed uploads are excluded.
|
||||
The selection is taken when processing starts. No originals pass through Next.js.
|
||||
ZIP64, streaming multipart uploads and one-at-a-time input reads bound memory.
|
||||
One active export per requester/event is allowed; limit 10,000 photos / 20 GB.
|
||||
Archives expire after 24 hours; interrupted jobs fail and may be requested again.
|
||||
|
||||
## Verification
|
||||
|
||||
Validated locally: web/worker/migration Docker image builds (Linux ARM64), web
|
||||
container startup and health response, worker native Sharp and ZIP dependencies,
|
||||
Compose configuration, clean database migrations, typecheck and application build.
|
||||
The demo's eight exported originals were compared byte-for-byte successfully.
|
||||
No production services were deployed and no production emails were sent.
|
||||
|
||||
```sh
|
||||
EXPORT_INTEGRATION=1 bun --env-file=.env test apps/worker/src/exports.integration.test.ts
|
||||
cd apps/web
|
||||
EXPORT_PERMISSIONS_INTEGRATION=1 bun --env-file=../../.env test src/server/exports.integration.test.ts
|
||||
```
|
||||
|
||||
The local-only integration tests cover archive contents, original bytes,
|
||||
approved/private filtering, expiry deletion, duplicate jobs, event/requester
|
||||
scoping, denied roles, revoked permissions, and expired download rejection.
|
||||
They remove their own temporary fixtures. Validate your actual S3 provider and
|
||||
target architecture in staging before launch.
|
||||
Reference in New Issue
Block a user