document media hosting in readme and usecases

This commit is contained in:
2026-03-19 22:34:47 -04:00
parent 4516ed7bab
commit 6b1e78350f
2 changed files with 78 additions and 3 deletions
+47
View File
@@ -80,6 +80,9 @@ and verifies the site is live.
installed. Uses the deploy SSH key from Bitwarden Secrets Manager -- no installed. Uses the deploy SSH key from Bitwarden Secrets Manager -- no
personal server access needed. See USECASES.md for full details. 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.<domain>/media/`.
After the site is live, clone the app repo and customize it: After the site is live, clone the app repo and customize it:
```bash ```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 This only needs to be done once. If only hantim is reprovisioned, argento
retains the layout and hantim reconnects automatically. 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.<domain>/media/<path>` — 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 ## Restoring from backup
Run the same three commands as provisioning. `bootstrap.sh` is idempotent: 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-vultr-api-key` | Vultr API key for DNS management | `new-app.sh` |
| `hantim-garage-rpc-secret` | Garage cluster RPC secret | `deploy.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-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). Machine accounts: `hantim-server` (token at `/etc/bws-token`), `hantim-ci` (reserved).
+31 -3
View File
@@ -62,9 +62,10 @@ From your dev machine (single command):
- `.gitea/workflows/deploy-example.com.yml` - `.gitea/workflows/deploy-example.com.yml`
- `docker/nginx/conf.d/example.com.conf` (HTTP->HTTPS, bare->www, proxy) - `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) 7. SSHes to server as deploy user, issues SSL cert via webroot (zero downtime)
8. Commits and pushes hantim-server (triggers deploy) 8. Creates Garage media bucket via admin API (create, allow media-key, enable website)
9. Triggers initial build of the app repo via Gitea API 9. Commits and pushes hantim-server (triggers deploy)
10. Polls `https://www.example.com` until it responds (up to 3 minutes) 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: After the site is live, clone the app repo and customize:
@@ -148,3 +149,30 @@ triggers automatically.
- Existing certs are skipped - Existing certs are skipped
- Brief nginx downtime (seconds) while certbot checks run - Brief nginx downtime (seconds) while certbot checks run
- Services are restarted - 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`.