frontend / src /services /authSession.ts
GitHub Actions
deploy: clean snapshot
4585b20
Raw History Blame Contribute Delete
5.39 kB
import { User, UserRole } from '../types';
import { AuthLoginResult, BackendAuthUser } from './authTypes';
const AUTH_SESSION_KEY = 'fishfarm_auth_session';
const TOKEN_KEY = 'fishfarm_token';
const REFRESH_TOKEN_KEY = 'fishfarm_refresh_token';
const USER_KEY = 'fishfarm_user';
interface StoredAuthSession {
token: string;
refreshToken?: string;
expiresIn?: number;
refreshExpiresIn?: number;
user: BackendAuthUser;
savedAt: number;
}
const isBrowser = (): boolean => typeof window !== 'undefined' && typeof window.localStorage !== 'undefined';
const isRecord = (value: unknown): value is Record<string, unknown> =>
typeof value === 'object' && value !== null && !Array.isArray(value);
const normalizeNumber = (value: unknown): number | undefined => {
if (typeof value === 'number' && Number.isFinite(value)) {
return value;
}
if (typeof value === 'string') {
const parsed = Number(value);
if (Number.isFinite(parsed)) {
return parsed;
}
}
return undefined;
};
const deriveNameFromEmail = (email: string): string => {
const localPart = email.split('@')[0] ?? 'User';
const words = localPart
.replace(/[._-]+/g, ' ')
.split(' ')
.filter(Boolean)
.map((word) => word[0].toUpperCase() + word.slice(1));
return words.length > 0 ? words.join(' ') : 'Farm Manager';
};
const normalizeRole = (role?: string): UserRole => {
const value = role?.toLowerCase();
if (value === 'admin' || value === 'manager' || value === 'worker') {
return value;
}
return 'manager';
};
export const mapBackendUserToAppUser = (backendUser: BackendAuthUser): User => ({
id: backendUser.id,
name: backendUser.name?.trim() || deriveNameFromEmail(backendUser.email),
email: backendUser.email,
phone: backendUser.phone?.trim() || 'N/A',
role: normalizeRole(backendUser.role),
farmId: backendUser.farmId,
});
const parseStoredSession = (raw: string | null): StoredAuthSession | null => {
if (!raw) {
return null;
}
try {
const parsed = JSON.parse(raw);
if (!isRecord(parsed)) {
return null;
}
const token = typeof parsed.token === 'string' ? parsed.token : undefined;
const savedAt = normalizeNumber(parsed.savedAt);
const user = isRecord(parsed.user) ? (parsed.user as BackendAuthUser) : undefined;
if (!token || savedAt === undefined || !user?.id || !user.email) {
return null;
}
return {
token,
refreshToken: typeof parsed.refreshToken === 'string' ? parsed.refreshToken : undefined,
expiresIn: normalizeNumber(parsed.expiresIn),
refreshExpiresIn: normalizeNumber(parsed.refreshExpiresIn),
user,
savedAt,
};
} catch {
return null;
}
};
export const persistAuthSession = (loginResult: AuthLoginResult, appUser: User): void => {
if (!isBrowser() || !loginResult.token || !loginResult.user) {
return;
}
const session: StoredAuthSession = {
token: loginResult.token,
refreshToken: loginResult.refreshToken,
expiresIn: normalizeNumber(loginResult.expiresIn),
refreshExpiresIn: normalizeNumber(loginResult.refreshExpiresIn),
user: loginResult.user,
savedAt: Date.now(),
};
localStorage.setItem(AUTH_SESSION_KEY, JSON.stringify(session));
localStorage.setItem(TOKEN_KEY, session.token);
if (session.refreshToken) {
localStorage.setItem(REFRESH_TOKEN_KEY, session.refreshToken);
} else {
localStorage.removeItem(REFRESH_TOKEN_KEY);
}
localStorage.setItem(USER_KEY, JSON.stringify(appUser));
};
export const getStoredAppUser = (): User | null => {
if (!isBrowser()) {
return null;
}
const getUserFromSession = (): User | null => {
const fallbackSession = parseStoredSession(localStorage.getItem(AUTH_SESSION_KEY));
if (!fallbackSession) {
return null;
}
const mappedUser = mapBackendUserToAppUser(fallbackSession.user);
localStorage.setItem(USER_KEY, JSON.stringify(mappedUser));
return mappedUser;
};
const raw = localStorage.getItem(USER_KEY);
if (!raw) {
return getUserFromSession();
}
try {
const parsed = JSON.parse(raw) as Partial<User>;
if (!parsed || typeof parsed !== 'object') {
return null;
}
const id = typeof parsed.id === 'string' ? parsed.id : '';
const email = typeof parsed.email === 'string' ? parsed.email : '';
if (!id || !email) {
return null;
}
return {
id,
name: typeof parsed.name === 'string' && parsed.name.trim() ? parsed.name : deriveNameFromEmail(email),
email,
phone: typeof parsed.phone === 'string' && parsed.phone.trim() ? parsed.phone : 'N/A',
role: normalizeRole(typeof parsed.role === 'string' ? parsed.role : undefined),
farmId: typeof parsed.farmId === 'string' ? parsed.farmId : undefined,
};
} catch {
return getUserFromSession();
}
};
export const getAccessToken = (): string | null => {
if (!isBrowser()) {
return null;
}
const token = localStorage.getItem(TOKEN_KEY);
if (token) {
return token;
}
return parseStoredSession(localStorage.getItem(AUTH_SESSION_KEY))?.token ?? null;
};
export const clearAuthSession = (): void => {
if (!isBrowser()) {
return;
}
localStorage.removeItem(AUTH_SESSION_KEY);
localStorage.removeItem(TOKEN_KEY);
localStorage.removeItem(REFRESH_TOKEN_KEY);
localStorage.removeItem(USER_KEY);
};