From 3e9a9f9dc59e19275bb88fd24157db684cee879e Mon Sep 17 00:00:00 2001 From: Tom Date: Sat, 5 Apr 2025 02:06:31 +0000 Subject: [PATCH] Fixed issues with impersonation. Show warning or error depending on connection's remaining time before expiry. Replaced use of /api/... in urls. --- angular.json | 6 +- package-lock.json | 10 ++++ package.json | 4 +- src/app/app.component.ts | 28 +++++---- .../impersonation/impersonation.component.ts | 4 +- .../auth/tts-login/tts-login.component.html | 1 + src/app/auth/tts-login/tts-login.component.ts | 12 +++- .../connection-item-edit.component.ts | 3 +- .../connection-item.component.html | 8 +++ .../connection-item.component.ts | 18 +++++- src/app/hermes-socket.service.ts | 21 +++---- .../key-item-edit/key-item-edit.component.ts | 3 +- src/app/keys/key-item/key-item.component.ts | 3 +- .../navigation/topbar/topbar.component.html | 2 +- src/app/navigation/topbar/topbar.component.ts | 2 +- .../api/api-authentication.service.ts | 15 +++-- .../twitch-auth-callback.component.ts | 57 +++++++++++-------- .../twitch-user-item-add.component.ts | 3 +- 18 files changed, 129 insertions(+), 71 deletions(-) diff --git a/angular.json b/angular.json index ebda60c..aa7bf73 100644 --- a/angular.json +++ b/angular.json @@ -47,8 +47,8 @@ "budgets": [ { "type": "initial", - "maximumWarning": "1024kB", - "maximumError": "1MB" + "maximumWarning": "3MB", + "maximumError": "5MB" }, { "type": "anyComponentStyle", @@ -93,7 +93,7 @@ }, "defaultConfiguration": "development", "options": { - "allowedHosts": ["*"] + "allowedHosts": ["beta.tomtospeech.com"] } }, "extract-i18n": { diff --git a/package-lock.json b/package-lock.json index ad45e80..e1654d8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "@angular/ssr": "^19.2.5", "angular-oauth2-oidc": "^17.0.2", "express": "^4.18.2", + "moment": "^2.30.1", "ngx-socket-io": "^4.7.0", "rxjs": "~7.8.0", "rxjs-websockets": "^9.0.0", @@ -10344,6 +10345,15 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/mrmime": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", diff --git a/package.json b/package.json index 7bc7710..bba845c 100644 --- a/package.json +++ b/package.json @@ -2,8 +2,7 @@ "name": "hermes-web-angular", "version": "0.0.0", "scripts": { - "ng": "ng", - "start": "ng serve -c production --host 0.0.0.0 --watch false", + "start": "ng serve -c development --host 0.0.0.0 --watch false", "build": "ng build", "watch": "ng serve -c development --host 0.0.0.0 --disable-host-check", "test": "ng test", @@ -25,6 +24,7 @@ "@angular/ssr": "^19.2.5", "angular-oauth2-oidc": "^17.0.2", "express": "^4.18.2", + "moment": "^2.30.1", "ngx-socket-io": "^4.7.0", "rxjs": "~7.8.0", "rxjs-websockets": "^9.0.0", diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 4a78ae4..c87102d 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -8,7 +8,6 @@ import EventService from './shared/services/EventService'; import { ApiAuthenticationService } from './shared/services/api/api-authentication.service'; import { AuthModule } from './auth/auth.module'; import { ApiKeyService } from './shared/services/api/api-key.service'; -import ApiKey from './shared/models/api-key'; import { ThemeService } from './shared/services/theme.service'; import { OverlayContainer } from '@angular/cdk/overlay'; import { MatIconModule } from '@angular/material/icon'; @@ -16,6 +15,7 @@ import { MatToolbarModule } from '@angular/material/toolbar'; import { MatButtonModule } from '@angular/material/button'; import { SidebarComponent } from "./navigation/sidebar/sidebar.component"; import { Topbar as TopbarComponent } from "./navigation/topbar/topbar.component"; +import ApiKey from './shared/models/api-key'; @Component({ selector: 'app-root', @@ -38,7 +38,6 @@ export class AppComponent implements OnInit, OnDestroy { private readonly overlayContainer = inject(OverlayContainer); private readonly themeService = inject(ThemeService); - private isBrowser: boolean; private ngZone: NgZone; private subscriptions: Subscription[]; @@ -57,7 +56,6 @@ export class AppComponent implements OnInit, OnDestroy { constructor(private auth: ApiAuthenticationService, private client: HermesClientService, private events: EventService, private router: Router, ngZone: NgZone, @Inject(PLATFORM_ID) private platformId: Object) { this.ngZone = ngZone; - this.isBrowser = isPlatformBrowser(this.platformId); this.subscriptions = []; this.subscriptions.push(this.events.listen('tts_login_ack', async _ => { @@ -72,16 +70,27 @@ export class AppComponent implements OnInit, OnDestroy { } })); + this.addSubscription(this.events.listen('login', () => { + this.keyService.fetch() + .pipe(timeout(3000), first()) + .subscribe(async (d: ApiKey[]) => { + if (d.length > 0) + this.client.login(d[0].id); + }); + })); + this.subscriptions.push(this.events.listen('tts_logoff', async _ => await this.router.navigate(['tts-login']))); this.subscriptions.push(this.events.listen('toggle_sidebar', () => this.isSidebarOpen = !this.isSidebarOpen)) } ngOnInit(): void { - if (!this.isBrowser) + if (!isPlatformBrowser(this.platformId)) return; this.auth.update(); + this.subscriptions.push(this.events.listen('login', async () => await this.router.navigate(['tts-login']))); + this.addSubscription(this.events.listen('logoff', async (message) => { localStorage.removeItem('jwt'); if (!document.location.href.includes('/login')) { @@ -94,17 +103,6 @@ export class AppComponent implements OnInit, OnDestroy { } })); - this.addSubscription(this.events.listen('login', () => { - this.keyService.fetch() - .pipe(timeout(3000), first()) - .subscribe(async (d: ApiKey[]) => { - if (d.length > 0) - this.client.login(d[0].id); - else if (['/login', '/auth'].some(partial => document.location.href.includes(partial))) - await this.router.navigate(['tts-login']); - }); - })); - let currentTheme = localStorage.getItem('ui-theme') ?? this.themeService.theme; if (currentTheme == 'light' || currentTheme == 'dark') { this.themeService.theme = currentTheme; diff --git a/src/app/auth/impersonation/impersonation.component.ts b/src/app/auth/impersonation/impersonation.component.ts index c069259..256233f 100644 --- a/src/app/auth/impersonation/impersonation.component.ts +++ b/src/app/auth/impersonation/impersonation.component.ts @@ -60,9 +60,8 @@ export class ImpersonationComponent implements OnInit { impersonation: impersonationId } }).subscribe(async (data: any) => { - this.impersonationControl.setValue(undefined); this.client.disconnect(true); - this.events.emit('impersonation', undefined); + this.events.emit('impersonation', impersonationId); }); } else { this.http.put(environment.API_HOST + '/admin/impersonate', { @@ -72,7 +71,6 @@ export class ImpersonationComponent implements OnInit { 'Authorization': 'Bearer ' + localStorage.getItem('jwt') } }).subscribe(async (data: any) => { - this.impersonationControl.setValue(impersonationId); this.client.disconnect(true); this.events.emit('impersonation', impersonationId); await this.router.navigate(['tts-login']); diff --git a/src/app/auth/tts-login/tts-login.component.html b/src/app/auth/tts-login/tts-login.component.html index 19c2b0a..5531a6f 100644 --- a/src/app/auth/tts-login/tts-login.component.html +++ b/src/app/auth/tts-login/tts-login.component.html @@ -18,6 +18,7 @@ diff --git a/src/app/auth/tts-login/tts-login.component.ts b/src/app/auth/tts-login/tts-login.component.ts index 38c3ab3..c0e9565 100644 --- a/src/app/auth/tts-login/tts-login.component.ts +++ b/src/app/auth/tts-login/tts-login.component.ts @@ -32,13 +32,17 @@ export class TtsLoginComponent implements OnInit, OnDestroy { keyControl = new FormControl(''); api_keys: { id: string, label: string }[] = []; subscriptions: (Subscription | null)[] = []; + disabled: boolean = false; ngOnInit(): void { this.route.data.subscribe(d => this.api_keys = d['keys']); this.subscriptions.push(this.eventService.listen('impersonation', _ => this.reset())); - this.subscriptions.push(this.eventService.listen('logoff', _ => this.reset())); + this.subscriptions.push(this.eventService.listen('logoff', impersonation => { + if (!impersonation) + this.reset(); + })); } ngOnDestroy(): void { @@ -57,7 +61,11 @@ export class TtsLoginComponent implements OnInit, OnDestroy { } private reset() { + this.disabled = true; this.api_keys = []; - this.keyService.fetch().subscribe(keys => this.api_keys = keys); + this.keyService.fetch().subscribe(keys => { + this.api_keys = keys; + this.disabled = false; + }); } } diff --git a/src/app/connections/connection-item-edit/connection-item-edit.component.ts b/src/app/connections/connection-item-edit/connection-item-edit.component.ts index 6023a7b..d890cb9 100644 --- a/src/app/connections/connection-item-edit/connection-item-edit.component.ts +++ b/src/app/connections/connection-item-edit/connection-item-edit.component.ts @@ -9,6 +9,7 @@ import { MatIconModule } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; import { MatSelectModule } from '@angular/material/select'; import { DOCUMENT } from '@angular/common'; +import { environment } from '../../../environments/environment'; @Component({ selector: 'connection-item-edit', @@ -54,7 +55,7 @@ export class ConnectionItemEditComponent { return; } - this.http.post('/api/auth/connections', { + this.http.post(environment.API_HOST + '/auth/connections', { name: this.nameControl.value, type: this.typeControl.value, client_id: this.clientIdControl.value, diff --git a/src/app/connections/connection-item/connection-item.component.html b/src/app/connections/connection-item/connection-item.component.html index e3e49c9..bf26c6c 100644 --- a/src/app/connections/connection-item/connection-item.component.html +++ b/src/app/connections/connection-item/connection-item.component.html @@ -2,6 +2,14 @@ [class.nightbot]="connection().type == 'nightbot'"> {{connection().name}} + @if (isExpired) { + error + } @else if (isExpiringSoon) { + warning + } +
Tom-to-Speech - @if (isLoggedIn) { + @if (isLoggedIn) {