diff --git a/README.md b/README.md index be457f0..146f92b 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,9 @@ and verifies the site is live. installed. Uses the deploy SSH key from Bitwarden Secrets Manager -- no personal server access needed. See USECASES.md for full details. +`new-app.sh` also creates a Garage media bucket for the domain, allowing +media files to be served at `https://www./media/`. + After the site is live, clone the app repo and customize it: ```bash @@ -119,6 +122,47 @@ docker exec garage /garage layout apply --version 1 This only needs to be done once. If only hantim is reprovisioned, argento retains the layout and hantim reconnects automatically. +## Media hosting + +Media files (images, videos) are stored in Garage and served via nginx at +`/media/` on each domain. This keeps large files out of git repos. + +**Endpoints:** +- `s3.hantim.net` — S3 API for uploads (authenticated with `media-key`) +- `garage.hantim.net` — Admin API for bucket management +- `https://www./media/` — public file access + +**Uploading media:** + +```bash +# Configure AWS CLI (one-time) +aws configure # key ID, secret key from bws, region: garage +aws configure set default.endpoint_url https://s3.hantim.net + +# Upload files +aws s3 cp photo.jpg s3://example.com/photo.jpg +aws s3 sync static/media/ s3://example.com/ +``` + +**Local development:** Store media in `static/media/` (gitignored) so files +are accessible at `/media/` when serving locally. + +**Creating buckets manually** (for existing domains without buckets): + +```bash +# Get admin token and media key ID from bws +# Create bucket +curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ + -d '{"globalAlias": "example.com"}' https://garage.hantim.net/v2/CreateBucket +# Note the bucket ID from the response, then: +curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ + -d '{"bucketId": "BUCKET_ID", "accessKeyId": "MEDIA_KEY_ID", "permissions": {"read": true, "write": true, "owner": false}}' \ + https://garage.hantim.net/v2/AllowBucketKey +curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ + -d '{"websiteAccess": {"enabled": true, "indexDocument": "index.html"}}' \ + "https://garage.hantim.net/v2/UpdateBucket?id=BUCKET_ID" +``` + ## Restoring from backup Run the same three commands as provisioning. `bootstrap.sh` is idempotent: @@ -139,6 +183,9 @@ Bitwarden Secrets Manager (project: `hantim`, fetched via `bws` CLI on the serve | `hantim-vultr-api-key` | Vultr API key for DNS management | `new-app.sh` | | `hantim-garage-rpc-secret` | Garage cluster RPC secret | `deploy.sh` | | `hantim-garage-argento-node-id` | Argento's Garage node ID + address | `deploy.sh` | +| `hantim-garage-admin-token` | Garage admin API token | `deploy.sh`, `new-app.sh` | +| `hantim-garage-media-key-id` | S3 access key ID for media uploads | `new-app.sh`, `aws` CLI | +| `hantim-garage-media-secret-key` | S3 secret key for media uploads | `aws` CLI | Machine accounts: `hantim-server` (token at `/etc/bws-token`), `hantim-ci` (reserved). diff --git a/USECASES.md b/USECASES.md index 0034dbf..d2de64d 100644 --- a/USECASES.md +++ b/USECASES.md @@ -62,9 +62,10 @@ From your dev machine (single command): - `.gitea/workflows/deploy-example.com.yml` - `docker/nginx/conf.d/example.com.conf` (HTTP->HTTPS, bare->www, proxy) 7. SSHes to server as deploy user, issues SSL cert via webroot (zero downtime) -8. Commits and pushes hantim-server (triggers deploy) -9. Triggers initial build of the app repo via Gitea API -10. Polls `https://www.example.com` until it responds (up to 3 minutes) +8. Creates Garage media bucket via admin API (create, allow media-key, enable website) +9. Commits and pushes hantim-server (triggers deploy) +10. Triggers initial build of the app repo via Gitea API +11. Polls `https://www.example.com` until it responds (up to 3 minutes) After the site is live, clone the app repo and customize: @@ -148,3 +149,30 @@ triggers automatically. - Existing certs are skipped - Brief nginx downtime (seconds) while certbot checks run - Services are restarted + +## 8. Upload media files + +Media files (images, videos) are stored in Garage and served at `/media/` +on each domain. + +```bash +# One-time setup +aws configure # key ID + secret from bws, region: garage +aws configure set default.endpoint_url https://s3.hantim.net + +# Upload a single file +aws s3 cp photo.jpg s3://example.com/photo.jpg + +# Sync a directory +aws s3 sync static/media/ s3://example.com/ +``` + +**What happens:** +1. File is uploaded to the Garage S3 API via `s3.hantim.net` +2. Garage replicates the file to both nodes (hantim + argento) +3. File is accessible at `https://www.example.com/media/photo.jpg` + +**Local development:** Store media in `static/media/` (gitignored). The app's +nginx serves these locally, matching the `/media/` path used in production. + +**Reference in HTML:** Use absolute paths like `/media/photo.jpg`.