feat(backend): public relay for off-network wallet scanning

Scan-to-connect only worked on the same Wi-Fi because the QR carried a LAN
relay URL. Give the relay a public address two ways:

- `npm run dev:tunnel` (scripts/dev-tunnel.mjs) opens a cloudflared tunnel
  to localhost:4000 and starts the backend with PUBLIC_RELAY_URL set to the
  public https URL, so the QR carries it and a phone on any network connects.
- Deploy configs for Fly.io (fly.toml), Render (render.yaml), and Railway
  (railway.json), all building the existing Dockerfile.

Also harden resolveRelayUrl to honor x-forwarded-proto so the derived QR URL
is https behind a TLS proxy (iOS ATS requires wss).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Khalid Abdi
2026-06-22 20:01:54 +03:00
parent 4e60361f77
commit f10d01546b
6 changed files with 203 additions and 1 deletions
+8 -1
View File
@@ -30,7 +30,14 @@ patientsWalletRouter.use(requireAuth, requireOrg);
function resolveRelayUrl(req: Request): string {
if (env.PUBLIC_RELAY_URL) return env.PUBLIC_RELAY_URL;
const host = req.get("host");
if (host) return `${req.protocol}://${host}`;
if (host) {
// Behind a TLS-terminating proxy (Fly/Render/etc.) req.protocol is "http";
// trust x-forwarded-proto so the QR carries an https URL — the phone then
// connects over wss, which iOS App Transport Security requires.
const proto =
req.get("x-forwarded-proto")?.split(",")[0]?.trim() || req.protocol;
return `${proto}://${host}`;
}
return env.BETTER_AUTH_URL;
}