From eb2e5a44362692e66dae0777676649a167e8a0e5 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Sat, 4 Apr 2026 14:40:07 -0700 Subject: [PATCH] Add appUrl() helper and x-app-url header --- docs/GUIDE.md | 19 +++++++++++++++++-- src/server/apps.ts | 4 +++- src/server/proxy.ts | 3 +++ src/tools/index.ts | 1 + src/tools/url.ts | 2 ++ 5 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 src/tools/url.ts diff --git a/docs/GUIDE.md b/docs/GUIDE.md index 08d806b..a4d33f9 100644 --- a/docs/GUIDE.md +++ b/docs/GUIDE.md @@ -681,11 +681,25 @@ toes metrics my-app # Single app ```bash toes share my-app -# ↗ Sharing my-app... https://abc123.trycloudflare.com +# ↗ Sharing my-app... https://myapp.toes.space ``` **`toes unshare [name]`** — Stop sharing an app. +When an app is shared, every proxied request includes an `x-app-url` header set to the app's public tunnel URL (e.g., `https://myapp.toes.space`). When not shared, it's the local URL (e.g., `http://myapp.toes.local`). + +Use `appUrl()` from `@because/toes` to read this header: + +```tsx +import { appUrl } from '@because/toes' + +app.get('/callback', c => { + const url = appUrl(c.req.raw) + // url = "https://myapp.toes.space" if shared, "http://myapp.toes.local" otherwise + return c.redirect(`${url}/done`) +}) +``` + --- ## Environment Variables @@ -697,8 +711,9 @@ Toes injects these variables into every app process automatically: | `PORT` | Assigned port (3001-3100). Your app must listen on this port. | | `APPS_DIR` | Path to the apps directory on the server. | | `DATA_DIR` | Per-app data directory for persistent storage. | -| `TOES_URL` | Base URL of the Toes server (e.g., `http://toes.local:3000`). | +| `TOES_URL` | Base URL of the Toes server (e.g., `http://toes.local`). | | `TOES_DIR` | Path to the Toes config directory. | +| `APP_URL` | The app's local URL (e.g., `http://myapp.toes.local`). For the public URL that accounts for sharing, use `appUrl(req)` from `@because/toes` (see [Sharing](#sharing)). | You can set custom variables per-app or globally. Global variables are inherited by all apps. Per-app variables override globals. diff --git a/src/server/apps.ts b/src/server/apps.ts index 5a2c38f..6c350ce 100644 --- a/src/server/apps.ts +++ b/src/server/apps.ts @@ -19,7 +19,9 @@ export const TOES_DIR = process.env.TOES_DIR ?? join(process.env.DATA_DIR ?? '.' const dataRoot = process.env.DATA_DIR ?? '.' const defaultHost = process.env.NODE_ENV === 'production' ? LOCAL_HOST : 'localhost' -export const TOES_URL = process.env.TOES_URL ?? `http://${defaultHost}:${process.env.PORT || 3000}` +export const TOES_URL = process.env.TOES_URL ?? (process.env.NODE_ENV === 'production' + ? `http://${defaultHost}` + : `http://${defaultHost}:${process.env.PORT || 3000}`) const HEALTH_CHECK_FAILURES_BEFORE_RESTART = 3 const HEALTH_CHECK_INTERVAL = 30000 diff --git a/src/server/proxy.ts b/src/server/proxy.ts index 9733480..d4be98f 100644 --- a/src/server/proxy.ts +++ b/src/server/proxy.ts @@ -49,6 +49,9 @@ export async function proxySubdomain(subdomain: string, req: Request): Promise + req.headers.get('x-app-url') ?? process.env.APP_URL!