From 0b0bcd5de6158e6d61f79c3764b7e3d7def017d0 Mon Sep 17 00:00:00 2001 From: shockrah Date: Wed, 14 Apr 2021 22:38:43 -0700 Subject: [PATCH] First pass docs for rtc capabilities --- docs/content/rtc/_index.md | 36 ++++++++++++++++ docs/content/rtc/auth.md | 29 +++++++++++++ docs/content/rtc/events.md | 83 +++++++++++++++++++++++++++++++++++++ docs/static/wss-arch.png | Bin 0 -> 20258 bytes 4 files changed, 148 insertions(+) create mode 100644 docs/content/rtc/_index.md create mode 100644 docs/content/rtc/auth.md create mode 100644 docs/content/rtc/events.md create mode 100644 docs/static/wss-arch.png diff --git a/docs/content/rtc/_index.md b/docs/content/rtc/_index.md new file mode 100644 index 0000000..8f40483 --- /dev/null +++ b/docs/content/rtc/_index.md @@ -0,0 +1,36 @@ +--- +title: WebRTC +anchor: webrtc +weight: 40 +--- + +## Configuration + +By default web socket servers are bound to port `5648`. +The public URL for this server is also defined in the `.env` configuration file under +the key value of `PUBLIC_WS_URL`. + +Due to how `events` are propagated to users the web socket server also requires +a special `wss-hmac.secret` file to be in the same directory as the required +`hmac.secret` file. +This HMAC is what is used to discern server connections from user connections + +## Data structure + +Event messages come serialized in JSON form. +They have the structure of: + +``` +{ + "type": , + : +} +``` + +The full list of supported events are as follows: + +* new-message +* delete-channel +* create-channel +* update-nick + diff --git a/docs/content/rtc/auth.md b/docs/content/rtc/auth.md new file mode 100644 index 0000000..a972b67 --- /dev/null +++ b/docs/content/rtc/auth.md @@ -0,0 +1,29 @@ +--- +title: RTC Authorization +anchor: rtc-auth +weight: 41 +--- + +## For server devs + +If you would like to follow the same model that the current build uses for +RTC notifications then simply have the REST API server use the `wss-hmac.secret` +to authenticate a "special" connection to the web socket server which is allowed +to broadcast messages to the user connections. +It should be noted that currently user connections that send messages after the +connection has been established are ignored. _The server is really the only one that +can actually broadcast messages to users_. + +The architecture for the web socket server is as follows: + +![Basic Architecture](/wss-arch.png) + +## For client devs + +To authenticate properly you must use the following url structure: + +> `/text?jwt=` + + +The JWT in the query string is the same JWT used for requests against the REST API. +The `/text` path listens for changes on all available text channels so even if a diff --git a/docs/content/rtc/events.md b/docs/content/rtc/events.md new file mode 100644 index 0000000..b27dc73 --- /dev/null +++ b/docs/content/rtc/events.md @@ -0,0 +1,83 @@ +--- +title: Events +anchor: events +weight: 42 +--- +## Pertinent Event Data Structures + +This section describes the data you probably care about; the data behind ``. + +### `new-message` + +Each valid `new-message` is completely flat and contains the following fields: + +* id: u64 +Public user id +* time: i64 + +Unix time-stamp (generated on the server's end) + +* content: String + +If the content type is `text/plain` then the content _is_ all there is for that message. +If the content type is another _valid_ type then this field will contain null. +A second `/message/get?id=` is required. This is done to avoid forcing tons of file data across a network when you don't want it. + +This also gives client devs the choice of putting that network hit behind a button(or some user action) or doing automatically. + + +* content\_type: String + +_For valid content-types refer to [/message/send documentation](#messages-ep)_ + +* author\_id: u64 + +Public Member id + +* channel\_id: u64 + +Text channel id. It is safe to assume that this field _will_ point to a valid text channel and not a misconfigured voice channel. + +* name: String + +Public username for that given user + + +### `delete-channel` + +Contains only 1 field: + +* id: u64 + +The id of the deleted channel + +### `create-channel` + +* id: u64 + +The id of the new channel + +* name: String + +Name of channel + +* description: String | null + +Optional description of channel + +* kind: i32 + +Recall: + +* Voice channels := 1 +* Text channels : 2 + +### `update-nick` + +* id: u64 + +Id of affected user + +* name: String + +New nickname of user diff --git a/docs/static/wss-arch.png b/docs/static/wss-arch.png new file mode 100644 index 0000000000000000000000000000000000000000..a777405ac0892e95900743644f89f8e4a01c2220 GIT binary patch literal 20258 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 AWB>pF literal 0 HcmV?d00001