feat(web_serviceworker): Enable service worker dashboard speedtests via TypedSocket, expose ServiceWorker instance to dashboard, and add server-side speedtest handler

This commit is contained in:
2025-12-04 12:16:24 +00:00
parent 4bae49cfb0
commit 78a5c53d19
6 changed files with 81 additions and 53 deletions

View File

@@ -1,4 +1,6 @@
import { getMetricsCollector } from './classes.metrics.js';
import { getServiceWorkerInstance } from './index.js';
import * as interfaces from './env.js';
/**
* Dashboard generator that creates a terminal-like metrics display
@@ -43,65 +45,50 @@ export class DashboardGenerator {
} = { isOnline: false };
try {
const sw = getServiceWorkerInstance();
// Check if TypedSocket is connected
if (!sw.typedsocket) {
results.error = 'TypedSocket not connected';
return new Response(JSON.stringify(results), {
headers: {
'Content-Type': 'application/json',
'Cache-Control': 'no-store',
},
});
}
// Create typed request for speedtest
const speedtestRequest = sw.typedsocket.createTypedRequest<
interfaces.serviceworker.IRequest_Serviceworker_Speedtest
>('serviceworker_speedtest');
// Latency test
const latencyStart = Date.now();
const latencyResponse = await fetch('/sw-typedrequest', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
method: 'serviceworker_speedtest',
request: { type: 'latency' },
}),
});
if (latencyResponse.ok) {
await latencyResponse.json(); // Consume response
const latencyDuration = Date.now() - latencyStart;
results.latency = { durationMs: latencyDuration, speedMbps: 0 };
metrics.recordSpeedtest('latency', latencyDuration);
results.isOnline = true;
metrics.setOnlineStatus(true);
}
await speedtestRequest.fire({ type: 'latency' });
const latencyDuration = Date.now() - latencyStart;
results.latency = { durationMs: latencyDuration, speedMbps: 0 };
metrics.recordSpeedtest('latency', latencyDuration);
results.isOnline = true;
metrics.setOnlineStatus(true);
// Download test (100KB)
const downloadStart = Date.now();
const downloadResponse = await fetch('/sw-typedrequest', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
method: 'serviceworker_speedtest',
request: { type: 'download', payloadSizeKB: 100 },
}),
});
if (downloadResponse.ok) {
const downloadData = await downloadResponse.json();
const downloadDuration = Date.now() - downloadStart;
const bytesTransferred = downloadData.response?.payload?.length || 0;
// Speed in Mbps: (bytes * 8) / (ms / 1000) / 1000000 = bytes * 8 / ms / 1000
const downloadSpeedMbps = downloadDuration > 0 ? (bytesTransferred * 8) / (downloadDuration * 1000) : 0;
results.download = { durationMs: downloadDuration, speedMbps: downloadSpeedMbps, bytesTransferred };
metrics.recordSpeedtest('download', downloadSpeedMbps);
}
const downloadResult = await speedtestRequest.fire({ type: 'download', payloadSizeKB: 100 });
const downloadDuration = Date.now() - downloadStart;
const bytesTransferred = downloadResult.payload?.length || 0;
const downloadSpeedMbps = downloadDuration > 0 ? (bytesTransferred * 8) / (downloadDuration * 1000) : 0;
results.download = { durationMs: downloadDuration, speedMbps: downloadSpeedMbps, bytesTransferred };
metrics.recordSpeedtest('download', downloadSpeedMbps);
// Upload test (100KB)
const uploadPayload = 'x'.repeat(100 * 1024);
const uploadStart = Date.now();
const uploadResponse = await fetch('/sw-typedrequest', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
method: 'serviceworker_speedtest',
request: { type: 'upload', payload: uploadPayload },
}),
});
if (uploadResponse.ok) {
const uploadDuration = Date.now() - uploadStart;
const uploadSpeedMbps = uploadDuration > 0 ? (uploadPayload.length * 8) / (uploadDuration * 1000) : 0;
results.upload = { durationMs: uploadDuration, speedMbps: uploadSpeedMbps, bytesTransferred: uploadPayload.length };
metrics.recordSpeedtest('upload', uploadSpeedMbps);
}
await speedtestRequest.fire({ type: 'upload', payload: uploadPayload });
const uploadDuration = Date.now() - uploadStart;
const uploadSpeedMbps = uploadDuration > 0 ? (uploadPayload.length * 8) / (uploadDuration * 1000) : 0;
results.upload = { durationMs: uploadDuration, speedMbps: uploadSpeedMbps, bytesTransferred: uploadPayload.length };
metrics.recordSpeedtest('upload', uploadSpeedMbps);
} catch (error) {
results.error = error instanceof Error ? error.message : String(error);

View File

@@ -28,8 +28,8 @@ export class ServiceWorker {
public store: plugins.webstore.WebStore;
// TypedSocket connection for server communication
private typedsocket: plugins.typedsocket.TypedSocket;
private typedrouter = new plugins.typedrequest.TypedRouter();
public typedsocket: plugins.typedsocket.TypedSocket;
public typedrouter = new plugins.typedrequest.TypedRouter();
constructor(selfArg: interfaces.ServiceWindow) {
logger.log('info', `Service worker instantiating at ${Date.now()}`);

View File

@@ -5,3 +5,6 @@ declare var self: env.ServiceWindow;
import { ServiceWorker } from './classes.serviceworker.js';
const sw = new ServiceWorker(self);
// Export getter for service worker instance (used by dashboard for TypedSocket access)
export const getServiceWorkerInstance = (): ServiceWorker => sw;