ESP32 devices can't spare the 40KB+ RAM a TLS client needs, so Firebase
Storage's HTTPS-only download URLs were blocking melody downloads. Binaries
are now written to local disk (./data/melody_binaries) and served through a
new unauthenticated /api/melodies/download/{pid} route, exposed publicly on
a separate melodies.bellsystems.net vhost (plain HTTP, no TLS) so the main
console domain can stay HTTPS-only with no exceptions. Preview audio still
uses Firebase Storage since it's only ever fetched by the HTTPS admin UI.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
72 lines
3.9 KiB
Markdown
72 lines
3.9 KiB
Markdown
# Melody Binary Serving (Plain HTTP)
|
|
|
|
## Why this exists
|
|
|
|
ESP32 devices download `.bsm` melody files directly, using a URL supplied through the
|
|
Android app. The download previously pointed at a Firebase Storage public URL (HTTPS
|
|
only). ESP32's TLS client needs 40KB+ of RAM to open an HTTPS connection, which these
|
|
devices can't reliably spare. To fix this, melody `.bsm` binaries are now stored and
|
|
served by our own backend over plain HTTP, instead of Firebase Storage.
|
|
|
|
This does **not** affect the audio preview file (`information.previewURL`) — that's
|
|
only ever fetched by the browser admin UI, which is already HTTPS, so it stays on
|
|
Firebase Storage unchanged.
|
|
|
|
## What changed
|
|
|
|
- **Storage**: `.bsm` binaries are written to `./data/melody_binaries/{melody_uid}.bsm`
|
|
on the host (mounted into the backend container at `/app/storage/melody_binaries`,
|
|
same pattern as `./data/firmware` and `./data/built_melodies`).
|
|
- **Upload path**: `SelectArchetypeModal` / `BuildOnTheFlyModal` still call
|
|
`POST /api/melodies/{melodyId}/upload/binary` exactly as before. The backend
|
|
(`backend/melodies/service.py::save_binary_for_melody`) now writes the bytes to
|
|
local disk instead of uploading to Firebase Storage, and returns a URL like
|
|
`http://melodies.bellsystems.net/download/{pid}`.
|
|
- **Stored URL**: the melody's `url` field (Firestore + SQLite) now holds that
|
|
plain-HTTP URL instead of a Firebase public URL. No schema change — `url` was
|
|
already a plain string field.
|
|
- **Download route**: `GET /api/melodies/download/{pid}` (`backend/melodies/router.py`)
|
|
is unauthenticated (devices have no login token) and resolves `pid` → the melody's
|
|
`{uid}.bsm` file on disk, same pattern as the existing firmware download route
|
|
(`backend/firmware/router.py::download_firmware`).
|
|
- **PID uniqueness**: since `pid` is now the public lookup key for the download route,
|
|
`backend/melodies/service.py::_check_pid_unique` rejects creating/renaming a melody
|
|
to a PID already used by another melody (409 Conflict).
|
|
- **Cleanup**: deleting a melody or its binary file now also deletes the local
|
|
`.bsm` file (`service.delete_local_binary`).
|
|
|
|
## Infrastructure (outside this repo)
|
|
|
|
The plain-HTTP requirement is handled by keeping `melodies.bellsystems.net` on a
|
|
**separate** hostname from `console.bellsystems.net`, so the console's NPM proxy host
|
|
can stay HTTPS-only with no per-path exceptions.
|
|
|
|
Required, one-time setup outside this repository:
|
|
|
|
1. **DNS**: add an A/CNAME record for `melodies.bellsystems.net` pointing at the same
|
|
host as `console.bellsystems.net`.
|
|
2. **NPM (Nginx Proxy Manager) proxy host**: create a new proxy host for
|
|
`melodies.bellsystems.net` forwarding to the `nginx` container's exposed port
|
|
(`90` per `docker-compose.yml`). **Do not force SSL / do not enable the "Force
|
|
SSL" redirect** on this proxy host — it must remain reachable over plain HTTP.
|
|
|
|
The in-repo `nginx/nginx.conf` already has a dedicated `server_name
|
|
melodies.bellsystems.net` block that maps `/download/{pid}` to the backend's
|
|
`/api/melodies/download/{pid}` route, so no further nginx changes are needed once
|
|
the NPM proxy host exists.
|
|
|
|
## Verification
|
|
|
|
1. Build/select an archetype for a melody → confirm a file appears at
|
|
`./data/melody_binaries/{uid}.bsm` and the melody's `url` becomes
|
|
`http://melodies.bellsystems.net/download/{pid}`.
|
|
2. `curl http://localhost:8000/api/melodies/download/{pid}` (direct to backend,
|
|
bypassing nginx) → returns the `.bsm` bytes, no auth header needed.
|
|
3. `curl http://localhost:90/api/melodies/download/{pid}` (through nginx on the
|
|
console server block) → same result.
|
|
4. Once NPM is configured, `curl http://melodies.bellsystems.net/download/{pid}`
|
|
→ same result, over plain HTTP.
|
|
5. Delete the melody → confirm the local `.bsm` file is removed.
|
|
6. Upload a preview audio file → confirm it still lands in Firebase Storage and
|
|
`previewURL` still populates (regression check).
|