diff --git a/src/controllers/EventsController.ts b/src/controllers/EventsController.ts new file mode 100644 index 0000000..ddc90f5 --- /dev/null +++ b/src/controllers/EventsController.ts @@ -0,0 +1,53 @@ +import { Request, Response, Router } from 'express' +import { randomUUID } from 'crypto' +import log from '../log.js' + +const router = Router() + +export class EventsController { + private static clients: { id: string; res: Response }[] = [] + + static async eventsHandler(req: Request, res: Response) { + const headers = { + 'Content-Type': 'text/event-stream', + Connection: 'keep-alive', + 'Cache-Control': 'no-cache', + 'Access-Control-Allow-Origin': '*' + } + + res.writeHead(200, headers) + + const clientId = randomUUID() + + const newClient = { + id: clientId, + res + } + + EventsController.clients.push(newClient) + + log.info(new Date().toLocaleString(), `${clientId} Connection opened`) + + req.on('close', () => { + log.info(new Date().toLocaleString(), `${clientId} Connection closed`) + EventsController.clients = EventsController.clients.filter( + client => client.id !== clientId + ) + }) + } + + static async sendEvent(event: string, data: any = null) { + this.clients.forEach(client => { + log.info( + new Date().toLocaleString(), + `Sending event ${event} to ${client.id}` + ) + client.res.write(`event: ${event}\n`) + client.res.write(`data: ${JSON.stringify(data)}\n\n`) + }) + } +} + +router.get('/', EventsController.eventsHandler) + +export default router diff --git a/src/jobs.ts b/src/jobs.ts index 43bec94..d5a670a 100644 --- a/src/jobs.ts +++ b/src/jobs.ts @@ -1,11 +1,13 @@ import * as path from 'node:path' import { fileURLToPath } from 'node:url' import Bree from 'bree' +import { EventsController } from './controllers/EventsController.js' export const jobs = new Bree({ root: path.join(path.dirname(fileURLToPath(import.meta.url)), 'jobs'), defaultExtension: process.env.NODE_ENV == 'production' ? 'js' : 'ts', logger: false, + jobs: [ { name: 'updatePrinterStatus', @@ -18,3 +20,8 @@ export const jobs = new Bree({ } ] }) + +jobs.on('worker deleted', name => { + if (name == 'updatePrinterStatus') + EventsController.sendEvent('printerStatusUpdated', { status: 'success' }) +}) diff --git a/src/server.ts b/src/server.ts index 6b69904..9fe8075 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,4 +1,4 @@ -import express, { Request, Response } from 'express' +import express from 'express' import bodyParser from 'body-parser' import cors from 'cors' @@ -9,10 +9,16 @@ import LoginRouter from './controllers/LoginController.js' import PrinterRouter from './controllers/PrinterController.js' import PrinterStatusRouter from './controllers/PrinterStatusController.js' import PrinterDiscoveryRouter from './controllers/PrinterDiscoveryController.js' +import EventsRouter from './controllers/EventsController.js' export const app = express() -app.use(cors()) +app.use( + cors({ + // Allow all origins + origin: '*' + }) +) app.use(populateUserMiddleware) app.use('/api', loggerMiddleware) @@ -22,6 +28,7 @@ app.use('/api/login', LoginRouter) app.use('/api/printer', PrinterRouter) app.use('/api/status', PrinterStatusRouter) app.use('/api/discovery', PrinterDiscoveryRouter) +app.use('/api/events', EventsRouter) app.use('/', express.static('public')) diff --git a/web/.env.development b/web/.env.development index e6532df..d8ba05f 100644 --- a/web/.env.development +++ b/web/.env.development @@ -1 +1 @@ -VITE_BASE_URL=http://localhost:8000/api/ +VITE_BASE_API_URL=http://localhost:8000/api/ diff --git a/web/.env.production b/web/.env.production index 0d04996..ba7312f 100644 --- a/web/.env.production +++ b/web/.env.production @@ -1 +1 @@ -VITE_BASE_URL=https://printers.pp.ifms.edu.br/api/ +VITE_BASE_API_URL=https://printers.pp.ifms.edu.br/api/ diff --git a/web/src/App.vue b/web/src/App.vue index 3d07ebc..aba4ffb 100644 --- a/web/src/App.vue +++ b/web/src/App.vue @@ -14,6 +14,7 @@ import { useRouter } from 'vue-router' import { useAppStore } from './store/appStore' import { onBeforeMount } from 'vue' +import { BASE_URL } from './api' const router = useRouter() const appStore = useAppStore() @@ -29,5 +30,14 @@ onBeforeMount(async () => { appStore.selectedCampus = appStore.me?.campus || '' await appStore.fetchPrinters() + + const eventURI = `${BASE_URL}events` + + const events = new EventSource(eventURI) + + events.onmessage = async event => { + await appStore.fetchPrinters() + console.log('Event:', event) + } }) diff --git a/web/src/api.ts b/web/src/api.ts index 268480d..ff68608 100644 --- a/web/src/api.ts +++ b/web/src/api.ts @@ -1,4 +1,5 @@ -const BASE_URL = import.meta.env.VITE_BASE_URL || 'http://localhost:8000/api/' +export const BASE_URL = + import.meta.env.VITE_BASE_API_URL || 'http://localhost:8000/api/' export async function api(endpoint: string, options: any): Promise { const token = localStorage.getItem('token')