const VOICEMAIL_XML = `
Luke at the Roost is off the air right now. Leave a message after the beep and we may play it on the next show!
Thank you for calling. Goodbye!
`;
export default {
async fetch(request, env) {
const url = new URL(request.url);
if (url.pathname === "/api/signalwire/voice") {
try {
const body = await request.text();
const resp = await fetch("https://radioshow.macneilmediagroup.com/api/signalwire/voice", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: body,
signal: AbortSignal.timeout(5000),
});
if (resp.ok) {
return new Response(await resp.text(), {
status: 200,
headers: { "Content-Type": "application/xml" },
});
}
} catch (e) {
// Server unreachable or timed out
}
return new Response(VOICEMAIL_XML, {
status: 200,
headers: { "Content-Type": "application/xml" },
});
}
// RSS feed proxy
if (url.pathname === "/feed") {
try {
const resp = await fetch("https://podcast.macneilmediagroup.com/@LukeAtTheRoost/feed.xml", {
signal: AbortSignal.timeout(8000),
});
if (resp.ok) {
return new Response(await resp.text(), {
status: 200,
headers: {
"Content-Type": "application/xml",
"Access-Control-Allow-Origin": "*",
"Cache-Control": "public, max-age=300",
},
});
}
} catch (e) {
// Castopod unreachable
}
return new Response("Feed unavailable", { status: 502 });
}
// Umami analytics proxy (bypass ad blockers)
if (url.pathname === "/api/send" && request.method === "POST") {
const body = await request.text();
const resp = await fetch("https://plausible.macneilmediagroup.com/api/send", {
method: "POST",
headers: {
"Content-Type": "application/json",
"User-Agent": request.headers.get("User-Agent") || "",
"X-Forwarded-For": request.headers.get("CF-Connecting-IP") || request.headers.get("X-Forwarded-For") || "",
},
body,
});
return new Response(resp.body, {
status: resp.status,
headers: { "Content-Type": resp.headers.get("Content-Type") || "text/plain" },
});
}
if (url.pathname === "/p/script") {
const resp = await fetch("https://plausible.macneilmediagroup.com/script.js");
return new Response(await resp.text(), {
headers: {
"Content-Type": "application/javascript",
"Cache-Control": "public, max-age=86400",
},
});
}
if (url.pathname === "/p/event" && request.method === "POST") {
const body = await request.text();
const resp = await fetch("https://plausible.macneilmediagroup.com/api/send", {
method: "POST",
headers: {
"Content-Type": "application/json",
"User-Agent": request.headers.get("User-Agent") || "",
"X-Forwarded-For": request.headers.get("CF-Connecting-IP") || request.headers.get("X-Forwarded-For") || "",
},
body,
});
return new Response(resp.body, {
status: resp.status,
headers: { "Content-Type": resp.headers.get("Content-Type") || "text/plain" },
});
}
// Legacy query-param episode URLs -> clean static paths.
//
// This replaces the old user-agent-gated meta injection, which rewrote
//
/og: tags only for a hardcoded list of social crawlers. Googlebot
// was never on that list, so search engines only ever saw the generic
// shell — and serving crawlers different HTML than users is cloaking.
// Episode pages are static now, so nothing needs UA sniffing.
//
// Published YouTube descriptions and social posts still point at the old
// form, so this redirect has to stay indefinitely.
if (url.pathname === "/episode.html" && url.searchParams.get("slug")) {
// The slug lands in a Location header. Restricting it to the character
// set real slugs use prevents CRLF injection and open-redirect via a
// crafted ?slug= (e.g. "//evil.com" or a value containing %0d%0a).
const slug = url.searchParams.get("slug").replace(/[^a-z0-9-]/gi, "");
if (slug) {
return Response.redirect(`https://lukeattheroost.com/episode/${slug}/`, 301);
}
}
// All other requests — serve static assets
return env.ASSETS.fetch(request);
},
};