This commit is contained in:
@@ -0,0 +1,219 @@
|
||||
{
|
||||
admin off
|
||||
}
|
||||
|
||||
:80 {
|
||||
# Compact log format for development
|
||||
log {
|
||||
output stdout
|
||||
format transform "{common_log}"
|
||||
}
|
||||
|
||||
# Ember live reload (runs on separate port 4201)
|
||||
# This handles both the script injection and WebSocket connections
|
||||
handle /ember-cli-live-reload.js {
|
||||
reverse_proxy {env.ADMIN_LIVE_RELOAD_SERVER} {
|
||||
header_up Host {http.reverse_proxy.upstream.hostport}
|
||||
header_up X-Forwarded-Host {host}
|
||||
# Enable WebSocket support for live reload
|
||||
header_up Connection {>Connection}
|
||||
header_up Upgrade {>Upgrade}
|
||||
}
|
||||
}
|
||||
|
||||
# Ghost API - must go to Ghost backend, not admin dev server
|
||||
handle /ghost/api/* {
|
||||
reverse_proxy {env.GHOST_BACKEND} {
|
||||
header_up Host {host}
|
||||
header_up X-Real-IP {remote_host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
|
||||
# Always tell Ghost requests are HTTPS to prevent redirects
|
||||
header_up X-Forwarded-Proto https
|
||||
}
|
||||
}
|
||||
|
||||
# Analytics API - proxy analytics requests to analytics service
|
||||
# Handles paths like /.ghost/analytics/* or /blog/.ghost/analytics/*
|
||||
@analytics_paths path_regexp analytics_match ^(.*)/\.ghost/analytics(.*)$
|
||||
handle @analytics_paths {
|
||||
rewrite * {re.analytics_match.2}
|
||||
reverse_proxy {env.ANALYTICS_PROXY_TARGET} {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-Host {host}
|
||||
header_up X-Real-IP {remote_host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
}
|
||||
}
|
||||
|
||||
# ActivityPub API - proxy activityPub requests to activityPub service (running in separate project)
|
||||
# Requires activitypub containers to be running via the ActivityPub project's docker-compose
|
||||
handle /.ghost/activitypub/* {
|
||||
reverse_proxy {env.ACTIVITYPUB_PROXY_TARGET} {
|
||||
header_up Host {host}
|
||||
header_up X-Real-IP {remote_host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Proto https
|
||||
}
|
||||
}
|
||||
|
||||
# WebFinger - required for ActivityPub federation
|
||||
handle /.well-known/webfinger {
|
||||
reverse_proxy {env.ACTIVITYPUB_PROXY_TARGET} {
|
||||
header_up Host {host}
|
||||
header_up X-Real-IP {remote_host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Proto https
|
||||
}
|
||||
}
|
||||
|
||||
# NodeInfo - required for ActivityPub federation
|
||||
handle /.well-known/nodeinfo {
|
||||
reverse_proxy {env.ACTIVITYPUB_PROXY_TARGET} {
|
||||
header_up Host {host}
|
||||
header_up X-Real-IP {remote_host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Proto https
|
||||
}
|
||||
}
|
||||
|
||||
# Public app dev server assets - must come BEFORE general /ghost/* handler
|
||||
# Ghost is configured to load these from /ghost/assets/* via compose.dev.yaml
|
||||
handle /ghost/assets/* {
|
||||
# Strip /ghost/assets/ prefix
|
||||
uri strip_prefix /ghost/assets
|
||||
|
||||
# Koenig Lexical Editor (optional - for developing Lexical in separate Koenig repo)
|
||||
# Requires EDITOR_URL=/ghost/assets/koenig-lexical/ when starting admin dev server
|
||||
# Falls back to Ghost backend (built package) via handle_errors if dev server isn't running
|
||||
@lexical path /koenig-lexical/*
|
||||
handle @lexical {
|
||||
uri strip_prefix /koenig-lexical
|
||||
reverse_proxy {env.LEXICAL_DEV_SERVER} {
|
||||
header_up Host {http.reverse_proxy.upstream.hostport}
|
||||
header_up X-Forwarded-Host {host}
|
||||
# Fail quickly if dev server is down
|
||||
fail_duration 1s
|
||||
unhealthy_request_count 1
|
||||
}
|
||||
}
|
||||
|
||||
# Portal
|
||||
@portal path /portal/*
|
||||
handle @portal {
|
||||
uri strip_prefix /portal
|
||||
reverse_proxy {env.PORTAL_DEV_SERVER} {
|
||||
header_up Host {http.reverse_proxy.upstream.hostport}
|
||||
header_up X-Forwarded-Host {host}
|
||||
}
|
||||
}
|
||||
|
||||
# Comments UI
|
||||
@comments path /comments-ui/*
|
||||
handle @comments {
|
||||
uri strip_prefix /comments-ui
|
||||
reverse_proxy {env.COMMENTS_DEV_SERVER} {
|
||||
header_up Host {http.reverse_proxy.upstream.hostport}
|
||||
header_up X-Forwarded-Host {host}
|
||||
}
|
||||
}
|
||||
|
||||
# Signup Form
|
||||
@signup path /signup-form/*
|
||||
handle @signup {
|
||||
uri strip_prefix /signup-form
|
||||
reverse_proxy {env.SIGNUP_DEV_SERVER} {
|
||||
header_up Host {http.reverse_proxy.upstream.hostport}
|
||||
header_up X-Forwarded-Host {host}
|
||||
}
|
||||
}
|
||||
|
||||
# Sodo Search
|
||||
@search path /sodo-search/*
|
||||
handle @search {
|
||||
uri strip_prefix /sodo-search
|
||||
reverse_proxy {env.SEARCH_DEV_SERVER} {
|
||||
header_up Host {http.reverse_proxy.upstream.hostport}
|
||||
header_up X-Forwarded-Host {host}
|
||||
}
|
||||
}
|
||||
|
||||
# Announcement Bar
|
||||
@announcement path /announcement-bar/*
|
||||
handle @announcement {
|
||||
uri strip_prefix /announcement-bar
|
||||
reverse_proxy {env.ANNOUNCEMENT_DEV_SERVER} {
|
||||
header_up Host {http.reverse_proxy.upstream.hostport}
|
||||
header_up X-Forwarded-Host {host}
|
||||
}
|
||||
}
|
||||
|
||||
# Everything else under /ghost/assets/* goes to admin dev server
|
||||
handle {
|
||||
# Re-add the prefix we stripped for admin dev server
|
||||
rewrite * /ghost/assets{path}
|
||||
reverse_proxy {env.ADMIN_DEV_SERVER} {
|
||||
header_up Host {http.reverse_proxy.upstream.hostport}
|
||||
header_up X-Forwarded-Host {host}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Auth frame - must go to Ghost backend for comment admin authentication
|
||||
handle /ghost/auth-frame/* {
|
||||
reverse_proxy {env.GHOST_BACKEND} {
|
||||
header_up Host {host}
|
||||
header_up X-Real-IP {remote_host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Proto https
|
||||
}
|
||||
}
|
||||
|
||||
# JWKS endpoint - must go to Ghost backend for JWT verification
|
||||
handle /ghost/.well-known/* {
|
||||
reverse_proxy {env.GHOST_BACKEND} {
|
||||
header_up Host {host}
|
||||
header_up X-Real-IP {remote_host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Proto https
|
||||
}
|
||||
}
|
||||
|
||||
# Admin interface - served from admin dev server
|
||||
# This includes /ghost/, etc. (but /ghost/assets/* is handled above)
|
||||
# Also handles WebSocket upgrade requests for HMR
|
||||
handle /ghost* {
|
||||
reverse_proxy {env.ADMIN_DEV_SERVER} {
|
||||
header_up X-Forwarded-Host {host}
|
||||
}
|
||||
}
|
||||
|
||||
# Everything else goes to Ghost backend
|
||||
handle {
|
||||
reverse_proxy {env.GHOST_BACKEND} {
|
||||
header_up Host {host}
|
||||
header_up X-Real-IP {remote_host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
|
||||
# Always tell Ghost requests are HTTPS to prevent redirects
|
||||
header_up X-Forwarded-Proto https
|
||||
}
|
||||
}
|
||||
|
||||
# Handle errors
|
||||
handle_errors {
|
||||
# Fallback for Lexical when dev server is unavailable (502/503/504)
|
||||
# Forwards to Ghost backend which serves the built koenig-lexical package
|
||||
@lexical_fallback `{http.request.orig_uri.path}.startsWith("/ghost/assets/koenig-lexical/")`
|
||||
handle @lexical_fallback {
|
||||
rewrite * {http.request.orig_uri.path}
|
||||
reverse_proxy {env.GHOST_BACKEND} {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-Proto https
|
||||
}
|
||||
}
|
||||
|
||||
# Default error response
|
||||
respond "{err.status_code} {err.status_text}"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
# Build mode Caddyfile
|
||||
# Used for testing pre-built images (local or registry)
|
||||
|
||||
{
|
||||
admin off
|
||||
}
|
||||
|
||||
:80 {
|
||||
log {
|
||||
output stdout
|
||||
format console
|
||||
}
|
||||
|
||||
# Analytics API - proxy to analytics service
|
||||
# Handles paths like /.ghost/analytics/* or /blog/.ghost/analytics/*
|
||||
@analytics_paths path_regexp analytics_match ^(.*)/\.ghost/analytics(.*)$
|
||||
handle @analytics_paths {
|
||||
rewrite * {re.analytics_match.2}
|
||||
reverse_proxy {env.ANALYTICS_PROXY_TARGET} {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-Host {host}
|
||||
header_up X-Real-IP {remote_host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
}
|
||||
}
|
||||
|
||||
# Everything else to Ghost
|
||||
handle {
|
||||
reverse_proxy {env.GHOST_BACKEND} {
|
||||
header_up Host {host}
|
||||
header_up X-Real-IP {remote_host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Proto https
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
FROM caddy:2-alpine@sha256:fce4f15aad23222c0ac78a1220adf63bae7b94355d5ea28eee53910624acedfa
|
||||
|
||||
RUN caddy add-package github.com/caddyserver/transform-encoder
|
||||
|
||||
# Default proxy targets (can be overridden via environment variables)
|
||||
ENV GHOST_BACKEND=ghost-dev:2368 \
|
||||
ADMIN_DEV_SERVER=host.docker.internal:5174 \
|
||||
ADMIN_LIVE_RELOAD_SERVER=host.docker.internal:4200 \
|
||||
PORTAL_DEV_SERVER=host.docker.internal:4175 \
|
||||
COMMENTS_DEV_SERVER=host.docker.internal:7173 \
|
||||
SIGNUP_DEV_SERVER=host.docker.internal:6174 \
|
||||
SEARCH_DEV_SERVER=host.docker.internal:4178 \
|
||||
ANNOUNCEMENT_DEV_SERVER=host.docker.internal:4177 \
|
||||
LEXICAL_DEV_SERVER=host.docker.internal:4173 \
|
||||
ANALYTICS_PROXY_TARGET=analytics:3000 \
|
||||
ACTIVITYPUB_PROXY_TARGET=host.docker.internal:8080
|
||||
|
||||
COPY Caddyfile /etc/caddy/Caddyfile
|
||||
EXPOSE 80 2368
|
||||
@@ -0,0 +1,55 @@
|
||||
# Dev Gateway (Caddy)
|
||||
This directory contains the Caddy reverse proxy configuration for the Ghost development environment.
|
||||
|
||||
## Purpose
|
||||
The Caddy reverse proxy container:
|
||||
1. **Routes Ghost requests** to the Ghost container backend
|
||||
2. **Proxies asset requests** to local dev servers running on the host
|
||||
3. **Enables hot-reload** for frontend development without rebuilding Ghost
|
||||
|
||||
## Configuration
|
||||
### Environment Variables
|
||||
Caddy uses environment variables (set in `compose.dev.yaml`) to configure proxy targets:
|
||||
|
||||
- `GHOST_BACKEND` - Ghost container hostname (e.g., `ghost-dev:2368`)
|
||||
- `ADMIN_DEV_SERVER` - React admin dev server (e.g., `host.docker.internal:5174`)
|
||||
- `ADMIN_LIVE_RELOAD_SERVER` - Ember live reload WebSocket (e.g., `host.docker.internal:4200`)
|
||||
- `PORTAL_DEV_SERVER` - Portal dev server (e.g., `host.docker.internal:4175`)
|
||||
- `COMMENTS_DEV_SERVER` - Comments UI (e.g., `host.docker.internal:7173`)
|
||||
- `SIGNUP_DEV_SERVER` - Signup form (e.g., `host.docker.internal:6174`)
|
||||
- `SEARCH_DEV_SERVER` - Sodo search (e.g., `host.docker.internal:4178`)
|
||||
- `ANNOUNCEMENT_DEV_SERVER` - Announcement bar (e.g., `host.docker.internal:4177`)
|
||||
- `LEXICAL_DEV_SERVER` - *Optional:* Local Koenig Lexical editor dev server (e.g., `host.docker.internal:4173`)
|
||||
- For developing Lexical in the separate [Koenig repository](https://github.com/TryGhost/Koenig)
|
||||
- Requires `EDITOR_URL=/ghost/assets/koenig-lexical/` when starting admin dev server
|
||||
- Automatically falls back to Ghost backend (built package) if dev server is not running
|
||||
- `ACTIVITYPUB_PROXY_TARGET` - *Optional:* ActivityPub service (e.g., `host.docker.internal:8080`)
|
||||
- For developing with the [ActivityPub project](https://github.com/TryGhost/ActivityPub) running locally
|
||||
- Requires the ActivityPub docker-compose services to be running
|
||||
|
||||
**Note:** AdminX React apps (admin-x-settings, activitypub, posts, stats) are served through the admin dev server so they don't need separate proxy entries.
|
||||
|
||||
### Ghost Configuration
|
||||
Ghost is configured via environment variables in `compose.dev.yaml` to load public app assets from `/ghost/assets/*` (e.g., `portal__url: /ghost/assets/portal/portal.min.js`). This uses the same path structure as built admin assets.
|
||||
|
||||
### Routing Rules
|
||||
The Caddyfile defines these routing rules:
|
||||
|
||||
| Path Pattern | Target | Purpose |
|
||||
|--------------------------------------|-------------------------------------|------------------------------------------------------------------------|
|
||||
| `/ember-cli-live-reload.js` | Admin live reload (port 4200) | Ember hot-reload script and WebSocket |
|
||||
| `/ghost/api/*` | Ghost backend | Ghost API (bypasses admin dev server) |
|
||||
| `/.ghost/activitypub/*` | ActivityPub server (port 8080) | *Optional:* ActivityPub API (requires AP project running) |
|
||||
| `/.well-known/webfinger` | ActivityPub server (port 8080) | *Optional:* WebFinger for federation |
|
||||
| `/.well-known/nodeinfo` | ActivityPub server (port 8080) | *Optional:* NodeInfo for federation |
|
||||
| `/ghost/assets/koenig-lexical/*` | Lexical dev server (port 4173) | *Optional:* Koenig Lexical editor (falls back to Ghost if not running) |
|
||||
| `/ghost/assets/portal/*` | Portal dev server (port 4175) | Membership UI |
|
||||
| `/ghost/assets/comments-ui/*` | Comments dev server (port 7173) | Comments widget |
|
||||
| `/ghost/assets/signup-form/*` | Signup dev server (port 6174) | Signup form widget |
|
||||
| `/ghost/assets/sodo-search/*` | Search dev server (port 4178) | Search widget (JS + CSS) |
|
||||
| `/ghost/assets/announcement-bar/*` | Announcement dev server (port 4177) | Announcement widget |
|
||||
| `/ghost/assets/*` | Admin dev server (port 5174) | Other admin assets (fallback) |
|
||||
| `/ghost/*` | Admin dev server (port 5174) | Admin interface |
|
||||
| Everything else | Ghost backend | Main Ghost application |
|
||||
|
||||
**Note:** All port numbers listed are the host ports where dev servers run by default.
|
||||
Reference in New Issue
Block a user