mirror of
https://github.com/idanoo/autobrr
synced 2025-07-25 17:59:14 +00:00
fix(auth): cookie expiry and renewal (#1527)
* fix(auth/web): logout when expired/invalid/no cookie is present * fix(auth/web): specify error message in invalid cookie * fix(auth/web): reset error boundary on login * fix(auth/web): fix onboarding * chore: code cleanup * fix(web): revert tanstack/router to 1.31.0 * refactor(web): remove react-error-boundary * feat(auth): refresh cookie when close to expiry * enhancement(web): specify defaultError message in HttpClient * fix(web): use absolute paths for router links (#1530) * chore(web): bump `@tanstack/react-router` to `1.31.6` * fix(web): settings routes * fix(web): filter routes * fix(web): remove unused ReleasesIndexRoute * chore(web): add documentation for HttpClient * chore(lint): remove unnecessary whitespace
This commit is contained in:
parent
3dab295387
commit
8120c33f6b
19 changed files with 364 additions and 366 deletions
|
@ -7,11 +7,11 @@ import {
|
|||
createRootRouteWithContext,
|
||||
createRoute,
|
||||
createRouter,
|
||||
ErrorComponent,
|
||||
Navigate,
|
||||
notFound,
|
||||
Outlet,
|
||||
redirect,
|
||||
} from "@tanstack/react-router";
|
||||
} from "@tanstack/react-router";
|
||||
import { z } from "zod";
|
||||
import { QueryClient } from "@tanstack/react-query";
|
||||
|
||||
|
@ -46,11 +46,13 @@ import DownloadClientSettings from "@screens/settings/DownloadClient";
|
|||
import FeedSettings from "@screens/settings/Feed";
|
||||
import { Dashboard } from "@screens/Dashboard";
|
||||
import AccountSettings from "@screens/settings/Account";
|
||||
import { AuthContext, AuthCtx, localStorageUserKey, SettingsContext } from "@utils/Context";
|
||||
import { AuthContext, SettingsContext } from "@utils/Context";
|
||||
import { TanStackRouterDevtools } from "@tanstack/router-devtools";
|
||||
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
|
||||
import { queryClient } from "@api/QueryClient";
|
||||
|
||||
import { ErrorPage } from "@components/alerts";
|
||||
|
||||
const DashboardRoute = createRoute({
|
||||
getParentRoute: () => AuthIndexRoute,
|
||||
path: '/',
|
||||
|
@ -133,16 +135,9 @@ export const FilterActionsRoute = createRoute({
|
|||
component: Actions
|
||||
});
|
||||
|
||||
const ReleasesRoute = createRoute({
|
||||
export const ReleasesRoute = createRoute({
|
||||
getParentRoute: () => AuthIndexRoute,
|
||||
path: 'releases'
|
||||
});
|
||||
|
||||
// type ReleasesSearch = z.infer<typeof releasesSearchSchema>
|
||||
|
||||
export const ReleasesIndexRoute = createRoute({
|
||||
getParentRoute: () => ReleasesRoute,
|
||||
path: '/',
|
||||
path: 'releases',
|
||||
component: Releases,
|
||||
validateSearch: (search) => z.object({
|
||||
offset: z.number().optional(),
|
||||
|
@ -260,7 +255,7 @@ export const LoginRoute = createRoute({
|
|||
validateSearch: z.object({
|
||||
redirect: z.string().optional(),
|
||||
}),
|
||||
beforeLoad: ({ navigate}) => {
|
||||
beforeLoad: ({ navigate }) => {
|
||||
// handle canOnboard
|
||||
APIClient.auth.canOnboard().then(() => {
|
||||
console.info("onboarding available, redirecting")
|
||||
|
@ -277,44 +272,36 @@ export const AuthRoute = createRoute({
|
|||
id: 'auth',
|
||||
// Before loading, authenticate the user via our auth context
|
||||
// This will also happen during prefetching (e.g. hovering over links, etc.)
|
||||
beforeLoad: ({context, location}) => {
|
||||
beforeLoad: ({ context, location }) => {
|
||||
// If the user is not logged in, check for item in localStorage
|
||||
if (!context.auth.isLoggedIn) {
|
||||
const storage = localStorage.getItem(localStorageUserKey);
|
||||
if (storage) {
|
||||
try {
|
||||
const json = JSON.parse(storage);
|
||||
if (json === null) {
|
||||
console.warn(`JSON localStorage value for '${localStorageUserKey}' context state is null`);
|
||||
} else {
|
||||
context.auth.isLoggedIn = json.isLoggedIn
|
||||
context.auth.username = json.username
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(`auth Failed to merge ${localStorageUserKey} context state: ${e}`);
|
||||
}
|
||||
} else {
|
||||
// If the user is logged out, redirect them to the login page
|
||||
throw redirect({
|
||||
to: LoginRoute.to,
|
||||
search: {
|
||||
// Use the current location to power a redirect after login
|
||||
// (Do not use `router.state.resolvedLocation` as it can
|
||||
// potentially lag behind the actual current location)
|
||||
redirect: location.href,
|
||||
},
|
||||
})
|
||||
}
|
||||
if (!AuthContext.get().isLoggedIn) {
|
||||
throw redirect({
|
||||
to: LoginRoute.to,
|
||||
search: {
|
||||
// Use the current location to power a redirect after login
|
||||
// (Do not use `router.state.resolvedLocation` as it can
|
||||
// potentially lag behind the actual current location)
|
||||
redirect: location.href,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Otherwise, return the user in context
|
||||
return {
|
||||
username: AuthContext.username,
|
||||
}
|
||||
return context;
|
||||
},
|
||||
})
|
||||
|
||||
function AuthenticatedLayout() {
|
||||
const isLoggedIn = AuthContext.useSelector((s) => s.isLoggedIn);
|
||||
if (!isLoggedIn) {
|
||||
const redirect = (
|
||||
location.pathname.length > 1
|
||||
? { redirect: location.pathname }
|
||||
: undefined
|
||||
);
|
||||
return <Navigate to="/login" search={redirect} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col min-h-screen">
|
||||
<Header/>
|
||||
|
@ -345,7 +332,6 @@ export const RootComponent = () => {
|
|||
}
|
||||
|
||||
export const RootRoute = createRootRouteWithContext<{
|
||||
auth: AuthCtx,
|
||||
queryClient: QueryClient
|
||||
}>()({
|
||||
component: RootComponent,
|
||||
|
@ -354,7 +340,7 @@ export const RootRoute = createRootRouteWithContext<{
|
|||
|
||||
const filterRouteTree = FiltersRoute.addChildren([FilterIndexRoute, FilterGetByIdRoute.addChildren([FilterGeneralRoute, FilterMoviesTvRoute, FilterMusicRoute, FilterAdvancedRoute, FilterExternalRoute, FilterActionsRoute])])
|
||||
const settingsRouteTree = SettingsRoute.addChildren([SettingsIndexRoute, SettingsLogRoute, SettingsIndexersRoute, SettingsIrcRoute, SettingsFeedsRoute, SettingsClientsRoute, SettingsNotificationsRoute, SettingsApiRoute, SettingsReleasesRoute, SettingsAccountRoute])
|
||||
const authenticatedTree = AuthRoute.addChildren([AuthIndexRoute.addChildren([DashboardRoute, filterRouteTree, ReleasesRoute.addChildren([ReleasesIndexRoute]), settingsRouteTree, LogsRoute])])
|
||||
const authenticatedTree = AuthRoute.addChildren([AuthIndexRoute.addChildren([DashboardRoute, filterRouteTree, ReleasesRoute, settingsRouteTree, LogsRoute])])
|
||||
const routeTree = RootRoute.addChildren([
|
||||
authenticatedTree,
|
||||
LoginRoute,
|
||||
|
@ -368,9 +354,10 @@ export const Router = createRouter({
|
|||
<RingResizeSpinner className="text-blue-500 size-24"/>
|
||||
</div>
|
||||
),
|
||||
defaultErrorComponent: ({error}) => <ErrorComponent error={error}/>,
|
||||
defaultErrorComponent: (ctx) => (
|
||||
<ErrorPage error={ctx.error} reset={ctx.reset} />
|
||||
),
|
||||
context: {
|
||||
auth: undefined!, // We'll inject this when we render
|
||||
queryClient
|
||||
},
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue