From 48ab878bbf7835a6289e63e459492fa155237ef9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Oct 2023 10:51:39 +0000 Subject: [PATCH 01/14] Bump @babel/traverse from 7.22.10 to 7.23.2 Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.22.10 to 7.23.2. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.23.2/packages/babel-traverse) --- updated-dependencies: - dependency-name: "@babel/traverse" dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 92 +++++++++++++++++++++++------------------------ 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/package-lock.json b/package-lock.json index 69e1ba3..d1ee262 100644 --- a/package-lock.json +++ b/package-lock.json @@ -201,11 +201,11 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.10.tgz", - "integrity": "sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dependencies": { - "@babel/highlight": "^7.22.10", + "@babel/highlight": "^7.22.13", "chalk": "^2.4.2" }, "engines": { @@ -322,11 +322,11 @@ } }, "node_modules/@babel/generator": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.10.tgz", - "integrity": "sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "dependencies": { - "@babel/types": "^7.22.10", + "@babel/types": "^7.23.0", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -450,20 +450,20 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", - "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", - "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dependencies": { - "@babel/template": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" @@ -613,9 +613,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "engines": { "node": ">=6.9.0" } @@ -655,11 +655,11 @@ } }, "node_modules/@babel/highlight": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.10.tgz", - "integrity": "sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "dependencies": { - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, @@ -732,9 +732,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.10.tgz", - "integrity": "sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", "bin": { "parser": "bin/babel-parser.js" }, @@ -2064,31 +2064,31 @@ } }, "node_modules/@babel/template": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", - "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dependencies": { - "@babel/code-frame": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.10.tgz", - "integrity": "sha512-Q/urqV4pRByiNNpb/f5OSv28ZlGJiFiiTh+GAHktbIrkPhPbl90+uW6SmpoLyZqutrg9AEaEf3Q/ZBRHBXgxig==", - "dependencies": { - "@babel/code-frame": "^7.22.10", - "@babel/generator": "^7.22.10", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.10", - "@babel/types": "^7.22.10", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -2097,12 +2097,12 @@ } }, "node_modules/@babel/types": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.10.tgz", - "integrity": "sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "dependencies": { "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, "engines": { From c7d861daefdc9c36f0dbea97d706b57f4b8ee378 Mon Sep 17 00:00:00 2001 From: Thomas Feldtkeller Date: Fri, 12 Apr 2024 12:49:41 +0200 Subject: [PATCH 02/14] document eventhandling (#24) * document eventhandling --- .../Coding-Guidelines/event-handling.md | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 docs/schulcloud-server/Coding-Guidelines/event-handling.md diff --git a/docs/schulcloud-server/Coding-Guidelines/event-handling.md b/docs/schulcloud-server/Coding-Guidelines/event-handling.md new file mode 100644 index 0000000..9bee933 --- /dev/null +++ b/docs/schulcloud-server/Coding-Guidelines/event-handling.md @@ -0,0 +1,102 @@ +# Event Handling + +Internal Events are used as a mechanism for Dependency Inversion. + +If you are implementing an operation in a module that needs to trigger an operation in another module, that is simple if you can simply import a service. However, if that other module already has a dependency on your module, that would lead to a dependency cycle. In this case, you need to inverse one of the dependencies via events. + +The main thing you need to think about, is which module should know about which module(s). This is the dependency, and it only ever can point into one direction. As a general rule of thumb, the module that is more specific, or is changing more frequently, or is less central to the functionality of the system, should have the dependency on the other. + +In the following example, the course module has a dependency on the user module, but NOT vice versa. + +## How to implement Event Handling + +consider the following folder structure + +``` txt +- users + - api + - domain + - services + - events + - user-deleted.event.ts + - repo +- courses + - api + - domain + - services + - handlers + - user-deleted.handler.ts + - repo +``` + +each of the modules needs to import the CqrsModule + +``` ts +// users.module.ts +import { Module } from '@nestjs/common'; +import { CqrsModule } from '@nestjs/cqrs'; + +@Module({ + imports: [CqrsModule], + providers: [/* some things here */], + exports: [/* some things here */], +}) +export class GroupModule {} +``` + +### Defining an Event + +The event is in the end simply a class, containing any data required to handle the event + +``` ts +// users/domain/events/user-deleted.event.ts +export class UserDeletedEvent { + id: EntityId + + constructor(id: EntityId) { + this.id = id + } +} +``` + +Make sure to make your event public in the index file of your module + +### Sending an Event + +``` ts +// users/domain/services/service.ts +import { EventBus } from '@nestjs/cqrs'; +import { UserDeletedEvent } from '../events'; + +@Injectable() +export class Service { + constructor(private readonly eventBus: EventBus) {} + + public async delete(userId: EntityId): Promise { + doStuffForDeletion() + + await this.eventBus.publish(new UserDeletedEvent(userId)); + } +} +``` + +### Recieving an Event + +``` ts +// courses/domain/handler/user-deleted.handler.ts +import { UserDeletedEvent } from '@modules/users'; +import { EventsHandler, IEventHandler } from '@nestjs/cqrs'; +import { SomeService } from '../services' + +@Injectable() +@EventsHandler(UserDeletedEvent) +export class GroupDeletedHandlerService implements IEventHandler { + constructor(private readonly someService: SomeService) {} + + public async handle(event: GroupDeletedEvent): Promise { + await someService.doSomeStuff() + } +} +``` + +Note that the handler should not contain any logic, but only the orchestration of what needs to be done. From 8c8818f9b01971ead4bb12a039571914fe532650 Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Fri, 26 Apr 2024 11:54:26 +0200 Subject: [PATCH 03/14] add docu for modules and sub modules --- .../Coding-Guidelines/modules-submodules.md | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 docs/schulcloud-server/Coding-Guidelines/modules-submodules.md diff --git a/docs/schulcloud-server/Coding-Guidelines/modules-submodules.md b/docs/schulcloud-server/Coding-Guidelines/modules-submodules.md new file mode 100644 index 0000000..5cf06b7 --- /dev/null +++ b/docs/schulcloud-server/Coding-Guidelines/modules-submodules.md @@ -0,0 +1,66 @@ +# Implementation and usage of modules, submodule and barrel files in our project + +In this guide, we'll cover how to use modules, submodules and barrel files in our project. These concepts help you organize your code into separate files and directories, making it easier to manage and maintain. + +## Modules and Submodules + +In our project, modules are a way to create separate scopes. This means that interfaces, use cases, services, etc., declared in a module are not visible outside the module unless they are explicitly exported using the `export` keyword. To import a module, you use the `import` keyword followed by the module name. Here's an example: + +```typescript +import { ModuleName } from '@modules/module-name'; +``` + +Submodules are modules that are part of a larger module. They should be used within the main module. If it is necessary to use parts of the submodule outside the main module, the main module should export this via its barrel file(index.ts): + + +```typescript +// @modules/module-name/index.ts +export { SubmoduleServiceName } from './submodule-name/service.ts'; +``` + +## Barrel Files + +Barrel files are a way to rollup exports from several modules into a single convenient module. The barrel itself is a module file that re-exports selected exports of other modules. + +If you have several related modules in a directory, you can create a barrel to re-export all of their exports. This allows other modules to import everything from the barrel instead of having to import things individually from each module. + +Here's an example of a barrel file: + +```typescript +// @modules/module-name/index.ts +export { PublicService } from './services/public-service.ts'; +export * from './interfaces'; +export * from './submodule-name/interfaces'; +``` + +And here's how you can import from the barrel: + +```typescript +// @modules/other-module-name/service.ts +import { PublicService, InterfaceOfModule, InterfaceOfSubmodule } from '@modules/module-name'; +``` + +## Handling Circular Dependencies + +Circular dependencies occur when Module A depends on Module B, and Module B also depends on Module A. This can lead to unexpected behavior and hard-to-diagnose bugs. + +Here are some strategies to handle circular dependencies: + +1. **Refactor Your Code**: The best way to handle circular dependencies is to refactor your code to remove them. This might involve moving some code to a new module to break the dependency cycle. + +```typescript +// @modules/moduleC/service.ts +import { PublicService, InterfaceOfModule, InterfaceOfSubmodule } from '@modules/moduleA'; +import { PublicService, InterfaceOfModule, InterfaceOfSubmodule } from '@modules/moduleB'; +``` + +2. **Use Interfaces**: If the circular dependency is due to types, you can use interfaces and type-only imports to break the cycle. + +```typescript +// @modules/moduleC/service.ts +import { type PublicService } from '@modules/moduleA'; +import { type PublicService } from '@modules/moduleB'; +``` + +Remember, circular dependencies are usually a sign of tightly coupled code and can lead to maintenance issues down the line. It's best to refactor your code to avoid them if possible. + From 1974f48629b5172bf18e7c6b9b07dd99088d8b36 Mon Sep 17 00:00:00 2001 From: Cedric Evers <12080057+CeEv@users.noreply.github.com> Date: Tue, 30 Apr 2024 12:45:19 +0200 Subject: [PATCH 04/14] Add svg for modules and submodules --- .../img/Modules-SubModules.svg | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 docs/schulcloud-server/img/Modules-SubModules.svg diff --git a/docs/schulcloud-server/img/Modules-SubModules.svg b/docs/schulcloud-server/img/Modules-SubModules.svg new file mode 100644 index 0000000..c07e949 --- /dev/null +++ b/docs/schulcloud-server/img/Modules-SubModules.svg @@ -0,0 +1,21 @@ + + + + + + + + RepoDomainApiModuleRepoDomainApimodule.tsapi-module.tsconfig.tsindex.ts (explicit useable "interface". Also for other submodules,inside the module it self)Sub-ModuleRepoDomainApimodule.tsapi-module.tsconfig.tsindex.ts (explicit)Sub -ModuleRepoDomainApiSub-ModuleRepoDomainApiSub -Modulemodule.tsapi-module.tsconfig.tsindex.ts (define explicit what can be used from outside when someone import this module)Modulemodule.tsapi-module.tsconfig.tsindex.ts (define explicit what can be used from outside when someone import this module)module.tsapi-module.tsconfig.tsindex.ts (explicit)module.tsapi-module.tsconfig.tsindex.ts (explicit) \ No newline at end of file From 033733fdbf01a90420d32502e4cf00e9cd36195e Mon Sep 17 00:00:00 2001 From: Cedric Evers <12080057+CeEv@users.noreply.github.com> Date: Tue, 30 Apr 2024 12:49:08 +0200 Subject: [PATCH 05/14] replace svg with one that has background --- .../img/Modules-SubModules.svg | 21 ------------------- .../img/Modules-SubModules_background.svg | 21 +++++++++++++++++++ 2 files changed, 21 insertions(+), 21 deletions(-) delete mode 100644 docs/schulcloud-server/img/Modules-SubModules.svg create mode 100644 docs/schulcloud-server/img/Modules-SubModules_background.svg diff --git a/docs/schulcloud-server/img/Modules-SubModules.svg b/docs/schulcloud-server/img/Modules-SubModules.svg deleted file mode 100644 index c07e949..0000000 --- a/docs/schulcloud-server/img/Modules-SubModules.svg +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - RepoDomainApiModuleRepoDomainApimodule.tsapi-module.tsconfig.tsindex.ts (explicit useable "interface". Also for other submodules,inside the module it self)Sub-ModuleRepoDomainApimodule.tsapi-module.tsconfig.tsindex.ts (explicit)Sub -ModuleRepoDomainApiSub-ModuleRepoDomainApiSub -Modulemodule.tsapi-module.tsconfig.tsindex.ts (define explicit what can be used from outside when someone import this module)Modulemodule.tsapi-module.tsconfig.tsindex.ts (define explicit what can be used from outside when someone import this module)module.tsapi-module.tsconfig.tsindex.ts (explicit)module.tsapi-module.tsconfig.tsindex.ts (explicit) \ No newline at end of file diff --git a/docs/schulcloud-server/img/Modules-SubModules_background.svg b/docs/schulcloud-server/img/Modules-SubModules_background.svg new file mode 100644 index 0000000..d0f6588 --- /dev/null +++ b/docs/schulcloud-server/img/Modules-SubModules_background.svg @@ -0,0 +1,21 @@ + + + + + + + + RepoDomainApiModuleRepoDomainApimodule.tsapi-module.tsconfig.tsindex.ts (explicit useable "interface". Also for other submodules,inside the module it self)Sub-ModuleRepoDomainApimodule.tsapi-module.tsconfig.tsindex.ts (explicit)Sub -ModuleRepoDomainApiSub-ModuleRepoDomainApiSub -Modulemodule.tsapi-module.tsconfig.tsindex.ts (define explicit what can be used from outside when someone import this module)Modulemodule.tsapi-module.tsconfig.tsindex.ts (define explicit what can be used from outside when someone import this module)module.tsapi-module.tsconfig.tsindex.ts (explicit)module.tsapi-module.tsconfig.tsindex.ts (explicit) \ No newline at end of file From 0190bf2fb8244d792cba0181c5a58ab43aaab83d Mon Sep 17 00:00:00 2001 From: Cedric Evers <12080057+CeEv@users.noreply.github.com> Date: Tue, 30 Apr 2024 13:03:04 +0200 Subject: [PATCH 06/14] add svg to show allowed server area dependencies --- .../img/server_area_dependency.svg | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 docs/schulcloud-server/img/server_area_dependency.svg diff --git a/docs/schulcloud-server/img/server_area_dependency.svg b/docs/schulcloud-server/img/server_area_dependency.svg new file mode 100644 index 0000000..fcc998a --- /dev/null +++ b/docs/schulcloud-server/img/server_area_dependency.svg @@ -0,0 +1,21 @@ + + + + + + + + @shared@infra@core@modulessubmodules@apps@infra@modulessubmodules \ No newline at end of file From b638f22dc22607f5671df8f6e5718f8547147a4f Mon Sep 17 00:00:00 2001 From: Cedric Evers <12080057+CeEv@users.noreply.github.com> Date: Tue, 30 Apr 2024 15:00:40 +0200 Subject: [PATCH 07/14] Invert dependency file for sub modules in svg --- .../{server_area_dependency.svg => server_area_dependency_v1.svg} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/schulcloud-server/img/{server_area_dependency.svg => server_area_dependency_v1.svg} (100%) diff --git a/docs/schulcloud-server/img/server_area_dependency.svg b/docs/schulcloud-server/img/server_area_dependency_v1.svg similarity index 100% rename from docs/schulcloud-server/img/server_area_dependency.svg rename to docs/schulcloud-server/img/server_area_dependency_v1.svg From f48822303480ba68bcfdea187a2426004ad74881 Mon Sep 17 00:00:00 2001 From: Cedric Evers <12080057+CeEv@users.noreply.github.com> Date: Tue, 30 Apr 2024 15:04:30 +0200 Subject: [PATCH 08/14] update image --- ...ver_area_dependency_v1.svg => server_area_dependency_v2.svg} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename docs/schulcloud-server/img/{server_area_dependency_v1.svg => server_area_dependency_v2.svg} (87%) diff --git a/docs/schulcloud-server/img/server_area_dependency_v1.svg b/docs/schulcloud-server/img/server_area_dependency_v2.svg similarity index 87% rename from docs/schulcloud-server/img/server_area_dependency_v1.svg rename to docs/schulcloud-server/img/server_area_dependency_v2.svg index fcc998a..e573b71 100644 --- a/docs/schulcloud-server/img/server_area_dependency_v1.svg +++ b/docs/schulcloud-server/img/server_area_dependency_v2.svg @@ -18,4 +18,4 @@ - @shared@infra@core@modulessubmodules@apps@infra@modulessubmodules \ No newline at end of file + @shared@infra@core@modulessubmodules@apps@infra@modulessubmodules \ No newline at end of file From 005854a4fd76df8394bf38130b8a0c85db8f4abe Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Mon, 6 May 2024 15:22:02 +0200 Subject: [PATCH 09/14] add images --- .../Coding-Guidelines/modules-submodules.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/schulcloud-server/Coding-Guidelines/modules-submodules.md b/docs/schulcloud-server/Coding-Guidelines/modules-submodules.md index 5cf06b7..1f17978 100644 --- a/docs/schulcloud-server/Coding-Guidelines/modules-submodules.md +++ b/docs/schulcloud-server/Coding-Guidelines/modules-submodules.md @@ -2,6 +2,8 @@ In this guide, we'll cover how to use modules, submodules and barrel files in our project. These concepts help you organize your code into separate files and directories, making it easier to manage and maintain. +![Module Structure](./../img/Modules-SubModules_background.svg) + ## Modules and Submodules In our project, modules are a way to create separate scopes. This means that interfaces, use cases, services, etc., declared in a module are not visible outside the module unless they are explicitly exported using the `export` keyword. To import a module, you use the `import` keyword followed by the module name. Here's an example: @@ -12,7 +14,6 @@ import { ModuleName } from '@modules/module-name'; Submodules are modules that are part of a larger module. They should be used within the main module. If it is necessary to use parts of the submodule outside the main module, the main module should export this via its barrel file(index.ts): - ```typescript // @modules/module-name/index.ts export { SubmoduleServiceName } from './submodule-name/service.ts'; @@ -29,10 +30,12 @@ Here's an example of a barrel file: ```typescript // @modules/module-name/index.ts export { PublicService } from './services/public-service.ts'; -export * from './interfaces'; -export * from './submodule-name/interfaces'; +export { ServiceInterfaceA, InterfaceB } from './interfaces'; +export { InterfaceC } from './submodule-name/interfaces'; ``` +!!! Please don't export everything from a module in the barrel file. Only export the public API of the module. This will make it easier to understand what the module provides and avoid unnecessary dependencies. And don't use wildcard exports like `export * from './services'` in the barrel file. + And here's how you can import from the barrel: ```typescript @@ -44,6 +47,8 @@ import { PublicService, InterfaceOfModule, InterfaceOfSubmodule } from '@modules Circular dependencies occur when Module A depends on Module B, and Module B also depends on Module A. This can lead to unexpected behavior and hard-to-diagnose bugs. +![Module Structure](./../img/server_area_dependency_v2.svg) + Here are some strategies to handle circular dependencies: 1. **Refactor Your Code**: The best way to handle circular dependencies is to refactor your code to remove them. This might involve moving some code to a new module to break the dependency cycle. @@ -63,4 +68,3 @@ import { type PublicService } from '@modules/moduleB'; ``` Remember, circular dependencies are usually a sign of tightly coupled code and can lead to maintenance issues down the line. It's best to refactor your code to avoid them if possible. - From 91e54a0347326cc1776f1718e56aa69b49ac0b98 Mon Sep 17 00:00:00 2001 From: Cedric Evers <12080057+CeEv@users.noreply.github.com> Date: Wed, 8 May 2024 10:44:15 +0200 Subject: [PATCH 10/14] Create initial sides --- docs/services/tldraw/How it works.md | 41 ++++++++++++++++++ docs/services/tldraw/Local setup.md | 0 docs/services/tldraw/Technical details.md | 0 .../tldraw/assets/Create TLDRAW.drawio.png | Bin 0 -> 48015 bytes .../tldraw/assets/Create TLDRAW.drawio.svg | 4 ++ .../tldraw/assets/Delete TLDRAW.drawio.png | Bin 0 -> 44855 bytes .../tldraw/assets/Delete TLDRAW.drawio.svg | 4 ++ .../tldraw/assets/Use tldraw.drawio.png | Bin 0 -> 74052 bytes .../tldraw/assets/Use tldraw.drawio.svg | 4 ++ 9 files changed, 53 insertions(+) create mode 100644 docs/services/tldraw/How it works.md create mode 100644 docs/services/tldraw/Local setup.md create mode 100644 docs/services/tldraw/Technical details.md create mode 100644 docs/services/tldraw/assets/Create TLDRAW.drawio.png create mode 100644 docs/services/tldraw/assets/Create TLDRAW.drawio.svg create mode 100644 docs/services/tldraw/assets/Delete TLDRAW.drawio.png create mode 100644 docs/services/tldraw/assets/Delete TLDRAW.drawio.svg create mode 100644 docs/services/tldraw/assets/Use tldraw.drawio.png create mode 100644 docs/services/tldraw/assets/Use tldraw.drawio.svg diff --git a/docs/services/tldraw/How it works.md b/docs/services/tldraw/How it works.md new file mode 100644 index 0000000..ebe4331 --- /dev/null +++ b/docs/services/tldraw/How it works.md @@ -0,0 +1,41 @@ +How it works +============ + +# Create +![Create tldraw workflow](./assets/Create TLDRAW.drawio.svg) + +Creation of Tldraw starts with creation proccess for Courses and CourseBoard. It has Representation in CourseBoard as card's element (BoardNode in db). After creating Representation of drawing we can enter actual tldraw SPA client (left side of picture). + +1. User enters CourseBoard and creates Representation of whiteboard (tldraw) in CourseCard. +2. Data is saved and feedback with proper creation is given - user can see Representation and can enter whiteboard. +3. By entering whiteboard user is redirected to SPA tldraw-client. +4. Tldraw-client is starting WS connection with tldraw-server. +5. Tldraw-server firstly checks if user has permission to this resource (by checking if user has a permission to Representation of whiteboard -BoardNode). + Id of Representation is same as drawingName, which is visible in tldraw-client url. +6. If user has permission tldraw-server is allowing to remain connection and getting drawing data from separate tldraw-db. If there were no drawing data saved tldraw-server will create it automatically. + +# Usage +![Usage tldraw workflow](./assets/Use tldraw.drawio.svg) + +## Connection + +1. user joins tldraw board +2. tldraw-client connects to one of the tldraw-server pods and tries to establish websocket connection +3. tldraw-server calls schulcloud-server via HTTP requests to check user permissions, if everything is fine the websocket connection is established +4. tldraw-server gets stored tldraw board data from mongodb and sends it via websocket to connected users +5. tldraw-server starts subscribing to Redis PUBSUB channel corresponding to tldraw board name to listen to changes from other pods + +## Sending updates/storing data + +1. tldraw-client sends user's drawing changes to the tldraw-server via websocket connection +2. tldraw-server stores the board update in the mongodb - basically creates a diff between what's already stored and what's being updated +3. tldraw-server pushes the update to correct Redis channel so that clients connected to different pods have synchronized board data +4. other pods subscribing to Redis channel send updates to their connected clients via websocket whenever they see a new message on Redis channel + +# Delete +![Delete tldraw workflow](./assets/Delete TLDRAW.drawio.svg) + +1. User from schulcloud app in CourseBoard deletes whiteboard (tldraw) instance form CardBoard. +2. Having drawingName sc-server is removing Representation data in sc-database - BoardNodes collection ( drawingName === BoardNode id) +3. Sc-server is calling tldraw-server via tldraw-management rules in tldraw-server-svc to delete all data that has given id). +4. After deletion user sees refreshed state of CourseBoard. \ No newline at end of file diff --git a/docs/services/tldraw/Local setup.md b/docs/services/tldraw/Local setup.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/services/tldraw/Technical details.md b/docs/services/tldraw/Technical details.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/services/tldraw/assets/Create TLDRAW.drawio.png b/docs/services/tldraw/assets/Create TLDRAW.drawio.png new file mode 100644 index 0000000000000000000000000000000000000000..003d1fd74223171a28aefa0cd985005d9ca4047f GIT binary patch literal 48015 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 JR4H4B{x8mckW~Nx literal 0 HcmV?d00001 diff --git a/docs/services/tldraw/assets/Create TLDRAW.drawio.svg b/docs/services/tldraw/assets/Create TLDRAW.drawio.svg new file mode 100644 index 0000000..0f99857 --- /dev/null +++ b/docs/services/tldraw/assets/Create TLDRAW.drawio.svg @@ -0,0 +1,4 @@ + + + +
sc-namespace
sc-namespace
sc-client
sc-client
tldraw-client
tldraw-client
sc-server
sc-server
tldraw-server
tldraw-server
after creating 
redirect client to 
tldraw SPA
after crea...
user creates
board-node (tldraw)
as part of course
user creat...
response of created
drawing borad-node
response o...
save tldraw record
save tldra...
saving drawing data
saving dra...
authorization
communication 
for wb connection
authorizat...
websocket connection

websocket...
sc-db
sc-db
tldraw-db
tldraw-db
\ No newline at end of file diff --git a/docs/services/tldraw/assets/Delete TLDRAW.drawio.png b/docs/services/tldraw/assets/Delete TLDRAW.drawio.png new file mode 100644 index 0000000000000000000000000000000000000000..c09988de067703073041ee25fc931ccf20a871c5 GIT binary patch literal 44855 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 Q2ma{XHq>~d<`DP40AG6P=l}o! literal 0 HcmV?d00001 diff --git a/docs/services/tldraw/assets/Delete TLDRAW.drawio.svg b/docs/services/tldraw/assets/Delete TLDRAW.drawio.svg new file mode 100644 index 0000000..d8b7f38 --- /dev/null +++ b/docs/services/tldraw/assets/Delete TLDRAW.drawio.svg @@ -0,0 +1,4 @@ + + + +
sc-namespace
sc-namespace
sc-client
sc-client
sc-server
sc-server
tldraw-server
tldraw-server
user deletes
tldraw whiteboard
user delet...
response of deletation
tldraw data
response o...
delete tldraw record
delete tld...
delete drawing data
delete dra...
authorization
communication 
authorizat...
delete tldraw data
delete tldr...
sc-db
sc-db
tldraw-sb
tldraw-sb
\ No newline at end of file diff --git a/docs/services/tldraw/assets/Use tldraw.drawio.png b/docs/services/tldraw/assets/Use tldraw.drawio.png new file mode 100644 index 0000000000000000000000000000000000000000..e5541dc320f753ed7df179abd3bbb54389991346 GIT binary patch literal 74052 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 + + +
sc-namespace
sc-namespace
sc-server
sc-server
check connection
between user
and tldraw and course
check conn...
tldraw-server
tldraw-server
pod 1
pod 1
pod 2
pod 2
pod 3
pod 3
Redis
Redis

authorization
communication 
for wb connection


authorizat...
saving drawing data
saving dra...
Sync between pods
Sync betwe...
tldraw-client
tldraw-client
websocket connection

websocket...
sc-db
sc-db
tldraw-db
tldraw-db
\ No newline at end of file From 37060da3c104bcfb12682e57bb4e4b83dec70c82 Mon Sep 17 00:00:00 2001 From: Cedric Evers <12080057+CeEv@users.noreply.github.com> Date: Wed, 8 May 2024 10:53:03 +0200 Subject: [PATCH 11/14] Change title types --- docs/services/tldraw/How it works.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/services/tldraw/How it works.md b/docs/services/tldraw/How it works.md index ebe4331..e04efd1 100644 --- a/docs/services/tldraw/How it works.md +++ b/docs/services/tldraw/How it works.md @@ -1,7 +1,6 @@ -How it works -============ +# How it works -# Create +## Create ![Create tldraw workflow](./assets/Create TLDRAW.drawio.svg) Creation of Tldraw starts with creation proccess for Courses and CourseBoard. It has Representation in CourseBoard as card's element (BoardNode in db). After creating Representation of drawing we can enter actual tldraw SPA client (left side of picture). @@ -14,10 +13,10 @@ Creation of Tldraw starts with creation proccess for Courses and CourseBoard. It Id of Representation is same as drawingName, which is visible in tldraw-client url. 6. If user has permission tldraw-server is allowing to remain connection and getting drawing data from separate tldraw-db. If there were no drawing data saved tldraw-server will create it automatically. -# Usage +## Usage ![Usage tldraw workflow](./assets/Use tldraw.drawio.svg) -## Connection +### Connection 1. user joins tldraw board 2. tldraw-client connects to one of the tldraw-server pods and tries to establish websocket connection @@ -25,14 +24,14 @@ Creation of Tldraw starts with creation proccess for Courses and CourseBoard. It 4. tldraw-server gets stored tldraw board data from mongodb and sends it via websocket to connected users 5. tldraw-server starts subscribing to Redis PUBSUB channel corresponding to tldraw board name to listen to changes from other pods -## Sending updates/storing data +### Sending updates/storing data 1. tldraw-client sends user's drawing changes to the tldraw-server via websocket connection 2. tldraw-server stores the board update in the mongodb - basically creates a diff between what's already stored and what's being updated 3. tldraw-server pushes the update to correct Redis channel so that clients connected to different pods have synchronized board data 4. other pods subscribing to Redis channel send updates to their connected clients via websocket whenever they see a new message on Redis channel -# Delete +## Delete ![Delete tldraw workflow](./assets/Delete TLDRAW.drawio.svg) 1. User from schulcloud app in CourseBoard deletes whiteboard (tldraw) instance form CardBoard. From effb53e71087ecde42bf795f72538aaa724fece7 Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Thu, 16 May 2024 15:17:49 +0200 Subject: [PATCH 12/14] code review --- .../Coding-Guidelines/modules-submodules.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/schulcloud-server/Coding-Guidelines/modules-submodules.md b/docs/schulcloud-server/Coding-Guidelines/modules-submodules.md index 1f17978..5c1cab8 100644 --- a/docs/schulcloud-server/Coding-Guidelines/modules-submodules.md +++ b/docs/schulcloud-server/Coding-Guidelines/modules-submodules.md @@ -67,4 +67,10 @@ import { type PublicService } from '@modules/moduleA'; import { type PublicService } from '@modules/moduleB'; ``` +3. **Use Events**: If you have a circular dependency between two modules that need to communicate with each other, consider using events to decouple them. This way, one module can emit an event that the other module listens to, without directly importing it. + +- + +- + Remember, circular dependencies are usually a sign of tightly coupled code and can lead to maintenance issues down the line. It's best to refactor your code to avoid them if possible. From 37aa4410b3b3ab3845cb277908a4500c2f95719f Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Fri, 17 May 2024 08:20:15 +0200 Subject: [PATCH 13/14] code review --- .../schulcloud-server/Coding-Guidelines/modules-submodules.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/schulcloud-server/Coding-Guidelines/modules-submodules.md b/docs/schulcloud-server/Coding-Guidelines/modules-submodules.md index 5c1cab8..e0a315c 100644 --- a/docs/schulcloud-server/Coding-Guidelines/modules-submodules.md +++ b/docs/schulcloud-server/Coding-Guidelines/modules-submodules.md @@ -21,9 +21,9 @@ export { SubmoduleServiceName } from './submodule-name/service.ts'; ## Barrel Files -Barrel files are a way to rollup exports from several modules into a single convenient module. The barrel itself is a module file that re-exports selected exports of other modules. +Barrel files are a way to rollup exports from several folders into a single convenient nest-module. The barrel itself is a module file that re-exports selected exports of other submodules. -If you have several related modules in a directory, you can create a barrel to re-export all of their exports. This allows other modules to import everything from the barrel instead of having to import things individually from each module. +If you have several related service/interface files in a directory/module that should be publicly accessible, you can create a barrel file to export all these files from the main module again. Here's an example of a barrel file: From 084d73a8595bc9afe5af8514e0a2e2e38e2ccb79 Mon Sep 17 00:00:00 2001 From: Cedric Evers <12080057+CeEv@users.noreply.github.com> Date: Fri, 17 May 2024 10:29:53 +0200 Subject: [PATCH 14/14] Move the next parts of the documentation --- docs/services/tldraw/How it works.md | 43 ++++++++++- docs/services/tldraw/Local setup.md | 18 +++++ docs/services/tldraw/Technical details.md | 90 +++++++++++++++++++++++ 3 files changed, 150 insertions(+), 1 deletion(-) diff --git a/docs/services/tldraw/How it works.md b/docs/services/tldraw/How it works.md index e04efd1..02992d2 100644 --- a/docs/services/tldraw/How it works.md +++ b/docs/services/tldraw/How it works.md @@ -1,5 +1,33 @@ # How it works +## Configuration + +- NEST_LOG_LEVEL - logging level +- FEATURE_TLDRAW_ENABLED - flag determining if tldraw is enabled +- TLDRAW_URI - address of tldraw server +- INCOMING_REQUEST_TIMEOUT - request timeout +- TLDRAW_DB_URL - mongoDB connection string +- TLDRAW__SOCKET_PORT - port number for websockets connection +- TLDRAW__PING_TIMEOUT - timeout for ping-pong during establishing websockets connection +- TLDRAW__FINALIZE_DELAY - delay in ms before checking if can finalize a tldraw board +- TLDRAW__GC_ENABLED - if tldraw garbage collector should be enabled +- TLDRAW__DB_COMPRESS_THRESHOLD - threshold size for tldraw mongo documents compression +- TLDRAW__MAX_DOCUMENT_SIZE - max size of a single tldraw document in mongo +- TLDRAW__ASSETS_ENABLED - enables uploading assets to tldraw board +- TLDRAW__ASSETS_SYNC_ENABLED - enables synch of tldraw board assets with filestorage (no longer used) +- TLDRAW__ASSETS_MAX_SIZE - maximum asset size in bytes +- TLDRAW__ASSETS_ALLOWED_MIME_TYPES_LIST - listy of allowed assets MIME types +- REDIS_URI - Redis connection string +- TLDRAW_CLIENT_REPLICAS - number of pods for tldraw-client +- TLDRAW_SERVER_REPLICAS - number of pods for tldraw-server +- TLDRAW_ADMIN_API_CLIENT__API_KEY - authorization API key for accessing tldraw controller (delete flow) +- TLDRAW_ADMIN_API_CLIENT__BASE_URL - address of tldraw controller (delete flow) + +In order to have deletion functionality fully working you have to fill those feature flags, e.g.: +- ADMIN_API__ALLOWED_API_KEYS=["7ccd4e11-c6f6-48b0-81eb-abcdef123456"] +- TLDRAW_ADMIN_API_CLIENT__API_KEY="7ccd4e11-c6f6-48b0-81eb-abcdef123456" +- TLDRAW_ADMIN_API_CLIENT__BASE_URL="http://localhost:3349" + ## Create ![Create tldraw workflow](./assets/Create TLDRAW.drawio.svg) @@ -37,4 +65,17 @@ Creation of Tldraw starts with creation proccess for Courses and CourseBoard. It 1. User from schulcloud app in CourseBoard deletes whiteboard (tldraw) instance form CardBoard. 2. Having drawingName sc-server is removing Representation data in sc-database - BoardNodes collection ( drawingName === BoardNode id) 3. Sc-server is calling tldraw-server via tldraw-management rules in tldraw-server-svc to delete all data that has given id). -4. After deletion user sees refreshed state of CourseBoard. \ No newline at end of file +4. After deletion user sees refreshed state of CourseBoard. + +## Assets +### files upload + +Images/GIFs can be uploaded into tldraw whiteboard by every user with access to the board. We use s3 storage to physically store uploaded assets while tldraw only holds URL to a resource. + +The files are uploaded by calling schulcloud-api's fileController upload endpoint. This is possible because tldraw is represented as a boardnode called drawing-element. Mongo id of this drawing-element is a roomId used in URL param when connecting to a specific board. + +### files deletion + +Because of the undo/redo functionality of tldraw (user can basically undo an upload of an image, undo a deletion, then redo upload etc.) we needed a way to clean up unused assets from the storage. We could not use soft delete/restore endpoints every time undo/redo happens due to various issues with performance/user experience and technical challenges that arose when testing different scenarios. We decided to go with cron job solution: once per day, at midnight by default, we would go through each board stored in database, get every asset that's stored as URL but no longer used as an active drawing and then delete all of them via amqp call to filesStorage service. + +For implementation details, take a look at: tldraw-files.console.ts. \ No newline at end of file diff --git a/docs/services/tldraw/Local setup.md b/docs/services/tldraw/Local setup.md index e69de29..f929965 100644 --- a/docs/services/tldraw/Local setup.md +++ b/docs/services/tldraw/Local setup.md @@ -0,0 +1,18 @@ +# Local setup + +### To run tldraw locally: + +1. Run all of the apps needed for schulcloud like mongodb, backend, frontend, file storage etc. +2. Run redis i.e. in a docker container, it will work on localhost:6379 by default which is what the REDIS_URI env var is set to +3. On schulcloud-server repo: npm run nest:start:tldraw:dev +4. On tldraw-client repo: npm run dev + +### Create new whiteboard: + +1. Go to a course +2. Go to 'Column board' +3. Create a new card and a new 'Whiteboard' element within it, then click it +4. A new browser tab with URL like: http://localhost:4000/tldraw?roomName=65c37329b2f97cc714d31c00 will open +5. Change the port part from 4000 to 3046, which is the default port of tldraw-client app +6. You should see a working tldraw whiteboard now + diff --git a/docs/services/tldraw/Technical details.md b/docs/services/tldraw/Technical details.md index e69de29..c6f4f42 100644 --- a/docs/services/tldraw/Technical details.md +++ b/docs/services/tldraw/Technical details.md @@ -0,0 +1,90 @@ +# Technical details + +## Backend +We are using pure Web Sockets implementation to establish connection between client and server. The reason why we used pure websockets is because tldraw-client is using y-websockets from Yjs library, that does not connect with socket.io web sockets. We also have to implement broadcasting mechanism to provide stateless solution. To achive that goal we decided to use Redis. We used ioredis library to connect to our Redis instance. Everytime user make some changes at first it is handled to the server instance that he is connected to an then this change is send to the channel with the name of the board and servers that also operate that board are listening on this channel so they can recive messages from other servers and provide those changes to users that are connected to this pod. We added the same mechanism for awareness channel so every user from every pod can see other users cursours. + + +Tldraw is deployed as a separate application from schoulcloud-server working on the same namespace as schoulcloud-server. On the backend side we have added couple new resources: + +- tldraw-deployment - deployment for tldraw-server's instances. +- tldraw-server-svc - service for tldraw-service to communicate with tldraw-client (WS) and schoulcloud-server (management e.g. deletion of data) +- tldraw-svc-monitor - service to collect metrics from tldraw. Apart from typical metrics like request time we also added two application-level metrics: + - sc_tldraw_users - number of active users on boards + - sc_tldraw_boards - number of active boards +- tldraw-ingress - for steering web external traffic to tldraw-server (for now management rules in tldraw-server-svc are closed from external clients) + + +### Tldraw-server code structure + +- tldraw.ws.service.ts - main service responsible for establishing web socket connection as well as saving data to database. Responsibe for Redis communication. +- tldraw.controller.ts - controller that expose HTTP deletion method outside the tldraw-server application. +- tldraw.server.ts - service used by TldrawController. +- y-mongodb.ts - main adapter to connect with mongodb, provides transaction mechanism, calucalate diffs between revision and to apply updates. +- tldraw-board.repo.ts - repository object to connect TldrawWsService and YMongodb. +- tldraw.repo.ts - repository used by TldrawService to find and delete boards from database. +- ws-shared-doc.do.ts - main structure representing tldraw drawing during web socket communication. it holds all the web-socket addresses that are connected to this board, so we can inform all the connected clients about changes. +- tldraw-drawing.entity.ts - object representing tldraw drawingn entity in database. +- metrics.service.ts - service resonsible for storing application-level metrics. + + +On the backend side we are also using Yjs library to store tldraw board in memory and to calculate diffs after the board is changed. + +## Frontend + +### Key Files +- stores/setup.ts – this file provides a real-time collaboration environment for a drawing application using the WebSocket and Yjs libraries. +- hooks/useMultiplayerState.ts – custom hook for managing multiplayer state. +- App.tsx – main application component integrating Tldraw and multiplayer state. + +### Frontend Technologies + +The frontend of the project is built using React and leverages various libraries and tools for enhanced functionality. Here is an overview of the key frontend technologies: + +- React: A JavaScript library for building user interfaces. +- Yjs: A real-time collaboration framework for synchronizing shared state. +- Tldraw: A library for drawing functionalities in the application. We use the old version of tldraw: https://github.com/tldraw/tldraw-v1, after the tldraw team releases the official update of the new version, we will work on the new version and integrate it with the needs of our users. + +### State Managment + +1. Yjs is integrated into the project for real-time collaboration. The central state (shapes, bindings, assets) is managed using Yjs maps. +2. store.ts handles the configuration of Yjs, WebSocket connections, and provides centralized maps for shapes, bindings, and assets +3. useMultiplayerState.ts -This hook manages the multiplayer state, including loading rooms, handling file system operations, and updating Yjs maps: + - Mounting and handling changes in Tldraw App. + - Presence management and user updates. + - File system operations like opening and saving projects. + +#### useTldrawUiSanitizer.ts +This hook is designed to observe changes in the DOM, specifically targeting certain buttons and a horizontal rule (< hr>), and hides them if they match a specific ID pattern. We hide this elements and left just only Language and Keyboard shortcuts. + +#### Event Handling + - onMount: Handles mounting of the Tldraw app. + - onChangePage: Manages page changes and updates Yjs maps. + - onUndo and onRedo: Handle undo and redo operations. + - onChangePresence: Manages presence changes in the collaborative environment. + - onAssetCreate: This function is triggered when a user attempts to upload an asset (like an image or a file). + +#### Useful links +- https://tldraw.dev/ - documentation for the new version of tldraw + +- https://old.tldraw.com/ - tldraw live application + +- https://github.com/tldraw/tldraw-v1 - tldraw v1 repo + +- https://github.com/MaxNoetzold/y-mongodb-provider - code from this package was used to add mongodb as a persistence to tldraw + +- https://discord.com/invite/SBBEVCA4PG discord channel with open questions and answers + +- https://grafana.dbildungscloud.dev/d/b6b28b2b-3129-4772-8102-e32981d2c2e3/devops-tldraw-metrics?orgId=1&refresh=1m&var-source=sc-dev-dbc&var-env=main&var-env=tldraw-debugging - grafana v + +- https://grafana.dbildungscloud.org/d/b6b28b2b-3129-4772-8102-e32981d2c2e0/devops-tldraw?orgId=1&from=now-6h&to=now&refresh=1m - grafana metrics + +- https://github.com/nimeshnayaju/yjs-tldraw - yjs with tldraw POC + +- https://github.com/yjs/y-websocket/tree/master/bin - yjs/y-websocket repo + +- https://github.com/erdtool/yjs-scalable-ws-backend/tree/main - Yjs scalable WS backend with redis example + +- https://teamchat.dbildungscloud.de/channel/G9hJWv92zXEESKK3X - rocketchat discussion "tldraw syncronisation for release again" + +- https://teamchat.dbildungscloud.de/group/SagK4sCyujhu6yZr8 - rocketchat discussion "Tldraw deployment"s +