From 651482a1b98ee09bc07cd9164aa075a4fd88a110 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Sun, 16 Oct 2022 01:55:20 +0200 Subject: [PATCH] Added api connection check #70 --- kdb-bot/src/bot/bot.json | 4 +- kdb-bot/src/bot_api/api.py | 25 +++- kdb-web/package-lock.json | 107 ++++++++++++++---- kdb-web/package.json | 2 + kdb-web/src/app/app.component.ts | 3 + kdb-web/src/app/app.module.ts | 4 +- .../services/socket/socket.service.spec.ts | 16 +++ .../src/app/services/socket/socket.service.ts | 71 ++++++++++++ kdb-web/src/assets/config.json | 2 +- 9 files changed, 210 insertions(+), 24 deletions(-) create mode 100644 kdb-web/src/app/services/socket/socket.service.spec.ts create mode 100644 kdb-web/src/app/services/socket/socket.service.ts diff --git a/kdb-bot/src/bot/bot.json b/kdb-bot/src/bot/bot.json index 799d3fe8fd..985c978b2c 100644 --- a/kdb-bot/src/bot/bot.json +++ b/kdb-bot/src/bot/bot.json @@ -24,7 +24,9 @@ "Flask-Classful==0.14.2", "Flask-Cors==3.0.10", "PyJWT==2.5.0", - "waitress==2.1.2" + "waitress==2.1.2", + "Flask-SocketIO==5.3.1", + "eventlet==0.33.1" ], "DevDependencies": [ "cpl-cli==2022.10.0" diff --git a/kdb-bot/src/bot_api/api.py b/kdb-bot/src/bot_api/api.py index 7930b861ce..b79a12d20f 100644 --- a/kdb-bot/src/bot_api/api.py +++ b/kdb-bot/src/bot_api/api.py @@ -3,11 +3,15 @@ import sys import uuid from functools import partial +import eventlet from cpl_core.dependency_injection import ServiceProviderABC +from eventlet import wsgi from flask import Flask, request, jsonify, Response, make_response from flask_cors import CORS +from flask_socketio import SocketIO from bot_api.configuration.api_settings import ApiSettings +from bot_api.configuration.frontend_settings import FrontendSettings from bot_api.exception.service_exception import ServiceException from bot_api.logging.api_logger import ApiLogger from bot_api.model.error_dto import ErrorDTO @@ -21,6 +25,7 @@ class Api(Flask): logger: ApiLogger, services: ServiceProviderABC, api_settings: ApiSettings, + frontend_settings: FrontendSettings, *args, **kwargs ): if not args: @@ -39,6 +44,11 @@ class Api(Flask): exc_class, code = self._get_exc_class_and_code(Exception) self.error_handler_spec[None][code][exc_class] = self.handle_exception + # websockets + self._socketio = SocketIO(self, cors_allowed_origins='*') + self._socketio.on_event('connect', self.on_connect) + self._socketio.on_event('disconnect', self.on_disconnect) + def _register_routes(self): for path, f in Route.registered_routes.items(): route = f[0] @@ -76,6 +86,17 @@ class Api(Flask): def start(self): self._logger.info(__name__, f'Starting API {self._apt_settings.host}:{self._apt_settings.port}') self._register_routes() - from waitress import serve + # from waitress import serve # https://docs.pylonsproject.org/projects/waitress/en/stable/arguments.html - serve(self, host=self._apt_settings.host, port=self._apt_settings.port, threads=10, connection_limit=1000, channel_timeout=10) + # serve(self, host=self._apt_settings.host, port=self._apt_settings.port, threads=10, connection_limit=1000, channel_timeout=10) + wsgi.server( + eventlet.listen((self._apt_settings.host, self._apt_settings.port)), + self, + log_output=False + ) + + def on_connect(self): + self._logger.info(__name__, f'Client connected') + + def on_disconnect(self): + self._logger.info(__name__, f'Client disconnected') diff --git a/kdb-web/package-lock.json b/kdb-web/package-lock.json index 46d080cf42..273efa031e 100644 --- a/kdb-web/package-lock.json +++ b/kdb-web/package-lock.json @@ -1,12 +1,12 @@ { "name": "kdb-web", - "version": "0.0.0", + "version": "0.3.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "kdb-web", - "version": "0.0.0", + "version": "0.3.0", "dependencies": { "@angular/animations": "^14.0.0", "@angular/common": "^14.0.0", @@ -20,9 +20,11 @@ "@microsoft/signalr": "^6.0.9", "@ngx-translate/core": "^14.0.0", "@ngx-translate/http-loader": "^7.0.0", + "@types/socket.io-client": "^3.0.0", "primeicons": "^6.0.1", "primeng": "^14.1.2", "rxjs": "~7.5.0", + "socket.io-client": "^4.5.3", "tslib": "^2.3.0", "zone.js": "~0.11.4" }, @@ -3080,8 +3082,7 @@ "node_modules/@socket.io/component-emitter": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", - "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", - "dev": true + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" }, "node_modules/@tootallnate/once": { "version": "1.1.2", @@ -3267,6 +3268,15 @@ "@types/node": "*" } }, + "node_modules/@types/socket.io-client": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/socket.io-client/-/socket.io-client-3.0.0.tgz", + "integrity": "sha512-s+IPvFoEIjKA3RdJz/Z2dGR4gLgysKi8owcnrVwNjgvc01Lk68LJDDsG2GRqegFITcxmvCMYM7bhMpwEMlHmDg==", + "deprecated": "This is a stub types definition. socket.io-client provides its own type definitions, so you do not need this installed.", + "dependencies": { + "socket.io-client": "*" + } + }, "node_modules/@types/sockjs": { "version": "0.3.33", "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", @@ -4902,7 +4912,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -5194,11 +5203,22 @@ "node": ">=10.0.0" } }, + "node_modules/engine.io-client": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.2.3.tgz", + "integrity": "sha512-aXPtgF1JS3RuuKcpSrBtimSjYvrbhKW9froICH4s0F3XQWLxsKNxqzG39nnvQZQnva4CMvUK63T7shevxRyYHw==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.2.3", + "xmlhttprequest-ssl": "~2.0.0" + } + }, "node_modules/engine.io-parser": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz", "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==", - "dev": true, "engines": { "node": ">=10.0.0" } @@ -8305,8 +8325,7 @@ "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/multicast-dns": { "version": "7.2.5", @@ -11018,11 +11037,24 @@ "integrity": "sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg==", "dev": true }, + "node_modules/socket.io-client": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.5.3.tgz", + "integrity": "sha512-I/hqDYpQ6JKwtJOf5ikM+Qz+YujZPMEl6qBLhxiP0nX+TfXKhW4KZZG8lamrD6Y5ngjmYHreESVasVCgi5Kl3A==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.2.3", + "socket.io-parser": "~4.2.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/socket.io-parser": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.1.tgz", "integrity": "sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==", - "dev": true, "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1" @@ -12416,7 +12448,6 @@ "version": "8.2.3", "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", - "dev": true, "engines": { "node": ">=10.0.0" }, @@ -12433,6 +12464,14 @@ } } }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -14566,8 +14605,7 @@ "@socket.io/component-emitter": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", - "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", - "dev": true + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" }, "@tootallnate/once": { "version": "1.1.2", @@ -14750,6 +14788,14 @@ "@types/node": "*" } }, + "@types/socket.io-client": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/socket.io-client/-/socket.io-client-3.0.0.tgz", + "integrity": "sha512-s+IPvFoEIjKA3RdJz/Z2dGR4gLgysKi8owcnrVwNjgvc01Lk68LJDDsG2GRqegFITcxmvCMYM7bhMpwEMlHmDg==", + "requires": { + "socket.io-client": "*" + } + }, "@types/sockjs": { "version": "0.3.33", "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", @@ -16001,7 +16047,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "requires": { "ms": "2.1.2" } @@ -16223,11 +16268,22 @@ "ws": "~8.2.3" } }, + "engine.io-client": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.2.3.tgz", + "integrity": "sha512-aXPtgF1JS3RuuKcpSrBtimSjYvrbhKW9froICH4s0F3XQWLxsKNxqzG39nnvQZQnva4CMvUK63T7shevxRyYHw==", + "requires": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.2.3", + "xmlhttprequest-ssl": "~2.0.0" + } + }, "engine.io-parser": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz", - "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==", - "dev": true + "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==" }, "enhanced-resolve": { "version": "5.10.0", @@ -18475,8 +18531,7 @@ "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "multicast-dns": { "version": "7.2.5", @@ -20407,11 +20462,21 @@ "integrity": "sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg==", "dev": true }, + "socket.io-client": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.5.3.tgz", + "integrity": "sha512-I/hqDYpQ6JKwtJOf5ikM+Qz+YujZPMEl6qBLhxiP0nX+TfXKhW4KZZG8lamrD6Y5ngjmYHreESVasVCgi5Kl3A==", + "requires": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.2.3", + "socket.io-parser": "~4.2.0" + } + }, "socket.io-parser": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.1.tgz", "integrity": "sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==", - "dev": true, "requires": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1" @@ -21424,9 +21489,13 @@ "version": "8.2.3", "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", - "dev": true, "requires": {} }, + "xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==" + }, "y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/kdb-web/package.json b/kdb-web/package.json index 5d0195d078..e38246960c 100644 --- a/kdb-web/package.json +++ b/kdb-web/package.json @@ -25,9 +25,11 @@ "@microsoft/signalr": "^6.0.9", "@ngx-translate/core": "^14.0.0", "@ngx-translate/http-loader": "^7.0.0", + "@types/socket.io-client": "^3.0.0", "primeicons": "^6.0.1", "primeng": "^14.1.2", "rxjs": "~7.5.0", + "socket.io-client": "^4.5.3", "tslib": "^2.3.0", "zone.js": "~0.11.4" }, diff --git a/kdb-web/src/app/app.component.ts b/kdb-web/src/app/app.component.ts index 90758e7d54..6d8046ff17 100644 --- a/kdb-web/src/app/app.component.ts +++ b/kdb-web/src/app/app.component.ts @@ -1,5 +1,6 @@ import { Component, OnInit } from '@angular/core'; import { AuthService } from './services/auth/auth.service'; +import { SocketService } from './services/socket/socket.service'; import { ThemeService } from './services/theme/theme.service'; @Component({ @@ -12,9 +13,11 @@ export class AppComponent implements OnInit { constructor( public authService: AuthService, public themeService: ThemeService, + private socket: SocketService ) { } ngOnInit(): void { + this.socket.startSocket(); this.themeService.loadTheme(); this.themeService.loadMenu(); } diff --git a/kdb-web/src/app/app.module.ts b/kdb-web/src/app/app.module.ts index c8ae9aed0a..bda8098f9d 100644 --- a/kdb-web/src/app/app.module.ts +++ b/kdb-web/src/app/app.module.ts @@ -9,6 +9,7 @@ import { ConfirmationService, MessageService } from 'primeng/api'; import { DialogService } from 'primeng/dynamicdialog'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; +import { NotFoundComponent } from './components/error/not-found/not-found.component'; import { FooterComponent } from './components/footer/footer.component'; import { HeaderComponent } from './components/header/header.component'; import { SidebarComponent } from './components/sidebar/sidebar.component'; @@ -16,7 +17,8 @@ import { SpinnerComponent } from './components/spinner/spinner.component'; import { SharedModule } from './modules/shared/shared.module'; import { ErrorHandlerService } from './services/error-handler/error-handler.service'; import { SettingsService } from './services/settings/settings.service'; -import { NotFoundComponent } from './components/error/not-found/not-found.component'; + + @NgModule({ declarations: [ diff --git a/kdb-web/src/app/services/socket/socket.service.spec.ts b/kdb-web/src/app/services/socket/socket.service.spec.ts new file mode 100644 index 0000000000..7ceaf59829 --- /dev/null +++ b/kdb-web/src/app/services/socket/socket.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { SocketService } from './socket.service'; + +describe('SocketService', () => { + let service: SocketService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(SocketService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/kdb-web/src/app/services/socket/socket.service.ts b/kdb-web/src/app/services/socket/socket.service.ts new file mode 100644 index 0000000000..4493587408 --- /dev/null +++ b/kdb-web/src/app/services/socket/socket.service.ts @@ -0,0 +1,71 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { ToastOptions } from 'src/app/models/utils/toast-options'; +import { SettingsService } from '../settings/settings.service'; +import { SpinnerService } from '../spinner/spinner.service'; +import { ToastService } from '../toast/toast.service'; +import io from "socket.io-client"; +import { MessageService } from 'primeng/api'; + +@Injectable({ + providedIn: 'root' +}) +export class SocketService { + private socket: any; + disconnected = false; + + constructor( + private settingsService: SettingsService, + private toastService: ToastService, + private spinnerService: SpinnerService, + private messageService: MessageService, + ) { + this.socket = io(`${settingsService.getApiURL()}`) + } + + startSocket() { + this.socket.on('connect', () => { + if (this.disconnected) { + if (this.spinnerService.showSpinnerState) { + this.spinnerService.hideSpinner(); + const options: ToastOptions = { + closable: false + }; + this.messageService.clear(); + this.toastService.info("Server verbunden", "Die Verbindung zum Server konnte hergestellt werden.", options); + } + } + + this.disconnected = false; + console.log('Connected!') + }); + + this.socket.on('connect_error', (err: Error) => { + if (this.disconnected) { + this.spinnerService.showSpinner(); + return; + } + + this.disconnected = true; + + const options: ToastOptions = { + sticky: true, + closable: false + }; + this.messageService.clear(); + this.toastService.error("Server nicht erreichbar", "Die Verbindung zum Server konnte nicht hergestellt werden!\nLaden Sie die Seite neu.", options); + console.error(err.toString()); + }); + + this.socket.on('disconnect', () => { + console.log('Disconnected!'); + const options: ToastOptions = { + sticky: true, + closable: false + }; + this.spinnerService.showSpinner(); + this.messageService.clear(); + this.toastService.error("Verbindung unterbrochen", "Die Verbindung zum Server konnte nicht hergestellt werden!\nLaden Sie die Seite neu.", options); + }); + } +} diff --git a/kdb-web/src/assets/config.json b/kdb-web/src/assets/config.json index 905b2d2f3c..f135341693 100644 --- a/kdb-web/src/assets/config.json +++ b/kdb-web/src/assets/config.json @@ -2,7 +2,7 @@ "ApiURL": "http://localhost:5000", "WebVersion": { "Major": "0", - "Minor": "3", + "Minor": "0", "Micro": "0" }, "Themes": [