Files
Job-Info/frontend/src/utils/service-worker-manager.ts
T
aewing 42ff3e8f33
Build & Deploy / Test & Lint (push) Has been cancelled
Build & Deploy / Security Scan (push) Has been cancelled
Code Quality / Code Quality Checks (push) Has been cancelled
Code Quality / Dependency Vulnerabilities (push) Has been cancelled
Code Quality / Performance Testing (push) Has been cancelled
Build & Deploy / Build Docker Images (push) Has been cancelled
Build & Deploy / Deploy to Kubernetes (push) Has been cancelled
Add Mode6Test: copy of Mode5Test running on port 3000
2026-01-20 13:43:29 +00:00

195 lines
4.9 KiB
TypeScript

/**
* UTILITY: Service Worker Manager
*
* PURPOSE: Register and communicate with Service Worker
* FEATURES:
* - Register Service Worker on app load
* - Send messages to SW
* - Receive messages from SW
* - Handle token refresh via SW
*/
export interface TokenCheckResult {
needsRefresh: boolean;
timeUntilExpiry: number;
}
export interface TokenRefreshResult {
success: boolean;
accessToken?: string;
expiresIn?: number;
error?: string;
}
/**
* FUNCTION: registerServiceWorker
*
* Register the service worker on app load
* Called once from App.svelte onMount
*/
export async function registerServiceWorker(): Promise<ServiceWorkerContainer | null> {
// Service Workers only available in production (HTTPS) or localhost
if (!('serviceWorker' in navigator)) {
console.log('[SW Manager] Service Workers not supported');
return null;
}
try {
console.log('[SW Manager] Registering Service Worker...');
const registration = await navigator.serviceWorker.register('/service-worker.js', {
scope: '/',
});
console.log('[SW Manager] Service Worker registered:', registration);
// Listen for messages from Service Worker
navigator.serviceWorker.addEventListener('message', (event) => {
console.log('[SW Manager] Message from Service Worker:', event.data);
});
return registration;
} catch (error) {
console.error('[SW Manager] Failed to register Service Worker:', error);
return null;
}
}
/**
* FUNCTION: checkTokenExpiry
*
* Ask Service Worker if token needs refresh
*
* PARAMS:
* - tokenExpiry: number - Token expiry timestamp (ms since epoch)
*
* RETURNS: TokenCheckResult
*/
export async function checkTokenExpiry(tokenExpiry: number): Promise<TokenCheckResult> {
if (!navigator.serviceWorker.controller) {
console.log('[SW Manager] Service Worker not active');
return { needsRefresh: false, timeUntilExpiry: tokenExpiry - Date.now() };
}
return new Promise((resolve, reject) => {
const channel = new MessageChannel();
channel.port1.onmessage = (event) => {
if (event.data.error) {
reject(new Error(event.data.error));
} else {
resolve(event.data);
}
};
navigator.serviceWorker.controller?.postMessage(
{
type: 'CHECK_TOKEN',
payload: { tokenExpiry },
},
[channel.port2]
);
// Timeout after 5 seconds
setTimeout(() => reject(new Error('Token check timeout')), 5000);
});
}
/**
* FUNCTION: refreshTokenViaServiceWorker
*
* Ask Service Worker to refresh access token
*
* PARAMS:
* - refreshToken: string - Refresh token
* - apiUrl: string - Backend API URL
*
* RETURNS: TokenRefreshResult
*/
export async function refreshTokenViaServiceWorker(
refreshToken: string,
apiUrl: string
): Promise<TokenRefreshResult> {
if (!navigator.serviceWorker.controller) {
console.log('[SW Manager] Service Worker not active');
return { success: false, error: 'Service Worker not active' };
}
return new Promise((resolve, reject) => {
const channel = new MessageChannel();
channel.port1.onmessage = (event) => {
if (event.data.error) {
resolve({ success: false, error: event.data.error });
} else {
resolve(event.data);
}
};
navigator.serviceWorker.controller?.postMessage(
{
type: 'REFRESH_TOKEN',
payload: { refreshToken, apiUrl },
},
[channel.port2]
);
// Timeout after 10 seconds
setTimeout(() => reject(new Error('Token refresh timeout')), 10000);
});
}
/**
* FUNCTION: getServiceWorkerStatus
*
* Get Service Worker status and version
*
* RETURNS: { active: boolean, version?: string }
*/
export async function getServiceWorkerStatus(): Promise<{ active: boolean; version?: string }> {
if (!navigator.serviceWorker.controller) {
return { active: false };
}
return new Promise((resolve, reject) => {
const channel = new MessageChannel();
channel.port1.onmessage = (event) => {
if (event.data.error) {
reject(new Error(event.data.error));
} else {
resolve(event.data);
}
};
navigator.serviceWorker.controller?.postMessage(
{ type: 'GET_STATUS' },
[channel.port2]
);
// Timeout after 5 seconds
setTimeout(() => reject(new Error('Status check timeout')), 5000);
});
}
/**
* FUNCTION: unregisterServiceWorker
*
* Unregister the service worker (for development)
*/
export async function unregisterServiceWorker(): Promise<void> {
if (!('serviceWorker' in navigator)) {
return;
}
try {
const registrations = await navigator.serviceWorker.getRegistrations();
for (const registration of registrations) {
await registration.unregister();
}
console.log('[SW Manager] Service Worker unregistered');
} catch (error) {
console.error('[SW Manager] Failed to unregister Service Worker:', error);
}
}