Skip to content
File

Blob: worker/src/server/routes/viewers.ts

typescript64 lines
1import { Hono } from 'hono';
2import type { Context } from 'hono';
3import {
4 answerInputSchema,
5 emptySchema,
6 leaveSchema,
7 viewerKeySchema,
8} from '@/shared/contracts/signaling.ts';
9import { demand } from '@/server/rpc.ts';
10import type { HttpApp } from '@/server/http.ts';
11import { input, result, room } from './context.ts';
12 
13const app = new Hono<HttpApp>();
14function viewer(c: Context<HttpApp>, beaconToken?: string) {
15 const key = viewerKeySchema.safeParse({
16 id: c.req.param('id'),
17 token: c.req.header('X-Viewer-Token') ?? beaconToken,
18 });
19 demand(key.success, 403, 'This browser does not own that viewer session.');
20 return key.data;
21}
22 
23app.post('/', async (c) => {
24 await input(c, emptySchema);
25 return result(c, await room(c).joinViewer());
26});
27app.post('/:id/answer', async (c) =>
28 result(
29 c,
30 await room(c).answerViewer(viewer(c), (await input(c, answerInputSchema)).sessionDescription),
31 ),
32);
33app.post('/:id/renegotiate', async (c) =>
34 result(
35 c,
36 await room(c).renegotiateViewer(
37 viewer(c),
38 (await input(c, answerInputSchema)).sessionDescription,
39 ),
40 ),
41);
42app.post('/:id/leave', async (c) => {
43 const { viewerToken } = await input(c, leaveSchema);
44 return result(c, await room(c).leaveViewer(viewer(c, viewerToken)));
45});
46app.post('/:id/audio', async (c) => {
47 await input(c, emptySchema);
48 return result(c, await room(c).subscribeAudio(viewer(c)));
49});
50app.post('/:id/heartbeat', async (c) => {
51 await input(c, emptySchema);
52 return result(c, await room(c).heartbeatViewer(viewer(c)));
53});
54app.post('/:id/claim', async (c) => {
55 await input(c, emptySchema);
56 return result(c, await room(c).claimControl(viewer(c)));
57});
58app.post('/:id/release', async (c) => {
59 await input(c, emptySchema);
60 return result(c, await room(c).releaseControl(viewer(c)));
61});
62 
63export default app;