mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-12-18 20:04:17 +01:00
Merge 53671ef13f into 3529849bca
This commit is contained in:
commit
e99a99e161
@ -157,6 +157,13 @@ describe('Login', () => {
|
||||
refreshSession: vi.fn(),
|
||||
});
|
||||
|
||||
// Mock window.location.replace
|
||||
const mockLocationReplace = vi.fn();
|
||||
Object.defineProperty(window, 'location', {
|
||||
writable: true,
|
||||
value: { replace: mockLocationReplace }
|
||||
});
|
||||
|
||||
render(
|
||||
<TestWrapper>
|
||||
<BrowserRouter>
|
||||
@ -166,7 +173,7 @@ describe('Login', () => {
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockNavigate).toHaveBeenCalledWith('/', { replace: true });
|
||||
expect(mockLocationReplace).toHaveBeenCalledWith('/');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Navigate, useNavigate, useSearchParams } from 'react-router-dom';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { Navigate, useLocation, useNavigate, useSearchParams, type Location } from 'react-router-dom';
|
||||
import { Text, Stack, Alert } from '@mantine/core';
|
||||
import { springAuth } from '@app/auth/springAuthClient';
|
||||
import { useAuth } from '@app/auth/UseSession';
|
||||
@ -19,7 +19,6 @@ import ErrorMessage from '@app/routes/login/ErrorMessage';
|
||||
import EmailPasswordForm from '@app/routes/login/EmailPasswordForm';
|
||||
import OAuthButtons, { DEBUG_SHOW_ALL_PROVIDERS, oauthProviderConfig } from '@app/routes/login/OAuthButtons';
|
||||
import DividerWithText from '@app/components/shared/DividerWithText';
|
||||
import LoggedInState from '@app/routes/login/LoggedInState';
|
||||
|
||||
export default function Login() {
|
||||
const navigate = useNavigate();
|
||||
@ -39,8 +38,32 @@ export default function Login() {
|
||||
const backendProbe = useBackendProbe();
|
||||
const [isFirstTimeSetup, setIsFirstTimeSetup] = useState(false);
|
||||
const [showDefaultCredentials, setShowDefaultCredentials] = useState(false);
|
||||
const location = useLocation();
|
||||
const loginDisabled = backendProbe.loginDisabled === true || _enableLogin === false;
|
||||
|
||||
const redirectTarget = useMemo(() => {
|
||||
const fromParam = searchParams.get('from');
|
||||
const stateFrom =
|
||||
location.state && typeof location.state === 'object'
|
||||
? (location.state as { from?: Location })?.from
|
||||
: undefined;
|
||||
|
||||
const requestedPath = fromParam || (stateFrom ? `${stateFrom.pathname}${stateFrom.search || ''}` : null);
|
||||
if (!requestedPath) return null;
|
||||
|
||||
// Strip BASE_PATH if it was captured in the URL (e.g., when running under a subpath)
|
||||
const basePath = BASE_PATH || '';
|
||||
const normalizedPath = requestedPath.startsWith(basePath) ? requestedPath.slice(basePath.length) || '/' : requestedPath;
|
||||
|
||||
// Only allow same-site relative navigations to non-auth routes
|
||||
const blockedPrefixes = ['/login', '/signup', '/auth/', '/invite/'];
|
||||
if (!normalizedPath.startsWith('/') || blockedPrefixes.some(prefix => normalizedPath.startsWith(prefix))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return normalizedPath;
|
||||
}, [location.state, searchParams]);
|
||||
|
||||
// Periodically probe while backend isn't up so the screen can auto-advance when it comes online
|
||||
useEffect(() => {
|
||||
if (backendProbe.status === 'up' || backendProbe.loginDisabled) {
|
||||
@ -64,10 +87,12 @@ export default function Login() {
|
||||
// Redirect immediately if user has valid session (JWT already validated by AuthProvider)
|
||||
useEffect(() => {
|
||||
if (!loading && session) {
|
||||
console.debug('[Login] User already authenticated, redirecting to home');
|
||||
navigate('/', { replace: true });
|
||||
const target = redirectTarget || '/';
|
||||
console.debug('[Login] User already authenticated, redirecting to', target);
|
||||
// Use a full replace to ensure the destination route fully mounts (avoids stale login view)
|
||||
window.location.replace(`${BASE_PATH}${target}`);
|
||||
}
|
||||
}, [session, loading, navigate]);
|
||||
}, [session, loading, redirectTarget]);
|
||||
|
||||
// If backend reports login is disabled, redirect to home (anonymous mode)
|
||||
useEffect(() => {
|
||||
@ -188,9 +213,9 @@ export default function Login() {
|
||||
return <Navigate to="/" replace />;
|
||||
}
|
||||
|
||||
// Show logged in state if authenticated
|
||||
// Redirect effect runs immediately via window.location.replace() - no UI needed
|
||||
if (session && !loading) {
|
||||
return <LoggedInState />;
|
||||
return null;
|
||||
}
|
||||
|
||||
// If backend isn't ready yet, show a lightweight status screen instead of the form
|
||||
|
||||
Loading…
Reference in New Issue
Block a user