fix: security and UX improvements for notifications and shares

- Add ownership checks on all notification and share routes (403 Forbidden)
- Validate permission field (only view/edit/admin allowed)
- Remove user_id from POST notifications (only self-notifications)
- Add getNotification/getProjectShare to DB interface + adapter
- Add res.ok checks on all frontend API calls
- Add click-outside handler for notification dropdown
- Add initial notification load on mount for badge count
- Add email validation + duplicate check in ShareDialog
- Add Enter key handler in ShareDialog
- Add submitting state to prevent double-click
- Guard against null token in Dashboard
This commit is contained in:
2026-06-28 14:16:50 +02:00
parent 20432f4b47
commit e9b6f188c8
8 changed files with 114 additions and 31 deletions
+8 -5
View File
@@ -404,10 +404,10 @@ export async function getNotifications(token: string): Promise<NotificationItem[
return res.json();
}
export async function createNotification(token: string, data: { type?: string; title: string; message: string; user_id?: string }): Promise<NotificationItem> {
export async function createNotification(token: string, data: { type?: string; title: string; message: string }): Promise<NotificationItem> {
const res = await fetch(`${API_BASE}/api/notifications`, {
method: 'POST',
headers: authHeaders(token),
headers: { ...authHeaders(token), 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
if (!res.ok) throw new Error('Failed to create notification');
@@ -415,17 +415,19 @@ export async function createNotification(token: string, data: { type?: string; t
}
export async function markNotificationRead(token: string, id: string): Promise<void> {
await fetch(`${API_BASE}/api/notifications/${id}/read`, {
const res = await fetch(`${API_BASE}/api/notifications/${id}/read`, {
method: 'PATCH',
headers: authHeaders(token),
});
if (!res.ok) throw new Error('Failed to mark notification as read');
}
export async function deleteNotification(token: string, id: string): Promise<void> {
await fetch(`${API_BASE}/api/notifications/${id}`, {
const res = await fetch(`${API_BASE}/api/notifications/${id}`, {
method: 'DELETE',
headers: authHeaders(token),
});
if (!res.ok) throw new Error('Failed to delete notification');
}
// ─── Project Shares ──────────────────────────────────────
@@ -456,10 +458,11 @@ export async function createProjectShare(token: string, projectId: string, data:
}
export async function deleteProjectShare(token: string, shareId: string): Promise<void> {
await fetch(`${API_BASE}/api/shares/${shareId}`, {
const res = await fetch(`${API_BASE}/api/shares/${shareId}`, {
method: 'DELETE',
headers: authHeaders(token),
});
if (!res.ok) throw new Error('Failed to delete share');
}
export { API_BASE };