Introduce NotificationManager, NotificationPrompt, NotificationSettings, NotificationSettingsSheet and integrate into the profile panel. Add server GET/POST endpoints for notification preferences. Add lucide-svelte dependency and update CSP connect-src to allow fcm.googleapis.com and android.googleapis.com
72 lines
2.3 KiB
TypeScript
72 lines
2.3 KiB
TypeScript
import type { Handle } from '@sveltejs/kit';
|
|
import * as auth from '$lib/server/auth';
|
|
|
|
export const handle: Handle = async ({ event, resolve }) => {
|
|
// CSRF protection - verify origin header for state-changing requests
|
|
const method = event.request.method;
|
|
const origin = event.request.headers.get('origin');
|
|
|
|
// Skip CSRF check for GET/HEAD requests
|
|
if (method !== 'GET' && method !== 'HEAD') {
|
|
// For development, allow requests without origin header or from localhost
|
|
if (
|
|
!origin ||
|
|
origin.includes('localhost') ||
|
|
origin.includes('127.0.0.1') ||
|
|
origin.includes('serengo.ziasvannes.tech')
|
|
) {
|
|
// Allow in development and serengo.ziasvannes.tech
|
|
}
|
|
// In production, you would add: else if (origin !== 'yourdomain.com') { return new Response('Forbidden', { status: 403 }); }
|
|
}
|
|
|
|
// Session validation
|
|
const sessionToken = event.cookies.get(auth.sessionCookieName);
|
|
|
|
if (!sessionToken) {
|
|
event.locals.user = null;
|
|
event.locals.session = null;
|
|
return resolve(event);
|
|
}
|
|
|
|
const { session, user } = await auth.validateSessionToken(sessionToken);
|
|
|
|
if (session) {
|
|
auth.setSessionTokenCookie(event, sessionToken, session.expiresAt);
|
|
} else {
|
|
auth.deleteSessionTokenCookie(event);
|
|
}
|
|
|
|
event.locals.user = user;
|
|
event.locals.session = session;
|
|
|
|
const response = await resolve(event);
|
|
|
|
// Add security headers
|
|
response.headers.set(
|
|
'Content-Security-Policy',
|
|
"default-src 'self'; " +
|
|
"script-src 'self' 'unsafe-inline' 'unsafe-eval'; " +
|
|
"worker-src 'self' blob:; " +
|
|
"style-src 'self' 'unsafe-inline' fonts.googleapis.com; " +
|
|
"font-src 'self' fonts.gstatic.com; " +
|
|
"img-src 'self' data: blob: *.openstreetmap.org *.tile.openstreetmap.org *.r2.cloudflarestorage.com *.r2.dev; " +
|
|
"media-src 'self' *.r2.cloudflarestorage.com *.r2.dev; " +
|
|
"connect-src 'self' *.openstreetmap.org https://fcm.googleapis.com https://android.googleapis.com; " +
|
|
"frame-ancestors 'none'; " +
|
|
"base-uri 'self'; " +
|
|
"form-action 'self';"
|
|
);
|
|
|
|
response.headers.set('X-Frame-Options', 'DENY');
|
|
response.headers.set('X-Content-Type-Options', 'nosniff');
|
|
response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
|
|
|
|
// Add HSTS for HTTPS in production
|
|
if (event.url.protocol === 'https:') {
|
|
response.headers.set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
|
|
}
|
|
|
|
return response;
|
|
};
|