# elevenlabs.io/Caddyfile
# Outbound API gateway. Two listeners, one shared route set:
#   :8080  — plain HTTP (published as 8082 on the host, legacy clients)
#   {$DOMAIN} — HTTPS 443 with automatic Let's Encrypt certificates
{
	email {$ACME_EMAIL}
	admin off

	# The error log dumps request headers, and Caddy redacts only
	# Authorization/Cookie on its own: X-Proxy-Token used to land in
	# `docker logs` in clear text on every failed upstream call.
	log default {
		format filter {
			wrap json
			fields {
				request>headers>X-Proxy-Token delete
			}
		}
	}
}

# --- Access control (generated by docker-entrypoint.sh from env) ---
# matchers.caddy: @denied_ip / @bad_token definitions (empty when disabled)
# guard.caddy:    matching respond directives (empty when disabled)
# scrub_extra.caddy: strips the optional auth key header from upstream requests

# --- Shared upstream request hygiene ---
(scrub) {
	header_up Host {args[0]}
	header_up -X-Forwarded-For
	header_up -X-Forwarded-Proto
	header_up -X-Forwarded-Host
	header_up -X-Real-IP
	header_up -True-Client-IP
	header_up -CF-Connecting-IP
	header_up -X-Client-IP
	header_up -Forwarded
	header_up -Via
	header_up -X-Proxy-Token
	import /etc/caddy/scrub_extra.caddy
}

# (api) <prefix> <upstream-host> <upstream-path-prefix>
(api) {
	handle_path /{args[0]}/* {
		rewrite * {args[2]}{uri}
		reverse_proxy https://{args[1]} {
			import scrub {args[1]}
			flush_interval -1
			transport http {
				dial_timeout 15s
				response_header_timeout 300s
				read_timeout 300s
			}
		}
	}
}

# --- Proxy-originated errors ---
# Errors produced by the proxy itself (the upstream answered nothing) are
# marked with X-Proxy-Error so that a client with several proxies can tell
# them apart from an upstream's own 5xx and fail over:
#   dial     — the upstream was never reached (DNS, connect, no route): the
#              request did not leave this host, retrying elsewhere is safe
#              even for non-idempotent calls
#   upstream — anything else (timeouts, broken responses): the upstream may
#              have processed the request
# X-Proxy-Error-Detail carries the reason. Only clients that passed the
# guards ever get here (the guards respond directly, without an error).
# Note: handled errors are logged by Caddy at DEBUG level only — the reason
# is visible to the client via X-Proxy-Error-Detail.
(proxy_errors) {
	handle_errors {
		@dial `{err.message}.startsWith("dial ")`
		handle @dial {
			header X-Proxy-Error dial
			header X-Proxy-Error-Detail "{err.message}"
			respond `{"error":"proxy_dial_failed"}` {err.status_code}
		}
		handle {
			header X-Proxy-Error upstream
			header X-Proxy-Error-Detail "{err.message}"
			respond `{"error":"proxy_upstream_failed"}` {err.status_code}
		}
	}
}

(routes) {
	import /etc/caddy/matchers.caddy
	import proxy_errors

	route {
		# Health check — no auth, evaluated before the guards
		respond /health `{"status":"ok","version":"2.0"}` 200

		import /etc/caddy/guard.caddy

		import api elevenlabs api.elevenlabs.io ""
		import api openai api.openai.com ""
		import api telegram api.telegram.org ""
		import api deepseek api.deepseek.com ""
		import api openrouter openrouter.ai /api/v1

		respond `{"error":"unknown_upstream","hint":"use /elevenlabs/, /openai/, /telegram/, /openrouter/ or /deepseek/"}` 404
	}
}

{$DOMAIN} {
	import routes
}

:8080 {
	import routes
}
