- Replace bell dropdown with full-screen NotificationsModal (mirrors SearchModal pattern) - Notifications visible to all logged-in users (not just admin) - Admin users excluded from new-chapter fan-out (dedup vs Scrape Complete notification) - Users with notify_new_chapters=false opted out of new-chapter in-app notifications - Toggle in profile page to enable/disable in-app new-chapter notifications - PATCH /api/profile endpoint to save notification preferences - User-facing /notifications page (admin redirects to /admin/notifications)
62 lines
1.9 KiB
TypeScript
62 lines
1.9 KiB
TypeScript
import { json, error } from '@sveltejs/kit';
|
|
import type { RequestHandler } from './$types';
|
|
import { deleteUserAccount, updateUserNotificationPrefs } from '$lib/server/pocketbase';
|
|
import { log } from '$lib/server/logger';
|
|
|
|
/**
|
|
* PATCH /api/profile
|
|
*
|
|
* Update mutable profile preferences (currently: notification preferences).
|
|
* Body: { notify_new_chapters?: boolean }
|
|
*/
|
|
export const PATCH: RequestHandler = async ({ locals, request }) => {
|
|
if (!locals.user) error(401, 'Not authenticated');
|
|
|
|
let body: Record<string, unknown>;
|
|
try {
|
|
body = await request.json();
|
|
} catch {
|
|
error(400, 'Invalid JSON');
|
|
}
|
|
|
|
const prefs: { notify_new_chapters?: boolean } = {};
|
|
if (typeof body.notify_new_chapters === 'boolean') {
|
|
prefs.notify_new_chapters = body.notify_new_chapters;
|
|
}
|
|
|
|
if (Object.keys(prefs).length === 0) {
|
|
error(400, 'No valid preferences provided');
|
|
}
|
|
|
|
try {
|
|
await updateUserNotificationPrefs(locals.user.id, prefs);
|
|
} catch (e) {
|
|
log.error('profile', 'PATCH /api/profile failed', { userId: locals.user.id, err: String(e) });
|
|
error(500, { message: 'Failed to update preferences. Please try again.' });
|
|
}
|
|
|
|
return json({ ok: true });
|
|
};
|
|
|
|
/**
|
|
* DELETE /api/profile
|
|
*
|
|
* Permanently deletes the authenticated user's account and all associated data:
|
|
* settings, library, progress, votes, ratings, sessions, notifications.
|
|
*
|
|
* The app_users record is removed last. The caller should immediately log the
|
|
* user out (submit the logout form) to clear the session cookie.
|
|
*/
|
|
export const DELETE: RequestHandler = async ({ locals }) => {
|
|
if (!locals.user) error(401, 'Not authenticated');
|
|
|
|
try {
|
|
await deleteUserAccount(locals.user.id, locals.sessionId);
|
|
} catch (e) {
|
|
log.error('profile', 'DELETE /api/profile failed', { userId: locals.user.id, err: String(e) });
|
|
error(500, { message: 'Failed to delete account. Please try again or contact support.' });
|
|
}
|
|
|
|
return json({ ok: true });
|
|
};
|