-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into feat/v0.1.1
- Loading branch information
Showing
2 changed files
with
336 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,177 @@ | ||
# excalidraw-ws-go | ||
WebSocket Server on Go for managing shared session for Excalidraw | ||
<p align="center"> | ||
<h1 align="center">Excaliroom</h1> | ||
<p align="center">A simple WebSocket server for collaborative drawing with Excalidraw</p> | ||
</p> | ||
|
||
--- | ||
|
||
>[!WARNING] | ||
> Current project major version is _**0.x.**_ It means that the project is still in development and may have breaking changes in the future. | ||
This is an _**unofficial**_ implementation of the [Excalidraw](https://excalidraw.com/) collaboration server. | ||
It uses WebSockets to communicate between clients and broadcast the changes to all connected clients. | ||
|
||
## Table of Contents | ||
|
||
- [Features](#features) | ||
- [Configuration](#configuration) | ||
- [JWT and Board URLs](#jwt-and-board-urls) | ||
- [Storage](#storage) | ||
- [Installation](#installation) | ||
- [Docker](#docker) | ||
- [Docker Compose](#docker-compose) | ||
- [Build Go binary](#build-go-binary) | ||
- [How to use](#how-to-use) | ||
- [Contributing](#contributing) | ||
- [License](#license) | ||
|
||
## Features | ||
|
||
- Real-time collaboration with multiple users | ||
- Authentication and validation with JWT | ||
- Configurable storage (currently only supports **in-memory** storage) | ||
|
||
## Configuration | ||
|
||
The server uses **_.yaml_** configuration file to set up. | ||
You can find an example configuration file [here](./config-example.yaml). | ||
|
||
```yaml | ||
apps: | ||
log_level: "DEBUG" | ||
rest: | ||
port: 8080 | ||
validation: | ||
jwt_header_name: "<YOUR_JWT_HEADER_NAME>" | ||
jwt_validation_url: "<YOUR_JWT_VALIDATION_URL>" | ||
board_validation_url: "<YOUR_BOARD_VALIDATION_URL>" | ||
|
||
storage: | ||
users: | ||
type: "in-memory" | ||
rooms: | ||
type: "in-memory" | ||
``` | ||
Currently, the `apps` section contains the following configurations: | ||
- `log_level`: The log level of the server. It can be one of the following: `DEBUG`, `INFO` (More levels will be added in the future). | ||
- `rest`: The REST API configuration. | ||
- `port`: The port of the REST API. | ||
- `validation`: The JWT validation configuration. | ||
- `jwt_header_name`: The name of the header, in which `Excaliroom` will set the JWT token from client. | ||
- `jwt_validation_url`: The URL to validate the JWT token, which will be used to authenticate the user. | ||
- `board_validation_url`: The URL to validate the access to the board with the JWT token. | ||
|
||
The `storage` section contains the following configurations: | ||
- `users`: The user storage configuration. It specifies where the server will store the user data. | ||
- `type`: The type of the storage. Currently, only `in-memory` is supported. | ||
- `rooms`: The room storage configuration. It specifies where the server will store the room data. | ||
- `type`: The type of the storage. Currently, only `in-memory` is supported. | ||
|
||
### JWT and Board URLs | ||
|
||
To authenticate the user and validate the access to the board, you need to provide the URLs in the configuration file. | ||
|
||
The `Excaliroom` server requires 2 URLs: | ||
- `jwt_validation_url`: The URL to validate the JWT token. The `Excaliroom` server will send a `GET` request to this URL with the JWT token in the header. The server should return `200 OK` if the token is valid and the following JSON response: | ||
```json | ||
{ | ||
"id": "<USER_ID>" | ||
} | ||
``` | ||
The `id` will be used to identify the user. | ||
|
||
|
||
- `board_validation_url`: The URL to validate the access to the board with the JWT token. The `Excaliroom` server will send a `GET` request to this URL with the JWT token in the header. The server should return `200 OK`. | ||
|
||
### Storage | ||
|
||
Currently, the server only supports `in-memory` storage for users and rooms. | ||
|
||
## Installation | ||
|
||
### Docker | ||
|
||
>[!WARNING] | ||
> Currently, the Docker image from the Docker Hub is only available for **linux/amd64** platform. | ||
> | ||
> If you use another platform (e.g., **linux/arm64**), you can provide `--platform` flag to the `docker pull` command. | ||
|
||
1. You can pull the Docker image from the Docker Hub: | ||
|
||
```bash | ||
docker pull icerzack/excaliroom:latest | ||
``` | ||
|
||
Then, you can run the Docker container with the following command: | ||
|
||
```bash | ||
docker run -d -p 8080:8080 -v path/to/config.yaml:/config.yaml -e CONFIG_PATH="/config.yaml" icerzack/excaliroom:latest | ||
``` | ||
|
||
2. You can build the Docker image by yourself with the following command: | ||
|
||
```bash | ||
docker build -f build/Dockerfile -e CONFIG_PATH="path/to/config.yaml" -t excaliroom . | ||
``` | ||
|
||
Then, you can run the Docker container with the following command: | ||
|
||
```bash | ||
docker run -d -p 8080:8080 -v path/to/config.yaml:/config.yaml -e CONFIG_PATH="/config.yaml" excaliroom | ||
``` | ||
|
||
### Docker Compose | ||
|
||
You can use Docker Compose to run the server with the following `docker-compose.yml` file: | ||
|
||
```yaml | ||
services: | ||
app: | ||
image: icerzack/excaliroom:latest | ||
environment: | ||
- CONFIG_PATH=config.yaml | ||
ports: | ||
- "8080:8080" | ||
volumes: | ||
- ./config.yaml:/config.yaml | ||
``` | ||
|
||
Then, you can run the server with the following command: | ||
|
||
```bash | ||
docker-compose up -d | ||
``` | ||
|
||
### Build Go binary | ||
|
||
Set the environment variable `CONFIG_PATH` to the path of the configuration file and build the binary: | ||
|
||
```bash | ||
export CONFIG_PATH="path/to/config.yaml" | ||
go build -o excaliroom main.go | ||
``` | ||
|
||
Then, you can run the binary: | ||
|
||
```bash | ||
./excaliroom | ||
``` | ||
|
||
## How to use | ||
|
||
Check the [docs](./docs) directory of this repo for the guides on how to use and integrate the `Excaliroom` server with your JavaScript application. | ||
|
||
## Contributing | ||
|
||
If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement". Don't forget to give the project a star! Thanks again! | ||
|
||
1. Fork the Project | ||
2. Create your Feature Branch (git checkout -b feature/AmazingFeature) | ||
3. Commit your Changes (git commit -m 'Add some AmazingFeature') | ||
4. Push to the Branch (git push origin feature/AmazingFeature) | ||
5. Open a Pull Request | ||
|
||
## License | ||
|
||
This project is licensed under the Apache License 2.0 - see the [LICENSE](./LICENSE) file for details. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
# How to use Excaliroom | ||
|
||
This directory contains guidance for using and integrating the `Excaliroom` server with your existing project. | ||
|
||
## Table of contents | ||
|
||
- [Getting started](#getting-started) | ||
- [Pre-requisites](#pre-requisites) | ||
- [How Excaliroom works](#how-excaliroom-works) | ||
- [API reference](#api-reference) | ||
- [Examples](#examples) | ||
- [FAQ](#faq) | ||
|
||
## Getting started | ||
|
||
### Pre-requisites | ||
|
||
First of all, it is implied that you have `Backend` and `Frontend` applications: | ||
- `Backend`: It may contain some of your business logic, and it may be written in any programming language. | ||
- `Frontend`: It is your JavaScript application that will communicate with the `Excaliroom` server. | ||
|
||
The `Excaliroom` WebSocket server is only responsible for handling real-time communication between your clients on the same board. | ||
It does not store any data (except for the current state of the board and connected clients) and does not have any business logic. | ||
|
||
However, `Excaliroom` server requires a `JWT` token to authenticate and authorize users. | ||
The `JWT` token should be: | ||
- Validated by your `Backend` application. | ||
- Provided by your `Frontend` application. | ||
|
||
The `Excaliroom` server also requires a URL to validate user access to the board. | ||
|
||
The neccessary URLs for `JWT` validation and board validation should be provided in the `Excaliroom` server configuration file. | ||
See the [Configuration](../README.md#configuration) section for more information. | ||
|
||
### How Excaliroom works | ||
|
||
The `Excaliroom` server uses WebSockets to communicate between clients and broadcast the changes to all connected clients. | ||
It is a real-time collaboration server that allows multiple users to draw on the same board. | ||
|
||
Currently, the sharing mechanism is simple: | ||
- When a first user connects to the `Excaliroom`, it sends the current state of the board to the WebSocket server. This happens only if the user is the first one to connect to the board. After receiving the board state, the `Excaliroom` creates a new room and stores the board state and the user in the room. | ||
- With the next user connecting to the same board, the `Excaliroom` adds the user to the existing room and broadcasts the current room state to all connected users. | ||
- By default, no one can modify the board state. `Excaliroom` can handle board updates only from the _**Leader**_ of the room. By default, after creating a new room, no one is the _**Leader**_ of the room. The _**Leader**_ is the user who can modify the board state. The _**Leader**_ can be dropped by the _**Leader**_ itself. If the _**Leader**_ leaves the room, the _**Leader**_ role is reset so anyone can become the _**Leader**_. | ||
- When the _**Leader**_ sends a new board state to the `Excaliroom`, the server broadcasts the new board state to all connected users. In other words, the _**Leader**_ is the only user who can modify the board state, while all other users can only view the board state. | ||
- When the last user leaves the room, the room is deleted from the `Excaliroom`. | ||
|
||
The `Excaliroom` sends and receives messages in JSON format. The message format is described in the [API reference](#api-reference) section. | ||
|
||
## API reference | ||
|
||
Each JSON message contains `event` field that describes the type of the message. The `event` field can have the following values: | ||
- `connect`: The message is sent by `Frontend` when the user requests to connect to the board. | ||
- `userConnected`: The message is sent by `Excaliroom` to all connected users when a new user connects to the board. | ||
- `userDisconnected`: The message is sent by `Excaliroom` to all connected users when a user disconnects from the board. | ||
- `setLeader`: The message is sent by `Frontend` when the user requests to become the _**Leader**_ of the room and sent by `Excaliroom` to all connected users when the _**Leader**_ changes. | ||
- `newData`: The message is sent by `Frontend` when the user sends new board data to the server and sent by `Excaliroom` to all connected users when the _**Leader**_ sends new board data. | ||
|
||
The JSON message format is as follows: | ||
1. `connect` event: | ||
```json | ||
{ | ||
"event": "connect", | ||
"board_id": "<BOARD_ID>", | ||
"jwt": "<JWT_TOKEN>" | ||
} | ||
``` | ||
- `board_id`: The unique identifier of the board. | ||
- `jwt`: The JWT token that is used to authenticate and authorize the user. The `Excaliroom` server will use `jwt_validation_url` to validate the JWT token on your `Backend` and `jwt_header_name` to set the JWT to the header. After validating the JWT token, the `Excaliroom` server will use `board_validation_url` to validate the access to the board. See the [Configuration](../README.md#jwt-and-board-urls) section for more information. | ||
|
||
2. `userConnected` event: | ||
```json | ||
{ | ||
"event": "userConnected", | ||
"user_ids": ["<USER_ID_1>", "<USER_ID_2>", ...], | ||
"leader_id": "<LEADER_ID>" | ||
} | ||
``` | ||
- `user_ids`: The list of user identifiers that are connected to the board. | ||
- `leader_id`: The identifier of the _**Leader**_ of the room. If the _**Leader**_ is not set, the `leader_id` will be `0`. | ||
|
||
3. `userDisconnected` event: | ||
```json | ||
{ | ||
"event": "userDisconnected", | ||
"user_ids": ["<USER_ID_1>", "<USER_ID_2>", ...], | ||
"leader_id": "<LEADER_ID>" | ||
} | ||
``` | ||
- `user_ids`: The list of user identifiers that are connected to the board. | ||
- `leader_id`: The identifier of the _**Leader**_ of the room. If the _**Leader**_ is not set, the `leader_id` will be `0`. | ||
|
||
4. `setLeader` event (request): | ||
```json | ||
{ | ||
"event": "setLeader", | ||
"board_id": "<BOARD_ID>", | ||
"jwt": "<JWT_TOKEN>" | ||
} | ||
``` | ||
- `board_id`: The unique identifier of the board. | ||
- `jwt`: The JWT token that is used to authenticate and authorize the user. The `Excaliroom` server will use `jwt_validation_url` to validate the JWT token on your `Backend` and `jwt_header_name` to set the JWT to the header. After validating the JWT token, the `Excaliroom` server will use `board_validation_url` to validate the access to the board. See the [Configuration](../README.md#jwt-and-board-urls) section for more information. | ||
|
||
5. `setLeader` event (response): | ||
```json | ||
{ | ||
"event": "setLeader", | ||
"board_id": "<BOARD_ID>", | ||
"user_id": "<USER_ID>" | ||
} | ||
``` | ||
- `board_id`: The unique identifier of the board. | ||
- `user_id`: The identifier of the _**Leader**_ of the room. | ||
|
||
6. `newData` event (request): | ||
```json | ||
{ | ||
"event": "newData", | ||
"board_id": "<BOARD_ID>", | ||
"jwt": "<JWT_TOKEN>", | ||
"data": { | ||
"elements": "EXCALIDRAW_ELEMENTS_JSON", | ||
"appState": "EXCALIDRAW_APP_STATE_JSON" | ||
} | ||
} | ||
``` | ||
- `board_id`: The unique identifier of the board. | ||
- `jwt`: The JWT token that is used to authenticate and authorize the user. The `Excaliroom` server will use `jwt_validation_url` to validate the JWT token on your `Backend` and `jwt_header_name` to set the JWT to the header. After validating the JWT token, the `Excaliroom` server will use `board_validation_url` to validate the access to the board. See the [Configuration](../README.md#jwt-and-board-urls) section for more information. | ||
- `data`: The board data that is sent by the _**Leader**_ of the room. | ||
- `elements`: The JSON string of the Excalidraw `elements`. | ||
- `appState`: The JSON string of the Excalidraw `appState`. | ||
|
||
See the [Excalidraw Docs](https://docs.excalidraw.com/docs/@excalidraw/excalidraw/api/props/initialdata) documentation for more information. | ||
|
||
7. `newData` event (response): | ||
```json | ||
{ | ||
"event": "newData", | ||
"board_id": "<BOARD_ID>", | ||
"data": { | ||
"elements": "EXCALIDRAW_ELEMENTS_JSON", | ||
"appState": "EXCALIDRAW_APP_STATE_JSON" | ||
} | ||
} | ||
``` | ||
- `board_id`: The unique identifier of the board. | ||
- `data`: The board data that is sent by the _**Leader**_ of the room. | ||
- `elements`: The JSON string of the Excalidraw `elements`. | ||
- `appState`: The JSON string of the Excalidraw `appState`. | ||
|
||
See the [Excalidraw Docs](https://docs.excalidraw.com/docs/@excalidraw/excalidraw/api/props/initialdata) documentation for more information. | ||
|
||
## Examples | ||
|
||
_Later_ | ||
|
||
## FAQ | ||
|
||
_Later_ | ||
|