summaryrefslogtreecommitdiff
path: root/packages/integrations/netlify/src/ssr-function.ts
blob: 33aea271719df2637b28bbffd4d864ade16de89d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import type { Context } from '@netlify/functions';
import type { SSRManifest } from 'astro';
import { App } from 'astro/app';
import { applyPolyfills } from 'astro/app/node';

applyPolyfills();

// biome-ignore lint/complexity/noBannedTypes: safe to use in this case
export type Args = {};

const clientAddressSymbol = Symbol.for('astro.clientAddress');

export const createExports = (manifest: SSRManifest, _args: Args) => {
	const app = new App(manifest);

	function createHandler(integrationConfig: { cacheOnDemandPages: boolean, notFoundContent?: string }) {
		return async function handler(request: Request, context: Context) {
			const routeData = app.match(request);
			if (!routeData && typeof integrationConfig.notFoundContent !== 'undefined') {
				return new Response(integrationConfig.notFoundContent, { status: 404 });
			}

			Reflect.set(request, clientAddressSymbol, context.ip);
			let locals: Record<string, unknown> = {};

			const astroLocalsHeader = request.headers.get('x-astro-locals');
			if (astroLocalsHeader) {
				locals = JSON.parse(astroLocalsHeader);
			}

			locals.netlify = { context };

			const response = await app.render(request, { routeData, locals });

			if (app.setCookieHeaders) {
				for (const setCookieHeader of app.setCookieHeaders(response)) {
					response.headers.append('Set-Cookie', setCookieHeader);
				}
			}

			if (integrationConfig.cacheOnDemandPages) {
				const isCacheableMethod = ['GET', 'HEAD'].includes(request.method);

				// any user-provided Cache-Control headers take precedence
				const hasCacheControl = [
					'Cache-Control',
					'CDN-Cache-Control',
					'Netlify-CDN-Cache-Control',
				].some((header) => response.headers.has(header));

				if (isCacheableMethod && !hasCacheControl) {
					// caches this page for up to a year
					response.headers.append('CDN-Cache-Control', 'public, max-age=31536000, must-revalidate');
				}
			}

			return response;
		};
	}

	return { default: createHandler };
};