first commit
Copilot Setup Steps / copilot-setup-steps (push) Has been cancelled

This commit is contained in:
2026-04-22 19:51:20 +07:00
commit 93d1b7c3d3
579 changed files with 99797 additions and 0 deletions
+219
View File
@@ -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}"
}
}
+36
View File
@@ -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
}
}
}
+19
View File
@@ -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
+55
View File
@@ -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.