Skip to content

Commit 4de9076

Browse files
Merge pull request #3 from alkem-io/develop
Release: Fixes, Events
2 parents 8f221ba + e535318 commit 4de9076

18 files changed

+245
-166
lines changed

.travis.yml

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
dist: focal
12
language: node_js
23
node_js:
34
- v20.12.1
@@ -11,5 +12,3 @@ install:
1112
- npm install
1213
before_install:
1314
- npm i -g [email protected]
14-
script:
15-
- npm run test:ci

README.md

+31-73
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,31 @@
1-
<p align="center">
2-
<a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo-small.svg" width="200" alt="Nest Logo" /></a>
3-
</p>
4-
5-
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
6-
[circleci-url]: https://circleci.com/gh/nestjs/nest
7-
8-
<p align="center">A progressive <a href="http://nodejs.org" target="_blank">Node.js</a> framework for building efficient and scalable server-side applications.</p>
9-
<p align="center">
10-
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a>
11-
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
12-
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/dm/@nestjs/common.svg" alt="NPM Downloads" /></a>
13-
<a href="https://circleci.com/gh/nestjs/nest" target="_blank"><img src="https://img.shields.io/circleci/build/github/nestjs/nest/master" alt="CircleCI" /></a>
14-
<a href="https://coveralls.io/github/nestjs/nest?branch=master" target="_blank"><img src="https://coveralls.io/repos/github/nestjs/nest/badge.svg?branch=master#9" alt="Coverage" /></a>
15-
<a href="https://discord.gg/G7Qnnhy" target="_blank"><img src="https://img.shields.io/badge/discord-online-brightgreen.svg" alt="Discord"/></a>
16-
<a href="https://opencollective.com/nest#backer" target="_blank"><img src="https://opencollective.com/nest/backers/badge.svg" alt="Backers on Open Collective" /></a>
17-
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a>
18-
<a href="https://paypal.me/kamilmysliwiec" target="_blank"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg"/></a>
19-
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://img.shields.io/badge/Support%20us-Open%20Collective-41B883.svg" alt="Support us"></a>
20-
<a href="https://twitter.com/nestframework" target="_blank"><img src="https://img.shields.io/twitter/follow/nestframework.svg?style=social&label=Follow"></a>
21-
</p>
22-
<!--[![Backers on Open Collective](https://opencollective.com/nest/backers/badge.svg)](https://opencollective.com/nest#backer)
23-
[![Sponsors on Open Collective](https://opencollective.com/nest/sponsors/badge.svg)](https://opencollective.com/nest#sponsor)-->
24-
25-
## Description
26-
27-
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
28-
29-
## Installation
30-
31-
```bash
32-
$ npm install
33-
```
34-
35-
## Running the app
36-
37-
```bash
38-
# development
39-
$ npm run start
40-
41-
# watch mode
42-
$ npm run start:dev
43-
44-
# production mode
45-
$ npm run start:prod
46-
```
47-
48-
## Test
49-
50-
```bash
51-
# unit tests
52-
$ npm run test
53-
54-
# e2e tests
55-
$ npm run test:e2e
56-
57-
# test coverage
58-
$ npm run test:cov
59-
```
60-
61-
## Support
62-
63-
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
64-
65-
## Stay in touch
66-
67-
- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
68-
- Website - [https://nestjs.com](https://nestjs.com/)
69-
- Twitter - [@nestframework](https://twitter.com/nestframework)
70-
71-
## License
72-
73-
Nest is [MIT licensed](LICENSE).
1+
# Whiteboard collaboration service
2+
A NestJS backend service to enable real-time collaboration on a whiteboards.
3+
4+
## Features
5+
- Authentication & Authorization
6+
- Inactivity tracker
7+
- Contribution tracker
8+
- Auto safe
9+
- Real-time collaboration
10+
11+
## Implementation
12+
<img src="docs/images/diagram.png" alt="Component Diagram" width="600" />
13+
14+
1. A client is requesting to connect to the collaboration service.
15+
The service will consult with the alkemio-server through a message queue who is this client.
16+
If the client is authenticated the client can join.
17+
2. A client is requesting to collaborate on a whiteboard.
18+
The service will consult with the alkemio-server through a message queue if this client is authorized to do so.
19+
3. If the client is authorized, the alkemio-server will return information regarding the whiteboard
20+
- the access levels
21+
- is it multi-user
22+
- how many users can collaborate at a time
23+
- the collaboration level of the client
24+
4. Trackers
25+
- Inactivity - If the user hasn't been activty for a while, based on inactivity interactions with the whiteboard,
26+
the service will reduce the collaboration level to view-only.
27+
The client is also notified of this change, so it can take proper actions, like changing the view, disabling controls, etc.
28+
- Contributions - If the user has made contributions in a certain time window, the service will notify the alkemio-server,
29+
so it can register a contribution.
30+
- Auto save - The service will auto save the whiteboard state to the database on intervals.
31+
The current implementation will ask users of the whiteboard, with the appropriate access in a random fashion to save it.

config.yml

+41-18
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,53 @@
11
rabbitmq:
2-
# Connection in the form of 'amqp://[user]:[password]@[host]:[port]?heartbeat=30'
2+
# Connection in the form of 'amqp://[user]:[password]@[host]:[port]?heartbeat=[heartbeat]'
33
connection:
44
# RabbitMQ host
55
host: ${RABBITMQ_HOST}:localhost
6-
76
# RabbitMQ AMQP port. Used by AMQP 0-9-1 and 1.0 clients without and with TLS
87
port: ${RABBITMQ_PORT}:5672
9-
108
# RabbitMQ user
119
user: ${RABBITMQ_USER}:alkemio-admin
12-
1310
# RabbitMQ password
1411
password: ${RABBITMQ_PASSWORD}:alkemio!
12+
# heartbeat
13+
heartbeat: ${RABBITMQ_HEARTBEAT}:30
14+
15+
monitoring:
16+
logging:
17+
# A flag setting whether Winston Console transport will be enabled.
18+
# If the flag is set to true logs of the appropriate level (see below) will be outputted to the console
19+
# after the application has been bootstrapped.
20+
# The NestJS bootstrap process is handled by the internal NestJS logging.
21+
enabled: ${LOGGING_CONSOLE_ENABLED}:true
22+
# Logging level for outputs to console.
23+
# Valid values are log|error|warn|debug|verbose.
24+
level: ${LOGGING_LEVEL_CONSOLE}:verbose
25+
# The logging format will be in json - useful for parsing
26+
# if disabled - will be in a human-readable form
27+
json: ${LOGGING_FORMAT_JSON}:false
1528

1629
settings:
17-
enabled: ${ENABLED}:true
18-
# the window in which contributions are accepted to be counted towards a single contribution event;
19-
# time is in SECONDS
20-
contribution_window: ${CONTRIBUTION_WINDOW}:600
21-
# SECONDS between saves
22-
save_interval: ${AUTOSAVE_INTERVAL}:15
23-
# SECONDS to wait after the request is saved, before it fails
24-
save_timeout: ${AUTOSAVE_TIMEOUT}:10
25-
# SECONDS of inactivity before a collaborator is made view-only
26-
collaborator_inactivity: ${COLLABORATOR_INACTIVITY}:1800
27-
# how often the inactivity timer is reset;
28-
# you want it to be large enough to not reset too frequently,
29-
# but small enough to be accurate
30-
reset_collaborator_mode_debounce: ${INACTIVITY_DEBOUNCE}:1000
30+
# application level settings
31+
application:
32+
# queue
33+
queue: ${QUEUE}:auth
34+
# MILLISECONDS wait time for a response after a request on the message queue
35+
queue_response_timeout: ${QUEUE_RESPONSE_TIMEOUT}:10000
36+
# the collaboration experience
37+
collaboration:
38+
enabled: ${ENABLED}:true
39+
#
40+
port: ${PORT}:4002
41+
# the window in which contributions are accepted to be counted towards a single contribution event;
42+
# time is in SECONDS
43+
contribution_window: ${CONTRIBUTION_WINDOW}:600
44+
# SECONDS between saves
45+
save_interval: ${AUTOSAVE_INTERVAL}:15
46+
# SECONDS to wait after the request is saved, before it fails
47+
save_timeout: ${AUTOSAVE_TIMEOUT}:10
48+
# SECONDS of inactivity before a collaborator is made view-only
49+
collaborator_inactivity: ${COLLABORATOR_INACTIVITY}:1800
50+
# how often the inactivity timer is reset;
51+
# you want it to be large enough to not reset too frequently,
52+
# but small enough to be accurate
53+
reset_collaborator_mode_debounce: ${INACTIVITY_DEBOUNCE}:1000

docs/images/diagram.png

24.8 KB
Loading

package-lock.json

+13-10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "whiteboard-collaboration-service",
3-
"version": "0.1.0",
3+
"version": "0.2.0",
44
"description": "Alkemio Whiteboard Collaboration Service for Excalidraw backend",
55
"author": "Alkemio Foundation",
66
"private": false,
@@ -32,7 +32,7 @@
3232
"rxjs": "^7.8.1",
3333
"socket.io": "^4.7.5",
3434
"socket.io-adapter": "^2.5.4",
35-
"yaml": "^2.0.0-7"
35+
"yaml": "^2.4.2"
3636
},
3737
"devDependencies": {
3838
"@nestjs/cli": "^10.0.0",

src/config/config.type.ts

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
export interface ConfigType {
2+
rabbitmq: {
3+
connection: {
4+
host: string;
5+
port: number;
6+
user: string;
7+
password: string;
8+
heartbeat: number;
9+
};
10+
};
11+
monitoring: {
12+
logging: {
13+
enabled: boolean;
14+
level: string;
15+
json: boolean;
16+
};
17+
};
18+
settings: {
19+
application: {
20+
queue: string;
21+
queue_response_timeout: number;
22+
};
23+
collaboration: {
24+
enabled: boolean;
25+
port: number;
26+
contribution_window: number;
27+
save_interval: number;
28+
save_timeout: number;
29+
collaborator_inactivity: number;
30+
reset_collaborator_mode_debounce: number;
31+
};
32+
};
33+
}

src/config/configuration.ts

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import { readFileSync } from 'fs';
22
import { join } from 'path';
3-
// import YAML from 'yaml';
4-
// eslint-disable-next-line @typescript-eslint/no-var-requires
5-
const YAML = require('yaml'); // todo: WHY??
3+
import * as YAML from 'yaml';
64

75
const YAML_CONFIG_FILENAME = 'config.yml';
86

@@ -16,7 +14,7 @@ export default () => {
1614
const envConfig = process.env;
1715

1816
YAML.visit(doc, {
19-
Scalar(key: any, node: { type: string; value: any }) {
17+
Scalar(key, node) {
2018
if (node.type === 'PLAIN') {
2119
node.value = buildYamlNodeValue(node.value, envConfig);
2220
}

src/config/index.ts

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export * from './config.type';
2+
export * from './configuration';
3+
export * from './winston.config';

src/config/winston.config.ts

+14-9
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@ import { Injectable } from '@nestjs/common';
22
import * as winston from 'winston';
33
import { utilities as nestWinstonModuleUtilities } from 'nest-winston';
44
import * as logform from 'logform';
5+
import { ConfigService } from '@nestjs/config';
6+
import { ConfigType } from './config.type';
57

6-
const LOG_LABEL = 'alkemio-server';
8+
const LOG_LABEL = 'alkemio-whiteboard-collaboration';
79

810
const consoleLoggingStandardFormat: logform.Format[] = [
911
winston.format.timestamp(),
1012
nestWinstonModuleUtilities.format.nestLike(undefined, {
1113
colors: true,
12-
prettyPrint: false,
14+
prettyPrint: true,
1315
}),
1416
];
1517

@@ -21,19 +23,22 @@ const consoleLoggingProdFormat: logform.Format[] = [
2123

2224
@Injectable()
2325
export class WinstonConfigService {
24-
constructor() {}
25-
26-
async createWinstonModuleOptions() {
27-
const json = false;
28-
const consoleEnabled = true;
26+
constructor(
27+
private readonly configService: ConfigService<ConfigType, true>,
28+
) {}
2929

30+
createWinstonModuleOptions() {
31+
const { enabled, level, json } = this.configService.get(
32+
'monitoring.logging',
33+
{ infer: true },
34+
);
3035
const transports: any[] = [
3136
new winston.transports.Console({
3237
format: winston.format.combine(
3338
...(json ? consoleLoggingProdFormat : consoleLoggingStandardFormat),
3439
),
35-
level: 'verbose',
36-
silent: !consoleEnabled,
40+
level,
41+
silent: !enabled,
3742
}),
3843
];
3944

src/excalidraw-backend/get.excalidraw.base.server.ts

+7-8
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
1-
// import http from 'http';
2-
// eslint-disable-next-line @typescript-eslint/no-var-requires
3-
const http = require('http'); // todo why is that?
1+
import * as http from 'node:http';
2+
import { LoggerService } from '@nestjs/common';
43
import { Namespace, Server as SocketIO } from 'socket.io';
54
import { Adapter } from 'socket.io-adapter';
65
import { SocketIoServer } from './types';
7-
// eslint-disable-next-line @typescript-eslint/no-var-requires
6+
87
export const getExcalidrawBaseServerOrFail = (
8+
port: number,
9+
logger: LoggerService,
910
adapterFactory?: typeof Adapter | ((nsp: Namespace) => Adapter),
10-
): SocketIoServer | never => {
11-
const port = 4002;
12-
11+
): SocketIoServer => {
1312
const httpServer = http.createServer();
1413
httpServer.listen(port, () => {
15-
console.log('Listening on port', port);
14+
logger.verbose?.(`Listening on port ${port}`);
1615
});
1716

1817
return new SocketIO(httpServer, {

0 commit comments

Comments
 (0)