feat(docker): add Caddy reverse proxy and domain-portable Reverb config
Add an optional Caddy service (proxy profile) to compose.prod.yaml for automatic HTTPS on a custom domain, with a Caddyfile that transparently proxies WebSocket upgrades. Move Echo setup into resources/js/echo.ts and derive the Reverb host from window.location when VITE_REVERB_* is unset, so the published image's frontend connects on any domain without a rebuild. The release workflow bakes a fixed public Reverb key and leaves host/port/scheme empty to enable this; the Herd dev setup keeps its explicit VITE_REVERB_* values, so its behaviour is unchanged. Drop the now-unneeded 8080 host port — the WebSocket rides the main HTTP(S) port via the in-container nginx /app proxy.
This commit is contained in:
parent
5b9226569f
commit
48727c80cd
5 changed files with 61 additions and 9 deletions
7
.github/workflows/release-docker.yml
vendored
7
.github/workflows/release-docker.yml
vendored
|
|
@ -63,6 +63,13 @@ jobs:
|
||||||
target: production
|
target: production
|
||||||
platforms: ${{ matrix.platform }}
|
platforms: ${{ matrix.platform }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
# Bake a fixed public Reverb key; leave host/port/scheme empty so the
|
||||||
|
# frontend derives them from window.location (works on any domain).
|
||||||
|
build-args: |
|
||||||
|
VITE_REVERB_APP_KEY=trypost-reverb-key
|
||||||
|
VITE_REVERB_HOST=
|
||||||
|
VITE_REVERB_PORT=
|
||||||
|
VITE_REVERB_SCHEME=
|
||||||
cache-from: type=gha,scope=${{ env.PLATFORM_PAIR }}
|
cache-from: type=gha,scope=${{ env.PLATFORM_PAIR }}
|
||||||
cache-to: type=gha,mode=max,scope=${{ env.PLATFORM_PAIR }}
|
cache-to: type=gha,mode=max,scope=${{ env.PLATFORM_PAIR }}
|
||||||
outputs: type=image,name=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true
|
outputs: type=image,name=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true
|
||||||
|
|
|
||||||
6
Caddyfile
Normal file
6
Caddyfile
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
# Caddy serves TryPost on your domain with automatic HTTPS (Let's Encrypt).
|
||||||
|
# APP_DOMAIN is provided by the caddy service in compose.prod.yaml.
|
||||||
|
# reverse_proxy transparently passes through WebSocket upgrades (Reverb on /app).
|
||||||
|
{$APP_DOMAIN} {
|
||||||
|
reverse_proxy app:80
|
||||||
|
}
|
||||||
|
|
@ -40,10 +40,13 @@ services:
|
||||||
BROADCAST_CONNECTION: reverb
|
BROADCAST_CONNECTION: reverb
|
||||||
|
|
||||||
# ===== WebSockets (Reverb) =====
|
# ===== WebSockets (Reverb) =====
|
||||||
|
# These are the INTERNAL server->Reverb connection — leave as-is even on a
|
||||||
|
# domain. The browser derives the WebSocket host from its own URL, and the
|
||||||
|
# in-container nginx proxies it on /app, so it just works on any domain.
|
||||||
REVERB_APP_ID: "1001"
|
REVERB_APP_ID: "1001"
|
||||||
REVERB_APP_KEY: trypost-reverb-key
|
REVERB_APP_KEY: trypost-reverb-key # matches the key baked into the published image
|
||||||
REVERB_APP_SECRET: change-me-reverb-secret # <- change me
|
REVERB_APP_SECRET: change-me-reverb-secret # <- change me
|
||||||
REVERB_HOST: localhost # <- for a real domain, set to that host (and PORT 443 / SCHEME https)
|
REVERB_HOST: localhost
|
||||||
REVERB_PORT: "8080"
|
REVERB_PORT: "8080"
|
||||||
REVERB_SCHEME: http
|
REVERB_SCHEME: http
|
||||||
|
|
||||||
|
|
@ -102,8 +105,7 @@ services:
|
||||||
# ANTHROPIC_API_KEY: ""
|
# ANTHROPIC_API_KEY: ""
|
||||||
# GEMINI_API_KEY: ""
|
# GEMINI_API_KEY: ""
|
||||||
ports:
|
ports:
|
||||||
- "8000:80" # app (nginx)
|
- "8000:80" # app (nginx); WebSocket rides this same port via /app
|
||||||
- "8080:8080" # Reverb WebSocket
|
|
||||||
volumes:
|
volumes:
|
||||||
- storage:/var/www/html/storage/app
|
- storage:/var/www/html/storage/app
|
||||||
depends_on:
|
depends_on:
|
||||||
|
|
@ -141,7 +143,33 @@ services:
|
||||||
timeout: 3s
|
timeout: 3s
|
||||||
retries: 5
|
retries: 5
|
||||||
|
|
||||||
|
# Optional reverse proxy with automatic HTTPS (Let's Encrypt).
|
||||||
|
# To serve on a domain:
|
||||||
|
# 1. Point the domain's DNS at this host.
|
||||||
|
# 2. Set APP_DOMAIN below and APP_URL above to https://<that domain>.
|
||||||
|
# 3. Start with the proxy profile:
|
||||||
|
# docker compose -f compose.prod.yaml --profile proxy up -d
|
||||||
|
# Without the profile, the app is served directly on http://localhost:8000.
|
||||||
|
caddy:
|
||||||
|
image: caddy:2-alpine
|
||||||
|
container_name: trypost-caddy
|
||||||
|
restart: unless-stopped
|
||||||
|
profiles: [proxy]
|
||||||
|
environment:
|
||||||
|
APP_DOMAIN: post.example.com # <- your domain
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
- "443:443"
|
||||||
|
volumes:
|
||||||
|
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
||||||
|
- caddy-data:/data
|
||||||
|
- caddy-config:/config
|
||||||
|
depends_on:
|
||||||
|
- app
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
pgdata:
|
pgdata:
|
||||||
redisdata:
|
redisdata:
|
||||||
storage:
|
storage:
|
||||||
|
caddy-data:
|
||||||
|
caddy-config:
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import '../css/app.css';
|
import '../css/app.css';
|
||||||
|
import './echo';
|
||||||
|
|
||||||
import { createInertiaApp, router } from '@inertiajs/vue3';
|
import { createInertiaApp, router } from '@inertiajs/vue3';
|
||||||
import { configureEcho } from '@laravel/echo-vue';
|
|
||||||
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';
|
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';
|
||||||
import { i18nVue } from 'laravel-vue-i18n';
|
import { i18nVue } from 'laravel-vue-i18n';
|
||||||
import type { DefineComponent } from 'vue';
|
import type { DefineComponent } from 'vue';
|
||||||
|
|
@ -12,10 +12,6 @@ import dayjs from './dayjs';
|
||||||
import { capturePageview, initializePostHog, syncPostHogContext } from './posthog';
|
import { capturePageview, initializePostHog, syncPostHogContext } from './posthog';
|
||||||
import type { Auth } from './types';
|
import type { Auth } from './types';
|
||||||
|
|
||||||
configureEcho({
|
|
||||||
broadcaster: 'reverb',
|
|
||||||
});
|
|
||||||
|
|
||||||
const appName = import.meta.env.VITE_APP_NAME || 'TryPost.it';
|
const appName = import.meta.env.VITE_APP_NAME || 'TryPost.it';
|
||||||
|
|
||||||
createInertiaApp({
|
createInertiaApp({
|
||||||
|
|
|
||||||
15
resources/js/echo.ts
Normal file
15
resources/js/echo.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { configureEcho } from '@laravel/echo-vue';
|
||||||
|
|
||||||
|
const scheme = import.meta.env.VITE_REVERB_SCHEME || (window.location.protocol === 'https:' ? 'https' : 'http');
|
||||||
|
const forceTLS = scheme === 'https';
|
||||||
|
const port = Number(import.meta.env.VITE_REVERB_PORT || window.location.port || (forceTLS ? 443 : 80));
|
||||||
|
|
||||||
|
configureEcho({
|
||||||
|
broadcaster: 'reverb',
|
||||||
|
key: import.meta.env.VITE_REVERB_APP_KEY,
|
||||||
|
wsHost: import.meta.env.VITE_REVERB_HOST || window.location.hostname,
|
||||||
|
wsPort: port,
|
||||||
|
wssPort: port,
|
||||||
|
forceTLS,
|
||||||
|
enabledTransports: ['ws', 'wss'],
|
||||||
|
});
|
||||||
Loading…
Reference in a new issue