[Task 3] SSE

This commit is contained in:
2025-11-16 20:00:33 +01:00
parent 13cc143888
commit 0429e3057f
2 changed files with 83 additions and 1 deletions

View File

@@ -0,0 +1,21 @@
interface SSEMessage {
'event': string,
'data': string
}
const eventSource: EventSource = new EventSource( '/sse' );
eventSource.onopen = () => {
document.dispatchEvent( new CustomEvent( 'sse:connect', {
'detail': 'success',
'cancelable': false
} ) );
};
eventSource.onmessage = event => {
const data: SSEMessage = JSON.parse( event.data );
document.dispatchEvent( new CustomEvent( 'sse:' + data.event, {
'cancelable': false,
'detail': data.data
} ) );
};

View File

@@ -1,4 +1,7 @@
import * as fs from 'node:fs/promises'; import * as fs from 'node:fs/promises';
import {
EventEmitter
} from 'node:stream';
import ViteExpress from 'vite-express'; import ViteExpress from 'vite-express';
import express from 'express'; import express from 'express';
import multer from 'multer'; import multer from 'multer';
@@ -17,6 +20,8 @@ const storage = multer.diskStorage( {
// Suggested in Multer's readme // Suggested in Multer's readme
const uniqueSuffix = Date.now() + '-' + Math.round( Math.random() * 1E3 ); const uniqueSuffix = Date.now() + '-' + Math.round( Math.random() * 1E3 );
fileEvent.emit( 'uploaded', file.fieldname + '-' + uniqueSuffix );
cb( null, file.fieldname + '-' + uniqueSuffix ); cb( null, file.fieldname + '-' + uniqueSuffix );
} }
} ); } );
@@ -25,6 +30,10 @@ const upload = multer( {
'storage': storage 'storage': storage
} ); } );
class FileEvent extends EventEmitter {}
const fileEvent = new FileEvent();
app.post( app.post(
'/upload', '/upload',
upload.single( 'dataFile' ), upload.single( 'dataFile' ),
@@ -78,8 +87,9 @@ app.delete( '/delete/:fileName', async ( req, res ) => {
); );
try { try {
await fs.unlink( filePath ); // deletes the file await fs.rm( filePath ); // deletes the file
res.status( 200 ).send( 'File deleted successfully' ); res.status( 200 ).send( 'File deleted successfully' );
fileEvent.emit( 'deleted', filePath );
} catch ( error ) { } catch ( error ) {
console.error( 'Error deleting file:', error ); console.error( 'Error deleting file:', error );
res.status( 500 ).send( 'Error deleting file' ); res.status( 500 ).send( 'Error deleting file' );
@@ -93,6 +103,57 @@ app.get( '/hello', async function ( _req, res ) {
} ); } );
} ); } );
interface SSESubscriber {
'uuid': string;
'response': express.Response;
}
interface SSESubscribers {
[id: string]: SSESubscriber | undefined;
}
const subscribers: SSESubscribers = {};
app.get( '/sse', async ( request: express.Request, response: express.Response ) => {
response.writeHead( 200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
} );
response.status( 200 );
response.flushHeaders();
response.write( `data: ${ JSON.stringify( [] ) }\n\n` );
const uuid = crypto.randomUUID();
subscribers[uuid] = {
'uuid': uuid,
'response': response
};
request.on( 'close', () => {
subscribers[ uuid ] = undefined;
} );
} );
const sendSSEData = ( event: string, data: string ) => {
const subs = Object.values( subscribers );
for ( let i = 0; i < subs.length; i++ ) {
subs[i]!.response.write( `data: ${ JSON.stringify( {
'event': event,
'data': data
} ) }` );
}
};
fileEvent.on( 'uploaded', file => {
sendSSEData( 'uploaded', file );
} );
fileEvent.on( 'deleted', file => {
sendSSEData( 'deleted', file );
} );
// Do not change below this line // Do not change below this line
ViteExpress.listen( ViteExpress.listen(
app, 5173, () => console.log( 'Server is listening on http://localhost:5173' ), app, 5173, () => console.log( 'Server is listening on http://localhost:5173' ),