Add optional Authentik (OIDC) sign-in with server-side preference sync
Signed-in users get their preferences (hidden feeds, weather location, weather toggle) stored server-side keyed by their OIDC subject and synced across devices. Anonymous visitors keep using browser localStorage, so the site stays public. First sign-in migrates existing localStorage prefs up. - config: [web.auth] section (issuer, client_id/secret, redirect, session_secret) - storage: user_preferences table + Get/PutUserPrefs - web/auth: OIDC code flow, HMAC-signed session cookie, CSRF state + nonce - web/prefs_api: GET/PUT /api/preferences (auth-gated, 64KB cap) - frontend: prefs.js sync layer seeds localStorage from server, pushes on write - header: sign-in / account control OIDC discovery is non-fatal at boot: if Authentik is down, Pete serves anonymously rather than refusing to start.
This commit is contained in:
@@ -65,6 +65,28 @@
|
||||
class="inline-flex shrink-0 items-center gap-1.5 rounded-full bg-[color:var(--card)] px-3 py-2 text-sm font-semibold shadow-pete border-2 border-[color:var(--ink)]/10 hover:bg-[color:var(--ink)]/5 transition">
|
||||
<span data-weather-chip class="tabular-nums">📍 Weather</span>
|
||||
</button>
|
||||
{{if .AuthEnabled}}
|
||||
{{if .User}}
|
||||
<a href="/auth/logout" data-account
|
||||
title="Signed in as {{.User.Display}}{{if .User.Email}} · {{.User.Email}}{{end}} — sign out"
|
||||
class="inline-flex shrink-0 items-center gap-2 rounded-full bg-[color:var(--card)] px-2.5 py-1.5 text-sm font-semibold shadow-pete border-2 border-[color:var(--ink)]/10 hover:bg-[color:var(--ink)]/5 transition">
|
||||
<span class="grid h-6 w-6 place-items-center rounded-full bg-[color:var(--accent)] text-white text-xs font-bold">{{.User.Initial}}</span>
|
||||
<span class="hidden sm:inline max-w-[7rem] truncate">{{.User.Display}}</span>
|
||||
</a>
|
||||
{{else}}
|
||||
<a href="/auth/login?next={{.Path}}" data-signin
|
||||
title="Sign in to sync your preferences"
|
||||
class="inline-flex shrink-0 items-center gap-1.5 rounded-full bg-[color:var(--card)] px-3 py-2 text-sm font-semibold shadow-pete border-2 border-[color:var(--ink)]/10 hover:bg-[color:var(--ink)]/5 transition">
|
||||
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
|
||||
stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4 text-[color:var(--ink)]/60">
|
||||
<path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4"></path>
|
||||
<polyline points="10 17 15 12 10 7"></polyline>
|
||||
<line x1="15" y1="12" x2="3" y2="12"></line>
|
||||
</svg>
|
||||
<span class="hidden sm:inline">Sign in</span>
|
||||
</a>
|
||||
{{end}}
|
||||
{{end}}
|
||||
<div class="flex items-center gap-2 min-w-0 max-w-full">
|
||||
<button type="button" data-search-trigger title="Search (⌘K)"
|
||||
class="flex shrink-0 items-center gap-2 rounded-full bg-[color:var(--card)] px-3 py-2 text-sm shadow-pete border-2 border-[color:var(--ink)]/10 hover:bg-[color:var(--ink)]/5 transition">
|
||||
@@ -112,7 +134,7 @@
|
||||
<h2 class="font-display text-lg font-bold">Feed settings</h2>
|
||||
<button type="button" data-settings-close class="rounded-full px-2 py-1 text-sm text-[color:var(--ink)]/60 hover:bg-[color:var(--ink)]/5">esc</button>
|
||||
</div>
|
||||
<p class="px-5 pt-3 text-xs text-[color:var(--ink)]/60">Uncheck a feed to hide its stories. Saved in this browser.</p>
|
||||
<p class="px-5 pt-3 text-xs text-[color:var(--ink)]/60">Uncheck a feed to hide its stories. <span data-storage-note>Saved in this browser.</span></p>
|
||||
<div data-settings-list class="max-h-[55vh] overflow-y-auto p-3 space-y-1"></div>
|
||||
<div class="px-5 py-3 border-t border-[color:var(--ink)]/10 flex items-center justify-between text-xs">
|
||||
<button type="button" data-settings-reset class="text-[color:var(--ink)]/60 hover:text-[color:var(--ink)] font-semibold">Enable all</button>
|
||||
@@ -148,7 +170,12 @@
|
||||
})();
|
||||
</script>
|
||||
|
||||
<script>window.PETE_SOURCES = {{.AllSources}};</script>
|
||||
<script>
|
||||
window.PETE_SOURCES = {{.AllSources}};
|
||||
window.PETE_USER = {{if .User}}{ name: {{.User.Display}}, email: {{.User.Email}} }{{else}}null{{end}};
|
||||
window.PETE_PREFS = {{.UserPrefs}};
|
||||
</script>
|
||||
<script src="/static/js/prefs.js" defer></script>
|
||||
<script src="/static/js/weather.js" defer></script>
|
||||
<script src="/static/js/weather-forecast.js" defer></script>
|
||||
<script src="/static/js/search.js" defer></script>
|
||||
|
||||
Reference in New Issue
Block a user