mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-09 01:09:51 +03:00
eslint
This commit is contained in:
@@ -1,40 +1,56 @@
|
||||
import * as React from 'react';
|
||||
import { Redirect, Route, RouteProps, RouteComponentProps } from "react-router-dom";
|
||||
import {
|
||||
Redirect,
|
||||
Route,
|
||||
RouteProps,
|
||||
RouteComponentProps
|
||||
} from 'react-router-dom';
|
||||
import { withSnackbar, WithSnackbarProps } from 'notistack';
|
||||
|
||||
import * as Authentication from './Authentication';
|
||||
import { withAuthenticationContext, AuthenticationContextProps, AuthenticatedContext, AuthenticatedContextValue } from './AuthenticationContext';
|
||||
import {
|
||||
withAuthenticationContext,
|
||||
AuthenticationContextProps,
|
||||
AuthenticatedContext,
|
||||
AuthenticatedContextValue
|
||||
} from './AuthenticationContext';
|
||||
|
||||
interface AuthenticatedRouteProps extends RouteProps, WithSnackbarProps, AuthenticationContextProps {
|
||||
component: React.ComponentType<RouteComponentProps<any>> | React.ComponentType<any>;
|
||||
interface AuthenticatedRouteProps
|
||||
extends RouteProps,
|
||||
WithSnackbarProps,
|
||||
AuthenticationContextProps {
|
||||
component:
|
||||
| React.ComponentType<RouteComponentProps<any>>
|
||||
| React.ComponentType<any>;
|
||||
}
|
||||
|
||||
type RenderComponent = (props: RouteComponentProps<any>) => React.ReactNode;
|
||||
|
||||
export class AuthenticatedRoute extends React.Component<AuthenticatedRouteProps> {
|
||||
|
||||
render() {
|
||||
const { enqueueSnackbar, authenticationContext, component: Component, ...rest } = this.props;
|
||||
const {
|
||||
enqueueSnackbar,
|
||||
authenticationContext,
|
||||
component: Component,
|
||||
...rest
|
||||
} = this.props;
|
||||
const { location } = this.props;
|
||||
const renderComponent: RenderComponent = (props) => {
|
||||
if (authenticationContext.me) {
|
||||
return (
|
||||
<AuthenticatedContext.Provider value={authenticationContext as AuthenticatedContextValue}>
|
||||
<AuthenticatedContext.Provider
|
||||
value={authenticationContext as AuthenticatedContextValue}
|
||||
>
|
||||
<Component {...props} />
|
||||
</AuthenticatedContext.Provider>
|
||||
);
|
||||
}
|
||||
Authentication.storeLoginRedirect(location);
|
||||
enqueueSnackbar("Please sign in to continue", { variant: 'info' });
|
||||
return (
|
||||
<Redirect to='/' />
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Route {...rest} render={renderComponent} />
|
||||
);
|
||||
enqueueSnackbar('Please sign in to continue', { variant: 'info' });
|
||||
return <Redirect to="/" />;
|
||||
};
|
||||
return <Route {...rest} render={renderComponent} />;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default withSnackbar(withAuthenticationContext(AuthenticatedRoute));
|
||||
|
||||
@@ -1,42 +1,42 @@
|
||||
import * as H from 'history'
|
||||
import * as H from 'history';
|
||||
|
||||
import history from '../history'
|
||||
import { Features } from '../features/types'
|
||||
import { getDefaultRoute } from '../AppRouting'
|
||||
import history from '../history';
|
||||
import { Features } from '../features/types';
|
||||
import { getDefaultRoute } from '../AppRouting';
|
||||
|
||||
export const ACCESS_TOKEN = 'access_token'
|
||||
export const SIGN_IN_PATHNAME = 'signInPathname'
|
||||
export const SIGN_IN_SEARCH = 'signInSearch'
|
||||
export const ACCESS_TOKEN = 'access_token';
|
||||
export const SIGN_IN_PATHNAME = 'signInPathname';
|
||||
export const SIGN_IN_SEARCH = 'signInSearch';
|
||||
|
||||
/**
|
||||
* Fallback to sessionStorage if localStorage is absent. WebView may not have local storage enabled.
|
||||
*/
|
||||
export function getStorage() {
|
||||
return localStorage || sessionStorage
|
||||
return localStorage || sessionStorage;
|
||||
}
|
||||
|
||||
export function storeLoginRedirect(location?: H.Location) {
|
||||
if (location) {
|
||||
getStorage().setItem(SIGN_IN_PATHNAME, location.pathname)
|
||||
getStorage().setItem(SIGN_IN_SEARCH, location.search)
|
||||
getStorage().setItem(SIGN_IN_PATHNAME, location.pathname);
|
||||
getStorage().setItem(SIGN_IN_SEARCH, location.search);
|
||||
}
|
||||
}
|
||||
|
||||
export function clearLoginRedirect() {
|
||||
getStorage().removeItem(SIGN_IN_PATHNAME)
|
||||
getStorage().removeItem(SIGN_IN_SEARCH)
|
||||
getStorage().removeItem(SIGN_IN_PATHNAME);
|
||||
getStorage().removeItem(SIGN_IN_SEARCH);
|
||||
}
|
||||
|
||||
export function fetchLoginRedirect(
|
||||
features: Features,
|
||||
features: Features
|
||||
): H.LocationDescriptorObject {
|
||||
const signInPathname = getStorage().getItem(SIGN_IN_PATHNAME)
|
||||
const signInSearch = getStorage().getItem(SIGN_IN_SEARCH)
|
||||
clearLoginRedirect()
|
||||
const signInPathname = getStorage().getItem(SIGN_IN_PATHNAME);
|
||||
const signInSearch = getStorage().getItem(SIGN_IN_SEARCH);
|
||||
clearLoginRedirect();
|
||||
return {
|
||||
pathname: signInPathname || getDefaultRoute(features),
|
||||
search: (signInPathname && signInSearch) || undefined,
|
||||
}
|
||||
search: (signInPathname && signInSearch) || undefined
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -44,18 +44,18 @@ export function fetchLoginRedirect(
|
||||
*/
|
||||
export function authorizedFetch(
|
||||
url: RequestInfo,
|
||||
params?: RequestInit,
|
||||
params?: RequestInit
|
||||
): Promise<Response> {
|
||||
const accessToken = getStorage().getItem(ACCESS_TOKEN)
|
||||
const accessToken = getStorage().getItem(ACCESS_TOKEN);
|
||||
if (accessToken) {
|
||||
params = params || {}
|
||||
params.credentials = 'include'
|
||||
params = params || {};
|
||||
params.credentials = 'include';
|
||||
params.headers = {
|
||||
...params.headers,
|
||||
Authorization: 'Bearer ' + accessToken,
|
||||
}
|
||||
Authorization: 'Bearer ' + accessToken
|
||||
};
|
||||
}
|
||||
return fetch(url, params)
|
||||
return fetch(url, params);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,33 +67,33 @@ export function redirectingAuthorizedUpload(
|
||||
xhr: XMLHttpRequest,
|
||||
url: string,
|
||||
file: File,
|
||||
onProgress: (event: ProgressEvent<EventTarget>) => void,
|
||||
onProgress: (event: ProgressEvent<EventTarget>) => void
|
||||
): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
xhr.open('POST', url, true)
|
||||
const accessToken = getStorage().getItem(ACCESS_TOKEN)
|
||||
xhr.open('POST', url, true);
|
||||
const accessToken = getStorage().getItem(ACCESS_TOKEN);
|
||||
if (accessToken) {
|
||||
xhr.withCredentials = true
|
||||
xhr.setRequestHeader('Authorization', 'Bearer ' + accessToken)
|
||||
xhr.withCredentials = true;
|
||||
xhr.setRequestHeader('Authorization', 'Bearer ' + accessToken);
|
||||
}
|
||||
xhr.upload.onprogress = onProgress
|
||||
xhr.upload.onprogress = onProgress;
|
||||
xhr.onload = function () {
|
||||
if (xhr.status === 401 || xhr.status === 403) {
|
||||
history.push('/unauthorized')
|
||||
history.push('/unauthorized');
|
||||
} else {
|
||||
resolve()
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
xhr.onerror = function (event: ProgressEvent<EventTarget>) {
|
||||
reject(new DOMException('Error', 'UploadError'))
|
||||
}
|
||||
};
|
||||
xhr.onerror = function () {
|
||||
reject(new DOMException('Error', 'UploadError'));
|
||||
};
|
||||
xhr.onabort = function () {
|
||||
reject(new DOMException('Aborted', 'AbortError'))
|
||||
}
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
xhr.send(formData)
|
||||
})
|
||||
reject(new DOMException('Aborted', 'AbortError'));
|
||||
};
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
xhr.send(formData);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,29 +101,29 @@ export function redirectingAuthorizedUpload(
|
||||
*/
|
||||
export function redirectingAuthorizedFetch(
|
||||
url: RequestInfo,
|
||||
params?: RequestInit,
|
||||
params?: RequestInit
|
||||
): Promise<Response> {
|
||||
return new Promise<Response>((resolve, reject) => {
|
||||
authorizedFetch(url, params)
|
||||
.then((response) => {
|
||||
if (response.status === 401 || response.status === 403) {
|
||||
history.push('/unauthorized')
|
||||
history.push('/unauthorized');
|
||||
} else {
|
||||
resolve(response)
|
||||
resolve(response);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function addAccessTokenParameter(url: string) {
|
||||
const accessToken = getStorage().getItem(ACCESS_TOKEN)
|
||||
const accessToken = getStorage().getItem(ACCESS_TOKEN);
|
||||
if (!accessToken) {
|
||||
return url
|
||||
return url;
|
||||
}
|
||||
const parsedUrl = new URL(url)
|
||||
parsedUrl.searchParams.set(ACCESS_TOKEN, accessToken)
|
||||
return parsedUrl.toString()
|
||||
const parsedUrl = new URL(url);
|
||||
parsedUrl.searchParams.set(ACCESS_TOKEN, accessToken);
|
||||
return parsedUrl.toString();
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import * as React from "react";
|
||||
import * as React from 'react';
|
||||
|
||||
export interface Me {
|
||||
username: string;
|
||||
admin: boolean;
|
||||
version: string; // proddy added
|
||||
}
|
||||
|
||||
export interface AuthenticationContextValue {
|
||||
@@ -13,7 +12,7 @@ export interface AuthenticationContextValue {
|
||||
me?: Me;
|
||||
}
|
||||
|
||||
const AuthenticationContextDefaultValue = {} as AuthenticationContextValue
|
||||
const AuthenticationContextDefaultValue = {} as AuthenticationContextValue;
|
||||
export const AuthenticationContext = React.createContext(
|
||||
AuthenticationContextDefaultValue
|
||||
);
|
||||
@@ -22,12 +21,21 @@ export interface AuthenticationContextProps {
|
||||
authenticationContext: AuthenticationContextValue;
|
||||
}
|
||||
|
||||
export function withAuthenticationContext<T extends AuthenticationContextProps>(Component: React.ComponentType<T>) {
|
||||
return class extends React.Component<Omit<T, keyof AuthenticationContextProps>> {
|
||||
export function withAuthenticationContext<T extends AuthenticationContextProps>(
|
||||
Component: React.ComponentType<T>
|
||||
) {
|
||||
return class extends React.Component<
|
||||
Omit<T, keyof AuthenticationContextProps>
|
||||
> {
|
||||
render() {
|
||||
return (
|
||||
<AuthenticationContext.Consumer>
|
||||
{authenticationContext => <Component {...this.props as T} authenticationContext={authenticationContext} />}
|
||||
{(authenticationContext) => (
|
||||
<Component
|
||||
{...(this.props as T)}
|
||||
authenticationContext={authenticationContext}
|
||||
/>
|
||||
)}
|
||||
</AuthenticationContext.Consumer>
|
||||
);
|
||||
}
|
||||
@@ -38,7 +46,7 @@ export interface AuthenticatedContextValue extends AuthenticationContextValue {
|
||||
me: Me;
|
||||
}
|
||||
|
||||
const AuthenticatedContextDefaultValue = {} as AuthenticatedContextValue
|
||||
const AuthenticatedContextDefaultValue = {} as AuthenticatedContextValue;
|
||||
export const AuthenticatedContext = React.createContext(
|
||||
AuthenticatedContextDefaultValue
|
||||
);
|
||||
@@ -47,14 +55,23 @@ export interface AuthenticatedContextProps {
|
||||
authenticatedContext: AuthenticatedContextValue;
|
||||
}
|
||||
|
||||
export function withAuthenticatedContext<T extends AuthenticatedContextProps>(Component: React.ComponentType<T>) {
|
||||
return class extends React.Component<Omit<T, keyof AuthenticatedContextProps>> {
|
||||
export function withAuthenticatedContext<T extends AuthenticatedContextProps>(
|
||||
Component: React.ComponentType<T>
|
||||
) {
|
||||
return class extends React.Component<
|
||||
Omit<T, keyof AuthenticatedContextProps>
|
||||
> {
|
||||
render() {
|
||||
return (
|
||||
<AuthenticatedContext.Consumer>
|
||||
{authenticatedContext => <Component {...this.props as T} authenticatedContext={authenticatedContext} />}
|
||||
{(authenticatedContext) => (
|
||||
<Component
|
||||
{...(this.props as T)}
|
||||
authenticatedContext={authenticatedContext}
|
||||
/>
|
||||
)}
|
||||
</AuthenticatedContext.Consumer>
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,14 +2,19 @@ import * as React from 'react';
|
||||
import { withSnackbar, WithSnackbarProps } from 'notistack';
|
||||
import jwtDecode from 'jwt-decode';
|
||||
|
||||
import history from '../history'
|
||||
import history from '../history';
|
||||
import { VERIFY_AUTHORIZATION_ENDPOINT } from '../api';
|
||||
import { ACCESS_TOKEN, authorizedFetch, getStorage } from './Authentication';
|
||||
import { AuthenticationContext, AuthenticationContextValue, Me } from './AuthenticationContext';
|
||||
import {
|
||||
AuthenticationContext,
|
||||
AuthenticationContextValue,
|
||||
Me
|
||||
} from './AuthenticationContext';
|
||||
import FullScreenLoading from '../components/FullScreenLoading';
|
||||
import { withFeatures, WithFeaturesProps } from '../features/FeaturesContext';
|
||||
|
||||
export const decodeMeJWT = (accessToken: string): Me => jwtDecode(accessToken) as Me;
|
||||
export const decodeMeJWT = (accessToken: string): Me =>
|
||||
jwtDecode(accessToken) as Me;
|
||||
|
||||
interface AuthenticationWrapperState {
|
||||
context: AuthenticationContextValue;
|
||||
@@ -18,15 +23,17 @@ interface AuthenticationWrapperState {
|
||||
|
||||
type AuthenticationWrapperProps = WithSnackbarProps & WithFeaturesProps;
|
||||
|
||||
class AuthenticationWrapper extends React.Component<AuthenticationWrapperProps, AuthenticationWrapperState> {
|
||||
|
||||
class AuthenticationWrapper extends React.Component<
|
||||
AuthenticationWrapperProps,
|
||||
AuthenticationWrapperState
|
||||
> {
|
||||
constructor(props: AuthenticationWrapperProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
context: {
|
||||
refresh: this.refresh,
|
||||
signIn: this.signIn,
|
||||
signOut: this.signOut,
|
||||
signOut: this.signOut
|
||||
},
|
||||
initialized: false
|
||||
};
|
||||
@@ -39,7 +46,9 @@ class AuthenticationWrapper extends React.Component<AuthenticationWrapperProps,
|
||||
render() {
|
||||
return (
|
||||
<React.Fragment>
|
||||
{this.state.initialized ? this.renderContent() : this.renderContentLoading()}
|
||||
{this.state.initialized
|
||||
? this.renderContent()
|
||||
: this.renderContentLoading()}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
@@ -53,9 +62,7 @@ class AuthenticationWrapper extends React.Component<AuthenticationWrapperProps,
|
||||
}
|
||||
|
||||
renderContentLoading() {
|
||||
return (
|
||||
<FullScreenLoading />
|
||||
);
|
||||
return <FullScreenLoading />;
|
||||
}
|
||||
|
||||
refresh = () => {
|
||||
@@ -64,34 +71,53 @@ class AuthenticationWrapper extends React.Component<AuthenticationWrapperProps,
|
||||
// this.setState({ initialized: true, context: { ...this.state.context, me: { admin: true, username: "admin" } } });
|
||||
// return;
|
||||
// }
|
||||
const accessToken = getStorage().getItem(ACCESS_TOKEN)
|
||||
const accessToken = getStorage().getItem(ACCESS_TOKEN);
|
||||
if (accessToken) {
|
||||
authorizedFetch(VERIFY_AUTHORIZATION_ENDPOINT)
|
||||
.then(response => {
|
||||
const me = response.status === 200 ? decodeMeJWT(accessToken) : undefined;
|
||||
this.setState({ initialized: true, context: { ...this.state.context, me } });
|
||||
}).catch(error => {
|
||||
this.setState({ initialized: true, context: { ...this.state.context, me: undefined } });
|
||||
this.props.enqueueSnackbar("Error verifying authorization: " + error.message, {
|
||||
variant: 'error',
|
||||
.then((response) => {
|
||||
const me =
|
||||
response.status === 200 ? decodeMeJWT(accessToken) : undefined;
|
||||
this.setState({
|
||||
initialized: true,
|
||||
context: { ...this.state.context, me }
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
this.setState({
|
||||
initialized: true,
|
||||
context: { ...this.state.context, me: undefined }
|
||||
});
|
||||
this.props.enqueueSnackbar(
|
||||
'Error verifying authorization: ' + error.message,
|
||||
{
|
||||
variant: 'error'
|
||||
}
|
||||
);
|
||||
});
|
||||
} else {
|
||||
this.setState({ initialized: true, context: { ...this.state.context, me: undefined } });
|
||||
this.setState({
|
||||
initialized: true,
|
||||
context: { ...this.state.context, me: undefined }
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
signIn = (accessToken: string) => {
|
||||
try {
|
||||
getStorage().setItem(ACCESS_TOKEN, accessToken);
|
||||
const me: Me = decodeMeJWT(accessToken);
|
||||
this.setState({ context: { ...this.state.context, me } });
|
||||
this.props.enqueueSnackbar(`Logged in as ${me.username}`, { variant: 'success' });
|
||||
this.props.enqueueSnackbar(`Logged in as ${me.username}`, {
|
||||
variant: 'success'
|
||||
});
|
||||
} catch (err) {
|
||||
this.setState({ initialized: true, context: { ...this.state.context, me: undefined } });
|
||||
throw new Error("Failed to parse JWT " + err.message);
|
||||
this.setState({
|
||||
initialized: true,
|
||||
context: { ...this.state.context, me: undefined }
|
||||
});
|
||||
throw new Error('Failed to parse JWT ' + err.message);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
signOut = () => {
|
||||
getStorage().removeItem(ACCESS_TOKEN);
|
||||
@@ -101,10 +127,9 @@ class AuthenticationWrapper extends React.Component<AuthenticationWrapperProps,
|
||||
me: undefined
|
||||
}
|
||||
});
|
||||
this.props.enqueueSnackbar("You have signed out", { variant: 'success', });
|
||||
this.props.enqueueSnackbar('You have signed out', { variant: 'success' });
|
||||
history.push('/');
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
export default withFeatures(withSnackbar(AuthenticationWrapper))
|
||||
export default withFeatures(withSnackbar(AuthenticationWrapper));
|
||||
|
||||
@@ -1,31 +1,46 @@
|
||||
import * as React from 'react';
|
||||
import { Redirect, Route, RouteProps, RouteComponentProps } from "react-router-dom";
|
||||
import {
|
||||
Redirect,
|
||||
Route,
|
||||
RouteProps,
|
||||
RouteComponentProps
|
||||
} from 'react-router-dom';
|
||||
|
||||
import { withAuthenticationContext, AuthenticationContextProps } from './AuthenticationContext';
|
||||
import {
|
||||
withAuthenticationContext,
|
||||
AuthenticationContextProps
|
||||
} from './AuthenticationContext';
|
||||
import * as Authentication from './Authentication';
|
||||
import { WithFeaturesProps, withFeatures } from '../features/FeaturesContext';
|
||||
|
||||
interface UnauthenticatedRouteProps extends RouteProps, AuthenticationContextProps, WithFeaturesProps {
|
||||
component: React.ComponentType<RouteComponentProps<any>> | React.ComponentType<any>;
|
||||
interface UnauthenticatedRouteProps
|
||||
extends RouteProps,
|
||||
AuthenticationContextProps,
|
||||
WithFeaturesProps {
|
||||
component:
|
||||
| React.ComponentType<RouteComponentProps<any>>
|
||||
| React.ComponentType<any>;
|
||||
}
|
||||
|
||||
type RenderComponent = (props: RouteComponentProps<any>) => React.ReactNode;
|
||||
|
||||
class UnauthenticatedRoute extends Route<UnauthenticatedRouteProps> {
|
||||
|
||||
public render() {
|
||||
const { authenticationContext, component: Component, features, ...rest } = this.props;
|
||||
const {
|
||||
authenticationContext,
|
||||
component: Component,
|
||||
features,
|
||||
...rest
|
||||
} = this.props;
|
||||
const renderComponent: RenderComponent = (props) => {
|
||||
if (authenticationContext.me) {
|
||||
return (<Redirect to={Authentication.fetchLoginRedirect(features)} />);
|
||||
return <Redirect to={Authentication.fetchLoginRedirect(features)} />;
|
||||
}
|
||||
if (Component) {
|
||||
return (<Component {...props} />);
|
||||
return <Component {...props} />;
|
||||
}
|
||||
}
|
||||
return (
|
||||
<Route {...rest} render={renderComponent} />
|
||||
);
|
||||
};
|
||||
return <Route {...rest} render={renderComponent} />;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export { default as AuthenticatedRoute } from './AuthenticatedRoute'
|
||||
export { default as AuthenticationWrapper } from './AuthenticationWrapper'
|
||||
export { default as UnauthenticatedRoute } from './UnauthenticatedRoute'
|
||||
export { default as AuthenticatedRoute } from './AuthenticatedRoute';
|
||||
export { default as AuthenticationWrapper } from './AuthenticationWrapper';
|
||||
export { default as UnauthenticatedRoute } from './UnauthenticatedRoute';
|
||||
|
||||
export * from './Authentication'
|
||||
export * from './AuthenticationContext'
|
||||
export * from './Authentication';
|
||||
export * from './AuthenticationContext';
|
||||
|
||||
Reference in New Issue
Block a user