From e014baadc303e908c05045026e3e3b661be79738 Mon Sep 17 00:00:00 2001 From: Zak Bearman Date: Wed, 9 Sep 2026 22:42:58 +1200 Subject: [PATCH] Preserve monthly recap destinations through sign-in --- frontend/app/login/page.tsx | 5 ++++- frontend/app/profile/page.tsx | 6 +++--- scripts/review_monthly_reports_ui.cjs | 14 ++++++++++++++ 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/frontend/app/login/page.tsx b/frontend/app/login/page.tsx index 46622b7..8bbb06f 100644 --- a/frontend/app/login/page.tsx +++ b/frontend/app/login/page.tsx @@ -71,7 +71,10 @@ export default function LoginPage() { if (!data?.authenticated) { setError('Could not sign in. Please try again.'); return } setToken('cookie') const next = new URLSearchParams(window.location.search).get('next') || '' - window.location.assign(next === '/insights' || /^\/issues\/confirm\/\d+$/.test(next) ? next : '/welcome') + const allowedNext = ['/insights', '/insights/reports', '/profile', '/profile#monthly-recaps', '/admin/recaps'].includes(next) + || /^\/insights\/reports\?month=[0-9]{4}-(?:0[1-9]|1[0-2])$/.test(next) + || /^\/issues\/confirm\/\d+$/.test(next) + window.location.assign(allowedNext ? next : '/welcome') } catch { setError('Could not reach Magent. Check your connection and try again.') } finally { setLoading(false) } diff --git a/frontend/app/profile/page.tsx b/frontend/app/profile/page.tsx index 9f57fd2..31987e4 100644 --- a/frontend/app/profile/page.tsx +++ b/frontend/app/profile/page.tsx @@ -69,12 +69,12 @@ export default function ProfilePage() { const [showAllActivity, setShowAllActivity] = useState(false) const loadProfile = useCallback(async () => { - if (!getToken()) { router.replace('/login'); return } + if (!getToken()) { router.replace('/login?next=%2Fprofile'); return } setLoading(true) setLoadError('') try { const response = await authFetch(`${getApiBase()}/auth/profile`) - if (response.status === 401) { clearToken(); router.replace('/login'); return } + if (response.status === 401) { clearToken(); router.replace('/login?next=%2Fprofile'); return } if (!response.ok) throw new Error('Could not load your profile. Please try again.') const profile = await response.json() as ProfileResponse setData(profile) @@ -120,7 +120,7 @@ export default function ProfilePage() { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: email.trim() || null }), }) - if (response.status === 401) { clearToken(); router.replace('/login'); return } + if (response.status === 401) { clearToken(); router.replace('/login?next=%2Fprofile'); return } if (!response.ok) throw new Error(await responseMessage(response, 'Could not save your email. Please try again.')) const result = await response.json() const saved = typeof result.email === 'string' ? result.email : '' diff --git a/scripts/review_monthly_reports_ui.cjs b/scripts/review_monthly_reports_ui.cjs index 1c52713..7439f64 100644 --- a/scripts/review_monthly_reports_ui.cjs +++ b/scripts/review_monthly_reports_ui.cjs @@ -45,6 +45,7 @@ const output = process.env.REVIEW_DIR const url = new URL(request.url()) calls.push({ method: request.method(), path: url.pathname, month: url.searchParams.get('month') }) if (url.pathname === '/api/auth/me') return route.fulfill({ json: { username: 'Fixture viewer', role } }) + if (url.pathname === '/api/auth/jellyfin/login' || url.pathname === '/api/auth/login') return route.fulfill({ json: { authenticated: true } }) if (url.pathname.startsWith('/api/insights/artwork/')) return route.fulfill({ contentType: 'image/svg+xml', body: '' }) if (url.pathname === '/api/insights/reports/monthly.csv') { if (failDownload) return route.fulfill({ status: 502, json: { detail: 'Unavailable' } }) @@ -138,6 +139,19 @@ const output = process.env.REVIEW_DIR await page.waitForURL(/\/login\?next=/) assert.equal(new URL(page.url()).searchParams.get('next'), `/insights/reports?month=${months.at(-1)}`) mode = 'ready' + await page.getByLabel('Username', { exact: true }).fill('fixture-viewer') + await page.getByLabel('Password', { exact: true }).fill('Fixture-only-password') + await page.getByRole('button', { name: 'Sign in', exact: true }).click() + await page.getByRole('heading', { name: 'October 2024', exact: true }).waitFor() + assert.equal(new URL(page.url()).searchParams.get('month'), months.at(-1)) + // An external or unrecognized destination must still use the safe welcome fallback. + for (const next of ['https://outside.example.test', '//outside.example.test', '/insights/reports?month=2026-07&redirect=https://outside.example.test']) { + await page.goto(`${base}/login?next=${encodeURIComponent(next)}`) + await page.getByLabel('Username', { exact: true }).fill('fixture-viewer') + await page.getByLabel('Password', { exact: true }).fill('Fixture-only-password') + await page.getByRole('button', { name: 'Sign in', exact: true }).click() + await page.waitForURL(`${base}/welcome`) + } await page.goto(base + '/insights/reports') await page.getByRole('heading', { name: 'August 2026', exact: true }).waitFor() await page.getByRole('navigation', { name: 'My Stats views' }).getByRole('link', { name: 'Overview' }).click()