fix(production) : prevent hydration and service failures
This commit is contained in:
@@ -36,6 +36,7 @@ export default function RootLayout({ children }: LayoutProps<"/">) {
|
|||||||
<html
|
<html
|
||||||
lang="th"
|
lang="th"
|
||||||
className={`dark ${anuphan.variable} ${geistMono.variable} h-full antialiased`}
|
className={`dark ${anuphan.variable} ${geistMono.variable} h-full antialiased`}
|
||||||
|
suppressHydrationWarning
|
||||||
>
|
>
|
||||||
<body className="flex min-h-full flex-col">
|
<body className="flex min-h-full flex-col">
|
||||||
<PageProgress />
|
<PageProgress />
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import Script from "next/script";
|
|
||||||
|
|
||||||
const clientId = process.env.NEXT_PUBLIC_GOOGLE_ADSENSE_CLIENT_ID?.trim();
|
const clientId = process.env.NEXT_PUBLIC_GOOGLE_ADSENSE_CLIENT_ID?.trim();
|
||||||
|
|
||||||
export function GoogleAdsense() {
|
export function GoogleAdsense() {
|
||||||
@@ -8,10 +6,9 @@ export function GoogleAdsense() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Script
|
<script
|
||||||
id="google-adsense"
|
async
|
||||||
src={`https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=${encodeURIComponent(clientId)}`}
|
src={`https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=${encodeURIComponent(clientId)}`}
|
||||||
strategy="lazyOnload"
|
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
+6
-1
@@ -770,7 +770,12 @@ async function executeCatalogSyncJob(initial: CatalogSyncJob, actor: AuditActor)
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getCurrentCatalogVersion(): Promise<string | null> {
|
export async function getCurrentCatalogVersion(): Promise<string | null> {
|
||||||
return (await getCurrentCatalogState())?.version ?? null;
|
try {
|
||||||
|
return (await getCurrentCatalogState())?.version ?? null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Unable to read the current catalog version", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getCurrentCatalogState() {
|
async function getCurrentCatalogState() {
|
||||||
|
|||||||
@@ -121,10 +121,15 @@ export async function getActiveCatalog() {
|
|||||||
if (!characters.length) return null;
|
if (!characters.length) return null;
|
||||||
const groupedAliases = aliasesByItem(aliases);
|
const groupedAliases = aliasesByItem(aliases);
|
||||||
const materialByKey = new Map(materials.map((item) => [item.key, item]));
|
const materialByKey = new Map(materials.map((item) => [item.key, item]));
|
||||||
const storage = await getMediaStorage();
|
let version = "unknown";
|
||||||
const { version } = catalogVersionSchema.parse(
|
try {
|
||||||
await storage.file("catalog/current.json").json(),
|
const storage = await getMediaStorage();
|
||||||
);
|
version = catalogVersionSchema.parse(
|
||||||
|
await storage.file("catalog/current.json").json(),
|
||||||
|
).version;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Unable to read the active catalog version", error);
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
version,
|
version,
|
||||||
characters: characters.map((item) => ({ ...catalogCharacterWithUrls(item, materialByKey), aliases: groupedAliases.character.get(item.key) ?? [] })),
|
characters: characters.map((item) => ({ ...catalogCharacterWithUrls(item, materialByKey), aliases: groupedAliases.character.get(item.key) ?? [] })),
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
export function securityHeaders(production: boolean) {
|
export function securityHeaders(production: boolean) {
|
||||||
const ads = "https://*.googlesyndication.com https://*.doubleclick.net https://*.googleadservices.com https://*.googletagservices.com https://www.google.com";
|
const ads = "https://*.googlesyndication.com https://*.doubleclick.net https://*.googleadservices.com https://*.googletagservices.com https://www.google.com";
|
||||||
|
const cloudflareAnalytics = "https://static.cloudflareinsights.com";
|
||||||
|
const adConnections = "https://*.adtrafficquality.google";
|
||||||
const policy = [
|
const policy = [
|
||||||
"default-src 'self'",
|
"default-src 'self'",
|
||||||
`script-src 'self' 'unsafe-inline' ${production ? "" : "'unsafe-eval'"} https://challenges.cloudflare.com ${ads}`,
|
`script-src 'self' 'unsafe-inline' ${production ? "" : "'unsafe-eval'"} https://challenges.cloudflare.com ${cloudflareAnalytics} ${ads}`,
|
||||||
"style-src 'self' 'unsafe-inline'",
|
"style-src 'self' 'unsafe-inline'",
|
||||||
`img-src 'self' data: blob: https://buzz-cdn.astrxl.dev ${ads}`,
|
`img-src 'self' data: blob: https://buzz-cdn.astrxl.dev ${ads}`,
|
||||||
"font-src 'self' data:",
|
"font-src 'self' data:",
|
||||||
`connect-src 'self' https://buzz-cdn.astrxl.dev https://challenges.cloudflare.com ${ads}${production ? "" : " ws: wss:"}`,
|
`connect-src 'self' https://buzz-cdn.astrxl.dev https://challenges.cloudflare.com ${adConnections} ${ads}${production ? "" : " ws: wss:"}`,
|
||||||
`frame-src https://challenges.cloudflare.com https://www.youtube-nocookie.com ${ads}`,
|
`frame-src https://challenges.cloudflare.com https://www.youtube-nocookie.com ${ads}`,
|
||||||
"media-src 'self' blob:",
|
"media-src 'self' blob:",
|
||||||
"worker-src 'self' blob:",
|
"worker-src 'self' blob:",
|
||||||
|
|||||||
@@ -72,6 +72,8 @@ describe("public rendering performance", () => {
|
|||||||
expect(adsense).not.toContain('from "@/lib/auth/client"');
|
expect(adsense).not.toContain('from "@/lib/auth/client"');
|
||||||
expect(adsense).toContain('process.env.NODE_ENV !== "production"');
|
expect(adsense).toContain('process.env.NODE_ENV !== "production"');
|
||||||
expect(adsense).toContain("pagead2.googlesyndication.com");
|
expect(adsense).toContain("pagead2.googlesyndication.com");
|
||||||
|
expect(adsense).toContain("<script");
|
||||||
|
expect(adsense).not.toContain('from "next/script"');
|
||||||
expect(home).toContain("<GoogleAdsense />");
|
expect(home).toContain("<GoogleAdsense />");
|
||||||
expect(guide).toContain("<GoogleAdsense />");
|
expect(guide).toContain("<GoogleAdsense />");
|
||||||
expect(guideSection).toContain("<GoogleAdsense />");
|
expect(guideSection).toContain("<GoogleAdsense />");
|
||||||
@@ -79,4 +81,17 @@ describe("public rendering performance", () => {
|
|||||||
"google.com, pub-9687404323559597, DIRECT, f08c47fec0942fa0",
|
"google.com, pub-9687404323559597, DIRECT, f08c47fec0942fa0",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("allows the production analytics and advertising endpoints", async () => {
|
||||||
|
const headers = await repositoryFile("lib/security/headers.ts");
|
||||||
|
|
||||||
|
expect(headers).toContain("https://static.cloudflareinsights.com");
|
||||||
|
expect(headers).toContain("https://*.adtrafficquality.google");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("accepts Next.js deployment metadata on the document root", async () => {
|
||||||
|
const layout = await repositoryFile("app/layout.tsx");
|
||||||
|
|
||||||
|
expect(layout).toContain("suppressHydrationWarning");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user