feat: harden auth, shutdown, and session bundle (0.35.0)
Three features bundled into one release: - rate-limit /auth/login, /register, /me/password (token bucket, 5 req/sec sustained with 10-request burst by default; 429 + Retry-After header on hit; tracing::warn! per hit so operators see attack patterns; AUTH_RATE_PER_SEC / AUTH_RATE_BURST env knobs) - handle SIGTERM for graceful container stops (replaces bare ctrl_c() with a select over ctrl_c + SignalKind::terminate() so docker compose stop runs the daemon shutdown path instead of letting Chromium leak past SIGKILL) - clear session.user on 401 from any API call (setOn401Hook in api/client.ts, registered from session.svelte.ts gated on $app/environment::browser so the SSR bundle never installs it; fixes "logged in but no bookmarks/collections" mid-session expiry state) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,17 @@
|
||||
// Only mutated client-side (onMount / form submits) so the module-level
|
||||
// instance can't leak across SSR requests — SSR always renders the
|
||||
// `loaded === false` state, and the client refreshes after hydration.
|
||||
//
|
||||
// IMPORTANT: do not call any `api/*` helper from `+page.server.ts` /
|
||||
// `+layout.server.ts`. The `setOn401Hook` below is registered at
|
||||
// module load (gated on `browser`, so it only fires in the client
|
||||
// bundle), so a 401 from a server-side fetch would mutate this
|
||||
// module-level `session.user` across SvelteKit requests — a real
|
||||
// cross-request state leak. The `if (browser)` guard makes that
|
||||
// failure mode mechanical rather than convention-based.
|
||||
|
||||
import { browser } from '$app/environment';
|
||||
import { setOn401Hook } from './api/client';
|
||||
import { me, type User } from './api/auth';
|
||||
|
||||
class SessionStore {
|
||||
@@ -31,3 +41,16 @@ class SessionStore {
|
||||
}
|
||||
|
||||
export const session = new SessionStore();
|
||||
|
||||
// When any backend call returns 401, drop the cached user. Before this
|
||||
// hook, the `*OrEmpty` wrappers silently returned empty pages on 401
|
||||
// — so a mid-session expiry left the UI rendering as "logged in but
|
||||
// no bookmarks/collections/etc." until the user manually reloaded.
|
||||
// With the hook the session.user reactive store flips to null on the
|
||||
// first 401, so the layout re-renders the login affordance.
|
||||
//
|
||||
// Gated on `browser` so it's only installed in the client bundle.
|
||||
// See the module-level comment above for the SSR rationale.
|
||||
if (browser) {
|
||||
setOn401Hook(() => session.setUser(null));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user