From a6fd79a197bb1722a8278fc5713b5ca75caa9d33 Mon Sep 17 00:00:00 2001 From: Amr Mohammed El-Sheraey <141947355+AmrMsCLL@users.noreply.github.com> Date: Sat, 26 Sep 2026 09:44:20 +0300 Subject: [PATCH] fix: close the Phase 1-4 audit defects - Password reset, password change and email confirmation cancel pending email changes and reset links; a requested email change warns the old address; account emails that fail are logged without the address and forgot-password answers the same either way. - SMTP errors keep code, status and command but drop the server response. - Digests count only refused recipients and rejected messages against a reader, treat sender and connection failures as outages, send only the releases a run claimed and requeue sends interrupted by a crash; new release markers, inbox rows and digest events are written together. - Notifications pause instead of following another source when a title's preferred source is switched off; unsubscribing a deleted account works. - Imports attach matches without overwriting stored titles, and saved titles are refreshed from their source weekly. - Progress fields accept values past stored totals; removing a game's last platform turns its notifications off; a concurrent first add retries. - Private ratings stay off public profiles; the review editor uses a Private switch that starts off. - Open reports stay in the admin queue when review text is cleared. - Import matches carry the adult flag, imports cannot be deleted while running and leftover uploads are removed at start-up. - IGDB release dates repeated per region are merged. - Security headers, including HSTS behind HTTPS; X-Powered-By is off. --- .../src/components/LibraryEntryEditor.tsx | 17 +- app/frontend/src/components/ReviewEditor.tsx | 24 +- app/frontend/src/components/TitleReviews.tsx | 9 - app/frontend/src/components/Toggle.tsx | 4 +- app/frontend/src/pages/SettingsPage.tsx | 2 +- app/frontend/test/review-editor.spec.tsx | 103 +++++++++ app/server/src/admin/admin.service.ts | 1 - app/server/src/auth/auth.service.ts | 60 ++++- .../src/catalog/catalog-items.service.ts | 5 + .../src/catalog/catalog-refresh.service.ts | 59 +++++ app/server/src/catalog/catalog.module.ts | 5 +- app/server/src/imports/imports.controller.ts | 5 +- app/server/src/imports/imports.service.ts | 22 +- app/server/src/library/effective-source.ts | 11 + app/server/src/library/library.service.ts | 19 +- app/server/src/mail/mail.service.ts | 32 ++- app/server/src/main.ts | 12 +- .../src/notifications/digest.service.ts | 57 +++-- .../src/notifications/notifications.module.ts | 3 +- .../notifications/notifications.scheduler.ts | 3 + .../notifications/notifications.service.ts | 26 ++- .../notifications/release-monitor.service.ts | 66 +++--- app/server/src/notifications/subscriptions.ts | 14 +- app/server/src/sources/igdb/igdb.service.ts | 21 +- app/server/src/users/profiles.service.ts | 20 +- app/server/test/account.spec.ts | 112 +++++++++- app/server/test/admin.spec.ts | 4 +- app/server/test/catalog-items.service.spec.ts | 100 +++++++++ app/server/test/connectors.spec.ts | 3 +- app/server/test/imports.spec.ts | 101 ++++++++- app/server/test/library.service.spec.ts | 94 +++++++- app/server/test/notifications.spec.ts | 211 +++++++++++++++--- app/server/test/profiles.spec.ts | 51 ++++- 33 files changed, 1079 insertions(+), 197 deletions(-) create mode 100644 app/frontend/test/review-editor.spec.tsx create mode 100644 app/server/src/catalog/catalog-refresh.service.ts diff --git a/app/frontend/src/components/LibraryEntryEditor.tsx b/app/frontend/src/components/LibraryEntryEditor.tsx index e91ac04..96b1c27 100644 --- a/app/frontend/src/components/LibraryEntryEditor.tsx +++ b/app/frontend/src/components/LibraryEntryEditor.tsx @@ -88,16 +88,16 @@ function ProgressFields({ return ( <> {units.includes('season') && ( - + )} {units.includes('episode') && ( - + )} {units.includes('chapter') && ( - + )} {units.includes('volume') && ( - + )} {units.includes('hours') && ( @@ -151,6 +151,9 @@ export function LibraryEntryEditor({ const [error, setError] = useState(''); const [removing, setRemoving] = useState(false); const unavailableSources = entry.item.sources.filter((source) => !source.active); + const unavailablePreferredSource = unavailableSources.find( + (source) => source.key === entry.preferredSource, + ); const finished = entry.state === 'completed' || entry.state === 'dropped'; const needsPlatform = entry.item.category === 'game' && entry.progress.platforms.length === 0; @@ -273,6 +276,12 @@ export function LibraryEntryEditor({

) )} + {entry.notificationsEnabled && unavailablePreferredSource && ( +

+ Paused while {unavailablePreferredSource.name} is unavailable. Choose another preferred + source or Automatic to get notifications from it instead. +

+ )} {entry.notificationsEnabled && emailsOff && (

Emails for {categoryLabels[entry.item.category].toLowerCase()} are off in{' '} diff --git a/app/frontend/src/components/ReviewEditor.tsx b/app/frontend/src/components/ReviewEditor.tsx index 6265242..e4e9a00 100644 --- a/app/frontend/src/components/ReviewEditor.tsx +++ b/app/frontend/src/components/ReviewEditor.tsx @@ -4,15 +4,14 @@ import { useAuth } from '../auth'; import { reviewBodyLimit, type OwnReview } from '../reviews'; import { ConfirmDialog } from './ConfirmDialog'; import { ReviewCard } from './ReviewCard'; +import { Toggle } from './Toggle'; export function ReviewEditor({ - defaultVisibility, itemId, onClose, onSaved, review, }: { - defaultVisibility: OwnReview['visibility']; itemId: string; onClose: () => void; onSaved: (review: OwnReview | null) => void; @@ -23,7 +22,7 @@ export function ReviewEditor({ const [title, setTitle] = useState(review?.title ?? ''); const [body, setBody] = useState(review?.body ?? ''); const [containsSpoilers, setContainsSpoilers] = useState(review?.containsSpoilers ?? false); - const [visibility, setVisibility] = useState(review?.visibility ?? defaultVisibility); + const [visibility, setVisibility] = useState(review?.visibility ?? 'public'); const [preview, setPreview] = useState(false); const [busy, setBusy] = useState(false); const [error, setError] = useState(''); @@ -98,18 +97,13 @@ export function ReviewEditor({ )} -

- -