import crypto from 'crypto'; import type { HtmlProps } from 'next/dist/shared/lib/utils'; import Document, { Html, Head, Main, NextScript } from 'next/document'; import { ANALYTICS_CSP } from '@/data/csp'; import { IS_DEV, IS_PROD } from '@/utils/environment'; const favicon = IS_DEV ? '/svg/favicon-dev.svg' : '/svg/favicon.svg'; const cspHashOf = (text) => { const hash = crypto.createHash('sha256'); hash.update(text); return `sha256-${hash.digest('base64')}`; }; // See: https://github.com/vercel/next.js/blob/master/examples/with-strict-csp/pages/_document.js const getCSPContent = (context: Readonly) => { const cspInlineScriptHash = cspHashOf( NextScript.getInlineScriptSource(context) ); // Dev environment if (!IS_PROD) { return `default-src 'self'; style-src 'self' 'unsafe-inline'; font-src 'self' data:; frame-src 'self' ${ANALYTICS_CSP.all.frame.join( ' ' )} *.youtube.com https://snack.expo.dev; img-src 'self' blob: ${ANALYTICS_CSP.all.img.join(' ')}; connect-src 'self' *.shortbread.aws.dev ${ANALYTICS_CSP.all.connect.join( ' ' )} https://*.algolia.net https://*.algolianet.com https://cdn.jsdelivr.net https://tfhub.dev https://storage.googleapis.com; script-src 'unsafe-eval' 'self' '${cspInlineScriptHash}' ${ANALYTICS_CSP.all.script.join( ' ' )}; `; } // Prod environment return `default-src 'self'; style-src 'self' 'unsafe-inline'; font-src 'self'; frame-src 'self' ${[ ...ANALYTICS_CSP.all.frame, ...ANALYTICS_CSP.prod.frame, ].join(' ')} *.youtube.com https://snack.expo.dev; img-src 'self' blob: ${[ ...ANALYTICS_CSP.all.img, ...ANALYTICS_CSP.prod.img, ].join(' ')}; connect-src 'self' *.shortbread.aws.dev ${[ ...ANALYTICS_CSP.all.connect, ...ANALYTICS_CSP.prod.connect, ].join(' ')} https://*.algolia.net https://*.algolianet.com; script-src 'unsafe-eval' 'self' '${cspInlineScriptHash}' ${ANALYTICS_CSP.all.script.join( ' ' )}; `; }; class MyDocument extends Document { render() { return (
); } } export default MyDocument;