fix(routes): add pre-hydration auth redirects to all protected pages

Replaced slow $effect-based auth checks with synchronous IIFE scripts in <svelte:head> that run during HTML parsing, before Svelte hydration. Covers: /today, /admin, /admin/schedule, /manage, /notifications (admin-only), and /account (auth-required). All redirect via window.location.replace() with no page flash.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-06-04 20:04:54 +01:00
co-authored by Sisyphus
parent 413b28b759
commit 24b1823a5d
6 changed files with 80 additions and 0 deletions
+10
View File
@@ -766,6 +766,16 @@
</script>
<svelte:head>
<script>
(function() {
try {
var token = localStorage.getItem('authToken');
if (!token) { window.location.replace('/login'); return; }
var payload = JSON.parse(atob(token.split('.')[1]));
if (payload.exp * 1000 <= Date.now()) { window.location.replace('/login'); }
} catch(e) { window.location.replace('/login'); }
})();
</script>
<style>
:root {
--bgColorMenu: #1d1d27;
+14
View File
@@ -66,6 +66,20 @@
}
</script>
<svelte:head>
<script>
(function() {
try {
var token = localStorage.getItem('authToken');
if (!token) { window.location.replace('/login'); return; }
var payload = JSON.parse(atob(token.split('.')[1]));
if (payload.exp * 1000 <= Date.now()) { window.location.replace('/login'); return; }
if (payload.role !== 'admin') { window.location.replace('/'); }
} catch(e) { window.location.replace('/login'); }
})();
</script>
</svelte:head>
{#if pageState === 'loading'}
<!-- Full page skeleton loading -->
<div class="mx-auto max-w-6xl space-y-6 p-6">
@@ -372,6 +372,20 @@
});
</script>
<svelte:head>
<script>
(function() {
try {
var token = localStorage.getItem('authToken');
if (!token) { window.location.replace('/login'); return; }
var payload = JSON.parse(atob(token.split('.')[1]));
if (payload.exp * 1000 <= Date.now()) { window.location.replace('/login'); return; }
if (payload.role !== 'admin') { window.location.replace('/'); }
} catch(e) { window.location.replace('/login'); }
})();
</script>
</svelte:head>
{#if pageState === 'loading'}
<div class="mx-auto max-w-7xl space-y-6 p-4 md:p-6">
<Skeleton class="h-8 w-64" />
+14
View File
@@ -134,6 +134,20 @@
});
</script>
<svelte:head>
<script>
(function() {
try {
var token = localStorage.getItem('authToken');
if (!token) { window.location.replace('/login'); return; }
var payload = JSON.parse(atob(token.split('.')[1]));
if (payload.exp * 1000 <= Date.now()) { window.location.replace('/login'); return; }
if (payload.role !== 'admin') { window.location.replace('/'); }
} catch(e) { window.location.replace('/login'); }
})();
</script>
</svelte:head>
{#if pageState === 'loading'}
<!-- Full page skeleton loading -->
<div class="mx-auto max-w-6xl space-y-6 p-6">
@@ -340,6 +340,20 @@
});
</script>
<svelte:head>
<script>
(function() {
try {
var token = localStorage.getItem('authToken');
if (!token) { window.location.replace('/login'); return; }
var payload = JSON.parse(atob(token.split('.')[1]));
if (payload.exp * 1000 <= Date.now()) { window.location.replace('/login'); return; }
if (payload.role !== 'admin') { window.location.replace('/'); }
} catch(e) { window.location.replace('/login'); }
})();
</script>
</svelte:head>
{#if pageState === 'loading'}
<div class="mx-auto max-w-3xl space-y-4 p-6 pt-24">
<Skeleton class="h-8 w-48" />
+14
View File
@@ -77,6 +77,20 @@
});
</script>
<svelte:head>
<script>
(function() {
try {
var token = localStorage.getItem('authToken');
if (!token) { window.location.replace('/login'); return; }
var payload = JSON.parse(atob(token.split('.')[1]));
if (payload.exp * 1000 <= Date.now()) { window.location.replace('/login'); return; }
if (payload.role !== 'admin') { window.location.replace('/'); }
} catch(e) { window.location.replace('/login'); }
})();
</script>
</svelte:head>
{#if pageState === 'loading'}
<div class="mx-auto max-w-7xl space-y-6 p-4 md:p-6">
<!-- Header Skeleton -->