docs: updated documentation (#476)

Co-authored-by: Monta <monta@HP-ProBook-445-14-inch-G9-Notebook-PC-505aadfc.localdomain>
This commit is contained in:
Montassar Ghanmy
2024-05-07 17:21:17 +01:00
committed by GitHub
parent 3b20806622
commit 24da0c8cfb
22 changed files with 102 additions and 858 deletions
+4 -63
View File
@@ -1,86 +1,27 @@
# Table of contents
- [☀️ TDrive](index.md)
- [☀️ Twake Drive](index.md)
## Getting started <a href="#gettingstarted" id="gettingstarted"></a>
- [▶ Use TDrive on tdrive.app](gettingstarted/use-tdrive-on-tdrive.app.md)
- [▶ Use Twake Drive on tdrive.app](gettingstarted/use-tdrive-on-tdrive.app.md)
- [🏗 Install on your server](onprem/installation.md)
- [⚙ Configuration](onprem/configuration/index.md)
- [🔒 Security](onprem/configuration/security.md)
- [🔗 Custom domain + HTTPS](onprem/configuration/custom-domain-and-https/README.md)
- [Apache2 configuration](onprem/configuration/custom-domain-and-https/apache2-configuration.md)
- [💌 Configure mail server](onprem/configuration/configure-mail-server.md)
- [🎨 Customisation](onprem/configuration/customisation.md)
- [🔌 Connectors and plugins](onprem/configuration/connectors-and-plugins.md)
- [👨‍💻 Authentication modes](onprem/configuration/authentication-modes/README.md)
- [Using Keycloak (LDAP, OpenID and more)](onprem/configuration/authentication-modes/using-keycloak-ldap-openid-and-more.md)
- [Installing TDrive with LemonLDAP (LDAP, OpenID and more)](onprem/configuration/authentication-modes/installing-tdrive-with-lemonldap-ldap-openid-and-more.md)
## How to use it
- [👋 Welcome to TDrive !](how-to-use-it/welcome.md)
- [🧰 Console](how-to-use-it/console/README.md)
- [Users](how-to-use-it/console/users.md)
- [🏢 Company & workspace](how-to-use-it/company-and-workspace/README.md)
- [Invite user from Chat](how-to-use-it/company-and-workspace/invite-user-from-chat.md)
- [Rights](how-to-use-it/company-and-workspace/rights.md)
- [💠 Applications](how-to-use-it/applications/README.md)
- [💬 Chat](how-to-use-it/applications/how-to-use-chat/README.md)
- [Channels](how-to-use-it/applications/how-to-use-chat/channels.md)
- [Message](how-to-use-it/applications/how-to-use-chat/message.md)
- [📂 Drive](how-to-use-it/applications/drive/README.md)
- [File and folder](how-to-use-it/applications/drive/file-and-folder.md)
- [Share file with public link](how-to-use-it/applications/drive/share-file-with-public-link.md)
- [📆 Calendar](how-to-use-it/applications/calendar.md)
- [✅ Tasks](how-to-use-it/applications/tasks.md)
- [🔃 Connectors](how-to-use-it/applications/connectors/README.md)
- [n8n](how-to-use-it/applications/connectors/n8n.md)
- [🖥 Desktop and mobile app](how-to-use-it/desktop-and-mobile-app.md)
- [🔒 Privacy and security](how-to-use-it/privacy-security.md)
## Developers API
- [🏠 Home](developers-api/home.md)
- [🥇 Getting started](developers-api/get-started/README.md)
- [Create your first application](developers-api/get-started/create-your-first-application.md)
- [Authenticate with Postman](developers-api/get-started/authenticate-postman.md)
- [Send a message with your application](developers-api/get-started/send-a-message-for-tdrive.md)
- [Trigger action from command](developers-api/get-started/trigger-action-from-command.md)
- [⚙ Application settings](developers-api/application-settings/README.md)
- [Api](developers-api/application-settings/api.md)
- [Display](developers-api/application-settings/application-visibility-example.md)
- [Privileges](developers-api/application-settings/privileges.md)
- [Identity](developers-api/application-settings/identity.md)
- [📖 API Reference](developers-api/api-reference/README.md)
- [Webhook](developers-api/api-reference/wehbook.md)
- [Drive](developers-api/api-reference/drive.md)
- [Message](developers-api/api-reference/message/README.md)
- [Authentication](developers-api/api-reference/auth.md)
- [🧱 Blocks](developers-api/blocks.md)
## Internal Documentation
- [🥇 Get started](developers-api/run-tdrive-as-developer.md)
- [🎨 TDrive Ecosystem Guidelines](internal-documentation/tdrive-ecosystem-guidelines.md)
- [📚 Our stack](internal-documentation/our-stack.md)
- [🧱 Backend and APIs](internal-documentation/backend-services/README.md)
- [👥 Users, workspaces and companies](internal-documentation/backend-services/users/README.md)
- [🍎 Applications](internal-documentation/backend-services/applications/README.md)
- [Database models](internal-documentation/backend-services/applications/database-models.md)
- [REST APIs](internal-documentation/backend-services/applications/rest-apis.md)
- [🎩 Channels and tabs](internal-documentation/backend-services/channels/README.md)
- [Database models](internal-documentation/backend-services/channels/database-models.md)
- [💬 Messages](internal-documentation/backend-services/messages/README.md)
- [Database models](internal-documentation/backend-services/messages/database-model.md)
- [📄 Files](internal-documentation/backend-services/files/README.md)
- [Database models](internal-documentation/backend-services/files/database-models.md)
- [Resumable.js](internal-documentation/backend-services/files/resumablejs.md)
- [📲 Notifications](internal-documentation/backend-services/notifications/README.md)
- [Database models](internal-documentation/backend-services/notifications/database-models.md)
- [🛠 TDrive service development](internal-documentation/backend-services/intro/README.md)
- [What is a service in TDrive ?](internal-documentation/backend-services/intro/start-working-into-a-service.md)
- [Create a new service](internal-documentation/backend-services/intro/create-a-new-tdrive-service.md)
- [🛠 Twake Drive service development](internal-documentation/backend-services/intro/README.md)
- [What is a service in Twake Drive ?](internal-documentation/backend-services/intro/start-working-into-a-service.md)
- [Platform/Technical services](internal-documentation/backend-services/intro/platform/README.md)
- [Database ORM platform service](internal-documentation/backend-services/intro/platform/database-orm-platform-service.md)
- [🎭 Translation](internal-documentation/translation.md)
+4 -4
View File
@@ -1,12 +1,12 @@
# ☀️ Welcome to TDrive
# ☀️ Welcome to Twake Drive
TDrive is an open source collaborative drive. It can be self-hosted, used is saas and easily integrated into your teams. TDrive offers all the features for collaboration : team chat, file storage, team calendar, tasks manager.
Twake Drive is an open source collaborative drive. It can be self-hosted, used is saas and easily integrated into your teams. Twake Drive offers all the features for collaboration : team chat, file storage, team calendar, tasks manager.
This **documentation** assists developers and users in having the best TDrive experience as possible. Weve developed TDrive to have a better teamwork.
This **documentation** assists developers and users in having the best Twake Drive experience as possible. Weve developed Twake Drive to have a better teamwork.
---
#### Start using TDrive now
#### Start using Twake Drive now
Install it on your own server: [click here](onprem/installation.md)).
@@ -7,9 +7,9 @@ description: >-
# 🧱 Backend and APIs
## TDrive Services
## Twake Drive Services
As a frontend developer / connector developer to read our APIs, or to understand our data models or just for fun, here is all the details over TDrive backend services.
As a frontend developer / connector developer to read our APIs, or to understand our data models or just for fun, here is all the details over Twake Drive backend services.
If document are empty, check out our Notion documentation: [https://www.notion.so/tdrive/Backend-documentation-e219323593d2401c9887d0e11b2a597b](https://www.notion.so/tdrive/Backend-documentation-e219323593d2401c9887d0e11b2a597b)
@@ -25,9 +25,9 @@ If document are empty, check out our Notion documentation: [https://www.notion.s
[notifications/](notifications/README.md))
## Get started to code in TDrive
## Get started to code in Twake Drive
Want to edit TDrive code ? Congratulation ! You participate in the development of a great product 😃
Want to edit Twake Drive code ? Congratulation ! You participate in the development of a great product 😃
[start-working-into-a-service](intro/start-working-into-a-service.md)
@@ -1,12 +1,12 @@
---
description: How applications are managed in TDrive backend
description: How applications are managed in Twake Drive backend
---
# 🍎 Applications
#### Description
**Applications** is everything related to connectors in TDrive.
**Applications** is everything related to connectors in Twake Drive.
An application is described by an identity sheet containing this information:
@@ -3,7 +3,7 @@ description: Documents API
---
# Authentication
<!-- TODO[NOT UP TO DATE] -->
All the following routes require the usual authentication header. But you can also use other ways of authentication:
- For the download routes, you can use a token generated by the `/internal/services/documents/v1/companies/:company_id/download/token` route (see bellow).
@@ -299,12 +299,12 @@ Used to create a zip archive containing the requested drive items ( files and fo
**Auth required** : Yes
# Tabs (for TDrive)
# Tabs (for Twake Drive)
<!-- TODO[REMOVE SECTION] -->
If you want to use the TDrive tabs, you must store the configuration of the tabs in the database.
<!-- TODO[REMOVE SECTION] -->
## Get tab configuration
<!-- TODO[REMOVE SECTION] -->
Get a tab configuration to get the attached folder/document id.
**URL** : `/internal/services/documents/v1/companies/:company_id/tabs/:id`
@@ -326,7 +326,7 @@ Get a tab configuration to get the attached folder/document id.
```
## Set tab configuration
<!-- TODO[REMOVE SECTION] -->
Get a tab configuration to get the attached folder/document id.
**URL** : `/internal/services/documents/v1/companies/:company_id/tabs/:id`
@@ -1,14 +1,14 @@
---
description: File on TDrive
description: File on Twake Drive
---
# 📄 Files
<!-- TODO[NOT UP TO DATE] -->
## description
**Files** is everything related to file upload in TDrive after the migration to Node.js. Note that the TDrive Drive isn't part of this migration because it will be replaced by Linshare.
**Files** is everything related to file upload in Twake Drive after the migration to Node.js. Note that the Twake Drive isn't part of this migration because it will be replaced by Linshare.
TDrive Files upload support chunk upload and file encryption.
Twake Drive Files upload support chunk upload and file encryption.
## Wording
@@ -18,7 +18,7 @@ TDrive Files upload support chunk upload and file encryption.
## Encryption
Files and Storage services in TDrive feature encryption at rest in **aes-256-cbc**.
Files and Storage services in Twake Drive feature encryption at rest in **aes-256-cbc**.
Each file is encrypted with two layers:
@@ -1,7 +1,7 @@
---
description: File database models
---
<!-- TODO[NOT UP TO DATE] -->
# Database models
* **files** The main file object in database
@@ -1,5 +1,5 @@
---
description: How we use resumable on TDrive
description: How we use resumable on Twake Drive
---
# Resumable.js
@@ -9,7 +9,7 @@ description: How we use resumable on TDrive
## What is Resumable.js
::: info
Using Resumable is not mandatory to upload a file to TDrive. You can do it manually, see REST APIs page.
Using Resumable is not mandatory to upload a file to Twake Drive. You can do it manually, see REST APIs page.
:::
Its a JavaScript library providing multiple simultaneous, stable and resumable uploads via the HTML5 File API.
@@ -1,11 +1,11 @@
---
description: Get started with TDrive service development
description: Get started with Twake Drive service development
---
# 🛠 TDrive service development
# 🛠 Twake Drive service development
[start-working-into-a-service](start-working-into-a-service.md)
[create-a-new-tdrive-service](create-a-new-tdrive-service.md)
[platform/](platform/README.md))
[platform/](platform/README.md)
@@ -1,7 +1,7 @@
---
description: >-
If you are here, you probably have a very great idea for TDrive, like adding a
brand new feature into TDrive, maybe a coffee maker service ? ☕️
If you are here, you probably have a very great idea for Twake Drive, like adding a
brand new feature into Twake Drive, maybe a coffee maker service ? ☕️
---
# Create a new service
@@ -19,10 +19,10 @@ In order to illustrate how to create a component, let's create a fake Notificati
```javascript
// File src/services/notification/index.ts
import { TDriveService } from "../../core/platform/framework";
import { TdriveService } from "../../core/platform/framework";
import NotificationServiceAPI from "./api.ts";
export default class NotificationService extends TDriveService<NotificationServiceAPI> {
export default class NotificationService extends TdriveService<NotificationServiceAPI> {
version = "1";
name = "notification";
service: NotificationServiceAPI;
@@ -33,15 +33,15 @@ export default class NotificationService extends TDriveService<NotificationServi
}
```
1. Our `NotificationService` class extends the generic `TDriveService` class and we defined the `NotificationServiceAPI` as its generic type parameter. It means that in the platform, the other components will be able to retrieve the component from its name and then consume the API defined in the `NotificationServiceAPI` interface and exposed by the `api` method.
1. Our `NotificationService` class extends the generic `TdriveService` class and we defined the `NotificationServiceAPI` as its generic type parameter. It means that in the platform, the other components will be able to retrieve the component from its name and then consume the API defined in the `NotificationServiceAPI` interface and exposed by the `api` method.
We need to create this `NotificationServiceAPI` interface which must extend the `TDriveServiceProvider` from the platform like:
We need to create this `NotificationServiceAPI` interface which must extend the `TdriveServiceProvider` from the platform like:
```javascript
// File src/services/notification/api.ts
import { TDriveServiceProvider } from "../../core/platform/framework/api";
import { TdriveServiceProvider } from "../../core/platform/framework/api";
export default interface NotificationServiceAPI extends TDriveServiceProvider {
export default interface NotificationServiceAPI extends TdriveServiceProvider {
/**
* Send a message to a list of recipients
@@ -65,17 +65,17 @@ export class NotificationServiceImpl implements NotificationServiceAPI {
}
```
1. `NotificationServiceImpl` now needs to be instanciated from the `NotificationService` class since this is where we choose to keep its reference and expose it. There are several places which can be used to instanciate it, in the constructor itself, or in one of the `TDriveService` lifecycle hooks. The `TDriveService` abstract class has several lifecycle hooks which can be extended by the service implementation for customization pusposes:
1. `NotificationServiceImpl` now needs to be instanciated from the `NotificationService` class since this is where we choose to keep its reference and expose it. There are several places which can be used to instanciate it, in the constructor itself, or in one of the `TdriveService` lifecycle hooks. The `TdriveService` abstract class has several lifecycle hooks which can be extended by the service implementation for customization pusposes:
2. `public async doInit(): Promise<this>;` Customize the `init` step of the component. This is generally the place where services are instanciated. From this step, you can retrieve services consumed by the current component which have been already initialized by the platform.
3. `public async doStart(): Promise<this>;` Customize the `start` step of the component. You have access to all other services which are already started.
```javascript
// File src/services/notification/index.ts
import { TDriveService } from "../../core/platform/framework";
import { TdriveService } from "../../core/platform/framework";
import NotificationServiceAPI from "./api.ts";
import NotificationServiceImpl from "./services/api.ts";
export default class NotificationService extends TDriveService<NotificationServiceAPI> {
export default class NotificationService extends TdriveService<NotificationServiceAPI> {
version = "1";
name = "notification";
service: NotificationServiceAPI;
@@ -95,12 +95,12 @@ export default class NotificationService extends TDriveService<NotificationServi
1. Now that the service is fully created, we can consume it from any other service in the platform. To do this, we rely on Typescript decorators to define the links between components. For example, let's say that the a `MessageService` needs to call the `NotificationServiceAPI`, we can create the link with the help of the `@Consumes` decorator and get a reference to the `NotificationServiceAPI` by calling the `getProvider` on the component context like:
```javascript
import { TDriveService, Consumes } from "../../core/platform/framework";
import { TdriveService, Consumes } from "../../core/platform/framework";
import MessageServiceAPI from "./providapier";
import NotificationServiceAPI from "../notification/api";
@Consumes(["notification"])
export default class MessageService extends TDriveService<MessageServiceAPI> {
export default class MessageService extends TdriveService<MessageServiceAPI> {
public async doInit(): Promise<this> {
const notificationService = this.context.getProvider<NotificationServiceAPI>("notification");
@@ -115,12 +115,12 @@ export default class MessageService extends TDriveService<MessageServiceAPI> {
The platform and services configuration is defined in the `config/default.json` file. It uses [node-config](https://github.com/lorenwest/node-config) under the hood and to configuration file inheritence is supported in the platform.
The list of services to start is defined in the `services` array like:
<!-- TODO[NOT UP TO DATE] -->
```javascript
{
"services": ["auth", "user", "channels", "webserver", "websocket", "database", "realtime"]
}
```
```<!-- TODO[NOT UP TO DATE] -->
Then each service can have its own configuration block which is accessible from its service name i.e. `websocket` service configuration is defined in the `websocket` element like:
@@ -143,7 +143,7 @@ Then each service can have its own configuration block which is accessible from
On the component class side, the configuration object is directly accessible from the `configuration` property like:
```javascript
export default class WebSocket extends TDriveService<WebSocketAPI> {
export default class WebSocket extends TdriveService<WebSocketAPI> {
async doInit(): Promise<this> {
// get the "path" value, defaults to "/socket" if not defined
const path = this.configuration.get < string > ("path", "/socket");
@@ -166,7 +166,7 @@ After creating a new service, you can add controllers, business services and ent
## Create a new technical service
Now you are bringing things a step further, you are going to add new core services in TDrive, like for instance a new database connector or encryption system.
Now you are bringing things a step further, you are going to add new core services in Twake Drive, like for instance a new database connector or encryption system.
Creating a new core service is as easy as creating a functional service. But it must be in `src/core/platform/services` .
@@ -1,247 +1,15 @@
---
description: >-
List of core shared components in TDrive backend, available in
List of core shared components in Twake Drive backend, available in
src/core/platform/services
---
# Platform/Technical services
## **Database Technical Service**
TDrive uses a custom ORM to work with both MongoDB and CassandraDB/ScyllaDB.
<!-- TODO[NOT UP TO DATE] -->
Twake Drive uses a custom ORM to work with both MongoDB and CassandraDB/ScyllaDB.
::: info
This paragraph is not ready yet, you can contribute to this documentation on our Github!
:::
## **Realtime Technical Service**
The framework provides simple way to create CRUD Services which will notify clients using Websockets by following some conventions:
1. The service must implement the `CRUDService` generic interface
2. In order to push notification to clients, Typescript decorators must be added on methods \(Create, Update, Delete methods\)
For example, let's say that we want to implement a CRUD Service for `messages`:
```javascript
import {
CRUDService,
CreateResult,
DeleteResult,
UpdateResult,
EntityId,
} from "@/core/platform/framework/api/crud-service";
class Message {
text: string;
createdAt: Date;
author: string;
}
class MessageService implements CRUDService<Message> {
async create(item: Message): Promise<CreateResult<Message>> {
// save the message then return a CreateResult instance
}
async get(id: EntityId): Promise<Message> {
// get the message from its ID and return it
}
async update(id: EntityId, item: Message): Promise<UpdateResult<Message>> {
// update the message with the given id, patch it with `item` values
// then return an instance of UpdateResult
}
async delete(id: EntityId): Promise<DeleteResult<Message>> {
// delete the message from its id then return a DeleteResult instance
}
async list(): Promise<Message[]> {
// get a list of messages
}
}
```
By implementing the CRUD service following the API, we can now add realtime message to clients connected to Websocket on a selected collection of CRUD operations. For example, if we want to just notify when a `message` is created, we just have to add the right decorator on the `create` method like:
```javascript
import { RealtimeCreated } from "@/core/platform/framework/decorators";
// top and bottom code removed for clarity
class MessageService implements CRUDService<Message> {
@RealtimeCreated<Message>({
room: "/messages",
path: message => `/messages/${message.id}`
})
async create(item: Message): Promise<CreateResult<Message>> {
// save the message then return a CreateResult instance
const created: Message = new Message(/* */);
return new CreateResult<Message>("message", created)
}
// ...
}
```
The `RealtimeCreated` decorator will intercept the `create` call and will publish an event in an internal event bus with the creation result, the input data and the `"/messages"` path. On the other side of the event bus, an event listener will be in charge of delivering the event to the right Websocket clients as described next.
The Realtime\* decorators to add on methods to intercept calls and publish data are all following the same API. A decorator takes two parameters as input:
- First one is the room name to publish the notification to \(`/messages` in the example above\)
- Second one is the full path of the resource linked to the action \(`message => /messages/${message.id}` in the example above\)
Both parameters can take a string or an arrow function as parameter. If arrow function is used, the input parameter will be the result element. By doing this, the paths can be generated dynamically at runtime.
## **Websocket Technical Service**
Services annotated as described above automatically publish events to WebSockets. Under the hood, it uses Socket.IO rooms to send events to the right clients.
**Authentication**
- Client have to provide a valid JWT token to be able to connect and be authenticated by providing it as string in the `authenticate` event like `{ token: "the jwt token value" }`
- If the JWT token is valid, the client will receive a `authenticated` event
- If the JWT token is not valid or empty, the client will receive a `unauthorized` event
**Example**
```javascript
const io = require("socket.io-client");
// Get a JWT token from the TDrive API first
const token =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOjEsImlhdCI6MTYwMzE5ODkzMn0.NvQoV9KeWuTNzRvzqbJ5uZCQ8Nmi2rCYQzcKk-WsJJ8";
const socket = io.connect("http://localhost:3000", { path: "/socket" });
socket.on("connect", () => {
socket
.emit("authenticate", { token })
.on("authenticated", () => {
console.log("User Authenticated");
})
.on("unauthorized", (err) => {
console.log("User is not authorized", err);
});
});
socket.on("disconnected", () => console.log("Disconnected"));
```
**Joining rooms**
CRUD operations on resources are pushing events in Socket.io rooms. In order to receive events, clients must subscribe to rooms by sending an `realtime:join` on an authenticated socket with the name of the room to join and with a valid JWT token like `{ name: "room name", token: "the jwt token for this room" }`: Users can not subscribe to arbitratry rooms, they have to be authorized to.
As a result, the client will receive events:
- `realtime:join:success` when join is succesful with data containing the name of the linked room like `{ name: "room" }`.
- `realtime:join:error` when join failed with data containing the name of the linked room and the error details like `{ name: "room", error: "some error message" }`.
**Example**
```javascript
const io = require("socket.io-client");
const token =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOjEsImlhdCI6MTYwMzE5ODkzMn0.NvQoV9KeWuTNzRvzqbJ5uZCQ8Nmi2rCYQzcKk-WsJJ8";
const socket = io.connect("http://localhost:3000", { path: "/socket" });
socket.on("connect", () => {
socket
.emit("authenticate", { token })
.on("authenticated", () => {
// join the /channels room
socket.emit("realtime:join", { name: "/channels", token: "tdrive" });
socket.on("realtime:join:error", (message) => {
// will fire when join does not provide a valid token
console.log("Error on join", message);
});
// will be fired on each successful join.
// As event based, this event is not linked only to the join above
// but to all joins. So you have to dig into the message to know which one
// is successful.
socket.on("realtime:join:success", (message) => {
console.log("Successfully joined room", message.name);
});
})
.on("unauthorized", (err) => {
console.log("Unauthorized", err);
});
});
socket.on("disconnected", () => console.log("Disconnected"));
```
**Leaving rooms**
The client can leave the room by emitting a `realtime:leave` event with the name of the room given as `{ name: "the room to leave" }`.
As a result, the client will receive events:
- `realtime:leave:success` when leave is succesful with data containing the name of the linked room like `{ name: "room" }`.
- `realtime:leave:error` when leave failed with data containing the name of the linked room and the error details like `{ name: "room", error: "some error message" }`.
Note: Asking to leave a room which has not been joined will not fire any error.
**Example**
```javascript
socket.on("connect", () => {
socket.emit("authenticate", { token }).on("authenticated", () => {
// leave the "/channels" room
socket.emit("realtime:leave", { name: "/channels" });
socket.on("realtime:leave:error", (message) => {
// will fire when join does not provide a valid token
console.log("Error on leave", message);
});
// will be fired on each successful leave.
// As event based, this event is not linked only to the leave above
// but to all leaves. So you have to dig into the message to know which one
// is successful.
socket.on("realtime:leave:success", (message) => {
console.log("Successfully left room", message.name);
});
});
});
```
**Subscribe to resource events**
Once the given room has been joined, the client will receive `realtime:resource` events with the resource linked to the event as data:
```javascript
{
"action": "created",
"room": "/channels",
"type": "channel",
"path": "/channels/5f905327e3e1626399aaad79",
"resource": {
"name": "My channel",
"id": "5f905327e3e1626399aaad79"
}
```
**Example:**
```javascript
socket.on("connect", () => {
socket
.emit("authenticate", { token })
.on("authenticated", () => {
// join the "/channels" room
socket.emit("realtime:join", { name: "/channels", token: "tdrive" });
// will only occur when an action occured on a resource
// and if and only if the client joined the room
// in which the resource is linked
socket.on("realtime:resource", (event) => {
console.log("Resource has been ${event.action}", event.resource);
});
})
.on("unauthorized", (err) => {
console.log("Unauthorized", err);
});
});
```
@@ -1,245 +1,13 @@
---
description: >-
List of core shared components in TDrive backend, available in
List of core shared components in Twake Drive backend, available in
src/core/platform/services
---
# Platform/Technical services
## **Database Technical Service**
TDrive uses a custom ORM to work with both MongoDB and CassandraDB/ScyllaDB.
<!-- TODO[NOT UP TO DATE] -->
Twake Drive uses a custom ORM to work with both MongoDB and CassandraDB/ScyllaDB.
[database-orm-platform-service](database-orm-platform-service.md)
## **Realtime Technical Service**
The framework provides simple way to create CRUD Services which will notify clients using Websockets by following some conventions:
1. The service must implement the `CRUDService` generic interface
2. In order to push notification to clients, Typescript decorators must be added on methods \(Create, Update, Delete methods\)
For example, let's say that we want to implement a CRUD Service for `messages`:
```javascript
import {
CRUDService,
CreateResult,
DeleteResult,
UpdateResult,
EntityId,
} from "@/core/platform/framework/api/crud-service";
class Message {
text: string;
createdAt: Date;
author: string;
}
class MessageService implements CRUDService<Message> {
async create(item: Message): Promise<CreateResult<Message>> {
// save the message then return a CreateResult instance
}
async get(id: EntityId): Promise<Message> {
// get the message from its ID and return it
}
async update(id: EntityId, item: Message): Promise<UpdateResult<Message>> {
// update the message with the given id, patch it with `item` values
// then return an instance of UpdateResult
}
async delete(id: EntityId): Promise<DeleteResult<Message>> {
// delete the message from its id then return a DeleteResult instance
}
async list(): Promise<Message[]> {
// get a list of messages
}
}
```
By implementing the CRUD service following the API, we can now add realtime message to clients connected to Websocket on a selected collection of CRUD operations. For example, if we want to just notify when a `message` is created, we just have to add the right decorator on the `create` method like:
```javascript
import { RealtimeCreated } from "@/core/platform/framework/decorators";
// top and bottom code removed for clarity
class MessageService implements CRUDService<Message> {
@RealtimeCreated<Message>({
room: "/messages",
path: message => `/messages/${message.id}`
})
async create(item: Message): Promise<CreateResult<Message>> {
// save the message then return a CreateResult instance
const created: Message = new Message(/* */);
return new CreateResult<Message>("message", created)
}
// ...
}
```
The `RealtimeCreated` decorator will intercept the `create` call and will publish an event in an internal event bus with the creation result, the input data and the `"/messages"` path. On the other side of the event bus, an event listener will be in charge of delivering the event to the right Websocket clients as described next.
The Realtime\* decorators to add on methods to intercept calls and publish data are all following the same API. A decorator takes two parameters as input:
- First one is the room name to publish the notification to \(`/messages` in the example above\)
- Second one is the full path of the resource linked to the action \(`message => /messages/${message.id}` in the example above\)
Both parameters can take a string or an arrow function as parameter. If arrow function is used, the input parameter will be the result element. By doing this, the paths can be generated dynamically at runtime.
## **Websocket Technical Service**
Services annotated as described above automatically publish events to WebSockets. Under the hood, it uses Socket.IO rooms to send events to the right clients.
**Authentication**
- Client have to provide a valid JWT token to be able to connect and be authenticated by providing it as string in the `authenticate` event like `{ token: "the jwt token value" }`
- If the JWT token is valid, the client will receive a `authenticated` event
- If the JWT token is not valid or empty, the client will receive a `unauthorized` event
**Example**
```javascript
const io = require("socket.io-client");
// Get a JWT token from the TDrive API first
const token =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOjEsImlhdCI6MTYwMzE5ODkzMn0.NvQoV9KeWuTNzRvzqbJ5uZCQ8Nmi2rCYQzcKk-WsJJ8";
const socket = io.connect("http://localhost:3000", { path: "/socket" });
socket.on("connect", () => {
socket
.emit("authenticate", { token })
.on("authenticated", () => {
console.log("User Authenticated");
})
.on("unauthorized", (err) => {
console.log("User is not authorized", err);
});
});
socket.on("disconnected", () => console.log("Disconnected"));
```
**Joining rooms**
CRUD operations on resources are pushing events in Socket.io rooms. In order to receive events, clients must subscribe to rooms by sending an `realtime:join` on an authenticated socket with the name of the room to join and with a valid JWT token like `{ name: "room name", token: "the jwt token for this room" }`: Users can not subscribe to arbitratry rooms, they have to be authorized to.
As a result, the client will receive events:
- `realtime:join:success` when join is succesful with data containing the name of the linked room like `{ name: "room" }`.
- `realtime:join:error` when join failed with data containing the name of the linked room and the error details like `{ name: "room", error: "some error message" }`.
**Example**
```javascript
const io = require("socket.io-client");
const token =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOjEsImlhdCI6MTYwMzE5ODkzMn0.NvQoV9KeWuTNzRvzqbJ5uZCQ8Nmi2rCYQzcKk-WsJJ8";
const socket = io.connect("http://localhost:3000", { path: "/socket" });
socket.on("connect", () => {
socket
.emit("authenticate", { token })
.on("authenticated", () => {
// join the /channels room
socket.emit("realtime:join", { name: "/channels", token: "tdrive" });
socket.on("realtime:join:error", (message) => {
// will fire when join does not provide a valid token
console.log("Error on join", message);
});
// will be fired on each successful join.
// As event based, this event is not linked only to the join above
// but to all joins. So you have to dig into the message to know which one
// is successful.
socket.on("realtime:join:success", (message) => {
console.log("Successfully joined room", message.name);
});
})
.on("unauthorized", (err) => {
console.log("Unauthorized", err);
});
});
socket.on("disconnected", () => console.log("Disconnected"));
```
**Leaving rooms**
The client can leave the room by emitting a `realtime:leave` event with the name of the room given as `{ name: "the room to leave" }`.
As a result, the client will receive events:
- `realtime:leave:success` when leave is succesful with data containing the name of the linked room like `{ name: "room" }`.
- `realtime:leave:error` when leave failed with data containing the name of the linked room and the error details like `{ name: "room", error: "some error message" }`.
Note: Asking to leave a room which has not been joined will not fire any error.
**Example**
```javascript
socket.on("connect", () => {
socket.emit("authenticate", { token }).on("authenticated", () => {
// leave the "/channels" room
socket.emit("realtime:leave", { name: "/channels" });
socket.on("realtime:leave:error", (message) => {
// will fire when join does not provide a valid token
console.log("Error on leave", message);
});
// will be fired on each successful leave.
// As event based, this event is not linked only to the leave above
// but to all leaves. So you have to dig into the message to know which one
// is successful.
socket.on("realtime:leave:success", (message) => {
console.log("Successfully left room", message.name);
});
});
});
```
**Subscribe to resource events**
Once the given room has been joined, the client will receive `realtime:resource` events with the resource linked to the event as data:
```javascript
{
"action": "created",
"room": "/channels",
"type": "channel",
"path": "/channels/5f905327e3e1626399aaad79",
"resource": {
"name": "My channel",
"id": "5f905327e3e1626399aaad79"
}
```
**Example:**
```javascript
socket.on("connect", () => {
socket
.emit("authenticate", { token })
.on("authenticated", () => {
// join the "/channels" room
socket.emit("realtime:join", { name: "/channels", token: "tdrive" });
// will only occur when an action occured on a resource
// and if and only if the client joined the room
// in which the resource is linked
socket.on("realtime:resource", (event) => {
console.log("Resource has been ${event.action}", event.resource);
});
})
.on("unauthorized", (err) => {
console.log("Unauthorized", err);
});
});
```
@@ -4,7 +4,7 @@ description: >-
to our channel service ? You are in the right place !
---
# What is a service in TDrive ?
# What is a service in Twake Drive ?
The backend is developed using a **software component approach** in order to compose and adapt the platform based on needs and constraints. The current section describes this approach, and how to extend it by creating new components.
@@ -25,9 +25,9 @@ The platform currently have some limitations:
## Discover what is in a service
To unfold the internal ways of services in TDrive, we will follow a simple request journey into our framework.
To unfold the internal ways of services in Twake Drive, we will follow a simple request journey into our framework.
1. The requests starts from TDrive Frontend or Postman for instance,
1. The requests starts from Twake Drive Frontend or Postman for instance,
2. it then goes to a controller which validate the request parameters and extract them for the services,
3. the services uses the given parameters to get/set entities in database and returns a proper reply.
@@ -3,5 +3,5 @@ description: How users and workspaces are managed in backend
---
# 👥 Users and workspaces
<!-- TODO[NOT UP TO DATE] -->
Document still on notion: [https://www.notion.so/tdrive/Backend-documentation-e219323593d2401c9887d0e11b2a597b](https://www.notion.so/tdrive/Backend-documentation-e219323593d2401c9887d0e11b2a597b)
@@ -1,9 +1,9 @@
---
description: Get started with TDrive frontend.
description: Get started with Twake Drive frontend.
---
# Storybook
TDrive components are available through our Storybook. You can find it here:
<!-- TODO[NOT UP TO DATE] -->
Twake Drive components are available through our Storybook. You can find it here:
[https://web.tdrive.app/storybook/index.html](https://web.tdrive.app/storybook/index.html)
@@ -4,14 +4,14 @@ description: Here is the list of our middlewares and their usages.
# 📚 Our stack
Write an article describing our stack at TDrive composed of:
Write an article describing our stack at Twake Drive composed of:
- In full mode a docker containing: node, react behind nginx, elasticsearch, scylladb, redis, rabbitmq
- In simple mode a docker containing: node, react and mongodb for db and search
### Simple mode
The simple mode of TDrive messaging app is designed for quick setup and development purposes. It is a more straightforward mode that uses a single container to deploy the components. The technical stack of this mode includes the following components:
The simple mode of Twake Drive messaging app is designed for quick setup and development purposes. It is a more straightforward mode that uses a single container to deploy the components. The technical stack of this mode includes the following components:
- Node: A JavaScript runtime environment used to develop server-side applications.
- React: A JavaScript library used to build user interfaces.
@@ -21,7 +21,7 @@ The simple mode is great for companies that want a quick and easy setup for thei
### Full mode
The full mode of TDrive messaging app is designed to handle large-scale production loads with more than 1000 active users. It uses a container-based approach to deploy the components and ensure scalability. The technical stack of this mode includes the following components:
The full mode of Twake Drive messaging app is designed to handle large-scale production loads with more than 1000 active users. It uses a container-based approach to deploy the components and ensure scalability. The technical stack of this mode includes the following components:
- Node: A JavaScript runtime environment used to develop server-side applications.
- React: A JavaScript library used to build user interfaces.
@@ -1,18 +1,18 @@
---
description: Want to translate TDrive ?
description: Want to translate Twake Drive ?
---
# 🎭 Translation
## Translation process
TDrive is built for everyone. That means we support languages that user want. If we do not already support your language, or you find a mistake in the translation, you have a simply way to do that.
Twake Drive is built for everyone. That means we support languages that user want. If we do not already support your language, or you find a mistake in the translation, you have a simply way to do that.
Send us a mail to this address saying that you want to help us on the translation : [sales@tdrive.app](mailto:sales@tdrive.app). After, you'll have to follow the bellow steps.
## Join weblate server
At TDrive, we use [weblate](https://hosted.weblate.org/), a tool that allow you to translate TDrive without working on the code. After you sent an email, we'll invite you to the project. Inside the TDrive project you can find [tdrive-web-frontend](https://hosted.weblate.org/projects/tdrive/tdrive-web-frontend/) that contains all translation for the web version.
At Twake Drive, we use [weblate](https://hosted.weblate.org/), a tool that allow you to translate Twake Drive without working on the code. After you sent an email, we'll invite you to the project. Inside the Twake Drive project you can find [tdrive-web-frontend](https://hosted.weblate.org/projects/tdrive/tdrive-web-frontend/) that contains all translation for the web version.
## Start to translate
@@ -22,4 +22,4 @@ After choosing the language you want to contribute, you can click on categories
## Thank you!
TDrive is build by and for the community. Your commitment is highly appreciate! In reward, we can offer you a year on our SaaS version. You just have to contact us through this email : [sales@tdrive.app](mailto:sales@tdrive.app)
Twake Drive is build by and for the community. Your commitment is highly appreciate! In reward, we can offer you a year on our SaaS version. You just have to contact us through this email : [sales@tdrive.app](mailto:sales@tdrive.app)
@@ -1,16 +1,16 @@
---
description: More details about TDrive configuration.
description: More details about Twake Drive configuration.
---
# ⚙️ Server configuration
### Detach the configuration and start using your own
Each configuration file is optional, if not given, TDrive will fallback to default configuration.
Each configuration file is optional, if not given, Twake Drive will fallback to default configuration.
#### Backend configuration
You can find an example of TDrive configuration (default configuration) here: [https://github.com/TDriveApp/TDrive/blob/main/tdrive/backend/node/config/default.json](https://github.com/TDriveApp/TDrive/blob/main/tdrive/backend/node/config/default.json)
You can find an example of Twake Drive configuration (default configuration) here: [https://github.com/linagora/twake-drive/blob/main/tdrive/backend/node/config/default.json](https://github.com/linagora/twake-drive/blob/main/tdrive/backend/node/config/default.json)
Copy the content of this file and put it in `[docker-compose.yml location]/configuration/backend-node/production.json`
@@ -18,7 +18,7 @@ Copy the content of this file and put it in `[docker-compose.yml location]/confi
#### Frontend configuration (only for scalable mode)
You can find an example of TDrive configuration (default configuration) here: [https://github.com/TDriveApp/TDrive/blob/main/tdrive/frontend/src/app/environment/environment.ts.dist](https://github.com/TDriveApp/TDrive/blob/main/tdrive/frontend/src/app/environment/environment.ts.dist)
You can find an example of Twake Drive configuration (default configuration) here: [https://github.com/linagora/twake-drive/blob/main/tdrive/frontend/src/app/environment/environment.ts.dist](https://github.com/linagora/twake-drive/blob/main/tdrive/frontend/src/app/environment/environment.ts.dist)
Copy the content of this file and put it in `[docker-compose.yml location]/configuration/frontend/environment.ts`
@@ -1,12 +1,12 @@
---
description: You should update this security keys to ship TDrive in production.
description: You should update this security keys to ship Twake Drive in production.
---
# 🔒 Security
<!-- TODO[NOT UP TO DATE] -->
> See how to [Detach Configuration](./) first.
The following keys must be updated to increase TDrive security in [docker-compose.yml location]/configuration/backend-node/production.json:
The following keys must be updated to increase Twake Drive security in [docker-compose.yml location]/configuration/backend-node/production.json:
```json
{
+21 -21
View File
@@ -1,32 +1,32 @@
# 🏗 Install on your server
## Use TDrive in SaaS
## Use Twake Drive in SaaS
You can test or use TDrive in our SaaS : [chat.tdrive.app](https://chat.tdrive.app)
You can test or use Twake Drive in our SaaS : [chat.tdrive.app](https://chat.tdrive.app)
## Run TDrive in your server
## Run Twake Drive in your server
1. First you'll need to [install docker and docker-compose](https://docs.docker.com/compose/install/).
2. Then you can install TDrive on your server with this command
2. Then you can install Twake Drive on your server with this command
```
git clone https://github.com/TDriveApp/TDrive.git
cd TDrive/tdrive
git clone git@github.com:linagora/twake-drive.git
cd tdrive/tdrive
docker-compose -f docker-compose.onpremise.mongo.yml up -d
```
TDrive will be running on port 3000
Twake Drive will be running on port 3000
### What's next ?
If you kept the default configuration, you can simply follow the signup steps, no email verification is required by default so you will get into TDrive right after the signup steps.
If you kept the default configuration, you can simply follow the signup steps, no email verification is required by default so you will get into Twake Drive right after the signup steps.
## Ship TDrive in production
## Ship Twake Drive in production
See how to [manage configuration](./configuration/index.md)). And then how to [update security keys](./configuration/security.md).
### Update TDrive
### Update Twake Drive
```
docker-compose stop
@@ -42,21 +42,21 @@ Currently you'll need at least a **2 cpu + 4 gb of ram** machine for **20-50 use
If you enable ElasticSearch, use two machines, or limit the cpu/ram dedicated to it and use a larger machine (at least 2gb of ram and 1 cpu dedicated to ES for 20-50 users).
If you need to deploy TDrive for more users, you can use only one big machine up to 500 users (Will need something like **12 cpu and 32go of ram**), then you'll need to use multiple nodes.
If you need to deploy Twake Drive for more users, you can use only one big machine up to 500 users (Will need something like **12 cpu and 32go of ram**), then you'll need to use multiple nodes.
---
# 🎡 Scale with TDrive
# 🎡 Scale with Twake Drive
<!-- TODO[NOT UP TO DATE] -->
> We deployed Twake Drive on production for companies of 10 to 50 users in a single node. We also deployed Twake Drive in a scalable mode and we support currently thousands of concurrent users.
> If you deploy Twake Drive in your own company we would love to have your feedback here [https://github.com/TDriveApp/TDrive/issues/289](https://github.com/TDriveApp/TDrive/issues/289) to improve our requirements documentation.
> Now if you want to scale with Twake Drive and support thousand of users, continue reading.
> We deployed TDrive on production for companies of 10 to 50 users in a single node. We also deployed TDrive in a scalable mode and we support currently thousands of concurrent users.
> If you deploy TDrive in your own company we would love to have your feedback here [https://github.com/TDriveApp/TDrive/issues/289](https://github.com/TDriveApp/TDrive/issues/289) to improve our requirements documentation.
> Now if you want to scale with TDrive and support thousand of users, continue reading.
Scaling with TDrive is possible if you install TDrive with **RabbitMQ, Redis, ElasticSearch and ScyllaDB**.
Scaling with Twake Drive is possible if you install Twake Drive with **RabbitMQ, Redis, ElasticSearch and ScyllaDB**.
```
git clone https://github.com/TDriveApp/TDrive.git
cd TDrive/tdrive
git clone git@github.com:linagora/twake-drive.git
cd twake-drive/tdrive
cp -n docker-compose.yml.dist.onpremise docker-compose.yml
cp -nR default-configuration/ configuration/
@@ -71,8 +71,8 @@ sleep 10m #Wait php to create tables in scylladb
docker-compose up -d
```
> To run ElasticSearch (optional, but enabled by default in the TDrive docker-compose) you must increase the max_map_count of your system: [https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html#\_set_vm_max_map_count_to_at_least_262144](https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html#_set_vm_max_map_count_to_at_least_262144)
> To run ElasticSearch (optional, but enabled by default in the Twake Drive docker-compose) you must increase the max_map_count of your system: [https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html#\_set_vm_max_map_count_to_at_least_262144](https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html#_set_vm_max_map_count_to_at_least_262144)
>
> To fix an other bug with ElasticSearch container, you must also run this command: `chmod 777 ./docker-data/es_tdrive` (create the folder if it doesn't exists in your docker-compose.yml folder)
TDrive will be running on port 8000 🎉
Twake Drive will be running on port 8000 🎉
+2 -2
View File
@@ -1,4 +1,4 @@
# Tdrive - How to enable plugins
# Twake Drive - How to enable plugins
## Edit the /backend/node/config/{env}.json file to add the plugin, here is an example with onlyoffice:
@@ -120,4 +120,4 @@ Note:
When starting the docker-compose, the plugin will be available on https://[frontend]/plugins/onlyoffice/ you can test everything works by opening the url https://[frontend]/plugins/onlyoffice/assets/logo.png .
If the logo displays, then open TDrive, and see if you can create a new OnlyOffice Spreadsheet when clicking on "New".
If the logo displays, then open Twake Drive, and see if you can create a new OnlyOffice Spreadsheet when clicking on "New".
+4 -237
View File
@@ -7,8 +7,8 @@
1. Clone and install dependencies (assumes that you have Node.js 12 and npm installed. If not, we suggest to use [nvm](https://github.com/nvm-sh/nvm/)):
```sh
git clone git@github.com:Tdrive/Tdrive.git
cd Tdrive/Tdrive/backend/node
git clone git@github.com:linagora/twake-drive.git
cd twake-drive/tdrive/backend/node
npm install
```
@@ -279,7 +279,7 @@ Supported databases are currently [MongoDB](https://www.mongodb.com/) and [Cassa
In the example above, the `type` is set to `cassandra`, so the `database.cassandra` document will be used to connect to cassandra.
##### Cassandra
<!-- TODO[NOT UP TO DATE] -->
In order to use Cassandra, we will have to:
1. Create a keyspace. From the configuration above, the keyspace is `tdrive`
@@ -303,237 +303,4 @@ CREATE TABLE channels(company_id uuid, workspace_id uuid, id uuid, archivation_d
##### MongoDB
There are no special steps to achieve to use MongoDB.
#### Realtime Technical Service
The framework provides simple way to create CRUD Services which will notify clients using Websockets by following some conventions:
1. The service must implement the `CRUDService` generic interface
2. In order to push notification to clients, Typescript decorators must be added on methods (Create, Update, Delete methods)
For example, let's say that we want to implement a CRUD Service for `messages`:
```js
import {
CRUDService,
CreateResult,
DeleteResult,
UpdateResult,
EntityId,
} from "@/core/platform/framework/api/crud-service";
class Message {
text: string;
createdAt: Date;
author: string;
}
class MessageService implements CRUDService<Message> {
async create(item: Message): Promise<CreateResult<Message>> {
// save the message then return a CreateResult instance
}
async get(id: EntityId): Promise<Message> {
// get the message from its ID and return it
}
async update(id: EntityId, item: Message): Promise<UpdateResult<Message>> {
// update the message with the given id, patch it with `item` values
// then return an instance of UpdateResult
}
async delete(id: EntityId): Promise<DeleteResult<Message>> {
// delete the message from its id then return a DeleteResult instance
}
async list(): Promise<Message[]> {
// get a list of messages
}
}
```
By implementing the CRUD service following the API, we can now add realtime message to clients connected to Websocket on a selected collection of CRUD operations. For example, if we want to just notify when a `message` is created, we just have to add the right decorator on the `create` method like:
```js
import { RealtimeCreated } from "@/core/platform/framework/decorators";
// top and bottom code removed for clarity
class MessageService implements CRUDService<Message> {
@RealtimeCreated<Message>({
room: "/messages",
path: message => `/messages/${message.id}`
})
async create(item: Message): Promise<CreateResult<Message>> {
// save the message then return a CreateResult instance
const created: Message = new Message(/* */);
return new CreateResult<Message>("message", created)
}
// ...
}
```
The `RealtimeCreated` decorator will intercept the `create` call and will publish an event in an internal event bus with the creation result, the input data and the `"/messages"` path. On the other side of the event bus, an event listener will be in charge of delivering the event to the right Websocket clients as described next.
The Realtime\* decorators to add on methods to intercept calls and publish data are all following the same API.
A decorator takes two parameters as input:
- First one is the room name to publish the notification to (`/messages` in the example above)
- Second one is the full path of the resource linked to the action (`message => /messages/${message.id}` in the example above)
Both parameters can take a string or an arrow function as parameter. If arrow function is used, the input parameter will be the result element. By doing this, the paths can be generated dynamically at runtime.
#### Websocket Technical Service
Services annotated as described above automatically publish events to WebSockets. Under the hood, it uses Socket.IO rooms to send events to the right clients.
##### Authentication
- Client have to provide a valid JWT token to be able to connect and be authenticated by providing it as string in the `authenticate` event like `{ token: "the jwt token value" }`
- If the JWT token is valid, the client will receive a `authenticated` event
- If the JWT token is not valid or empty, the client will receive a `unauthorized` event
**Example**
```js
const io = require("socket.io-client");
// Get a JWT token from the Tdrive API first
const token =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOjEsImlhdCI6MTYwMzE5ODkzMn0.NvQoV9KeWuTNzRvzqbJ5uZCQ8Nmi2rCYQzcKk-WsJJ8";
const socket = io.connect("http://localhost:3000", { path: "/socket" });
socket.on("connect", () => {
socket
.emit("authenticate", { token })
.on("authenticated", () => {
console.log("User Authenticated");
})
.on("unauthorized", err => {
console.log("User is not authorized", err);
});
});
socket.on("disconnected", () => console.log("Disconnected"));
```
##### Joining rooms
CRUD operations on resources are pushing events in Socket.io rooms. In order to receive events, clients must subscribe to rooms by sending an `realtime:join` on an authenticated socket with the name of the room to join and with a valid JWT token like `{ name: "room name", token: "the jwt token for this room" }`: Users can not subscribe to arbitratry rooms, they have to be authorized to.
As a result, the client will receive events:
- `realtime:join:success` when join is succesful with data containing the name of the linked room like `{ name: "room" }`.
- `realtime:join:error` when join failed with data containing the name of the linked room and the error details like `{ name: "room", error: "some error message" }`.
**Example**
```js
const io = require("socket.io-client");
const token =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOjEsImlhdCI6MTYwMzE5ODkzMn0.NvQoV9KeWuTNzRvzqbJ5uZCQ8Nmi2rCYQzcKk-WsJJ8";
const socket = io.connect("http://localhost:3000", { path: "/socket" });
socket.on("connect", () => {
socket
.emit("authenticate", { token })
.on("authenticated", () => {
// join the /channels room
socket.emit("realtime:join", { name: "/channels", token: "tdrive" });
socket.on("realtime:join:error", message => {
// will fire when join does not provide a valid token
console.log("Error on join", message);
});
// will be fired on each successful join.
// As event based, this event is not linked only to the join above
// but to all joins. So you have to dig into the message to know which one
// is successful.
socket.on("realtime:join:success", message => {
console.log("Successfully joined room", message.name);
});
})
.on("unauthorized", err => {
console.log("Unauthorized", err);
});
});
socket.on("disconnected", () => console.log("Disconnected"));
```
##### Leaving rooms
The client can leave the room by emitting a `realtime:leave` event with the name of the room given as `{ name: "the room to leave" }`.
As a result, the client will receive events:
- `realtime:leave:success` when leave is succesful with data containing the name of the linked room like `{ name: "room" }`.
- `realtime:leave:error` when leave failed with data containing the name of the linked room and the error details like `{ name: "room", error: "some error message" }`.
Note: Asking to leave a room which has not been joined will not fire any error.
**Example**
```js
socket.on("connect", () => {
socket.emit("authenticate", { token }).on("authenticated", () => {
// leave the "/channels" room
socket.emit("realtime:leave", { name: "/channels" });
socket.on("realtime:leave:error", message => {
// will fire when join does not provide a valid token
console.log("Error on leave", message);
});
// will be fired on each successful leave.
// As event based, this event is not linked only to the leave above
// but to all leaves. So you have to dig into the message to know which one
// is successful.
socket.on("realtime:leave:success", message => {
console.log("Successfully left room", message.name);
});
});
});
```
##### Subscribe to resource events
Once the given room has been joined, the client will receive `realtime:resource` events with the resource linked to the event as data:
```json
{
"action": "created",
"room": "/channels",
"type": "channel",
"path": "/channels/5f905327e3e1626399aaad79",
"resource": {
"name": "My channel",
"id": "5f905327e3e1626399aaad79"
}
```
**Example:**
```js
socket.on("connect", () => {
socket
.emit("authenticate", { token })
.on("authenticated", () => {
// join the "/channels" room
socket.emit("realtime:join", { name: "/channels", token: "tdrive" });
// will only occur when an action occured on a resource
// and if and only if the client joined the room
// in which the resource is linked
socket.on("realtime:resource", event => {
console.log("Resource has been ${event.action}", event.resource);
});
})
.on("unauthorized", err => {
console.log("Unauthorized", err);
});
});
```
There are no special steps to achieve to use MongoDB.