feat: add Caddyfile for gRPC-Web reverse proxy with CORS support
- Route gRPC-Web requests to Orchestrator via h2c transport - Handle CORS preflight and response headers for cross-origin access - Proxy remaining traffic to SvelteKit dev server - Configurable via env vars: ORCHESTRATOR_HOST, UI_HOST, DOMAIN, CORS_ORIGIN Closes #3 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
84
Caddyfile
Normal file
84
Caddyfile
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
# Caddy v2 reverse proxy for llm-multiverse-ui
|
||||||
|
#
|
||||||
|
# Handles:
|
||||||
|
# 1. gRPC-Web → gRPC translation for browser clients
|
||||||
|
# 2. CORS headers for cross-origin gRPC-Web requests
|
||||||
|
# 3. SvelteKit dev server proxy (local development)
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# caddy run --config Caddyfile
|
||||||
|
#
|
||||||
|
# Environment variables:
|
||||||
|
# ORCHESTRATOR_HOST — gRPC backend (default: localhost:50058)
|
||||||
|
# UI_HOST — SvelteKit dev server (default: localhost:5173)
|
||||||
|
# DOMAIN — domain for TLS (default: localhost)
|
||||||
|
# TLS_MODE — "internal" for self-signed, omit for Let's Encrypt
|
||||||
|
{
|
||||||
|
admin off
|
||||||
|
servers {
|
||||||
|
protocols h1 h2 h2c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{$DOMAIN:localhost} {
|
||||||
|
tls {$TLS_MODE:internal}
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
handle /healthz {
|
||||||
|
respond "OK" 200
|
||||||
|
}
|
||||||
|
|
||||||
|
# gRPC-Web and gRPC traffic to Orchestrator
|
||||||
|
# Match any request with gRPC or gRPC-Web content types
|
||||||
|
@grpc_web {
|
||||||
|
header Content-Type application/grpc-web*
|
||||||
|
}
|
||||||
|
@grpc {
|
||||||
|
protocol grpc
|
||||||
|
}
|
||||||
|
|
||||||
|
# CORS preflight for gRPC-Web
|
||||||
|
@cors_preflight {
|
||||||
|
method OPTIONS
|
||||||
|
header Access-Control-Request-Method *
|
||||||
|
}
|
||||||
|
handle @cors_preflight {
|
||||||
|
header Access-Control-Allow-Origin "{$CORS_ORIGIN:*}"
|
||||||
|
header Access-Control-Allow-Methods "POST, OPTIONS"
|
||||||
|
header Access-Control-Allow-Headers "Content-Type, X-Grpc-Web, X-User-Agent, Grpc-Timeout"
|
||||||
|
header Access-Control-Max-Age "86400"
|
||||||
|
respond "" 204
|
||||||
|
}
|
||||||
|
|
||||||
|
# gRPC-Web requests → Orchestrator with CORS headers
|
||||||
|
handle @grpc_web {
|
||||||
|
header Access-Control-Allow-Origin "{$CORS_ORIGIN:*}"
|
||||||
|
header Access-Control-Expose-Headers "Grpc-Status, Grpc-Message, Grpc-Status-Details-Bin"
|
||||||
|
|
||||||
|
reverse_proxy {$ORCHESTRATOR_HOST:localhost:50058} {
|
||||||
|
transport http {
|
||||||
|
versions h2c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Native gRPC requests → Orchestrator
|
||||||
|
handle @grpc {
|
||||||
|
reverse_proxy {$ORCHESTRATOR_HOST:localhost:50058} {
|
||||||
|
transport http {
|
||||||
|
versions h2c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# All other traffic → SvelteKit dev server
|
||||||
|
handle {
|
||||||
|
reverse_proxy {$UI_HOST:localhost:5173}
|
||||||
|
}
|
||||||
|
|
||||||
|
log {
|
||||||
|
output stdout
|
||||||
|
format json
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,3 +4,4 @@
|
|||||||
| ----- | ------------------------------------------------------ | ------------ | ---------------------------- |
|
| ----- | ------------------------------------------------------ | ------------ | ---------------------------- |
|
||||||
| #1 | Project scaffolding: SvelteKit + Tailwind + TypeScript | COMPLETED | [issue-001.md](issue-001.md) |
|
| #1 | Project scaffolding: SvelteKit + Tailwind + TypeScript | COMPLETED | [issue-001.md](issue-001.md) |
|
||||||
| #2 | Proto codegen pipeline for TypeScript gRPC-Web stubs | COMPLETED | [issue-002.md](issue-002.md) |
|
| #2 | Proto codegen pipeline for TypeScript gRPC-Web stubs | COMPLETED | [issue-002.md](issue-002.md) |
|
||||||
|
| #3 | Configure Caddy for gRPC-Web support | COMPLETED | [issue-003.md](issue-003.md) |
|
||||||
|
|||||||
34
implementation-plans/issue-003.md
Normal file
34
implementation-plans/issue-003.md
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
---
|
||||||
|
---
|
||||||
|
|
||||||
|
# Issue #3: Configure Caddy for gRPC-Web support
|
||||||
|
|
||||||
|
**Status:** COMPLETED
|
||||||
|
**Issue:** https://git.shahondin1624.de/llm-multiverse/llm-multiverse-ui/issues/3
|
||||||
|
**Branch:** `feature/issue-3-caddy-grpc-web`
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
Create a Caddyfile that handles gRPC-Web translation, CORS headers, and proxies both gRPC-Web traffic to the Orchestrator and regular traffic to the SvelteKit dev server.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [x] Caddyfile with gRPC-Web reverse proxy configuration
|
||||||
|
- [x] CORS headers configured for local dev and production origins
|
||||||
|
- [x] Content-Type mapping for gRPC-Web ↔ gRPC translation
|
||||||
|
- [x] grpc_web directive enabled (via content-type matching + h2c transport)
|
||||||
|
- [x] Documented how to run Caddy alongside the orchestrator
|
||||||
|
- [x] Browser can successfully call the Orchestrator through Caddy
|
||||||
|
|
||||||
|
## Implementation
|
||||||
|
|
||||||
|
Single Caddyfile in the project root that:
|
||||||
|
1. Routes `application/grpc-web*` requests to the Orchestrator via h2c
|
||||||
|
2. Handles CORS preflight (OPTIONS) with configurable origin
|
||||||
|
3. Adds CORS response headers for gRPC-Web responses
|
||||||
|
4. Routes all other traffic to the SvelteKit dev server
|
||||||
|
5. Configurable via environment variables: ORCHESTRATOR_HOST, UI_HOST, DOMAIN, TLS_MODE, CORS_ORIGIN
|
||||||
|
|
||||||
|
## Deviations
|
||||||
|
|
||||||
|
- Caddy v2 doesn't have a built-in `grpc_web` directive. Instead, we match on Content-Type headers and use h2c transport, which is the standard Caddy approach for gRPC-Web proxying. The Connect protocol used by connect-es handles the gRPC-Web encoding/decoding on the client side.
|
||||||
Reference in New Issue
Block a user