# SNAPSHOT (read-only) of the live Caddy config.
# Live source of truth: /home/jay/srv/caddy/caddy-config/Caddyfile (mounted into the 'caddy' container).
# Captured so the upbeatbytes try_files {path} {path}.html change is tracked. Do not edit here expecting it to deploy.

{
	email thejayman77@gmail.com
}

tjm77.com, www.tjm77.com {
	tls {
		dns cloudflare {env.CF_API_TOKEN}
	}
	root * /srv/sites/tjm77
	file_server
	encode gzip zstd
	log {
		output file /data/access-tjm77.log
	}
}

jsj-designs.com, www.jsj-designs.com {
	tls {
		dns cloudflare {env.CF_API_TOKEN}
	}
	root * /srv/sites/jsj
	file_server
	encode gzip zstd
	log {
		output file /data/access-jsj.log
	}
}

# Canonical host = apex. www redirects to it BEFORE the app, so OAuth always
# starts from the same host its callback uses (the ub_oauth cookie is host-only;
# starting from www then bouncing to the apex callback loses it → error=google).
www.upbeatbytes.com {
	tls {
		dns cloudflare {env.CF_API_TOKEN}
	}
	redir https://upbeatbytes.com{uri} permanent
}

upbeatbytes.com {
	tls {
		dns cloudflare {env.CF_API_TOKEN}
	}
	encode gzip zstd

	# Dynamic API + server-rendered pages (share, digest, sitemap) → FastAPI.
	@api path /api/* /healthz /docs /docs/* /openapi.json /a/* /today /sitemap.xml
	handle @api {
		reverse_proxy upbeatbytes-api:8000
	}

	# Everything else → the static SvelteKit SPA. try_files falls back to
	# index.html so deep client routes (e.g. /auth/verify) boot the app
	# instead of 404ing.
	handle {
		root * /srv/sites/upbeatbytes

		# Hidden in-progress prototypes — keep crawlers out at the HTTP level (the JS
		# <meta robots> isn't seen by non-JS bots since the static shell is generic).
		@hidden path /home2 /home3 /word /word.html /quote /quote.html /onthisday /onthisday.html /admin /admin.html
		header @hidden X-Robots-Tag "noindex, nofollow"

		# Content-hashed assets never change for a given URL — cache them forever.
		@immutable path /_app/immutable/*
		header @immutable Cache-Control "public, max-age=31536000, immutable"

		# Static texture + font assets — large/unchanging. Cache them forever like
		# immutable assets; rename the file if one ever changes.
		@assets path /textures/* /fonts/*
		header @assets Cache-Control "public, max-age=31536000, immutable"

		# The SPA shell: "/" and extensionless client routes (try_files → index.html).
		# Briefly cacheable at the CDN edge (s-maxage) so a first paint never depends
		# on this origin's uplink; browsers still revalidate every visit (max-age=0).
		# A deploy propagates within ≤2min and old immutable chunks are kept for a
		# 14-day grace window, so a briefly-stale shell still boots cleanly.
		# (Requires a Cloudflare Cache Rule marking these paths eligible — CF does
		# not cache HTML by default.)
		@shell {
			not path /_app/immutable/*
			not path *.*
		}
		header @shell Cache-Control "public, max-age=0, s-maxage=120, stale-while-revalidate=600"

		# Mutable FILES (service worker, version manifest, webmanifest, word lists,
		# icons) must revalidate every time — a pinned stale service worker is the
		# classic blank-screen cause behind a CDN.
		@revalidate {
			not path /_app/immutable/*
			not path /textures/*
			not path /fonts/*
			path *.*
		}
		header @revalidate Cache-Control "no-cache"

		# Serve a route's own prerendered HTML when it exists (e.g. /play -> play.html,
		# which carries its own canonical/OG metadata), else fall back to the SPA shell.
		# Cache-Control matchers above run on the ORIGINAL extensionless path, so /play
		# still gets the @shell header before this rewrite.
		try_files {path} {path}.html /index.html
		file_server
	}

	log {
		output file /data/access-upbeatbytes.log
	}
}

git.tjm77.com {
	tls {
		dns cloudflare {env.CF_API_TOKEN}
	}
	reverse_proxy gitea:3000
	encode gzip zstd
	log {
		output file /data/access-gitea.log
	}
}

api.tjm77.com {
	tls {
		dns cloudflare {env.CF_API_TOKEN}
	}
	encode gzip zstd
	# Recipe finder (What can I make? web tier) — must precede the arbiter catch-all.
	@finder path /v1/find-recipes /v1/find-recipes/*
	handle @finder {
		reverse_proxy finder:8090
	}
	# Per-device token registration → auth service.
	@register path /v1/register
	handle @register {
		reverse_proxy auth:8070
	}
	# In-app feedback relay → auth service (validates the device token + SMTP-sends).
	# Keeps the arbiter a pure LLM gateway. Must precede the arbiter catch-all.
	@feedback path /v1/feedback
	handle @feedback {
		reverse_proxy auth:8070
	}
	# App update policy — public static JSON (no secrets). Edit ~/srv/sites/api/app-version.json
	# to change what testers are prompted to update to; no redeploy needed. Precedes the catch-all.
	@appversion path /v1/app-version
	handle @appversion {
		root * /srv/sites
		rewrite * /api/app-version.json
		header Content-Type application/json
		file_server
	}
	# LLM Arbiter (everything else); Bearer auth enforced by the arbiter.
	handle {
		reverse_proxy arbiter:8080
	}
	log {
		output file /data/access-arbiter.log
	}
}
