168 lines
No EOL
4.1 KiB
TypeScript
168 lines
No EOL
4.1 KiB
TypeScript
import { LoginRequest } from "dbtype/mod.ts";
|
|
import {
|
|
ApiError,
|
|
loginService,
|
|
LoginResponse,
|
|
logoutService,
|
|
refreshService,
|
|
resetPasswordService,
|
|
} from "./api.ts";
|
|
import { useEffect, useState } from "react";
|
|
|
|
let localObj: LoginResponse | null = null;
|
|
|
|
function getUserSessions() {
|
|
if (!localObj) {
|
|
const storagestr = localStorage.getItem("UserLoginContext");
|
|
const storage = storagestr ? (JSON.parse(storagestr) as LoginResponse | null) : null;
|
|
// update localObj from storage
|
|
localObj = storage;
|
|
}
|
|
if (localObj && localObj.accessExpired > Math.floor(Date.now())) {
|
|
return {
|
|
username: localObj.username,
|
|
permission: localObj.permission,
|
|
accessExpired: localObj.accessExpired,
|
|
};
|
|
}
|
|
return null;
|
|
}
|
|
function setUserSessions(user: LoginResponse | null) {
|
|
localObj = user;
|
|
if (user) {
|
|
localStorage.setItem("UserLoginContext", JSON.stringify(user));
|
|
} else {
|
|
localStorage.removeItem("UserLoginContext");
|
|
}
|
|
}
|
|
|
|
class AuthEvent extends CustomEvent<{
|
|
type: "login" | "logout" | "refresh";
|
|
user: { username: string; permission: string[] } | null;
|
|
}> {
|
|
constructor(type: "login" | "logout" | "refresh", user: { username: string; permission: string[] } | null) {
|
|
super("auth", { detail: { type, user } });
|
|
}
|
|
}
|
|
|
|
declare global {
|
|
interface WindowEventMap {
|
|
auth: AuthEvent;
|
|
}
|
|
|
|
function addEventListener(
|
|
type: "auth",
|
|
listener: (this: Window, ev: AuthEvent) => void,
|
|
options?: boolean | AddEventListenerOptions
|
|
): void;
|
|
function removeEventListener(
|
|
type: "auth", listener: (this: Window, ev: AuthEvent) => void, options?: boolean | EventListenerOptions): void;
|
|
}
|
|
|
|
export async function refresh() {
|
|
try {
|
|
const r = await refreshService();
|
|
setUserSessions(r);
|
|
window.dispatchEvent(new AuthEvent("refresh", { username: r.username, permission: r.permission }));
|
|
return {
|
|
username: r.username,
|
|
permission: r.permission,
|
|
};
|
|
} catch (e) {
|
|
if (e instanceof ApiError) {
|
|
console.error(`Refresh failed: ${e.detail}`);
|
|
}
|
|
setUserSessions(null);
|
|
window.dispatchEvent(new AuthEvent("logout", null));
|
|
return {
|
|
username: "",
|
|
permission: [],
|
|
};
|
|
}
|
|
}
|
|
|
|
export const doLogout = async () => {
|
|
try {
|
|
const res = await logoutService();
|
|
setUserSessions(null);
|
|
window.dispatchEvent(new AuthEvent("logout", null));
|
|
return {
|
|
username: res.username,
|
|
permission: res.permission,
|
|
};
|
|
|
|
} catch (error) {
|
|
console.error(`Server Error ${error}`);
|
|
setUserSessions(null);
|
|
return { username: "", permission: [] };
|
|
}
|
|
};
|
|
|
|
export const doLogin = async (userLoginInfo: LoginRequest): Promise<{
|
|
ok: true;
|
|
data: LoginResponse;
|
|
} | {
|
|
ok: false;
|
|
error: string;
|
|
}> => {
|
|
try {
|
|
const b = await loginService(userLoginInfo);
|
|
setUserSessions(b);
|
|
window.dispatchEvent(new AuthEvent("login", { username: b.username, permission: b.permission }));
|
|
return { ok: true, data: b };
|
|
} catch (e) {
|
|
if (e instanceof ApiError) {
|
|
return { ok: false, error: e.detail ?? e.message };
|
|
}
|
|
return { ok: false, error: "An unknown error occurred." };
|
|
}
|
|
};
|
|
|
|
Object.assign(window, {
|
|
doLogin,
|
|
doLogout,
|
|
refresh,
|
|
});
|
|
|
|
export const doResetPassword = async (username: string, oldpassword: string, newpassword: string) => {
|
|
try {
|
|
return await resetPasswordService(username, oldpassword, newpassword);
|
|
} catch (e) {
|
|
if (e instanceof ApiError) {
|
|
return { ok: false as const, error: e.detail ?? e.message };
|
|
}
|
|
return { ok: false as const, error: "An unknown error occurred." };
|
|
}
|
|
};
|
|
|
|
|
|
export function useLogin() {
|
|
const [user, setUser] = useState(getUserSessions());
|
|
useEffect(() => {
|
|
const listener = (_e: AuthEvent) => {
|
|
setUser(getUserSessions());
|
|
};
|
|
window.addEventListener("auth", listener);
|
|
return () => {
|
|
window.removeEventListener("auth", listener);
|
|
};
|
|
}, []);
|
|
return user;
|
|
}
|
|
|
|
|
|
|
|
document.addEventListener("visibilitychange", async () => {
|
|
if (document.visibilityState === "visible") {
|
|
const session = getUserSessions();
|
|
if (!session) {
|
|
await refresh();
|
|
}
|
|
if (session && session.accessExpired - Date.now() < 5 * 60 * 1000) {
|
|
// access token will expire in 5 minutes, refresh it
|
|
await refresh();
|
|
}
|
|
|
|
// If the session is still valid, do nothing
|
|
}
|
|
}); |