1
0
Fork 0
mirror of https://github.com/jellyfin/jellyfin-web synced 2025-03-30 19:56:21 +00:00
jellyfin-web/src/components/ConnectionRequired.tsx
2023-02-28 15:38:08 -05:00

156 lines
6.8 KiB
TypeScript

import React, { FunctionComponent, useEffect, useState } from 'react';
import { Outlet, useLocation, useNavigate } from 'react-router-dom';
import type { ConnectResponse } from 'jellyfin-apiclient';
import alert from './alert';
import { appRouter } from './appRouter';
import Loading from './loading/LoadingComponent';
import ServerConnections from './ServerConnections';
import globalize from '../scripts/globalize';
import { ConnectionState } from '../utils/jellyfin-apiclient/ConnectionState';
enum BounceRoutes {
Home = '/home.html',
Login = '/login.html',
SelectServer = '/selectserver.html',
StartWizard = '/wizardstart.html'
}
type ConnectionRequiredProps = {
isAdminRequired?: boolean,
isUserRequired?: boolean
};
/**
* A component that ensures a server connection has been established.
* Additional parameters exist to verify a user or admin have authenticated.
* If a condition fails, this component will navigate to the appropriate page.
*/
const ConnectionRequired: FunctionComponent<ConnectionRequiredProps> = ({
isAdminRequired = false,
isUserRequired = true
}) => {
const navigate = useNavigate();
const location = useLocation();
const [ isLoading, setIsLoading ] = useState(true);
useEffect(() => {
const bounce = async (connectionResponse: ConnectResponse) => {
switch (connectionResponse.State) {
case ConnectionState.SignedIn:
// Already logged in, bounce to the home page
console.debug('[ConnectionRequired] already logged in, redirecting to home');
navigate(BounceRoutes.Home);
return;
case ConnectionState.ServerSignIn:
// Bounce to the login page
if (location.pathname === BounceRoutes.Login) {
setIsLoading(false);
} else {
console.debug('[ConnectionRequired] not logged in, redirecting to login page');
navigate(`${BounceRoutes.Login}?serverid=${connectionResponse.ApiClient.serverId()}`);
}
return;
case ConnectionState.ServerSelection:
// Bounce to select server page
console.debug('[ConnectionRequired] redirecting to select server page');
navigate(BounceRoutes.SelectServer);
return;
case ConnectionState.ServerUpdateNeeded:
// Show update needed message and bounce to select server page
try {
await alert({
text: globalize.translate('ServerUpdateNeeded', 'https://github.com/jellyfin/jellyfin'),
html: globalize.translate('ServerUpdateNeeded', '<a href="https://github.com/jellyfin/jellyfin">https://github.com/jellyfin/jellyfin</a>')
});
} catch (ex) {
console.warn('[ConnectionRequired] failed to show alert', ex);
}
console.debug('[ConnectionRequired] server update required, redirecting to select server page');
navigate(BounceRoutes.SelectServer);
return;
}
console.warn('[ConnectionRequired] unhandled connection state', connectionResponse.State);
};
const validateConnection = async () => {
// Check connection status on initial page load
const firstConnection = appRouter.firstConnectionResult;
appRouter.firstConnectionResult = null;
if (firstConnection && firstConnection.State !== ConnectionState.SignedIn) {
if (firstConnection.State === ConnectionState.ServerSignIn) {
// Verify the wizard is complete
try {
const infoResponse = await fetch(`${firstConnection.ApiClient.serverAddress()}/System/Info/Public`);
if (!infoResponse.ok) {
throw new Error('Public system info request failed');
}
const systemInfo = await infoResponse.json();
if (!systemInfo?.StartupWizardCompleted) {
// Update the current ApiClient
// TODO: Is there a better place to handle this?
ServerConnections.setLocalApiClient(firstConnection.ApiClient);
// Bounce to the wizard
console.info('[ConnectionRequired] startup wizard is not complete, redirecting there');
navigate(BounceRoutes.StartWizard);
return;
}
} catch (ex) {
console.error('[ConnectionRequired] checking wizard status failed', ex);
return;
}
}
// Bounce to the correct page in the login flow
bounce(firstConnection);
return;
}
// TODO: appRouter will call appHost.exit() if navigating back when you are already at the default route.
// This case will need to be handled elsewhere before appRouter can be killed.
const client = ServerConnections.currentApiClient();
// If this is a user route, ensure a user is logged in
if ((isAdminRequired || isUserRequired) && !client?.isLoggedIn()) {
try {
console.warn('[ConnectionRequired] unauthenticated user attempted to access user route');
bounce(await ServerConnections.connect());
} catch (ex) {
console.warn('[ConnectionRequired] error bouncing from user route', ex);
}
return;
}
// If this is an admin route, ensure the user has access
if (isAdminRequired) {
try {
const user = await client?.getCurrentUser();
if (!user?.Policy?.IsAdministrator) {
console.warn('[ConnectionRequired] normal user attempted to access admin route');
bounce(await ServerConnections.connect());
return;
}
} catch (ex) {
console.warn('[ConnectionRequired] error bouncing from admin route', ex);
return;
}
}
setIsLoading(false);
};
validateConnection();
}, [ isAdminRequired, isUserRequired, location.pathname, navigate ]);
if (isLoading) {
return <Loading />;
}
return <Outlet />;
};
export default ConnectionRequired;