Files
Job-Info/frontend/public/service-worker.js
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

209 lines
5.9 KiB
JavaScript

/**
* SERVICE WORKER: Token Refresh
*
* PURPOSE: Handle automatic token refresh in background
* FEATURES:
* - Periodically check if token needs refresh
* - Refresh token before expiry
* - Intercept requests to add auth headers
* - Handle token expiry gracefully
*
* INSTALLED: Before app loads
* ACTIVATED: On page load
* RUNS: Periodically in background
*/
// Token check interval (every 60 seconds)
const TOKEN_CHECK_INTERVAL = 60 * 1000;
// Token refresh buffer (refresh 5 minutes before expiry)
const REFRESH_BUFFER = 5 * 60 * 1000;
// ============================================================================
// INSTALLATION
// ============================================================================
/**
* EVENT: install
*
* Called when service worker is registered
* Used to cache critical assets
*/
self.addEventListener('install', (event: ExtendEvent) => {
console.log('[ServiceWorker] Installing...');
event.waitUntil(
Promise.resolve().then(() => {
console.log('[ServiceWorker] Installed');
// Skip waiting - activate immediately
self.skipWaiting();
})
);
});
// ============================================================================
// ACTIVATION
// ============================================================================
/**
* EVENT: activate
*
* Called when service worker takes control
* Used to clean up old caches
*/
self.addEventListener('activate', (event: ExtendEvent) => {
console.log('[ServiceWorker] Activating...');
event.waitUntil(
Promise.resolve().then(() => {
console.log('[ServiceWorker] Activated');
// Take control of all pages
return (self as any).clients.claim();
})
);
});
// ============================================================================
// TOKEN REFRESH LOOP
// ============================================================================
/**
* Periodically check and refresh token
*/
async function startTokenRefreshLoop() {
console.log('[ServiceWorker] Starting token refresh loop');
setInterval(async () => {
try {
// Get token expiry from session storage
// (accessed via message from client since SW can't access sessionStorage directly)
// This is handled via postMessage from client
console.log('[ServiceWorker] Checking token...');
// Token refresh is initiated from client via postMessage
// because ServiceWorker can't access sessionStorage directly
} catch (error) {
console.error('[ServiceWorker] Token check error:', error);
}
}, TOKEN_CHECK_INTERVAL);
}
// Start the loop when activated
startTokenRefreshLoop();
// ============================================================================
// MESSAGE HANDLING
// ============================================================================
/**
* EVENT: message
*
* Called when client (page) sends message to ServiceWorker
* Used to:
* - Check if token needs refresh
* - Perform token refresh
* - Get current token status
*/
self.addEventListener('message', async (event: ExtendMessageEvent) => {
const { type, payload } = event.data;
console.log('[ServiceWorker] Received message:', type);
try {
if (type === 'REFRESH_TOKEN') {
// Client asking us to refresh token
const { refreshToken, apiUrl } = payload;
console.log('[ServiceWorker] Refreshing token...');
// Call backend to refresh token
const response = await fetch(`${apiUrl}/api/auth/refresh`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ refreshToken }),
});
if (!response.ok) {
throw new Error('Token refresh failed');
}
const data = await response.json() as any;
console.log('[ServiceWorker] Token refreshed successfully');
// Send new token back to client
event.ports[0].postMessage({
success: true,
accessToken: data.accessToken,
expiresIn: data.expiresIn,
});
} else if (type === 'CHECK_TOKEN') {
// Client asking if token needs refresh
const { tokenExpiry } = payload;
const now = Date.now();
const timeUntilExpiry = tokenExpiry - now;
const needsRefresh = timeUntilExpiry < REFRESH_BUFFER;
console.log(`[ServiceWorker] Token expires in ${Math.round(timeUntilExpiry / 1000)}s`);
event.ports[0].postMessage({
needsRefresh,
timeUntilExpiry,
});
} else if (type === 'GET_STATUS') {
// Client asking for ServiceWorker status
event.ports[0].postMessage({
active: true,
version: '1.0.0',
});
}
} catch (error) {
console.error('[ServiceWorker] Message handling error:', error);
event.ports[0].postMessage({
error: (error as Error).message,
});
}
});
// ============================================================================
// REQUEST INTERCEPTION (future use)
// ============================================================================
/**
* EVENT: fetch
*
* Called for every network request
* Could be used to:
* - Intercept API requests
* - Add auth headers
* - Retry on auth failure
*
* Disabled for now since we handle auth in the app
* Enables when needed for request interception
*/
self.addEventListener('fetch', (event: ExtendFetchEvent) => {
// Currently, just pass through
// In future, could intercept API calls to add auth headers
// or retry on 401 by refreshing token
});
// ============================================================================
// TYPES
// ============================================================================
interface ExtendEvent extends Event {
waitUntil(promise: Promise<any>): void;
}
interface ExtendMessageEvent extends ExtendEvent {
data: {
type: string;
payload?: any;
};
ports: MessagePort[];
}
interface ExtendFetchEvent extends ExtendEvent {
request: Request;
respondWith(response: Response | Promise<Response>): void;
}