Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion src/lib/cloudflare/public-ssr-gateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,19 @@ export type PublicSsrLocale = "en-us" | "zh-cn";
const STATIC_PUBLIC_PATHS = new Set([
"/catalog/bus/map",
"/guides/markdown-support",
"/api-docs",
"/mobile-app",
"/privacy",
"/terms",
]);
const STATIC_PUBLIC_ROOTS = ["/api/docs"];
const DIRECT_REQUEST_PATHS = new Set([
"/",
"/error",
"/llms.txt",
"/robots.txt",
"/sitemap.xml",
]);

const CATALOG_QUERY_KEYS: Record<string, ReadonlySet<string>> = {
"/catalog/courses": new Set([
Expand Down Expand Up @@ -50,6 +58,7 @@ const DYNAMIC_OR_PRIVATE_ROOTS = [
"/api",
"/catalog",
"/community",
"/e2e",
"/oauth",
"/workspace",
];
Expand Down Expand Up @@ -92,7 +101,7 @@ export function resolvePublicSsrMode(request: Request): PublicSsrMode | null {
}

if (
url.pathname === "/" ||
DIRECT_REQUEST_PATHS.has(url.pathname) ||
url.pathname.startsWith("/_app/") ||
url.pathname.startsWith("/.well-known/") ||
DYNAMIC_OR_PRIVATE_ROOTS.some((root) => matchesPathRoot(url.pathname, root))
Expand Down
26 changes: 26 additions & 0 deletions src/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {
resolvePublicSsrLocale,
resolvePublicSsrMode,
} from "./lib/cloudflare/public-ssr-gateway";
import { buildContentSecurityPolicy } from "./lib/security/csp";
import { CONTENT_SIGNAL } from "./lib/seo/content-signal";

const app = svelteKitWorker;

Expand Down Expand Up @@ -53,6 +55,27 @@ function createNonce() {
return btoa(String.fromCharCode(...bytes));
}

function publicNotFoundResponse(locale, headRequest) {
const body = buildPublicNotFoundHtml(locale);
return new Response(headRequest ? null : body, {
status: 404,
headers: {
"Cache-Control": "private, no-store",
"Cloudflare-CDN-Cache-Control": "no-store",
"Content-Language": locale,
"Content-Security-Policy": buildContentSecurityPolicy(createNonce()),
"Content-Signal": CONTENT_SIGNAL,
"Content-Type": "text/html; charset=utf-8",
"Permissions-Policy": "camera=(), microphone=(), geolocation=()",
"Referrer-Policy": "strict-origin-when-cross-origin",
Vary: "Accept-Language, Cookie",
"X-Content-Type-Options": "nosniff",
"X-Frame-Options": "SAMEORIGIN",
"x-request-id": crypto.randomUUID(),
},
});
}

class ScriptNonceRewriter {
constructor(nonce) {
this.nonce = nonce;
Expand Down Expand Up @@ -164,6 +187,9 @@ export default {
if (!mode) return app.fetch(directRequest(request), env, context);

const locale = resolvePublicSsrLocale(request);
if (mode === "not-found") {
return publicNotFoundResponse(locale, request.method === "HEAD");
}
const cachedRequest = publicSsrRequest(request, mode, locale);
const cacheUrl = new URL(cachedRequest.url);
const response = await context.exports
Expand Down
6 changes: 6 additions & 0 deletions tests/unit/public-ssr-gateway.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ describe("public SSR gateway", () => {
"/catalog/sections?semesterId=301&teacher=Li",
"/catalog/teachers?departmentId=1",
"/catalog/bus/map",
"/api-docs",
"/api/docs/rest/catalog",
"/mobile-app",
"/privacy",
Expand All @@ -35,6 +36,11 @@ describe("public SSR gateway", () => {
"/catalog/courses?unknown=value",
"/catalog/courses?__life_locale=en-us",
"/community/users/example",
"/e2e/oauth/callback?code=example&state=test",
"/error?error=access_denied",
"/llms.txt",
"/robots.txt",
"/sitemap.xml",
"/workspace/overview",
])("bypasses private or mixed route %s", (path) => {
expect(resolvePublicSsrMode(request(path))).toBeNull();
Expand Down