UNPKG

@parkingboss/api

Version:
120 lines (119 loc) 3.99 kB
import { buildLoginUrl } from "./urls"; import { loadUser, setUser, unsetUser, jwtUser } from "./loadUser"; export function session(settings) { settings.user = loadUser(); const user = userStore(settings); const validUser = validUserStore(user); return Object.assign({ user, validUser, isLoggedIn: () => isLoggedIn(settings), logIn: (email, password) => email && password ? logIn(settings, user.set, email, password) : logIn(settings, user.set), renew: (password) => renew(settings, user.set, password), requestPasswordReset: (email) => requestPasswordReset(settings, email), logOut: () => logOut(user.set), }); } function userStore(settings) { const subscribers = new Set(); function notifyUserChanged(user) { subscribers.forEach((fn) => fn(user)); } function subscribe(fn) { fn(settings.user); subscribers.add(fn); return () => subscribers.delete(fn); } return { subscribe, set(user) { settings.user = user; if (user) { setUser(user); } else { unsetUser(); } notifyUserChanged(user); }, }; } function validUserStore(user) { return { subscribe(fn) { let timeout = null; const unsub = user.subscribe((currentUser) => { if (timeout != null) { clearTimeout(timeout); timeout = null; } if (isValidUser(currentUser)) { fn(currentUser); const msToExpiry = +currentUser.expires - Date.now(); timeout = setTimeout(() => { fn(null); timeout = null; }, msToExpiry); } else { fn(null); } }); return () => { if (timeout != null) { clearTimeout(timeout); } unsub(); }; }, }; } async function logIn(settings, setUser, email, password) { if (email && password) { const url = new URL(settings.apiBase + "/auth/tokens"); url.searchParams.set("lifetime", "P7D"); url.searchParams.set("email", email); url.searchParams.set("ts", new Date().toISOString()); const body = new FormData(); body.set("email", email); body.set("password", password); const result = await self.fetch(url.toString(), { method: "POST", body }); const responseBody = await result.json(); if (result.ok) { setUser(jwtUser(responseBody)); return responseBody; } return responseBody; } const loginParams = { clientId: settings.client }; if (settings.user?.email) { loginParams.email = settings.user.email; } window.location.href = buildLoginUrl(loginParams); } async function requestPasswordReset(settings, email) { if (!email) throw new Error("Email must be provided."); const url = new URL(settings.apiBase + "/auth/tokens/email"); url.searchParams.set("ts", new Date().toISOString()); const body = new FormData(); body.set("email", email); const result = await self.fetch(url.toString(), { method: "POST", body }); const responseBody = await result.json(); return responseBody; } function isLoggedIn(settings) { return isValidUser(settings.user); } function isValidUser(user) { return !!(user && (!user.expires || user.expires > new Date())); } async function logOut(setUser) { setUser(null); } function renew(settings, setUser, password) { if (!settings.user?.email) { throw new Error("Use logIn. Cannot log in without current user data."); } return logIn(settings, setUser, settings.user.email, password); }