mirror of
https://github.com/janishutz/fundamentals-of-webengineering.git
synced 2025-11-25 05:44:24 +00:00
[Task 3] SSE
This commit is contained in:
21
task_3_react/src/client/sse.ts
Normal file
21
task_3_react/src/client/sse.ts
Normal 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
|
||||||
|
} ) );
|
||||||
|
};
|
||||||
@@ -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' ),
|
||||||
|
|||||||
Reference in New Issue
Block a user