From 64b79ec65c5cc78df699a93c49c67ef508132be4 Mon Sep 17 00:00:00 2001 From: Bill Thornton Date: Mon, 31 Mar 2025 17:32:54 -0400 Subject: [PATCH 1/3] Migrate wizard pages to dedicated app --- src/RootAppRouter.tsx | 2 + .../routes/legacyRoutes/public.ts | 42 ---------- src/apps/stable/routes/legacyRoutes/public.ts | 44 +---------- .../wizard/controllers}/finish/index.html | 0 .../wizard/controllers}/finish/index.js | 2 +- .../wizard/controllers}/library.html | 0 .../wizard/controllers}/library.js | 2 +- .../wizard/controllers}/remote/index.html | 0 .../wizard/controllers}/remote/index.js | 12 +-- .../wizard/controllers}/settings/index.html | 0 .../wizard/controllers}/settings/index.js | 12 +-- .../wizard/controllers}/start/index.html | 0 .../wizard/controllers}/start/index.js | 10 +-- .../wizard/controllers}/user/index.html | 0 .../wizard/controllers}/user/index.js | 16 ++-- src/apps/wizard/routes/routes.tsx | 76 +++++++++++++++++++ .../viewManager/ViewManagerPage.tsx | 31 +++++--- src/constants/appType.ts | 3 +- 18 files changed, 127 insertions(+), 125 deletions(-) rename src/{controllers/wizard => apps/wizard/controllers}/finish/index.html (100%) rename src/{controllers/wizard => apps/wizard/controllers}/finish/index.js (85%) rename src/{controllers/wizard => apps/wizard/controllers}/library.html (100%) rename src/{controllers/wizard => apps/wizard/controllers}/library.js (99%) rename src/{controllers/wizard => apps/wizard/controllers}/remote/index.html (100%) rename src/{controllers/wizard => apps/wizard/controllers}/remote/index.js (75%) rename src/{controllers/wizard => apps/wizard/controllers}/settings/index.html (100%) rename src/{controllers/wizard => apps/wizard/controllers}/settings/index.js (89%) rename src/{controllers/wizard => apps/wizard/controllers}/start/index.html (100%) rename src/{controllers/wizard => apps/wizard/controllers}/start/index.js (87%) rename src/{controllers/wizard => apps/wizard/controllers}/user/index.html (100%) rename src/{controllers/wizard => apps/wizard/controllers}/user/index.js (84%) create mode 100644 src/apps/wizard/routes/routes.tsx diff --git a/src/RootAppRouter.tsx b/src/RootAppRouter.tsx index bed7a9e4a..b6831185a 100644 --- a/src/RootAppRouter.tsx +++ b/src/RootAppRouter.tsx @@ -15,6 +15,7 @@ import Backdrop from 'components/Backdrop'; import BangRedirect from 'components/router/BangRedirect'; import { createRouterHistory } from 'components/router/routerHistory'; import UserThemeProvider from 'themes/UserThemeProvider'; +import { WIZARD_APP_ROUTES } from 'apps/wizard/routes/routes'; const layoutMode = localStorage.getItem('layout'); const isExperimentalLayout = layoutMode === 'experimental'; @@ -25,6 +26,7 @@ const router = createHashRouter([ children: [ ...(isExperimentalLayout ? EXPERIMENTAL_APP_ROUTES : STABLE_APP_ROUTES), ...DASHBOARD_APP_ROUTES, + ...WIZARD_APP_ROUTES, { path: '!/*', Component: BangRedirect diff --git a/src/apps/experimental/routes/legacyRoutes/public.ts b/src/apps/experimental/routes/legacyRoutes/public.ts index 89fc17935..1808c1bfa 100644 --- a/src/apps/experimental/routes/legacyRoutes/public.ts +++ b/src/apps/experimental/routes/legacyRoutes/public.ts @@ -35,47 +35,5 @@ export const LEGACY_PUBLIC_ROUTES: LegacyRoute[] = [ controller: 'session/resetPassword/index', view: 'session/resetPassword/index.html' } - }, - { - path: 'wizardremoteaccess', - pageProps: { - controller: 'wizard/remote/index', - view: 'wizard/remote/index.html' - } - }, - { - path: 'wizardfinish', - pageProps: { - controller: 'wizard/finish/index', - view: 'wizard/finish/index.html' - } - }, - { - path: 'wizardlibrary', - pageProps: { - controller: 'wizard/library', - view: 'wizard/library.html' - } - }, - { - path: 'wizardsettings', - pageProps: { - controller: 'wizard/settings/index', - view: 'wizard/settings/index.html' - } - }, - { - path: 'wizardstart', - pageProps: { - controller: 'wizard/start/index', - view: 'wizard/start/index.html' - } - }, - { - path: 'wizarduser', - pageProps: { - controller: 'wizard/user/index', - view: 'wizard/user/index.html' - } } ]; diff --git a/src/apps/stable/routes/legacyRoutes/public.ts b/src/apps/stable/routes/legacyRoutes/public.ts index 89fc17935..abf6439bc 100644 --- a/src/apps/stable/routes/legacyRoutes/public.ts +++ b/src/apps/stable/routes/legacyRoutes/public.ts @@ -1,4 +1,4 @@ -import { LegacyRoute } from '../../../../components/router/LegacyRoute'; +import type { LegacyRoute } from 'components/router/LegacyRoute'; export const LEGACY_PUBLIC_ROUTES: LegacyRoute[] = [ { @@ -35,47 +35,5 @@ export const LEGACY_PUBLIC_ROUTES: LegacyRoute[] = [ controller: 'session/resetPassword/index', view: 'session/resetPassword/index.html' } - }, - { - path: 'wizardremoteaccess', - pageProps: { - controller: 'wizard/remote/index', - view: 'wizard/remote/index.html' - } - }, - { - path: 'wizardfinish', - pageProps: { - controller: 'wizard/finish/index', - view: 'wizard/finish/index.html' - } - }, - { - path: 'wizardlibrary', - pageProps: { - controller: 'wizard/library', - view: 'wizard/library.html' - } - }, - { - path: 'wizardsettings', - pageProps: { - controller: 'wizard/settings/index', - view: 'wizard/settings/index.html' - } - }, - { - path: 'wizardstart', - pageProps: { - controller: 'wizard/start/index', - view: 'wizard/start/index.html' - } - }, - { - path: 'wizarduser', - pageProps: { - controller: 'wizard/user/index', - view: 'wizard/user/index.html' - } } ]; diff --git a/src/controllers/wizard/finish/index.html b/src/apps/wizard/controllers/finish/index.html similarity index 100% rename from src/controllers/wizard/finish/index.html rename to src/apps/wizard/controllers/finish/index.html diff --git a/src/controllers/wizard/finish/index.js b/src/apps/wizard/controllers/finish/index.js similarity index 85% rename from src/controllers/wizard/finish/index.js rename to src/apps/wizard/controllers/finish/index.js index e60653fd7..51a3f8ad3 100644 --- a/src/controllers/wizard/finish/index.js +++ b/src/apps/wizard/controllers/finish/index.js @@ -1,4 +1,4 @@ -import loading from '../../../components/loading/loading'; +import loading from 'components/loading/loading'; function onFinish() { loading.show(); diff --git a/src/controllers/wizard/library.html b/src/apps/wizard/controllers/library.html similarity index 100% rename from src/controllers/wizard/library.html rename to src/apps/wizard/controllers/library.html diff --git a/src/controllers/wizard/library.js b/src/apps/wizard/controllers/library.js similarity index 99% rename from src/controllers/wizard/library.js rename to src/apps/wizard/controllers/library.js index 162e5b100..19ff51118 100644 --- a/src/controllers/wizard/library.js +++ b/src/apps/wizard/controllers/library.js @@ -361,7 +361,7 @@ function getVirtualFolderHtml(page, virtualFolder, index) { window.WizardLibraryPage = { next: function () { - Dashboard.navigate('wizardsettings'); + Dashboard.navigate('wizard/settings'); } }; pageClassOn('pageshow', 'mediaLibraryPage', function () { diff --git a/src/controllers/wizard/remote/index.html b/src/apps/wizard/controllers/remote/index.html similarity index 100% rename from src/controllers/wizard/remote/index.html rename to src/apps/wizard/controllers/remote/index.html diff --git a/src/controllers/wizard/remote/index.js b/src/apps/wizard/controllers/remote/index.js similarity index 75% rename from src/controllers/wizard/remote/index.js rename to src/apps/wizard/controllers/remote/index.js index 65b176488..2948a0824 100644 --- a/src/controllers/wizard/remote/index.js +++ b/src/apps/wizard/controllers/remote/index.js @@ -1,8 +1,8 @@ -import loading from '../../../components/loading/loading'; -import '../../../elements/emby-checkbox/emby-checkbox'; -import '../../../elements/emby-button/emby-button'; -import '../../../elements/emby-select/emby-select'; -import Dashboard from '../../../utils/dashboard'; +import loading from 'components/loading/loading'; +import 'elements/emby-checkbox/emby-checkbox'; +import 'elements/emby-button/emby-button'; +import 'elements/emby-select/emby-select'; +import Dashboard from 'utils/dashboard'; function save(page) { loading.show(); @@ -23,7 +23,7 @@ function save(page) { } function navigateToNextPage() { - Dashboard.navigate('wizardfinish'); + Dashboard.navigate('wizard/finish'); } function onSubmit(e) { diff --git a/src/controllers/wizard/settings/index.html b/src/apps/wizard/controllers/settings/index.html similarity index 100% rename from src/controllers/wizard/settings/index.html rename to src/apps/wizard/controllers/settings/index.html diff --git a/src/controllers/wizard/settings/index.js b/src/apps/wizard/controllers/settings/index.js similarity index 89% rename from src/controllers/wizard/settings/index.js rename to src/apps/wizard/controllers/settings/index.js index e37abceaa..1bf069ec0 100644 --- a/src/controllers/wizard/settings/index.js +++ b/src/apps/wizard/controllers/settings/index.js @@ -1,8 +1,8 @@ -import loading from '../../../components/loading/loading'; -import '../../../elements/emby-checkbox/emby-checkbox'; -import '../../../elements/emby-button/emby-button'; -import '../../../elements/emby-select/emby-select'; -import Dashboard from '../../../utils/dashboard'; +import loading from 'components/loading/loading'; +import 'elements/emby-checkbox/emby-checkbox'; +import 'elements/emby-button/emby-button'; +import 'elements/emby-select/emby-select'; +import Dashboard from 'utils/dashboard'; function save(page) { loading.show(); @@ -66,7 +66,7 @@ function reload(page) { } function navigateToNextPage() { - Dashboard.navigate('wizardremoteaccess'); + Dashboard.navigate('wizard/remoteaccess'); } function onSubmit(e) { diff --git a/src/controllers/wizard/start/index.html b/src/apps/wizard/controllers/start/index.html similarity index 100% rename from src/controllers/wizard/start/index.html rename to src/apps/wizard/controllers/start/index.html diff --git a/src/controllers/wizard/start/index.js b/src/apps/wizard/controllers/start/index.js similarity index 87% rename from src/controllers/wizard/start/index.js rename to src/apps/wizard/controllers/start/index.js index 08d3fcd2d..1ba18df01 100644 --- a/src/controllers/wizard/start/index.js +++ b/src/apps/wizard/controllers/start/index.js @@ -1,7 +1,7 @@ -import loading from '../../../components/loading/loading'; -import '../../../elements/emby-button/emby-button'; -import '../../../elements/emby-select/emby-select'; -import Dashboard from '../../../utils/dashboard'; +import loading from 'components/loading/loading'; +import 'elements/emby-button/emby-button'; +import 'elements/emby-select/emby-select'; +import Dashboard from 'utils/dashboard'; import dom from 'scripts/dom'; function loadPage(page, config, languageOptions) { @@ -24,7 +24,7 @@ function save(page) { url: apiClient.getUrl('Startup/Configuration'), contentType: 'application/json' }).then(function () { - Dashboard.navigate('wizarduser'); + Dashboard.navigate('wizard/user'); }); }); } diff --git a/src/controllers/wizard/user/index.html b/src/apps/wizard/controllers/user/index.html similarity index 100% rename from src/controllers/wizard/user/index.html rename to src/apps/wizard/controllers/user/index.html diff --git a/src/controllers/wizard/user/index.js b/src/apps/wizard/controllers/user/index.js similarity index 84% rename from src/controllers/wizard/user/index.js rename to src/apps/wizard/controllers/user/index.js index 1d4836f35..9e2fb68ba 100644 --- a/src/controllers/wizard/user/index.js +++ b/src/apps/wizard/controllers/user/index.js @@ -1,17 +1,17 @@ -import loading from '../../../components/loading/loading'; -import globalize from '../../../lib/globalize'; -import '../../../styles/dashboard.scss'; -import '../../../elements/emby-input/emby-input'; -import '../../../elements/emby-button/emby-button'; -import Dashboard from '../../../utils/dashboard'; -import toast from '../../../components/toast/toast'; +import loading from 'components/loading/loading'; +import globalize from 'lib/globalize'; +import 'styles/dashboard.scss'; +import 'elements/emby-input/emby-input'; +import 'elements/emby-button/emby-button'; +import Dashboard from 'utils/dashboard'; +import toast from 'components/toast/toast'; function getApiClient() { return ApiClient; } function nextWizardPage() { - Dashboard.navigate('wizardlibrary') + Dashboard.navigate('wizard/library') .catch(err => { console.error('[Wizard > User] error navigating to library setup', err); }); diff --git a/src/apps/wizard/routes/routes.tsx b/src/apps/wizard/routes/routes.tsx new file mode 100644 index 000000000..81f812cc9 --- /dev/null +++ b/src/apps/wizard/routes/routes.tsx @@ -0,0 +1,76 @@ +import React from 'react'; +import { Navigate, RouteObject } from 'react-router-dom'; + +import AppLayout from 'apps/stable/AppLayout'; +import { AppType } from 'constants/appType'; +import ConnectionRequired from 'components/ConnectionRequired'; +import ErrorBoundary from 'components/router/ErrorBoundary'; +import { type LegacyRoute, toViewManagerPageRoute } from 'components/router/LegacyRoute'; + +const ROUTES: LegacyRoute[] = [ + { + path: 'remoteaccess', + pageProps: { + appType: AppType.Wizard, + controller: 'remote/index', + view: 'remote/index.html' + } + }, + { + path: 'finish', + pageProps: { + appType: AppType.Wizard, + controller: 'finish/index', + view: 'finish/index.html' + } + }, + { + path: 'library', + pageProps: { + appType: AppType.Wizard, + controller: 'library', + view: 'library.html' + } + }, + { + path: 'settings', + pageProps: { + appType: AppType.Wizard, + controller: 'settings/index', + view: 'settings/index.html' + } + }, + { + path: 'start', + pageProps: { + appType: AppType.Wizard, + controller: 'start/index', + view: 'start/index.html' + } + }, + { + path: 'user', + pageProps: { + appType: AppType.Wizard, + controller: 'user/index', + view: 'user/index.html' + } + } +]; + +export const WIZARD_APP_ROUTES: RouteObject[] = [ + { + element: , + children: [ + { + Component: AppLayout, + path: 'wizard', + children: [ + { index: true, element: }, + ...ROUTES.map(toViewManagerPageRoute) + ], + ErrorBoundary + } + ] + } +]; diff --git a/src/components/viewManager/ViewManagerPage.tsx b/src/components/viewManager/ViewManagerPage.tsx index 1aea49ba6..51f76c327 100644 --- a/src/components/viewManager/ViewManagerPage.tsx +++ b/src/components/viewManager/ViewManagerPage.tsx @@ -38,19 +38,26 @@ const importController = ( controller: string, view: string ) => { - if (appType === AppType.Dashboard) { - return Promise.all([ - import(/* webpackChunkName: "[request]" */ `../../apps/dashboard/controllers/${controller}`), - import(/* webpackChunkName: "[request]" */ `../../apps/dashboard/controllers/${view}`) - .then(html => globalize.translateHtml(html)) - ]); + switch (appType) { + case AppType.Dashboard: + return Promise.all([ + import(/* webpackChunkName: "[request]" */ `../../apps/dashboard/controllers/${controller}`), + import(/* webpackChunkName: "[request]" */ `../../apps/dashboard/controllers/${view}`) + .then(html => globalize.translateHtml(html)) + ]); + case AppType.Wizard: + return Promise.all([ + import(/* webpackChunkName: "[request]" */ `../../apps/wizard/controllers/${controller}`), + import(/* webpackChunkName: "[request]" */ `../../apps/wizard/controllers/${view}`) + .then(html => globalize.translateHtml(html)) + ]); + default: + return Promise.all([ + import(/* webpackChunkName: "[request]" */ `../../controllers/${controller}`), + import(/* webpackChunkName: "[request]" */ `../../controllers/${view}`) + .then(html => globalize.translateHtml(html)) + ]); } - - return Promise.all([ - import(/* webpackChunkName: "[request]" */ `../../controllers/${controller}`), - import(/* webpackChunkName: "[request]" */ `../../controllers/${view}`) - .then(html => globalize.translateHtml(html)) - ]); }; const loadView = async ( diff --git a/src/constants/appType.ts b/src/constants/appType.ts index efed1f5ff..9a2fa12d1 100644 --- a/src/constants/appType.ts +++ b/src/constants/appType.ts @@ -4,5 +4,6 @@ export enum AppType { Dashboard = 'dashboard', Experimental = 'experimental', - Stable = 'stable' + Stable = 'stable', + Wizard = 'wizard' } From 65d25da85ff2be4e99beede9126990e6d990a6fe Mon Sep 17 00:00:00 2001 From: Bill Thornton Date: Mon, 31 Mar 2025 17:33:25 -0400 Subject: [PATCH 2/3] Add redirect for old wizard paths --- src/components/router/FallbackRoute.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/components/router/FallbackRoute.tsx b/src/components/router/FallbackRoute.tsx index 7f832c166..e2f3f8d77 100644 --- a/src/components/router/FallbackRoute.tsx +++ b/src/components/router/FallbackRoute.tsx @@ -15,6 +15,11 @@ const FallbackRoute = () => { hash: location.hash }; + // Redirect old wizard paths + if (RegExp(/^\/wizard[a-z]+\.html/i).test(location.pathname)) { + return { ..._to, pathname: `/wizard/${location.pathname.slice(7, -5)}` }; + } + // If a path ends in ".html", redirect to the path with it removed if (location.pathname.endsWith('.html')) { return { ..._to, pathname: location.pathname.slice(0, -5) }; From c446460ba7993ba2aac5db6dd1f8678b7dc2a9f6 Mon Sep 17 00:00:00 2001 From: Bill Thornton Date: Tue, 1 Apr 2025 11:56:51 -0400 Subject: [PATCH 3/3] Update ConnectionRequired to prevent wizard access when complete --- src/RootAppRouter.tsx | 2 +- src/apps/dashboard/routes/routes.tsx | 2 +- src/apps/experimental/routes/routes.tsx | 2 +- src/apps/stable/routes/routes.tsx | 2 +- src/apps/wizard/routes/routes.tsx | 2 +- src/components/ConnectionRequired.tsx | 78 +++++++++++++++++++------ 6 files changed, 65 insertions(+), 23 deletions(-) diff --git a/src/RootAppRouter.tsx b/src/RootAppRouter.tsx index b6831185a..837c2f38b 100644 --- a/src/RootAppRouter.tsx +++ b/src/RootAppRouter.tsx @@ -10,12 +10,12 @@ import { import { DASHBOARD_APP_PATHS, DASHBOARD_APP_ROUTES } from 'apps/dashboard/routes/routes'; import { EXPERIMENTAL_APP_ROUTES } from 'apps/experimental/routes/routes'; import { STABLE_APP_ROUTES } from 'apps/stable/routes/routes'; +import { WIZARD_APP_ROUTES } from 'apps/wizard/routes/routes'; import AppHeader from 'components/AppHeader'; import Backdrop from 'components/Backdrop'; import BangRedirect from 'components/router/BangRedirect'; import { createRouterHistory } from 'components/router/routerHistory'; import UserThemeProvider from 'themes/UserThemeProvider'; -import { WIZARD_APP_ROUTES } from 'apps/wizard/routes/routes'; const layoutMode = localStorage.getItem('layout'); const isExperimentalLayout = layoutMode === 'experimental'; diff --git a/src/apps/dashboard/routes/routes.tsx b/src/apps/dashboard/routes/routes.tsx index 576bdd620..035bcc352 100644 --- a/src/apps/dashboard/routes/routes.tsx +++ b/src/apps/dashboard/routes/routes.tsx @@ -17,7 +17,7 @@ export const DASHBOARD_APP_PATHS = { export const DASHBOARD_APP_ROUTES: RouteObject[] = [ { - element: , + element: , children: [ { lazy: () => import('../AppLayout'), diff --git a/src/apps/experimental/routes/routes.tsx b/src/apps/experimental/routes/routes.tsx index 533972b89..f783acb49 100644 --- a/src/apps/experimental/routes/routes.tsx +++ b/src/apps/experimental/routes/routes.tsx @@ -36,7 +36,7 @@ export const EXPERIMENTAL_APP_ROUTES: RouteObject[] = [ { /* Public routes */ - element: , + element: , children: [ ...LEGACY_PUBLIC_ROUTES.map(toViewManagerPageRoute), diff --git a/src/apps/stable/routes/routes.tsx b/src/apps/stable/routes/routes.tsx index 230a41953..bcd328ace 100644 --- a/src/apps/stable/routes/routes.tsx +++ b/src/apps/stable/routes/routes.tsx @@ -31,7 +31,7 @@ export const STABLE_APP_ROUTES: RouteObject[] = [ { /* Public routes */ - element: , + element: , children: [ ...LEGACY_PUBLIC_ROUTES.map(toViewManagerPageRoute), /* Fallback route for invalid paths */ diff --git a/src/apps/wizard/routes/routes.tsx b/src/apps/wizard/routes/routes.tsx index 81f812cc9..c328d7aae 100644 --- a/src/apps/wizard/routes/routes.tsx +++ b/src/apps/wizard/routes/routes.tsx @@ -60,7 +60,7 @@ const ROUTES: LegacyRoute[] = [ export const WIZARD_APP_ROUTES: RouteObject[] = [ { - element: , + element: , children: [ { Component: AppLayout, diff --git a/src/components/ConnectionRequired.tsx b/src/components/ConnectionRequired.tsx index 8106135e6..443a5e1a0 100644 --- a/src/components/ConnectionRequired.tsx +++ b/src/components/ConnectionRequired.tsx @@ -1,23 +1,49 @@ import React, { FunctionComponent, useCallback, useEffect, useState } from 'react'; import { Outlet, useLocation, useNavigate } from 'react-router-dom'; -import type { ConnectResponse } from 'jellyfin-apiclient'; +import type { ApiClient, ConnectResponse } from 'jellyfin-apiclient'; + +import globalize from 'lib/globalize'; +import { ConnectionState } from 'utils/jellyfin-apiclient/ConnectionState'; import alert from './alert'; import Loading from './loading/LoadingComponent'; import ServerConnections from './ServerConnections'; -import globalize from '../lib/globalize'; -import { ConnectionState } from '../utils/jellyfin-apiclient/ConnectionState'; + +enum AccessLevel { + /** Requires a user with administrator access */ + Admin = 'admin', + /** No access restrictions */ + Public = 'public', + /** Requires a valid user session */ + User = 'user', + /** Requires the startup wizard to NOT be completed */ + Wizard = 'wizard' +}; + +type AccessLevelValue = `${AccessLevel}`; enum BounceRoutes { Home = '/home', Login = '/login', SelectServer = '/selectserver', - StartWizard = '/wizardstart' + StartWizard = '/wizard/start' } type ConnectionRequiredProps = { - isAdminRequired?: boolean, - isUserRequired?: boolean + level?: AccessLevelValue +}; + +const fetchPublicSystemInfo = async (apiClient: ApiClient) => { + const infoResponse = await fetch( + `${apiClient.serverAddress()}/System/Info/Public`, + { cache: 'no-cache' } + ); + + if (!infoResponse.ok) { + throw new Error('Public system info request failed'); + } + + return infoResponse.json(); }; /** @@ -26,8 +52,7 @@ type ConnectionRequiredProps = { * If a condition fails, this component will navigate to the appropriate page. */ const ConnectionRequired: FunctionComponent = ({ - isAdminRequired = false, - isUserRequired = true + level = 'user' }) => { const navigate = useNavigate(); const location = useLocation(); @@ -76,17 +101,27 @@ const ConnectionRequired: FunctionComponent = ({ } console.warn('[ConnectionRequired] unhandled connection state', connectionResponse.State); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [location.pathname, navigate]); + const handleWizard = useCallback(async (firstConnection: ConnectResponse | null) => { + const apiClient = firstConnection?.ApiClient || ServerConnections.currentApiClient(); + if (!apiClient) { + throw new Error('No ApiClient available'); + } + + const systemInfo = await fetchPublicSystemInfo(apiClient); + if (systemInfo?.StartupWizardCompleted) { + console.info('[ConnectionRequired] startup wizard is complete, redirecting home'); + navigate(BounceRoutes.Home); + } + }, [ navigate ]); + const handleIncompleteWizard = useCallback(async (firstConnection: ConnectResponse) => { if (firstConnection.State === ConnectionState.ServerSignIn) { // Verify the wizard is complete try { - const infoResponse = await fetch(`${firstConnection.ApiClient.serverAddress()}/System/Info/Public`, { cache: 'no-cache' }); - if (!infoResponse.ok) { - throw new Error('Public system info request failed'); - } - const systemInfo = await infoResponse.json(); + const systemInfo = await fetchPublicSystemInfo(firstConnection.ApiClient); if (!systemInfo?.StartupWizardCompleted) { // Update the current ApiClient // TODO: Is there a better place to handle this? @@ -113,7 +148,7 @@ const ConnectionRequired: FunctionComponent = ({ const client = ServerConnections.currentApiClient(); // If this is a user route, ensure a user is logged in - if ((isAdminRequired || isUserRequired) && !client?.isLoggedIn()) { + if ((level === AccessLevel.Admin || level === AccessLevel.User) && !client?.isLoggedIn()) { try { console.warn('[ConnectionRequired] unauthenticated user attempted to access user route'); bounce(await ServerConnections.connect()) @@ -127,7 +162,7 @@ const ConnectionRequired: FunctionComponent = ({ } // If this is an admin route, ensure the user has access - if (isAdminRequired) { + if (level === AccessLevel.Admin) { try { const user = await client?.getCurrentUser(); if (!user?.Policy?.IsAdministrator) { @@ -145,7 +180,7 @@ const ConnectionRequired: FunctionComponent = ({ } setIsLoading(false); - }, [bounce, isAdminRequired, isUserRequired]); + }, [bounce, level]); useEffect(() => { // Check connection status on initial page load @@ -155,7 +190,14 @@ const ConnectionRequired: FunctionComponent = ({ console.debug('[ConnectionRequired] connection state', firstConnection?.State); ServerConnections.firstConnection = true; - if (firstConnection && firstConnection.State !== ConnectionState.SignedIn && !apiClient?.isLoggedIn()) { + if (level === AccessLevel.Wizard) { + handleWizard(firstConnection) + .catch(err => { + console.error('[ConnectionRequired] could not validate wizard status', err); + }); + } else if ( + firstConnection && firstConnection.State !== ConnectionState.SignedIn && !apiClient?.isLoggedIn() + ) { handleIncompleteWizard(firstConnection) .catch(err => { console.error('[ConnectionRequired] could not start wizard', err); @@ -169,7 +211,7 @@ const ConnectionRequired: FunctionComponent = ({ }).catch(err => { console.error('[ConnectionRequired] failed to connect', err); }); - }, [handleIncompleteWizard, validateUserAccess]); + }, [handleIncompleteWizard, handleWizard, level, validateUserAccess]); if (isLoading) { return ;