diff --git a/src/components/login.tsx b/src/components/login.tsx index 4d8e0fb..e6c0848 100644 --- a/src/components/login.tsx +++ b/src/components/login.tsx @@ -3,6 +3,7 @@ import { Button } from 'primereact/button'; import { Card } from 'primereact/card'; import { useNavigate } from 'react-router-dom'; import { Auth, Configuration, Session } from '../services'; +import { BackendReturnStatus } from '../types'; export const Login: React.FC = () => { const [loading, setLoading] = useState(true); @@ -10,8 +11,8 @@ export const Login: React.FC = () => { // check if the known bearer token is still valid useEffect(() => { - Auth.tokenIsValid().then((valid) => { - if (!valid) { + Auth.tokenIsValid().then((status) => { + if (status !== BackendReturnStatus.Ok) { Session.reset(); setLoading(false); } else { diff --git a/src/components/navbar.tsx b/src/components/navbar.tsx index 1ed59d5..ebf6f58 100644 --- a/src/components/navbar.tsx +++ b/src/components/navbar.tsx @@ -4,7 +4,7 @@ import { useNavigate } from 'react-router-dom'; import { AuthContext, TimeContext } from '../contexts'; import { Configuration } from '../services'; import { Airport } from '../services'; -import { AirportOverview, IAuthState } from '../types'; +import { AirportOverview, BackendReturnStatus, IAuthState } from '../types'; export const NavBar: React.FC = () => { const [timestamp, setTimestamp] = useState(''); @@ -63,7 +63,9 @@ export const NavBar: React.FC = () => { const updateMenuItems = async () => { if (currentAuth.current === undefined || !currentAuth.current.valid) return []; - Airport.all().then((airports) => { + Airport.all().then((response) => { + if (response.status !== BackendReturnStatus.Ok) return []; + const newMenuTree: { label: string; items?: any[]; command?: () => void }[] = [ { label: 'Airports', @@ -72,12 +74,12 @@ export const NavBar: React.FC = () => { ]; // create the airports subtree - const airportSubtree = firBasedSubMenu(airports, '/sequence'); + const airportSubtree = firBasedSubMenu(response.airports, '/sequence'); newMenuTree[0].items = airportSubtree; // collect all configuration airports const configurationAirports: AirportOverview[] = []; - airports.forEach((airport) => { + response.airports.forEach((airport) => { const idx = currentAuth.current?.user.airportConfigurationAccess.findIndex((value) => airport.icao === value); if (idx !== -1) configurationAirports.push(airport); }); diff --git a/src/contexts/authcontext.tsx b/src/contexts/authcontext.tsx index f505208..37ef17f 100644 --- a/src/contexts/authcontext.tsx +++ b/src/contexts/authcontext.tsx @@ -1,7 +1,7 @@ import { createContext, Dispatch, SetStateAction, useEffect, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { Auth } from '../services'; -import { IAuthState } from '../types'; +import { BackendReturnStatus, IAuthState } from '../types'; const InitialAuthState: IAuthState = { valid: false, @@ -27,9 +27,9 @@ export const AuthProvider = ({ children }: { children: any }) => { const resetAuth = () => setAuth(InitialAuthState); useEffect(() => { - Auth.user().then((user) => { - if (user !== undefined) { - setAuth({ valid: true, user }); + Auth.user().then((response) => { + if (response.status === BackendReturnStatus.Ok) { + setAuth({ valid: true, user: response.user }); } else { setAuth(InitialAuthState); } diff --git a/src/services/airport.ts b/src/services/airport.ts index dd5e2c8..e379ea0 100644 --- a/src/services/airport.ts +++ b/src/services/airport.ts @@ -1,12 +1,21 @@ import axios from 'axios'; import { Configuration } from './configuration'; import { Session } from './session'; -import { AirportOverview } from '../types'; +import { + AirportOverviewBackend, + AirportOverview, + BackendReturnStatus, +} from '../types'; export class Airport { - static async all(): Promise { + static async all(): Promise { const token = Session.bearerToken(); - if (!token) return []; + if (!token) { + return { + status: BackendReturnStatus.Unauthorized, + airports: [], + }; + } return axios .get(`${Configuration.resourceServer}/airport/all`, { @@ -14,7 +23,17 @@ export class Airport { Authorization: `Bearer ${token}`, }, }) - .then((response) => response.data) - .catch(() => []); + .then((response) => { + return { + status: BackendReturnStatus.Ok, + airports: response.data, + }; + }) + .catch((err) => { + return { + status: err.status === 401 ? BackendReturnStatus.Unauthorized : BackendReturnStatus.Failure, + airports: [], + }; + }); } } diff --git a/src/services/auth.ts b/src/services/auth.ts index 6637a41..dd9020a 100644 --- a/src/services/auth.ts +++ b/src/services/auth.ts @@ -1,12 +1,17 @@ import axios from 'axios'; import { Configuration } from './configuration'; import { Session } from './session'; -import { User } from '../types'; +import { + BackendReturnStatus, + DefaultUser, + UserBackend, + User, +} from '../types'; export class Auth { - static async tokenIsValid(): Promise { + static async tokenIsValid(): Promise { const token = Session.bearerToken(); - if (!token) return false; + if (!token) return BackendReturnStatus.Unauthorized; return axios .get(`${Configuration.resourceServer}/auth/validate`, { @@ -14,13 +19,21 @@ export class Auth { Authorization: `Bearer ${token}`, }, }) - .then(() => true) - .catch(() => false); + .then(() => BackendReturnStatus.Ok) + .catch((err) => { + if (err.status === 401) return BackendReturnStatus.Unauthorized; + return BackendReturnStatus.Failure; + }); } - static async user(): Promise { + static async user(): Promise { const token = Session.bearerToken(); - if (!token) return undefined; + if (!token) { + return { + status: BackendReturnStatus.Unauthorized, + user: DefaultUser, + }; + } return axios .get(`${Configuration.resourceServer}/auth/user`, { @@ -28,7 +41,31 @@ export class Auth { Authorization: `Bearer ${token}`, }, }) - .then((response) => response.data) - .catch(() => undefined); + .then((response) => { + return { + status: BackendReturnStatus.Ok, + user: response.data, + }; + }) + .catch((err) => { + return { + status: err.status === 401 ? BackendReturnStatus.Unauthorized : BackendReturnStatus.Failure, + user: DefaultUser, + }; + }); + } + + static async refreshRadarScopeKey(): Promise { + const token = Session.bearerToken(); + if (!token) return false; + + return axios + .get(`${Configuration.resourceServer}/auth/refreshRadarScopeKey`, { + headers: { + Authorization: `Bearer ${token}`, + }, + }) + .then(() => true) + .catch(() => false); } } diff --git a/src/types/index.ts b/src/types/index.ts index 6e10daf..d00ebb3 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,9 +1,20 @@ +export enum BackendReturnStatus { + Ok, + Unauthorized, + Failure, +}; + export interface AirportOverview { icao: string; name: string; flightInformationRegion: string; }; +export interface AirportOverviewBackend { + status: BackendReturnStatus; + airports: AirportOverview[]; +}; + export interface User { vatsimId: string; fullName: string; @@ -12,6 +23,19 @@ export interface User { airportConfigurationAccess: string[]; }; +export const DefaultUser: User = { + vatsimId: '', + fullName: '', + radarScopeKey: '', + administrator: false, + airportConfigurationAccess: [], +}; + +export interface UserBackend { + status: BackendReturnStatus; + user: User; +} + export interface IAuthState { valid: boolean, user: User,