Skip to content

Commit 06365a7

Browse files
Merge pull request #1905 from appwrite/fix-redirects
add back hooks
2 parents 104b536 + 496ed81 commit 06365a7

File tree

1 file changed

+126
-0
lines changed

1 file changed

+126
-0
lines changed

src/hooks.server.ts

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import type { Handle } from '@sveltejs/kit';
2+
import redirects from './redirects.json';
3+
import { sequence } from '@sveltejs/kit/hooks';
4+
import { BANNER_KEY } from '$lib/constants';
5+
import { dev } from '$app/environment';
6+
7+
const redirectMap = new Map(redirects.map(({ link, redirect }) => [link, redirect]));
8+
9+
const redirecter: Handle = async ({ event, resolve }) => {
10+
const currentPath = event.url.pathname;
11+
if (redirectMap.has(currentPath)) {
12+
return new Response(null, {
13+
status: 308,
14+
headers: {
15+
location: redirectMap.get(currentPath) ?? ''
16+
}
17+
});
18+
}
19+
20+
return await resolve(event);
21+
};
22+
23+
const securityheaders: Handle = async ({ event, resolve }) => {
24+
const nonce = Buffer.from(crypto.randomUUID()).toString('base64');
25+
(event.locals as { nonce: string }).nonce = nonce;
26+
27+
const response = await resolve(event, {
28+
transformPageChunk: ({ html }) => {
29+
return html.replace(/%sveltekit.nonce%/g, nonce);
30+
}
31+
});
32+
33+
// `true` if deployed via Coolify.
34+
const isPreview = !!process.env.COOLIFY_FQDN;
35+
// COOLIFY_FQDN already includes `http`.
36+
const previewDomain = isPreview ? `${process.env.COOLIFY_FQDN}` : null;
37+
const join = (arr: string[]) => arr.join(' ');
38+
39+
const cspDirectives: Record<string, string> = {
40+
'default-src': "'self'",
41+
'script-src': join([
42+
"'self'",
43+
'blob:',
44+
"'unsafe-inline'",
45+
"'unsafe-eval'",
46+
'https://*.posthog.com',
47+
'https://*.plausible.io',
48+
'https://*.reo.dev',
49+
'https://plausible.io',
50+
'https://js.zi-scripts.com',
51+
'https://ws.zoominfo.com'
52+
]),
53+
'style-src': "'self' 'unsafe-inline'",
54+
'img-src': "'self' data: https:",
55+
'font-src': "'self'",
56+
'object-src': "'none'",
57+
'base-uri': "'self'",
58+
'form-action': "'self'",
59+
'frame-ancestors': join(["'self'", 'https://www.youtube.com', 'https://*.vimeo.com']),
60+
'block-all-mixed-content': '',
61+
'upgrade-insecure-requests': '',
62+
'connect-src': join([
63+
"'self'",
64+
'https://*.appwrite.io',
65+
'https://*.appwrite.org',
66+
'https://*.posthog.com',
67+
'https://*.sentry.io',
68+
'https://*.plausible.io',
69+
'https://plausible.io',
70+
'https://*.reo.dev',
71+
'https://js.zi-scripts.com',
72+
'https://aorta.clickagy.com',
73+
'https://hemsync.clickagy.com',
74+
'https://ws.zoominfo.com '
75+
]),
76+
'frame-src': join([
77+
"'self'",
78+
'https://www.youtube.com',
79+
'https://status.appwrite.online',
80+
'https://www.youtube-nocookie.com',
81+
'https://player.vimeo.com',
82+
'https://hemsync.clickagy.com'
83+
])
84+
};
85+
86+
if (isPreview) {
87+
delete cspDirectives['block-all-mixed-content'];
88+
delete cspDirectives['upgrade-insecure-requests'];
89+
['default-src', 'script-src', 'style-src', 'img-src', 'font-src', 'connect-src'].forEach(
90+
(key) => {
91+
cspDirectives[key] += ` ${previewDomain}`;
92+
}
93+
);
94+
}
95+
96+
const cspDirectivesString = Object.entries(cspDirectives)
97+
.map(([key, value]) => `${key} ${value}`.trim())
98+
.join('; ');
99+
100+
// Set security headers
101+
response.headers.set('Content-Security-Policy', cspDirectivesString);
102+
103+
// HTTP Strict Transport Security
104+
// max-age is set to 1 year in seconds
105+
response.headers.set(
106+
'Strict-Transport-Security',
107+
'max-age=31536000; includeSubDomains; preload'
108+
);
109+
110+
// X-Content-Type-Options
111+
response.headers.set('X-Content-Type-Options', 'nosniff');
112+
113+
// X-Frame-Options
114+
response.headers.set('X-Frame-Options', 'DENY');
115+
116+
return response;
117+
};
118+
119+
const bannerRewriter: Handle = async ({ event, resolve }) => {
120+
const response = await resolve(event, {
121+
transformPageChunk: ({ html }) => html.replace('%aw_banner_key%', BANNER_KEY)
122+
});
123+
return response;
124+
};
125+
126+
export const handle = sequence(redirecter, bannerRewriter, securityheaders);

0 commit comments

Comments
 (0)