export const ReleasesIndexRoute = createRoute({
getParentRoute: () => ReleasesRoute,
path: '/',
component: Releases,
validateSearch: (search) => z.object({
offset: z.number().optional(),
limit: z.number().optional(),
filter: z.string().optional(),
q: z.string().optional(),
action_status: z.enum(['PUSH_APPROVED', 'PUSH_REJECTED', 'PUSH_ERROR', '']).optional(),
// filters: z.array().catch(''),
// sort: z.enum(['newest', 'oldest', 'price']).catch('newest'),
}).parse(search),
});
export const SettingsRoute = createRoute({
getParentRoute: () => AuthIndexRoute,
path: 'settings',
pendingMs: 3000,
component: Settings
});
export const SettingsIndexRoute = createRoute({
getParentRoute: () => SettingsRoute,
path: '/',
component: ApplicationSettings
});
export const SettingsLogRoute = createRoute({
getParentRoute: () => SettingsRoute,
path: 'logs',
loader: (opts) => opts.context.queryClient.ensureQueryData(ConfigQueryOptions()),
component: LogSettings
});
export const SettingsIndexersRoute = createRoute({
getParentRoute: () => SettingsRoute,
path: 'indexers',
loader: (opts) => opts.context.queryClient.ensureQueryData(IndexersQueryOptions()),
component: IndexerSettings
});
export const SettingsIrcRoute = createRoute({
getParentRoute: () => SettingsRoute,
path: 'irc',
loader: (opts) => opts.context.queryClient.ensureQueryData(IrcQueryOptions()),
component: IrcSettings
});
export const SettingsFeedsRoute = createRoute({
getParentRoute: () => SettingsRoute,
path: 'feeds',
loader: (opts) => opts.context.queryClient.ensureQueryData(FeedsQueryOptions()),
component: FeedSettings
});
export const SettingsClientsRoute = createRoute({
getParentRoute: () => SettingsRoute,
path: 'clients',
loader: (opts) => opts.context.queryClient.ensureQueryData(DownloadClientsQueryOptions()),
component: DownloadClientSettings
});
export const SettingsNotificationsRoute = createRoute({
getParentRoute: () => SettingsRoute,
path: 'notifications',
loader: (opts) => opts.context.queryClient.ensureQueryData(NotificationsQueryOptions()),
component: NotificationSettings
});
export const SettingsApiRoute = createRoute({
getParentRoute: () => SettingsRoute,
path: 'api',
loader: (opts) => opts.context.queryClient.ensureQueryData(ApikeysQueryOptions()),
component: APISettings
});
export const SettingsReleasesRoute = createRoute({
getParentRoute: () => SettingsRoute,
path: 'releases',
component: ReleaseSettings
});
export const SettingsAccountRoute = createRoute({
getParentRoute: () => SettingsRoute,
path: 'account',
component: AccountSettings
});
export const LogsRoute = createRoute({
getParentRoute: () => AuthIndexRoute,
path: 'logs',
component: Logs
});
export const OnboardRoute = createRoute({
getParentRoute: () => RootRoute,
path: 'onboard',
beforeLoad: async () => {
// Check if onboarding is available for this instance
// and redirect if needed
try {
await APIClient.auth.canOnboard()
} catch (e) {
console.error("onboarding not available, redirect to login")
throw redirect({
to: LoginRoute.to,
})
}
},
component: Onboarding
});
export const LoginRoute = createRoute({
getParentRoute: () => RootRoute,
path: 'login',
validateSearch: z.object({
redirect: z.string().optional(),
}),
beforeLoad: ({ navigate}) => {
// handle canOnboard
APIClient.auth.canOnboard().then(() => {
console.info("onboarding available, redirecting")
navigate({ to: OnboardRoute.to })
}).catch(() => {
console.info("onboarding not available, please login")
})
},
}).update({component: Login});
export const AuthRoute = createRoute({
getParentRoute: () => RootRoute,
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}) => {
// 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,
},
})
}
}
// Otherwise, return the user in context
return {
username: AuthContext.username,
}
},
})
function AuthenticatedLayout() {
return (
)
}
export const AuthIndexRoute = createRoute({
getParentRoute: () => AuthRoute,
component: AuthenticatedLayout,
id: 'authenticated-routes',
});
export const RootComponent = () => {
const settings = SettingsContext.useValue();
return (
{settings.debug ? (
<>
>
) : null}
)
}
export const RootRoute = createRootRouteWithContext<{
auth: AuthCtx,
queryClient: QueryClient
}>()({
component: RootComponent,
notFoundComponent: NotFound,
});
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 routeTree = RootRoute.addChildren([
authenticatedTree,
LoginRoute,
OnboardRoute
]);
export const Router = createRouter({
routeTree,
defaultPendingComponent: () => (
),
defaultErrorComponent: ({error}) => ,
context: {
auth: undefined!, // We'll inject this when we render
queryClient
},
});