Initial commit of Balout Pastry public website.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+153
@@ -0,0 +1,153 @@
|
||||
import type { UserRole } from './types'
|
||||
|
||||
const AUTH_COOKIE = 'balout.auth'
|
||||
const AUTH_HASH_PREFIX = '#balout_auth='
|
||||
|
||||
export type AuthUser = {
|
||||
id: string
|
||||
title: string
|
||||
firstName: string
|
||||
lastName: string
|
||||
cellNumber: string
|
||||
role: UserRole
|
||||
name: string
|
||||
}
|
||||
|
||||
export type AuthSession = {
|
||||
accessToken: string
|
||||
refreshToken: string
|
||||
user: AuthUser
|
||||
}
|
||||
|
||||
function isSession(value: unknown): value is AuthSession {
|
||||
if (!value || typeof value !== 'object') return false
|
||||
const session = value as Partial<AuthSession>
|
||||
return (
|
||||
typeof session.accessToken === 'string' &&
|
||||
typeof session.refreshToken === 'string' &&
|
||||
!!session.user &&
|
||||
typeof session.user === 'object' &&
|
||||
typeof session.user.name === 'string' &&
|
||||
typeof session.user.id === 'string'
|
||||
)
|
||||
}
|
||||
|
||||
function cookieDomain() {
|
||||
const host = window.location.hostname
|
||||
if (host === 'localhost' || host === '127.0.0.1') return undefined
|
||||
const configured = import.meta.env.VITE_COOKIE_DOMAIN as string | undefined
|
||||
if (configured) return configured
|
||||
if (host.endsWith('.baloutpastry.com') || host === 'baloutpastry.com') {
|
||||
return '.baloutpastry.com'
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
function readCookie(name: string) {
|
||||
const prefix = `${encodeURIComponent(name)}=`
|
||||
for (const part of document.cookie.split(';')) {
|
||||
const trimmed = part.trim()
|
||||
if (trimmed.startsWith(prefix)) {
|
||||
return decodeURIComponent(trimmed.slice(prefix.length))
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function writeCookie(name: string, value: string, maxAgeSeconds: number) {
|
||||
const domain = cookieDomain()
|
||||
const parts = [
|
||||
`${encodeURIComponent(name)}=${encodeURIComponent(value)}`,
|
||||
'Path=/',
|
||||
`Max-Age=${maxAgeSeconds}`,
|
||||
'SameSite=Lax',
|
||||
]
|
||||
if (domain) parts.push(`Domain=${domain}`)
|
||||
document.cookie = parts.join('; ')
|
||||
}
|
||||
|
||||
function clearCookie(name: string) {
|
||||
const domain = cookieDomain()
|
||||
const parts = [
|
||||
`${encodeURIComponent(name)}=`,
|
||||
'Path=/',
|
||||
'Max-Age=0',
|
||||
'SameSite=Lax',
|
||||
]
|
||||
if (domain) parts.push(`Domain=${domain}`)
|
||||
document.cookie = parts.join('; ')
|
||||
}
|
||||
|
||||
export function getSession(): AuthSession | null {
|
||||
try {
|
||||
const raw = readCookie(AUTH_COOKIE)
|
||||
if (!raw) return null
|
||||
const parsed = JSON.parse(raw) as unknown
|
||||
return isSession(parsed) ? parsed : null
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export function setSession(session: AuthSession) {
|
||||
writeCookie(AUTH_COOKIE, JSON.stringify(session), 60 * 60 * 24 * 7)
|
||||
}
|
||||
|
||||
export function clearSession() {
|
||||
clearCookie(AUTH_COOKIE)
|
||||
}
|
||||
|
||||
export function getAuthUser(): AuthUser | null {
|
||||
return getSession()?.user ?? null
|
||||
}
|
||||
|
||||
export function getAccessToken(): string | null {
|
||||
return getSession()?.accessToken ?? null
|
||||
}
|
||||
|
||||
export function isAuthenticated(): boolean {
|
||||
return getSession() !== null
|
||||
}
|
||||
|
||||
/** Import session handed off from customer login via URL hash. */
|
||||
export function consumeAuthHashFromUrl() {
|
||||
const hash = window.location.hash
|
||||
if (!hash.startsWith(AUTH_HASH_PREFIX)) return false
|
||||
try {
|
||||
const raw = decodeURIComponent(hash.slice(AUTH_HASH_PREFIX.length))
|
||||
const parsed = JSON.parse(raw) as unknown
|
||||
if (isSession(parsed)) {
|
||||
setSession(parsed)
|
||||
return true
|
||||
}
|
||||
} catch {
|
||||
// ignore malformed payload
|
||||
} finally {
|
||||
const clean = `${window.location.pathname}${window.location.search}`
|
||||
window.history.replaceState(null, '', clean)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export function getCustomerAppUrl() {
|
||||
return (
|
||||
(import.meta.env.VITE_CUSTOMER_APP_URL as string | undefined)?.replace(/\/$/, '') ||
|
||||
'http://customer.baloutpastry.com:5173'
|
||||
)
|
||||
}
|
||||
|
||||
export function getCustomerLoginUrl(returnUrl = window.location.origin + '/') {
|
||||
const url = new URL(`${getCustomerAppUrl()}/login`)
|
||||
url.searchParams.set('returnUrl', returnUrl)
|
||||
return url.toString()
|
||||
}
|
||||
|
||||
export function getCustomerDashboardUrl(path = '/') {
|
||||
const base = getCustomerAppUrl()
|
||||
return `${base}${path.startsWith('/') ? path : `/${path}`}`
|
||||
}
|
||||
|
||||
export function displayName(user: AuthUser) {
|
||||
const short = [user.firstName, user.lastName].filter(Boolean).join(' ').trim()
|
||||
return short || user.name
|
||||
}
|
||||
Reference in New Issue
Block a user