app.ts 2.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. import { opine, ErrorRequestHandler, Router, secret, createError, Color, deferred, Deferred } from "./deps.ts";
  2. import { BBB } from './bbb.ts';
  3. import { Servers, server } from './servers.ts'
  4. const date = () => new Date().toLocaleTimeString('de')
  5. const S = new Servers()
  6. await S.init()
  7. let queue: Record<string, Deferred<string>> = {}
  8. const router = Router()
  9. router.use((req, res, next)=> {
  10. res.set('Content-Type', 'text/xml')
  11. next()
  12. });
  13. // check authentication via checksum
  14. router.use("/:call", (req, res, next) => {
  15. const handler = new BBB(req)
  16. const authenticated = handler.authenticated(secret)
  17. res.locals.log = [`${date()} ${Color.green(handler.call)}${authenticated ? '':Color.red(' Rejected')}`]
  18. if (authenticated) {
  19. res.locals.handler = handler
  20. next()
  21. } else {
  22. next(createError(401))
  23. }
  24. })
  25. // if the param is call, check for races
  26. router.all('/create', async (req, res, next) => {
  27. const meeting_id = req.query.meetingID
  28. const existing_id = queue[meeting_id]
  29. if (existing_id) {
  30. console.log(`Race pending for meeting-ID: ${Color.red(meeting_id)}`)
  31. await existing_id
  32. }
  33. queue[meeting_id] = deferred<string>();
  34. next()
  35. })
  36. // the api itself answering to every call
  37. router.all("/:call", async (req, res, next) => {
  38. const handler = res.locals.handler
  39. let server: server
  40. try {
  41. server = await handler.find_meeting_id(S.servers)
  42. res.locals.log.push(`found, ${handler.call==='join'?'redirect to':'reply with'} ${server.host}`)
  43. } catch (e) {
  44. res.locals.log.push(`${Color.yellow("not found,")}`)
  45. if (handler.call === 'create') {
  46. S.get_available_server()
  47. res.locals.log.push(`open new room on ${Color.green(S.current_server.host)}`);
  48. }
  49. server = S.current_server
  50. }
  51. const redirect = handler.rewritten_query(server)
  52. if (handler.call === 'join') {
  53. res.redirect(redirect)
  54. } else {
  55. try {
  56. const data = await fetch(redirect)
  57. const body = await data.text()
  58. if (handler.call === 'create') { queue[handler.meeting_id]?.resolve(body); delete queue[handler.meeting_id] }
  59. res.send(body)
  60. } catch (e) {
  61. if (handler.call === 'create') { queue[handler.meeting_id]?.resolve(e); delete queue[handler.meeting_id] }
  62. next(createError(500))
  63. }
  64. }
  65. console.log(res.locals.log.join(' '));
  66. });
  67. // the fake answering machine to make sure we are recognized as a proper api
  68. router.get("/", (req, res, next) => {
  69. res.send(`<response><returncode>SUCCESS</returncode><version>2.0</version></response>`);
  70. })
  71. const errorHandler: ErrorRequestHandler = (err, req, res, next) => {
  72. res.setStatus(err.status ?? 500);
  73. res.end();
  74. console.log(`${Color.red(`${res.status}`)} ${req.originalUrl}`)
  75. };
  76. const app = opine()
  77. .use("/bigbluebutton/api", router)
  78. .use((req, res, next) => next(createError(404)))
  79. .use(errorHandler);
  80. export default app;