fix: merge hooks.ts into hooks.server.ts — root cause of K.resolve hydration crash

hooks.ts contained Node-only imports (fs, path) and was being included in
the client bundle by SvelteKit because it lacked the .server. designation.
This caused 'K.resolve is not a function' in the browser, which aborted
hydration and killed ALL client-side interactivity site-wide.

Fix: merge reroute export from hooks.ts into hooks.server.ts alongside the
existing CSRF handle. Delete hooks.ts. SvelteKit supports both handle and
reroute exports from hooks.server.ts.

This was the true root cause of today's outage. The CSS and  issues
were red herrings — the site was broken from commit 231eacc onward.
This commit is contained in:
Falkan
2026-03-20 15:10:14 -04:00
parent 4d2d7c77d3
commit 2a4a1fb61d
2 changed files with 55 additions and 54 deletions

View File

@@ -1,9 +1,61 @@
import { type Handle } from '@sveltejs/kit';
import fs from 'fs';
import path from 'path';
import { type Handle, type Reroute } from '@sveltejs/kit';
import { env } from '$env/dynamic/private';
// ── Admin path rerouting ────────────────────────────────────────────────────
const CONFIG_PATH = path.resolve('data', 'config.json');
// Cache adminPath with a 5-second TTL so changes take effect quickly
// but we don't hit disk on every request.
let cachedAdminPath = 'admin';
let cacheExpiry = 0;
function getAdminPath(): string {
const now = Date.now();
if (now < cacheExpiry) return cachedAdminPath;
try {
const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'));
cachedAdminPath = (config.adminPath ?? 'admin').replace(/^\/|\/$/g, '') || 'admin';
} catch {
// config.json missing or unreadable — keep current cached value
}
cacheExpiry = now + 5000;
return cachedAdminPath;
}
export { getAdminPath as adminPath };
/**
* Rewrites /<adminPath>/... to /admin/... before SvelteKit resolves the route.
* When adminPath is not 'admin', direct access to /admin is rewritten to /404
* so attackers cannot enumerate the admin URL.
*/
export const reroute: Reroute = ({ url }) => {
const adminPath = getAdminPath();
const prefix = `/${adminPath}`;
// Public path → internal /admin
if (url.pathname === prefix || url.pathname.startsWith(prefix + '/')) {
return '/admin' + url.pathname.slice(prefix.length);
}
// Block direct /admin access when a custom path is configured
if (adminPath !== 'admin') {
if (url.pathname === '/admin' || url.pathname.startsWith('/admin/')) {
return '/404';
}
}
return url.pathname;
};
// ── CSRF origin allowlist ───────────────────────────────────────────────────
/**
* CSRF origin allowlist.
*
* Production: set ALLOWED_ORIGINS to a comma-separated list of trusted origins.
* e.g. ALLOWED_ORIGINS=https://humorunits.com,https://dickloads.com
*

View File

@@ -1,51 +0,0 @@
import fs from 'fs';
import path from 'path';
import type { Reroute } from '@sveltejs/kit';
const CONFIG_PATH = path.resolve('data', 'config.json');
// Cache adminPath with a 5-second TTL so changes take effect quickly
// but we don't hit disk on every request.
let cachedAdminPath = 'admin';
let cacheExpiry = 0;
function getAdminPath(): string {
const now = Date.now();
if (now < cacheExpiry) return cachedAdminPath;
try {
const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'));
cachedAdminPath = (config.adminPath ?? 'admin').replace(/^\/|\/$/g, '') || 'admin';
} catch {
// config.json missing or unreadable — keep current cached value
}
cacheExpiry = now + 5000;
return cachedAdminPath;
}
export { getAdminPath as adminPath };
/**
* Rewrites /<adminPath>/... to /admin/... before SvelteKit resolves the route.
* When adminPath is not 'admin', direct access to /admin is rewritten to /404
* so attackers cannot enumerate the admin URL.
*/
export const reroute: Reroute = ({ url }) => {
const adminPath = getAdminPath();
const prefix = `/${adminPath}`;
// Public path → internal /admin
if (url.pathname === prefix || url.pathname.startsWith(prefix + '/')) {
return '/admin' + url.pathname.slice(prefix.length);
}
// Block direct /admin access when a custom path is configured
if (adminPath !== 'admin') {
if (url.pathname === '/admin' || url.pathname.startsWith('/admin/')) {
return '/404';
}
}
return url.pathname;
};