From 6ee99466f856f5a3db6f821ba361041bec361b58 Mon Sep 17 00:00:00 2001 From: Tom Date: Wed, 15 Jan 2025 20:19:44 +0000 Subject: [PATCH] Used resoler + service for API keys and TTS login page. --- src/app/app.routes.ts | 4 ++ src/app/auth/tts-login/tts-login.component.ts | 48 +++++++++---------- src/app/shared/models/api-key.ts | 4 ++ src/app/shared/resolvers/api-key-resolver.ts | 14 ++++++ .../resolvers/twitch-redemption-resolver.ts | 2 +- .../services/api/api-key.service.spec.ts | 16 +++++++ .../shared/services/api/api-key.service.ts | 40 ++++++++++++++++ 7 files changed, 101 insertions(+), 27 deletions(-) create mode 100644 src/app/shared/models/api-key.ts create mode 100644 src/app/shared/resolvers/api-key-resolver.ts create mode 100644 src/app/shared/services/api/api-key.service.spec.ts create mode 100644 src/app/shared/services/api/api-key.service.ts diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index 604000a..b674b9e 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -13,6 +13,7 @@ import RedemptionResolver from './shared/resolvers/redemption-resolver'; import TwitchRedemptionResolver from './shared/resolvers/twitch-redemption-resolver'; import RedeemableActionResolver from './shared/resolvers/redeemable-action-resolver'; import TtsFilterResolver from './shared/resolvers/tts-filter-resolver'; +import ApiKeyResolver from './shared/resolvers/api-key-resolver'; export const routes: Routes = [ { @@ -55,6 +56,9 @@ export const routes: Routes = [ path: 'tts-login', component: TtsLoginComponent, canActivate: [AuthUserGuard], + resolve: { + keys: ApiKeyResolver, + } }, { path: 'auth', diff --git a/src/app/auth/tts-login/tts-login.component.ts b/src/app/auth/tts-login/tts-login.component.ts index 4829f93..9e56c2f 100644 --- a/src/app/auth/tts-login/tts-login.component.ts +++ b/src/app/auth/tts-login/tts-login.component.ts @@ -1,4 +1,4 @@ -import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Component, inject, OnDestroy, OnInit } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatInputModule } from '@angular/material/input'; import { MatSelectModule } from '@angular/material/select'; @@ -6,11 +6,11 @@ import { MatFormFieldModule } from '@angular/material/form-field'; import { MatButtonModule } from '@angular/material/button'; import EventService from '../../shared/services/EventService'; import { HttpClient } from '@angular/common/http'; -import { Router } from '@angular/router'; -import { Subscription } from 'rxjs'; -import { environment } from '../../../environments/environment'; +import { ActivatedRoute, Router } from '@angular/router'; +import { first, Subscription, timeout } from 'rxjs'; import { HermesClientService } from '../../hermes-client.service'; -import { MatCard, MatCardModule } from '@angular/material/card'; +import { MatCardModule } from '@angular/material/card'; +import { ApiKeyService } from '../../shared/services/api/api-key.service'; @Component({ selector: 'tts-login', @@ -20,43 +20,39 @@ import { MatCard, MatCardModule } from '@angular/material/card'; styleUrl: './tts-login.component.scss' }) export class TtsLoginComponent implements OnInit, OnDestroy { - api_keys: { id: string, label: string }[]; + keyService = inject(ApiKeyService); + route = inject(ActivatedRoute); + + api_keys: { id: string, label: string }[] = []; selected_api_key: string | undefined; - private subscription: Subscription | undefined; + private subscriptions: Subscription[] = []; constructor(private hermes: HermesClientService, private events: EventService, private http: HttpClient, private router: Router) { - this.api_keys = []; } ngOnInit(): void { - this.http.get(environment.API_HOST + '/keys', { - headers: { - 'Authorization': 'Bearer ' + localStorage.getItem('jwt') - } - }).subscribe((data: any) => this.api_keys = data); + this.route.data.subscribe(d => this.api_keys = d['keys']); - this.subscription = this.events.listen('tts_login_ack', async _ => { + this.subscriptions.push(this.events.listen('tts_login_ack', async _ => { await this.router.navigate(['policies']) - }); - this.events.listen('tts_logoff', async _ => { + })); + this.subscriptions.push(this.events.listen('tts_logoff', async _ => { this.selected_api_key = undefined; await this.router.navigate(['tts-login']) - }); - this.events.listen('impersonation', _ => { + })); + this.subscriptions.push(this.events.listen('impersonation', _ => { this.selected_api_key = undefined; - this.http.get(environment.API_HOST + '/keys', { - headers: { - 'Authorization': 'Bearer ' + localStorage.getItem('jwt') - } - }).subscribe((data: any) => this.api_keys = data); - }); + this.keyService.fetch(true) + .pipe(timeout(3000), first()) + .subscribe(d => this.api_keys = d); + })); } ngOnDestroy(): void { - if (this.subscription) - this.subscription.unsubscribe(); + if (!this.hermes.logged_in) + this.subscriptions.forEach(s => s.unsubscribe()); } login() { diff --git a/src/app/shared/models/api-key.ts b/src/app/shared/models/api-key.ts new file mode 100644 index 0000000..ca7d9b5 --- /dev/null +++ b/src/app/shared/models/api-key.ts @@ -0,0 +1,4 @@ +export default interface ApiKey { + id: string; + label: string; +} \ No newline at end of file diff --git a/src/app/shared/resolvers/api-key-resolver.ts b/src/app/shared/resolvers/api-key-resolver.ts new file mode 100644 index 0000000..372fbad --- /dev/null +++ b/src/app/shared/resolvers/api-key-resolver.ts @@ -0,0 +1,14 @@ +import { Injectable } from '@angular/core'; +import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; +import { Observable } from 'rxjs'; +import ApiKey from '../models/api-key'; +import { ApiKeyService } from '../services/api/api-key.service'; + +@Injectable({ providedIn: 'root' }) +export default class ApiKeyResolver implements Resolve { + constructor(private service: ApiKeyService) { } + + resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { + return this.service.fetch(); + } +} \ No newline at end of file diff --git a/src/app/shared/resolvers/twitch-redemption-resolver.ts b/src/app/shared/resolvers/twitch-redemption-resolver.ts index f1af95a..abdc07c 100644 --- a/src/app/shared/resolvers/twitch-redemption-resolver.ts +++ b/src/app/shared/resolvers/twitch-redemption-resolver.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; -import { Observable, of } from 'rxjs'; +import { Observable } from 'rxjs'; import { TwitchRedemptionService } from '../services/twitch-redemption.service'; import TwitchRedemption from '../models/twitch-redemption'; diff --git a/src/app/shared/services/api/api-key.service.spec.ts b/src/app/shared/services/api/api-key.service.spec.ts new file mode 100644 index 0000000..12305a4 --- /dev/null +++ b/src/app/shared/services/api/api-key.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { ApiKeyService } from './api-key.service'; + +describe('KeyService', () => { + let service: ApiKeyService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(ApiKeyService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/shared/services/api/api-key.service.ts b/src/app/shared/services/api/api-key.service.ts new file mode 100644 index 0000000..96a4c0d --- /dev/null +++ b/src/app/shared/services/api/api-key.service.ts @@ -0,0 +1,40 @@ +import { HttpClient } from '@angular/common/http'; +import { inject, Injectable } from '@angular/core'; +import { of } from 'rxjs'; +import { environment } from '../../../../environments/environment'; +import EventService from '../EventService'; +import ApiKey from '../../models/api-key'; + +@Injectable({ + providedIn: 'root' +}) +export class ApiKeyService { + private readonly http = inject(HttpClient); + private readonly events = inject(EventService); + private keys: ApiKey[] = []; + private loaded = false; + + + constructor() { + this.events.listen('logoff', () => { + this.keys = []; + this.loaded = false; + }); + } + + fetch(force: boolean = false) { + if (!force && this.loaded) + return of(this.keys); + + const $ = this.http.get(environment.API_HOST + '/keys', { + headers: { + 'Authorization': 'Bearer ' + localStorage.getItem('jwt'), + } + }); + $.subscribe(d => { + this.keys = d; + this.loaded = true; + }); + return $; + } +}