Consolidate authentication code
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { useNavigate } from "@remix-run/react";
|
||||
import axios from "axios";
|
||||
|
||||
export const client = axios.create({
|
||||
@@ -13,15 +14,9 @@ client.interceptors.request.use(
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error) => {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
client.interceptors.request.use(
|
||||
(response) => response,
|
||||
async (error) => {
|
||||
const originalRequest = error.config;
|
||||
const navigate = useNavigate();
|
||||
if (error.response.status == 401 && !originalRequest._retry) {
|
||||
originalRequest._retry = true;
|
||||
const refreshToken = localStorage.getItem("refresh-token");
|
||||
@@ -36,7 +31,10 @@ client.interceptors.request.use(
|
||||
] = `Bearer ${data.refreshToken}`;
|
||||
return client(originalRequest);
|
||||
} catch (_error) {
|
||||
return Promise.reject(_error);
|
||||
localStorage.removeItem("token");
|
||||
localStorage.removeItem("refresh-token");
|
||||
console.log("Oh no!!!");
|
||||
navigate("/login");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
102
web/app/util/auth.tsx
Normal file
102
web/app/util/auth.tsx
Normal file
@@ -0,0 +1,102 @@
|
||||
import { client } from "./api";
|
||||
import { useNavigate } from "@remix-run/react";
|
||||
import { createContext, useContext, useEffect, useState } from "react";
|
||||
|
||||
interface AuthContextValues {
|
||||
user: string | null;
|
||||
loading: boolean;
|
||||
signin: ({
|
||||
username,
|
||||
password,
|
||||
}: {
|
||||
username: string;
|
||||
password: string;
|
||||
}) => void;
|
||||
signout: () => void;
|
||||
}
|
||||
|
||||
const AuthContext = createContext<AuthContextValues | null>(null);
|
||||
|
||||
export function AuthProvider({ children }: { children: React.ReactNode }) {
|
||||
const auth = useProvideAuth();
|
||||
return <AuthContext.Provider value={auth}>{children}</AuthContext.Provider>;
|
||||
}
|
||||
|
||||
export function useAuth(): AuthContextValues {
|
||||
const data = useContext(AuthContext);
|
||||
if (!data) {
|
||||
throw new Error("Could not find AuthContext provider");
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
function useProvideAuth() {
|
||||
const [user, setUser] = useState<string | null>(null);
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleUser = (rawUser: string | null) => {
|
||||
if (rawUser) {
|
||||
setUser(rawUser);
|
||||
setLoading(false);
|
||||
return rawUser;
|
||||
} else {
|
||||
setUser(null);
|
||||
clearTokens();
|
||||
setLoading(false);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleTokens = (tokens: {
|
||||
access_token: string;
|
||||
refresh_token: string;
|
||||
}) => {
|
||||
if (tokens) {
|
||||
localStorage.setItem("token", tokens.access_token);
|
||||
localStorage.setItem("refresh-token", tokens.refresh_token);
|
||||
}
|
||||
};
|
||||
|
||||
const clearTokens = () => {
|
||||
localStorage.removeItem("token");
|
||||
localStorage.removeItem("refresh-token");
|
||||
};
|
||||
|
||||
const signin = async (values: { username: string; password: string }) => {
|
||||
setLoading(true);
|
||||
await client
|
||||
.postForm("/auth/login", values)
|
||||
.then((response) => handleTokens(response.data))
|
||||
.catch(() => {
|
||||
setLoading(false);
|
||||
throw new Error("Invalid username or password");
|
||||
});
|
||||
|
||||
setLoading(false);
|
||||
await client
|
||||
.get("/users/me")
|
||||
.then((response) => handleUser(response.data.username))
|
||||
.catch(() => handleUser(null));
|
||||
navigate("/logbook");
|
||||
};
|
||||
|
||||
const signout = async () => {
|
||||
return await client.post("/auth/logout").then(() => handleUser(null));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
client
|
||||
.get("/users/me")
|
||||
.then((response) => handleUser(response.data.username))
|
||||
.catch(() => handleUser(null));
|
||||
}, []);
|
||||
|
||||
return {
|
||||
user,
|
||||
loading,
|
||||
signin,
|
||||
signout,
|
||||
};
|
||||
}
|
@@ -1,52 +0,0 @@
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { client } from "./api";
|
||||
import { useNavigate } from "@remix-run/react";
|
||||
|
||||
type User = {
|
||||
username: string;
|
||||
level: number;
|
||||
};
|
||||
|
||||
export function useMe() {
|
||||
return useQuery<User, Error>({
|
||||
queryKey: ["me"],
|
||||
queryFn: () => client.get(`/users/me`).then((res) => res.data),
|
||||
});
|
||||
}
|
||||
|
||||
export function useSignOut() {
|
||||
const queryClient = useQueryClient();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const onSignOut = async () => {
|
||||
queryClient.setQueryData(["user"], null);
|
||||
const res = await client.post("/auth/logout");
|
||||
if (res.status == 200) {
|
||||
navigate("/login");
|
||||
} else {
|
||||
console.error("Failed to log out");
|
||||
}
|
||||
};
|
||||
|
||||
return onSignOut;
|
||||
}
|
||||
|
||||
export function useLogin() {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const { mutate: signInMutation } = useMutation({
|
||||
mutationFn: async (values) => {
|
||||
return await client.postForm("/auth/login", values);
|
||||
},
|
||||
onSuccess: (data) => {
|
||||
localStorage.setItem("token", data.data.access_token);
|
||||
localStorage.setItem("refresh-token", data.data.refresh_token);
|
||||
navigate("/logbook");
|
||||
},
|
||||
onError: (error) => {
|
||||
console.error(error);
|
||||
},
|
||||
});
|
||||
|
||||
return signInMutation;
|
||||
}
|
52
web/app/util/types.ts
Normal file
52
web/app/util/types.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
type Flight = {
|
||||
id: string;
|
||||
user: string;
|
||||
|
||||
date: string;
|
||||
aircraft: string | null;
|
||||
waypoint_from: string | null;
|
||||
waypoint_to: string | null;
|
||||
route: string | null;
|
||||
|
||||
hobbs_start: number | null;
|
||||
hobbs_end: number | null;
|
||||
tach_start: number | null;
|
||||
tach_end: number | null;
|
||||
|
||||
time_start: number | null;
|
||||
time_off: number | null;
|
||||
time_down: number | null;
|
||||
time_stop: number | null;
|
||||
|
||||
time_total: number;
|
||||
time_pic: number;
|
||||
time_sic: number;
|
||||
time_night: number;
|
||||
time_solo: number;
|
||||
|
||||
time_xc: number;
|
||||
dist_xc: number;
|
||||
|
||||
takeoffs_day: number;
|
||||
landings_day: number;
|
||||
takeoffs_night: number;
|
||||
landings_night: number;
|
||||
|
||||
time_instrument: number;
|
||||
time_sim_instrument: number;
|
||||
holds_instrument: number;
|
||||
|
||||
dual_given: number;
|
||||
dual_recvd: number;
|
||||
time_sim: number;
|
||||
time_ground: number;
|
||||
|
||||
tags: string[];
|
||||
|
||||
pax: string[];
|
||||
crew: string[];
|
||||
|
||||
comments: string;
|
||||
};
|
||||
|
||||
export { type Flight };
|
Reference in New Issue
Block a user