adding sse for the landing page to update turnament stats for everyone
This commit is contained in:
parent
c61bcedb3a
commit
af4dbdea91
5 changed files with 63 additions and 7 deletions
|
|
@ -64,7 +64,7 @@ async fn main() -> Result<(), rocket::Error> {
|
||||||
|
|
||||||
let rocket = rocket::build()
|
let rocket = rocket::build()
|
||||||
.manage(state)
|
.manage(state)
|
||||||
.mount("/", routes![healthz, login, logout, events])
|
.mount("/", routes![healthz, login, logout, events, public_events])
|
||||||
.mount("/persons", routes::persons::routes())
|
.mount("/persons", routes::persons::routes())
|
||||||
.mount("/tournament", routes::tournaments::routes());
|
.mount("/tournament", routes::tournaments::routes());
|
||||||
|
|
||||||
|
|
@ -157,3 +157,25 @@ fn events(_user: AuthUser, state: &State<AppState>) -> EventStream![Event + '_]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[get("/events/public")]
|
||||||
|
fn public_events(state: &State<AppState>) -> EventStream![Event + '_] {
|
||||||
|
let mut receiver = state.event_sender.subscribe();
|
||||||
|
|
||||||
|
EventStream! {
|
||||||
|
loop {
|
||||||
|
match receiver.recv().await {
|
||||||
|
Ok(event) => {
|
||||||
|
match &event {
|
||||||
|
AppEvent::TournamentUpserted { .. } | AppEvent::TournamentDeleted { .. } => {
|
||||||
|
yield Event::json(&event);
|
||||||
|
}
|
||||||
|
_ => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(RecvError::Closed) => break,
|
||||||
|
Err(RecvError::Lagged(_)) => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,13 @@ export type AppEvent =
|
||||||
| { type: 'tournament_upserted'; tournament: TournamentInfo }
|
| { type: 'tournament_upserted'; tournament: TournamentInfo }
|
||||||
| { type: 'tournament_deleted'; tournament_id: number };
|
| { type: 'tournament_deleted'; tournament_id: number };
|
||||||
|
|
||||||
export function listenToEvents(onEvent: (event: AppEvent) => void) {
|
export function listenToEvents(onEvent: (event: AppEvent) => void, endpoint = '/api/events') {
|
||||||
let stopped = false;
|
let stopped = false;
|
||||||
let source: EventSource | null = null;
|
let source: EventSource | null = null;
|
||||||
|
|
||||||
function connect() {
|
function connect() {
|
||||||
if (stopped) return;
|
if (stopped) return;
|
||||||
source = new EventSource('/api/events');
|
source = new EventSource(endpoint);
|
||||||
source.onmessage = (event) => {
|
source.onmessage = (event) => {
|
||||||
try {
|
try {
|
||||||
const data = JSON.parse(event.data) as AppEvent;
|
const data = JSON.parse(event.data) as AppEvent;
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,16 @@ export type TournamentEvent =
|
||||||
| { type: 'tournament_upserted'; tournament: TournamentInfo }
|
| { type: 'tournament_upserted'; tournament: TournamentInfo }
|
||||||
| { type: 'tournament_deleted'; tournament_id: number };
|
| { type: 'tournament_deleted'; tournament_id: number };
|
||||||
|
|
||||||
|
type TournamentEventOptions = {
|
||||||
|
endpoint?: string;
|
||||||
|
};
|
||||||
|
|
||||||
export function listenToTournamentEvents(
|
export function listenToTournamentEvents(
|
||||||
onUpsert: (tournament: TournamentInfo) => void,
|
onUpsert: (tournament: TournamentInfo) => void,
|
||||||
onDelete: (tournamentId: number) => void
|
onDelete: (tournamentId: number) => void,
|
||||||
|
options: TournamentEventOptions = {}
|
||||||
) {
|
) {
|
||||||
|
const endpoint = options.endpoint ?? '/api/events';
|
||||||
return listenToEvents((event) => {
|
return listenToEvents((event) => {
|
||||||
if (event.type === 'tournament_upserted') {
|
if (event.type === 'tournament_upserted') {
|
||||||
onUpsert(event.tournament);
|
onUpsert(event.tournament);
|
||||||
|
|
@ -17,5 +23,5 @@ export function listenToTournamentEvents(
|
||||||
if (event.type === 'tournament_deleted') {
|
if (event.type === 'tournament_deleted') {
|
||||||
onDelete(event.tournament_id);
|
onDelete(event.tournament_id);
|
||||||
}
|
}
|
||||||
});
|
}, endpoint);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
const stop = listenToTournamentEvents(upsertTournament, removeTournament);
|
const stop = listenToTournamentEvents(upsertTournament, removeTournament, {
|
||||||
|
endpoint: '/api/public-events'
|
||||||
|
});
|
||||||
return () => {
|
return () => {
|
||||||
stop();
|
stop();
|
||||||
};
|
};
|
||||||
|
|
|
||||||
26
web/src/routes/api/public-events/+server.ts
Normal file
26
web/src/routes/api/public-events/+server.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
import type { RequestHandler } from './$types';
|
||||||
|
import { proxyRequest } from '$lib/server/backend';
|
||||||
|
|
||||||
|
export const GET: RequestHandler = async (event) => {
|
||||||
|
const { response, setCookies } = await proxyRequest(event, '/events/public', {
|
||||||
|
method: 'GET'
|
||||||
|
});
|
||||||
|
|
||||||
|
const headers = new Headers();
|
||||||
|
const contentType = response.headers.get('content-type');
|
||||||
|
if (contentType) {
|
||||||
|
headers.set('content-type', contentType);
|
||||||
|
} else {
|
||||||
|
headers.set('content-type', 'text/event-stream');
|
||||||
|
}
|
||||||
|
for (const cookie of setCookies) {
|
||||||
|
headers.append('set-cookie', cookie);
|
||||||
|
}
|
||||||
|
headers.set('cache-control', 'no-cache');
|
||||||
|
|
||||||
|
return new Response(response.body, {
|
||||||
|
status: response.status,
|
||||||
|
statusText: response.statusText,
|
||||||
|
headers
|
||||||
|
});
|
||||||
|
};
|
||||||
Loading…
Reference in a new issue