Jelajahi Sumber

proxy BBB responses and color logs

hmt 3 tahun lalu
induk
melakukan
baecc692da
4 mengubah file dengan 43 tambahan dan 38 penghapusan
  1. 2 2
      README.md
  2. 31 15
      app.ts
  3. 4 6
      bbb.ts
  4. 6 15
      deps.ts

+ 2 - 2
README.md

@@ -25,13 +25,13 @@ Then create a `servers.json` file like this here:
 
 Now you are ready to start the script with setting a port and a secret:
 
-    TINYSCALE_SECRET=some_secret_string deno run --allow-net --allow-read --allow-env https://deno.land/x/tinyscale@v1.2.0/mod.ts
+    TINYSCALE_SECRET=some_secret_string deno run --allow-net --allow-read --allow-env https://deno.land/x/tinyscale@v1.3.0/mod.ts
 
 tinyscales then runs on port 3005 and you will have to set up your reverse proxy so that it can pick up requests or you leave it on that port. If you prefer a different port you can set one with another env-var: `PORT 3006`
 
 When started, tinyscale will connect to each server and make a single call to check if your configuration is correct. If there is a problem tinyscale will abort. If your configuration works you can start using it in your environment by replacing your existing BBB settings with the new tinyscale url in your third party apps. Make sure to also replace the BBB secrets with your new `TINYSCALE_SECRET`.
 
-tinyscale has been tested to work with NextCloud and Moodle.
+tinyscale has been tested to work with NextCloud, Moodle and Greenlight.
 
 If you want to use recordings they will work but you cannot get a list of all recordings. tinyscale will only respond with the next available server since it is a call to bbb without a `meetingID`. The same goes for the call to get infos on all meetings.
 

+ 31 - 15
app.ts

@@ -1,6 +1,9 @@
-import { createError, opine, ErrorRequestHandler, Router, server, createHash } from "./deps.ts";
+import { opine, ErrorRequestHandler, Router, createHash, server, createError, Color } from "./deps.ts";
 import { BBB } from './bbb.ts';
 
+const date = () => new Date().toLocaleTimeString('de')
+const VERSION = 'v1.3.0'
+console.log(date() + Color.green(` Starting tinyscale ${VERSION}`))
 // give your tinyscale server a secret so it looks like a BBB server
 const secret = Deno.env.get("TINYSCALE_SECRET") || ""
 if (!secret) throw "No secret set for tinyscale"
@@ -22,15 +25,14 @@ servers.forEach(async s => {
     if (!res.ok) throw "Connection error. Please check your host configuration"
     const body = await res.text()
     const ok = body.includes('SUCCESS')
-    console.log(`${s.host} is ${ok ? 'ok':'misconfigured. Please check your secret in servers.json'}`)
+    console.log(`${s.host} is ${ok ? Color.green('ok') : Color.red('misconfigured. Please check your secret in servers.json')}`)
     if (!ok) throw "Configuration error. Exiting …"
   } catch (e) {
     // exit tinyscale if an error is encountered in servers.json
-    console.log(e)
+    console.log(Color.brightRed(e))
     Deno.exit(1);
   }
 })
-
 // pick the next server, using an iterator to cycle through all servers available
 function get_available_server(): server {
   let candidate = iterator.next()
@@ -38,30 +40,44 @@ function get_available_server(): server {
     iterator = servers[Symbol.iterator]()
     candidate = iterator.next()
   }
-  console.log(`Using next server ${candidate.value.host}`)
+  console.log(`Using next server ${Color.green(candidate.value.host)}`)
   return candidate.value;
 }
 
 const router = Router()
 // the api itself answering to every call
-router.all("/bigbluebutton/api/:call", async (req, res, next) => {
+router.all("/:call", async (req, res, next) => {
   const handler = new BBB(req)
+  console.log(`${date()} New call to ${Color.green(handler.call)}`)
   if (!handler.authenticated(secret)) {
-    res.setStatus(401).end()
+    console.log(`${Color.red("Rejected incoming call to "+handler.call)}`)
+    next(createError(401))
     return
   }
   let server: server
   try {
     server = await handler.find_meeting_id(servers)
   } catch (e) {
-    console.log(`Found no server with Meeting ID ${handler.meeting_id}`)
+    console.log(`Found no server with Meeting ID ${Color.yellow(handler.meeting_id)}`)
     server = get_available_server()
   }
   console.log(`Redirecting to ${server.host}`)
-  res.redirect(handler.rewritten_query(server))
+  const redirect = handler.rewritten_query(server)
+  if (handler.call === 'join') {
+    res.redirect(redirect)
+  } else {
+    try {
+      const data = await fetch(redirect)
+      const body = await data.text()
+      res.set('Content-Type', 'text/xml');
+      res.send(body)
+    } catch (e) {
+      next(createError(500))
+    }
+  }
 });
 // the fake answering machine to make sure we are recognized as a proper api
-router.get("/bigbluebutton/api", (req, res, next) => {
+router.get("/", (req, res, next) => {
   console.log('sending fake xml response')
   res.set('Content-Type', 'text/xml');
   res.send(`<response>
@@ -72,13 +88,13 @@ router.get("/bigbluebutton/api", (req, res, next) => {
 
 const errorHandler: ErrorRequestHandler = (err, req, res, next) => {
   res.setStatus(err.status ?? 500);
-  console.log(res.status, req.originalUrl)
   res.end();
+  console.log(`${Color.red(`${res.status}`)} ${req.originalUrl}`)
 };
 
 const app = opine()
-            .use("/", router)
-            .use((req, res, next) => { next(createError(404)); })
-            .use(errorHandler);
+  .use("/bigbluebutton/api", router)
+  .use((req, res, next) => next(createError(404)))
+  .use(errorHandler);
 
-export default app;
+export default app;

+ 4 - 6
bbb.ts

@@ -1,4 +1,4 @@
-import { Request, ParamsDictionary, server, createHash } from "./deps.ts";
+import { Request, ParamsDictionary,  createHash, server } from "./deps.ts";
 
 export class BBB {
   call: string
@@ -12,10 +12,9 @@ export class BBB {
     this.call = req.params.call
     this.checksum_incoming = req.query.checksum
     this.query = req._parsedUrl?.query || ""
-    this.params = this.query.replace(/[?|&]checksum.*$/, '')
+    this.params = this.query.replace(/[?&]?checksum.*$/, '')
     this.meeting_id = req.query.meetingID
     this.url = req.originalUrl
-    console.log(`New call to ${this.call}`)
   }
   // generate a checksum for various calls
   generate_checksum = (secret: string, call: string = this.call, params: string = this.params) => {
@@ -31,13 +30,12 @@ export class BBB {
   // write new query for target bbb server
   rewritten_query = (server: server) => {
     const checksum_outgoing = this.generate_checksum(server.secret)
-    return `${server.host}/${this.url.replace(this.checksum_incoming, checksum_outgoing)}`
+    return `${server.host}${this.url.replace(this.checksum_incoming, checksum_outgoing)}`
   }
   // check if request is autheticated with correct checksum
   authenticated = (secret: string) => {
     const checksum = this.generate_checksum(secret)
     const ok = checksum === this.checksum_incoming
-    if (!ok) console.log(`Rejected incoming call to ${this.call}`)
     return ok
   }
   find_meeting_id = (servers: server[]): Promise<server> => {
@@ -51,4 +49,4 @@ export class BBB {
     })
     return Promise.any(promises)
   }
-}
+}

+ 6 - 15
deps.ts

@@ -1,16 +1,7 @@
-export {
-  join,
-} from "https://deno.land/std@0.94.0/path/mod.ts";
-export {
-  json,
-  opine,
-  Router,
-} from "https://deno.land/x/opine@1.3.2/mod.ts";
-export type {
-  ErrorRequestHandler,
-  Request,
-  ParamsDictionary
- } from "https://deno.land/x/opine@1.3.2/mod.ts";
-export interface server { host: string; secret: string };
+export { join, } from "https://deno.land/std@0.95.0/path/mod.ts";
+export { createHash } from "https://deno.land/std@0.95.0/hash/mod.ts";
+export * as Color from "https://deno.land/std@0.95.0/fmt/colors.ts";
 export { createError } from "https://deno.land/x/http_errors@3.0.0/mod.ts";
-export { createHash } from "https://deno.land/std@0.94.0/hash/mod.ts";
+export { opine, Router } from "https://deno.land/x/opine@1.3.2/mod.ts";
+export type { ErrorRequestHandler, Request, ParamsDictionary } from "https://deno.land/x/opine@1.3.2/mod.ts";
+export interface server { host: string; secret: string };