Add Mode6Test: copy of Mode5Test running on port 3000
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
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
This commit is contained in:
@@ -0,0 +1,208 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
Reference in New Issue
Block a user