From f62eb5cc687a4080a76889bbede769039be2f4cc Mon Sep 17 00:00:00 2001 From: Trezy Date: Tue, 23 Jun 2026 11:57:17 -0500 Subject: [PATCH] chore: add cloudflare tunnels and caddy for reverse proxy for local dev Signed-off-by: Trezy Signed-off-by: Trezy --- .env.example | 7 ++++++ docker-compose.yml | 30 +++++++++++++++++++++++++ scripts/dev-Caddyfile | 41 ++++++++++++++++++++++++++++++++++ scripts/entrypoint.sh | 24 ++++++++++++++++++++ scripts/tunnel.sh | 51 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 153 insertions(+) create mode 100644 scripts/dev-Caddyfile create mode 100755 scripts/entrypoint.sh create mode 100755 scripts/tunnel.sh diff --git a/.env.example b/.env.example index 82bb8d5..83b3e72 100644 --- a/.env.example +++ b/.env.example @@ -12,6 +12,13 @@ SESSION_SECRET=change-me-in-production RELAY_URL=https://relay1.us-east.bsky.network PORT=3000 +# Cloudflare Tunnel — exposes the backend via a public HTTPS URL. +# Named tunnel (stable hostname): set CLOUDFLARE_TUNNEL_TOKEN from your +# Cloudflare dashboard and update PUBLIC_URL to match. +# Quick tunnel (random hostname): leave CLOUDFLARE_TUNNEL_TOKEN blank and +# check `docker compose logs tunnel` for the *.trycloudflare.com URL. +# CLOUDFLARE_TUNNEL_TOKEN= + # Web dashboard WEB_HOSTNAME=0.0.0.0 API_URL=http://happyview:3000 diff --git a/docker-compose.yml b/docker-compose.yml index c147bc3..26f2507 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -22,6 +22,7 @@ services: happyview: image: rust:1.93 working_dir: /app + entrypoint: ["sh", "/scripts/entrypoint.sh"] command: cargo run ports: - "3000:3000" @@ -30,17 +31,45 @@ services: - cargo-registry:/usr/local/cargo/registry - cargo-git:/usr/local/cargo/git - cargo-target:/app/target + - ./scripts/entrypoint.sh:/scripts/entrypoint.sh:ro + - tunnel-url:/shared environment: DATABASE_URL: ${DATABASE_URL} PUBLIC_URL: ${PUBLIC_URL} SESSION_SECRET: ${SESSION_SECRET} RELAY_URL: ${RELAY_URL} PORT: ${PORT} + TUNNEL_URL_FILE: /shared/tunnel-url + CLOUDFLARE_TUNNEL_TOKEN: ${CLOUDFLARE_TUNNEL_TOKEN:-} # depends_on: # Uncomment if using Postgres: # postgres: # condition: service_healthy + caddy: + image: caddy:2-alpine + volumes: + - ./scripts/dev-Caddyfile:/etc/caddy/Caddyfile:ro + ports: + - "3080:80" + depends_on: + - happyview + - web + + tunnel: + image: alpine:latest + entrypoint: ["sh", "/scripts/tunnel.sh"] + volumes: + - ./scripts/tunnel.sh:/scripts/tunnel.sh:ro + - tunnel-url:/shared + environment: + CLOUDFLARE_TUNNEL_TOKEN: ${CLOUDFLARE_TUNNEL_TOKEN:-} + TUNNEL_URL_FILE: /shared/tunnel-url + TUNNEL_UPSTREAM: http://caddy:80 + depends_on: + - caddy + restart: unless-stopped + web: image: node:24-alpine working_dir: /app @@ -61,3 +90,4 @@ volumes: cargo-git: cargo-target: web-node-modules: + tunnel-url: diff --git a/scripts/dev-Caddyfile b/scripts/dev-Caddyfile new file mode 100644 index 0000000..e3f2620 --- /dev/null +++ b/scripts/dev-Caddyfile @@ -0,0 +1,41 @@ +:80 { + # API, admin, auth, XRPC, and well-known routes → Rust backend + handle /api/* { + reverse_proxy happyview:3000 + } + handle /admin/* { + reverse_proxy happyview:3000 + } + handle /auth/* { + reverse_proxy happyview:3000 + } + handle /xrpc/* { + reverse_proxy happyview:3000 + } + handle /oauth/* { + reverse_proxy happyview:3000 + } + handle /external-auth/* { + reverse_proxy happyview:3000 + } + handle /.well-known/* { + reverse_proxy happyview:3000 + } + handle /oauth-client-metadata.json { + reverse_proxy happyview:3000 + } + handle /health { + reverse_proxy happyview:3000 + } + handle /config { + reverse_proxy happyview:3000 + } + handle /settings/* { + reverse_proxy happyview:3000 + } + + # Everything else → Next.js dev server + handle { + reverse_proxy web:3001 + } +} diff --git a/scripts/entrypoint.sh b/scripts/entrypoint.sh new file mode 100755 index 0000000..8812f86 --- /dev/null +++ b/scripts/entrypoint.sh @@ -0,0 +1,24 @@ +#!/bin/sh +set -e + +TUNNEL_URL_FILE="${TUNNEL_URL_FILE:-}" + +if [ -n "$TUNNEL_URL_FILE" ] && [ -z "$CLOUDFLARE_TUNNEL_TOKEN" ]; then + rm -f "$TUNNEL_URL_FILE" + echo "Waiting for quick tunnel URL..." + elapsed=0 + while [ ! -f "$TUNNEL_URL_FILE" ] && [ "$elapsed" -lt 30 ]; do + sleep 1 + elapsed=$((elapsed + 1)) + done + + if [ -f "$TUNNEL_URL_FILE" ]; then + url=$(cat "$TUNNEL_URL_FILE") + export PUBLIC_URL="$url" + echo "Using tunnel URL as PUBLIC_URL: $url" + else + echo "Tunnel URL not found after 30s, using PUBLIC_URL from env: $PUBLIC_URL" + fi +fi + +exec "$@" diff --git a/scripts/tunnel.sh b/scripts/tunnel.sh new file mode 100755 index 0000000..ed56d97 --- /dev/null +++ b/scripts/tunnel.sh @@ -0,0 +1,51 @@ +#!/bin/sh +set -e + +# Install cloudflared if not present +if ! command -v cloudflared >/dev/null 2>&1; then + ARCH=$(uname -m) + case "$ARCH" in + x86_64|amd64) CF_ARCH="amd64" ;; + aarch64|arm64) CF_ARCH="arm64" ;; + armv7l) CF_ARCH="arm" ;; + *) echo "Unsupported architecture: $ARCH"; exit 1 ;; + esac + echo "Installing cloudflared ($CF_ARCH)..." + wget -q -O /usr/local/bin/cloudflared \ + "https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-$CF_ARCH" + chmod +x /usr/local/bin/cloudflared +fi + +UPSTREAM="${TUNNEL_UPSTREAM:-http://happyview:3000}" +URL_FILE="${TUNNEL_URL_FILE:-/shared/tunnel-url}" + +if [ -n "$CLOUDFLARE_TUNNEL_TOKEN" ]; then + echo "══════════════════════════════════════════════════════════════" + echo " Starting named Cloudflare tunnel" + echo " Upstream: $UPSTREAM" + echo "══════════════════════════════════════════════════════════════" + exec cloudflared tunnel run --token "$CLOUDFLARE_TUNNEL_TOKEN" +fi + +rm -f "$URL_FILE" + +echo "══════════════════════════════════════════════════════════════" +echo " Starting quick Cloudflare tunnel" +echo " Upstream: $UPSTREAM" +echo "══════════════════════════════════════════════════════════════" + +cloudflared tunnel --url "$UPSTREAM" 2>&1 | while IFS= read -r line; do + echo "$line" + case "$line" in + *trycloudflare.com*) + url=$(echo "$line" | grep -o 'https://[a-zA-Z0-9._-]*trycloudflare\.com' | head -1) + if [ -n "$url" ]; then + mkdir -p "$(dirname "$URL_FILE")" + echo "$url" > "$URL_FILE" + echo "══════════════════════════════════════════════════════════════" + echo " Tunnel URL written to $URL_FILE" + echo "══════════════════════════════════════════════════════════════" + fi + ;; + esac +done -- 2.51.2