refactor(webui): route auth redirects through lib/routes

Step 3 of the route-registry migration: every /login redirect and post-login landing
fallback builds from lib/routes instead of hardcoded strings.

- protected-route, axios 401/403 interceptor, user-provider (logout, session-expiry,
  /login + public-route checks) -> routes.login(...) / routes.login()
- oauth-result error redirect -> routes.login()
- login-form / public-route / login page post-login fallback -> routes.newFlow

getReturnUrlParam now has a single in-app caller (the routes.login builder) plus the
logout suffix-override; dropped its now-unused imports from axios and protected-route.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Sergey Kozyrenko
2026-06-14 13:45:46 +07:00
co-authored by Claude Fable 5
parent d71feaf854
commit dc6b3a9e2c
7 changed files with 20 additions and 20 deletions
@@ -1,7 +1,7 @@
import * as React from 'react';
import { Navigate, useLocation } from 'react-router-dom';
import { getReturnUrlParam } from '@/lib/utils/auth';
import { routes } from '@/lib/routes';
import { useUser } from '@/providers/user-provider';
function ProtectedRoute({ children }: { children: React.ReactNode }) {
@@ -13,12 +13,10 @@ function ProtectedRoute({ children }: { children: React.ReactNode }) {
}
if (!isAuthenticated()) {
const returnParam = getReturnUrlParam(location.pathname);
return (
<Navigate
replace
to={`/login${returnParam}`}
to={routes.login(location.pathname)}
/>
);
}
@@ -1,6 +1,7 @@
import * as React from 'react';
import { Navigate, useSearchParams } from 'react-router-dom';
import { routes } from '@/lib/routes';
import { getSafeReturnUrl } from '@/lib/utils/auth';
import { useUser } from '@/providers/user-provider';
@@ -29,7 +30,7 @@ function PublicRoute({ children }: { children: React.ReactNode }) {
return children;
}
const returnUrl = getSafeReturnUrl(searchParams.get('returnUrl'), '/flows/new');
const returnUrl = getSafeReturnUrl(searchParams.get('returnUrl'), routes.newFlow);
return (
<Navigate
@@ -13,6 +13,7 @@ import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '
import { FormSubmitButton } from '@/components/ui/form-submit-button';
import { Input } from '@/components/ui/input';
import { InputPassword } from '@/components/ui/input-password';
import { routes } from '@/lib/routes';
import { useUser } from '@/providers/user-provider';
import { PasswordChangeForm } from './password-change-form';
@@ -61,7 +62,7 @@ interface LoginFormProps {
returnUrl?: string;
}
function LoginForm({ providers, returnUrl = '/flows/new' }: LoginFormProps) {
function LoginForm({ providers, returnUrl = routes.newFlow }: LoginFormProps) {
const form = useForm<z.infer<typeof formSchema>>({
defaultValues: {
mail: '',
+5 -7
View File
@@ -5,7 +5,7 @@ import Axios from 'axios';
import { AUTH_STORAGE_KEY } from '@/providers/user-provider';
import { Log } from './log';
import { getReturnUrlParam } from './utils/auth';
import { routes } from './routes';
// ── shared API protocol types ────────────────────────────────────────────────
//
@@ -106,9 +106,8 @@ axios.interceptors.response.use(
const currentPath = window.location.pathname;
if (currentPath !== '/login') {
const returnParam = getReturnUrlParam(currentPath);
window.location.href = `/login${returnParam}`;
if (currentPath !== routes.login()) {
window.location.href = routes.login(currentPath);
}
break;
@@ -129,9 +128,8 @@ axios.interceptors.response.use(
const currentPath = window.location.pathname;
if (currentPath !== '/login') {
const returnParam = getReturnUrlParam(currentPath);
window.location.href = `/login${returnParam}`;
if (currentPath !== routes.login()) {
window.location.href = routes.login(currentPath);
}
} else {
Log.warn(err.response?.data);
+2 -1
View File
@@ -3,6 +3,7 @@ import { useLocation, useSearchParams } from 'react-router-dom';
import Logo from '@/components/icons/logo';
import LoginForm from '@/features/authentication/login-form';
import { routes } from '@/lib/routes';
import { getSafeReturnUrl } from '@/lib/utils/auth';
import { useUser } from '@/providers/user-provider';
@@ -12,7 +13,7 @@ function Login() {
const { authInfo, isLoading } = useUser();
const authProviders = authInfo?.providers || [];
const returnUrl = getSafeReturnUrl((location.state?.from as string) || searchParams.get('returnUrl'), '/flows/new');
const returnUrl = getSafeReturnUrl((location.state?.from as string) || searchParams.get('returnUrl'), routes.newFlow);
return (
<div className="flex h-dvh w-full items-center justify-center">
+2 -1
View File
@@ -1,6 +1,7 @@
import { useEffect, useLayoutEffect, useRef, useState } from 'react';
import Logo from '@/components/icons/logo';
import { routes } from '@/lib/routes';
function OAuthResult() {
const [statusMessage, setStatusMessage] = useState('Authentication in progress...');
@@ -79,7 +80,7 @@ function OAuthResult() {
}
} else {
updateMessage('Authentication window opened directly. Redirecting to login page...');
handleRedirect('/login', errorDelay / 2);
handleRedirect(routes.login(), errorDelay / 2);
handleClose(errorDelay);
}
+5 -5
View File
@@ -8,6 +8,7 @@ import type { AuthInfo } from '@/models/info';
import type { User } from '@/models/user';
import { api } from '@/lib/axios';
import { routes } from '@/lib/routes';
import { getReturnUrlParam } from '@/lib/utils/auth';
import { baseUrl } from '@/models/api';
@@ -147,7 +148,7 @@ export function UserProvider({ children }: { children: ReactNode }) {
}, [setAuth, clearAuth]);
useEffect(() => {
if (location.pathname === '/login' && !isLoading) {
if (location.pathname === routes.login() && !isLoading) {
// eslint-disable-next-line react-hooks/set-state-in-effect -- refreshAuthInfo's setState runs after an async fetch, not synchronously
refreshAuthInfo();
}
@@ -165,7 +166,7 @@ export function UserProvider({ children }: { children: ReactNode }) {
toast.error('Logout failed, but clearing local session');
} finally {
clearAuth();
window.location.href = `/login${finalReturnUrl}`;
window.location.href = `${routes.login()}${finalReturnUrl}`;
}
},
[clearAuth, location.pathname],
@@ -328,7 +329,7 @@ export function UserProvider({ children }: { children: ReactNode }) {
useEffect(() => {
const updateAuth = async () => {
const publicRoutes = ['/login', '/oauth/result'];
const publicRoutes = [routes.login(), routes.oauthResult];
if (publicRoutes.includes(location.pathname)) {
return;
@@ -350,8 +351,7 @@ export function UserProvider({ children }: { children: ReactNode }) {
} else {
clearAuth();
toast.error('Session expired. Please login again.');
const returnParam = getReturnUrlParam(location.pathname);
navigate(`/login${returnParam}`);
navigate(routes.login(location.pathname));
}
} catch {
// A transient /info failure on navigation must not log the user out — a network