mirror of
https://github.com/kikootwo/ReadMeABook.git
synced 2026-06-03 12:50:09 +00:00
682836237b
Replaces scattered console statements with a unified RMABLogger across backend API routes and services. Adds LOG_LEVEL-based filtering, job-aware database persistence, and context-based logging. Updates documentation to describe the new logging system and usage patterns. Also documents qBittorrent CSRF header fix
79 lines
1.9 KiB
TypeScript
79 lines
1.9 KiB
TypeScript
/**
|
|
* Component: Client-Side JWT Utilities
|
|
* Documentation: documentation/frontend/routing-auth.md
|
|
*/
|
|
|
|
import { RMABLogger } from './logger';
|
|
|
|
const logger = RMABLogger.create('JWTClient');
|
|
|
|
interface JWTPayload {
|
|
sub: string;
|
|
plexId: string;
|
|
username: string;
|
|
role: string;
|
|
iat: number;
|
|
exp: number;
|
|
}
|
|
|
|
/**
|
|
* Decode JWT without verification (client-side only)
|
|
* Note: This does NOT verify the signature - only use for reading claims
|
|
*/
|
|
export function decodeJWT(token: string): JWTPayload | null {
|
|
try {
|
|
const parts = token.split('.');
|
|
if (parts.length !== 3) {
|
|
return null;
|
|
}
|
|
|
|
const payload = parts[1];
|
|
const decoded = JSON.parse(atob(payload.replace(/-/g, '+').replace(/_/g, '/')));
|
|
return decoded as JWTPayload;
|
|
} catch (error) {
|
|
logger.error('Failed to decode JWT', { error: error instanceof Error ? error.message : String(error) });
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if token is expired
|
|
*/
|
|
export function isTokenExpired(token: string): boolean {
|
|
const decoded = decodeJWT(token);
|
|
if (!decoded || !decoded.exp) {
|
|
return true;
|
|
}
|
|
|
|
const now = Math.floor(Date.now() / 1000);
|
|
return decoded.exp < now;
|
|
}
|
|
|
|
/**
|
|
* Get milliseconds until token expires
|
|
*/
|
|
export function getTokenExpiryMs(token: string): number | null {
|
|
const decoded = decodeJWT(token);
|
|
if (!decoded || !decoded.exp) {
|
|
return null;
|
|
}
|
|
|
|
const now = Math.floor(Date.now() / 1000);
|
|
const expiresIn = decoded.exp - now;
|
|
return expiresIn > 0 ? expiresIn * 1000 : 0;
|
|
}
|
|
|
|
/**
|
|
* Get milliseconds until token should be refreshed (5 mins before expiry)
|
|
*/
|
|
export function getRefreshTimeMs(token: string): number | null {
|
|
const expiryMs = getTokenExpiryMs(token);
|
|
if (expiryMs === null) {
|
|
return null;
|
|
}
|
|
|
|
const REFRESH_BUFFER_MS = 5 * 60 * 1000; // 5 minutes
|
|
const refreshTime = expiryMs - REFRESH_BUFFER_MS;
|
|
return refreshTime > 0 ? refreshTime : 0;
|
|
}
|