# ─── NovaHelm Reverse Proxy ─────────────────────────────────────────
# Caddy handles TLS termination, rate limiting, and routes traffic to
# internal services. Generated by `novahelm infra scaffold`.
#
# TLS Modes:
#   letsencrypt — automatic certs via ACME (default for domain names)
#   cloudflare  — DNS challenge via Cloudflare API token
#   custom      — user-provided cert/key files
#   off         — no TLS (Cloudflare Tunnel or local dev)
#
# NOTE: Let's Encrypt cannot issue certs for IP addresses. If DOMAIN is
# an IP (e.g., 192.168.1.100), set TLS_MODE=off or use TLS_MODE=custom.
#
# Docs: https://caddyserver.com/docs/

{
	# Disable admin API — prevents runtime config changes without redeploy.
	# If you need to reload config without restart, set to "localhost:2019".
	admin off

	# Rate limit ordering — must come before reverse_proxy
	# Requires caddy-rate-limit plugin (included in custom Caddy build)
	order rate_limit before reverse_proxy
}

{$DOMAIN:localhost} {
	# ─── TLS Configuration ────────────────────────────────────────
	# Default: automatic Let's Encrypt. Override via TLS_MODE env var.
	#
	# For Cloudflare DNS challenge, uncomment and set CF_API_TOKEN:
	#   tls {
	#     dns cloudflare {env.CF_API_TOKEN}
	#   }
	#
	# For custom certs:
	#   tls /etc/caddy/certs/cert.pem /etc/caddy/certs/key.pem
	#
	# For no TLS (behind Cloudflare Tunnel or local dev):
	#   Caddy auto-detects localhost and skips TLS.

	# ─── Auth Routes (strict: 20 req/min per IP) ────────────
	handle /api/auth/* {
		rate_limit {
			zone auth_zone {
				key {remote_host}
				events 20
				window 1m
			}
		}
		reverse_proxy web:{$WEB_PORT:3000} {
			transport http {
				read_timeout 300s
				write_timeout 300s
			}
		}
	}

	# ─── API Server (NovaRPC, 200 req/min per IP) ────────
	handle /api/* {
		rate_limit {
			zone api_zone {
				key {remote_host}
				events 200
				window 1m
			}
		}
		reverse_proxy web:{$WEB_PORT:3000} {
			# Allow large file uploads (matches storage.maxUploadSizeMb default)
			transport http {
				read_timeout 300s
				write_timeout 300s
			}
		}
	}

	# Legacy /trpc/* route — returns 410 Gone
	handle /trpc/* {
		respond "tRPC has been replaced by NovaRPC at /api/v1" 410
	}

	# ─── Realtime (Socket.io WebSocket — no rate limit) ──────
	handle /realtime/* {
		reverse_proxy realtime:{$REALTIME_PORT:3001} {
			# Disable response buffering for WebSocket/SSE streams
			flush_interval -1
		}
	}

	handle /socket.io/* {
		reverse_proxy realtime:{$REALTIME_PORT:3001} {
			flush_interval -1
		}
	}

	# ─── Storage (MinIO S3 API, 100 req/min per IP) ──────────
	handle /storage/* {
		rate_limit {
			zone storage_zone {
				key {remote_host}
				events 100
				window 1m
			}
		}
		reverse_proxy minio:9000 {
			transport http {
				read_timeout 300s
				write_timeout 300s
			}
		}
	}

	# ─── Console (Admin Dashboard — no rate limit) ───────────
	handle /console/* {
		uri strip_prefix /console
		reverse_proxy console:{$CONSOLE_PORT:3100}
	}

	# ─── Health Endpoint (no rate limit) ─────────────────────
	handle /health {
		respond "OK" 200
	}

	# ─── Default — Web App (200 req/min per IP) ──────────────
	handle {
		rate_limit {
			zone default_zone {
				key {remote_host}
				events 200
				window 1m
			}
		}
		reverse_proxy web:{$WEB_PORT:3000}
	}

	# ─── JWT Defense-in-Depth (optional) ─────────────────
	# Uncomment to reject requests without auth headers at the gateway layer.
	# This blocks obviously unauthenticated traffic before it hits the app
	# server. Only enable if ALL your /api/* endpoints require auth (no
	# public procedures). Fastify handles auth regardless — this is extra.
	#
	# @no_auth {
	# 	not header Authorization *
	# 	not header Cookie *
	# 	path /api/*
	# }
	# handle @no_auth {
	# 	respond "Unauthorized" 401
	# }

	# ─── Security Headers ────────────────────────────────────
	header {
		X-Content-Type-Options "nosniff"
		X-Frame-Options "DENY"
		Referrer-Policy "strict-origin-when-cross-origin"
		-Server
	}

	# ─── Access Logging ──────────────────────────────────────
	# Logs are captured by Vector via Docker stdout.
	# Authorization headers are stripped to prevent token leakage.
	log {
		output stdout
		format json {
			fields {
				request>headers>Authorization delete
				request>headers>Cookie delete
			}
		}
	}
}
