feat: serve melody .bsm binaries over plain HTTP instead of Firebase Storage
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>
This commit is contained in:
71
docs/melody-binary-serving.md
Normal file
71
docs/melody-binary-serving.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# 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).
|
||||
Reference in New Issue
Block a user