Added impersonation. More data available via auth service about the user. Added admin auth guard.
This commit is contained in:
parent
65f4172bc2
commit
2bde8b850a
@ -3,7 +3,7 @@ import { Component, OnInit, Inject, PLATFORM_ID, NgZone, OnDestroy } from '@angu
|
|||||||
import { Router, RouterOutlet } from '@angular/router';
|
import { Router, RouterOutlet } from '@angular/router';
|
||||||
import { FormsModule } from '@angular/forms'
|
import { FormsModule } from '@angular/forms'
|
||||||
import { HermesClientService } from './hermes-client.service';
|
import { HermesClientService } from './hermes-client.service';
|
||||||
import { AuthGuard } from './shared/auth/auth.guard'
|
import { AuthUserGuard } from './shared/auth/auth.user.guard'
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
import { PolicyComponent } from "./policy/policy.component";
|
import { PolicyComponent } from "./policy/policy.component";
|
||||||
import { NavigationComponent } from "./navigation/navigation.component";
|
import { NavigationComponent } from "./navigation/navigation.component";
|
||||||
@ -14,7 +14,7 @@ import { ApiAuthenticationService } from './shared/services/api/api-authenticati
|
|||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [RouterOutlet, CommonModule, FormsModule, PolicyComponent, NavigationComponent],
|
imports: [RouterOutlet, CommonModule, FormsModule, PolicyComponent, NavigationComponent],
|
||||||
providers: [AuthGuard],
|
providers: [AuthUserGuard],
|
||||||
templateUrl: './app.component.html',
|
templateUrl: './app.component.html',
|
||||||
styleUrl: './app.component.scss'
|
styleUrl: './app.component.scss'
|
||||||
})
|
})
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Routes } from '@angular/router';
|
import { Routes } from '@angular/router';
|
||||||
import { PolicyComponent } from './policy/policy.component';
|
import { PolicyComponent } from './policy/policy.component';
|
||||||
import { AuthGuard } from './shared/auth/auth.guard';
|
import { AuthUserGuard } from './shared/auth/auth.user.guard';
|
||||||
import { LoginComponent } from './login/login.component';
|
import { LoginComponent } from './login/login.component';
|
||||||
import { TtsLoginComponent } from './tts-login/tts-login.component';
|
import { TtsLoginComponent } from './tts-login/tts-login.component';
|
||||||
import { TwitchAuthCallbackComponent } from './twitch-auth-callback/twitch-auth-callback.component';
|
import { TwitchAuthCallbackComponent } from './twitch-auth-callback/twitch-auth-callback.component';
|
||||||
@ -9,7 +9,7 @@ export const routes: Routes = [
|
|||||||
{
|
{
|
||||||
path: 'policies',
|
path: 'policies',
|
||||||
component: PolicyComponent,
|
component: PolicyComponent,
|
||||||
canActivate: [AuthGuard],
|
canActivate: [AuthUserGuard],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'login',
|
path: 'login',
|
||||||
@ -18,7 +18,7 @@ export const routes: Routes = [
|
|||||||
{
|
{
|
||||||
path: 'tts-login',
|
path: 'tts-login',
|
||||||
component: TtsLoginComponent,
|
component: TtsLoginComponent,
|
||||||
canActivate: [AuthGuard],
|
canActivate: [AuthUserGuard],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'auth',
|
path: 'auth',
|
||||||
|
@ -42,8 +42,9 @@ export class HermesClientService {
|
|||||||
if (!this.connected)
|
if (!this.connected)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.socket.close();
|
|
||||||
this.connected = false;
|
this.connected = false;
|
||||||
|
this.logged_in = false;
|
||||||
|
this.socket.close();
|
||||||
this.events.emit('tts_logoff', null);
|
this.events.emit('tts_logoff', null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,6 +59,8 @@ export class HermesClientService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public login(api_key: string) {
|
public login(api_key: string) {
|
||||||
|
if (!this.connected)
|
||||||
|
this.connect();
|
||||||
if (this.logged_in)
|
if (this.logged_in)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
19
src/app/impersonation/impersonation.component.html
Normal file
19
src/app/impersonation/impersonation.component.html
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
@if (isAdmin()) {
|
||||||
|
<mat-card appearance="outlined">
|
||||||
|
<mat-card-header>
|
||||||
|
<mat-card-title> Impersonation</mat-card-title>
|
||||||
|
<mat-card-subtitle>Impersonate as another user</mat-card-subtitle>
|
||||||
|
</mat-card-header>
|
||||||
|
<mat-card-actions>
|
||||||
|
<mat-form-field>
|
||||||
|
<mat-label>User to impersonate</mat-label>
|
||||||
|
<mat-select (selectionChange)="onChange($event)" [(value)]="impersonated">
|
||||||
|
<mat-option>{{getUsername()}}</mat-option>
|
||||||
|
@for (user of users; track user) {
|
||||||
|
<mat-option [value]="user.id">{{ user.name }}</mat-option>
|
||||||
|
}
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
</mat-card-actions>
|
||||||
|
</mat-card>
|
||||||
|
}
|
0
src/app/impersonation/impersonation.component.scss
Normal file
0
src/app/impersonation/impersonation.component.scss
Normal file
23
src/app/impersonation/impersonation.component.spec.ts
Normal file
23
src/app/impersonation/impersonation.component.spec.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ImpersonationComponent } from './impersonation.component';
|
||||||
|
|
||||||
|
describe('ImpersonationComponent', () => {
|
||||||
|
let component: ImpersonationComponent;
|
||||||
|
let fixture: ComponentFixture<ImpersonationComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [ImpersonationComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ImpersonationComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
82
src/app/impersonation/impersonation.component.ts
Normal file
82
src/app/impersonation/impersonation.component.ts
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
import { Component, Inject, OnInit, PLATFORM_ID } from '@angular/core';
|
||||||
|
import { ApiAuthenticationService } from '../shared/services/api/api-authentication.service';
|
||||||
|
import { MatCardModule } from '@angular/material/card';
|
||||||
|
import { MatSelectModule } from '@angular/material/select';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { isPlatformBrowser } from '@angular/common';
|
||||||
|
import { environment } from '../../environments/environment';
|
||||||
|
import EventService from '../shared/services/EventService';
|
||||||
|
import { HermesClientService } from '../hermes-client.service';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'impersonation',
|
||||||
|
standalone: true,
|
||||||
|
imports: [MatCardModule, MatSelectModule],
|
||||||
|
templateUrl: './impersonation.component.html',
|
||||||
|
styleUrl: './impersonation.component.scss'
|
||||||
|
})
|
||||||
|
export class ImpersonationComponent implements OnInit {
|
||||||
|
impersonated: string | undefined;
|
||||||
|
users: { id: string, name: string }[];
|
||||||
|
|
||||||
|
constructor(private hermes: HermesClientService, private auth: ApiAuthenticationService, private router: Router, private events: EventService, private http: HttpClient, @Inject(PLATFORM_ID) private platformId: Object) {
|
||||||
|
this.users = []
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
if (!isPlatformBrowser(this.platformId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.http.get(environment.API_HOST + '/admin/users', {
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Bearer ' + localStorage.getItem('jwt')
|
||||||
|
}
|
||||||
|
}).subscribe((data: any) => {
|
||||||
|
this.users = data.filter((d: any) => d.name != this.auth.getUsername());
|
||||||
|
const id = this.auth.getImpersonatedId();
|
||||||
|
if (this.users.find(u => u.id == id)) {
|
||||||
|
this.impersonated = id;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public isAdmin() {
|
||||||
|
return this.auth.isAdmin();
|
||||||
|
}
|
||||||
|
|
||||||
|
public getUsername() {
|
||||||
|
return this.auth.getUsername();
|
||||||
|
}
|
||||||
|
|
||||||
|
public onChange(e: any) {
|
||||||
|
console.log('impersonate befre', e.value);
|
||||||
|
if (!e.value) {
|
||||||
|
this.http.delete(environment.API_HOST + '/admin/impersonate', {
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Bearer ' + localStorage.getItem('jwt')
|
||||||
|
},
|
||||||
|
body: {
|
||||||
|
impersonation: e.value
|
||||||
|
}
|
||||||
|
}).subscribe((data: any) => {
|
||||||
|
this.hermes.disconnect();
|
||||||
|
this.events.emit('impersonation', e.value);
|
||||||
|
this.router.navigate(['/tts-login']);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.http.put(environment.API_HOST + '/admin/impersonate', {
|
||||||
|
impersonation: e.value
|
||||||
|
}, {
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Bearer ' + localStorage.getItem('jwt')
|
||||||
|
}
|
||||||
|
}).subscribe((data: any) => {
|
||||||
|
this.hermes.disconnect();
|
||||||
|
this.events.emit('impersonation', e.value);
|
||||||
|
this.router.navigate(['/tts-login']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,11 @@
|
|||||||
<nav>
|
<nav>
|
||||||
|
<impersonation />
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<a
|
<a
|
||||||
routerLink="/login"
|
routerLink="/login"
|
||||||
routerLinkActive="active"
|
routerLinkActive="active"
|
||||||
*ngIf="!twitch_logged_in">
|
*ngIf="!isLoggedIn()">
|
||||||
Login
|
Login
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
@ -12,7 +13,7 @@
|
|||||||
<a
|
<a
|
||||||
routerLink="/tts-login"
|
routerLink="/tts-login"
|
||||||
routerLinkActive="active"
|
routerLinkActive="active"
|
||||||
*ngIf="twitch_logged_in && !tts_logged_in">
|
*ngIf="isLoggedIn() && !isTTSLoggedIn()">
|
||||||
TTS Login
|
TTS Login
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
@ -20,7 +21,7 @@
|
|||||||
<a
|
<a
|
||||||
routerLink="/policies"
|
routerLink="/policies"
|
||||||
routerLinkActive="active"
|
routerLinkActive="active"
|
||||||
*ngIf="twitch_logged_in && tts_logged_in">
|
*ngIf="isLoggedIn() && isTTSLoggedIn()">
|
||||||
Policies
|
Policies
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -3,22 +3,28 @@ import { RouterModule } from '@angular/router';
|
|||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { HermesClientService } from '../hermes-client.service';
|
import { HermesClientService } from '../hermes-client.service';
|
||||||
import { ApiAuthenticationService } from '../shared/services/api/api-authentication.service';
|
import { ApiAuthenticationService } from '../shared/services/api/api-authentication.service';
|
||||||
|
import { ImpersonationComponent } from '../impersonation/impersonation.component';
|
||||||
|
import { MatCardModule } from '@angular/material/card';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'navigation',
|
selector: 'navigation',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [CommonModule, RouterModule],
|
imports: [CommonModule, RouterModule, ImpersonationComponent, MatCardModule],
|
||||||
templateUrl: './navigation.component.html',
|
templateUrl: './navigation.component.html',
|
||||||
styleUrl: './navigation.component.scss'
|
styleUrl: './navigation.component.scss'
|
||||||
})
|
})
|
||||||
export class NavigationComponent {
|
export class NavigationComponent {
|
||||||
constructor(private auth: ApiAuthenticationService, private hermes: HermesClientService) { }
|
constructor(private auth: ApiAuthenticationService, private hermes: HermesClientService) { }
|
||||||
|
|
||||||
get twitch_logged_in() {
|
isLoggedIn() {
|
||||||
return this.auth.isAuthenticated();
|
return this.auth.isAuthenticated();
|
||||||
}
|
}
|
||||||
|
|
||||||
get tts_logged_in() {
|
isAdmin() {
|
||||||
|
return this.isLoggedIn() && this.auth.isAdmin()
|
||||||
|
}
|
||||||
|
|
||||||
|
isTTSLoggedIn() {
|
||||||
return this.hermes?.logged_in ?? false;
|
return this.hermes?.logged_in ?? false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,6 @@ export class PolicyTableComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
} else if (response.request.type == "get_permissions") {
|
} else if (response.request.type == "get_permissions") {
|
||||||
this.groups = Object.assign({}, ...response.data.groups.map((g: any) => ({ [g.id]: g })));
|
this.groups = Object.assign({}, ...response.data.groups.map((g: any) => ({ [g.id]: g })));
|
||||||
console.log(this.groups);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.hermes.fetchPolicies();
|
this.hermes.fetchPolicies();
|
||||||
|
@ -5,17 +5,11 @@ import { ApiAuthenticationService } from '../services/api/api-authentication.ser
|
|||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class AuthGuard implements CanActivate {
|
export class AuthAdminGuard implements CanActivate {
|
||||||
|
|
||||||
constructor(private auth: ApiAuthenticationService, private router: Router) {}
|
constructor(private auth: ApiAuthenticationService, private router: Router) {}
|
||||||
|
|
||||||
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
|
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
|
||||||
if (this.auth.isAuthenticated()) {
|
return this.auth.isAuthenticated() && this.auth.isAdmin();
|
||||||
console.log('Valid OAuth');
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("Invalid OAuth");
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
15
src/app/shared/auth/auth.user.guard.ts
Normal file
15
src/app/shared/auth/auth.user.guard.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
|
||||||
|
import { ApiAuthenticationService } from '../services/api/api-authentication.service';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class AuthUserGuard implements CanActivate {
|
||||||
|
|
||||||
|
constructor(private auth: ApiAuthenticationService, private router: Router) { }
|
||||||
|
|
||||||
|
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
|
||||||
|
return this.auth.isAuthenticated();
|
||||||
|
}
|
||||||
|
}
|
@ -7,10 +7,12 @@ import EventService from '../EventService';
|
|||||||
})
|
})
|
||||||
export class ApiAuthenticationService {
|
export class ApiAuthenticationService {
|
||||||
private authenticated: boolean;
|
private authenticated: boolean;
|
||||||
|
private user: any;
|
||||||
private lastCheck: Date;
|
private lastCheck: Date;
|
||||||
|
|
||||||
constructor(private http: HttpClient, private events: EventService) {
|
constructor(private http: HttpClient, private events: EventService) {
|
||||||
this.authenticated = false;
|
this.authenticated = false;
|
||||||
|
this.user = null;
|
||||||
this.lastCheck = new Date();
|
this.lastCheck = new Date();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -18,31 +20,44 @@ export class ApiAuthenticationService {
|
|||||||
return this.authenticated;
|
return this.authenticated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isAdmin() {
|
||||||
|
return this.isAuthenticated() && this.user.role == 'ADMIN';
|
||||||
|
}
|
||||||
|
|
||||||
|
getImpersonatedId() {
|
||||||
|
return this.user.impersonation.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
getUsername() {
|
||||||
|
return this.user.name;
|
||||||
|
}
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
const jwt = localStorage.getItem('jwt');
|
const jwt = localStorage.getItem('jwt');
|
||||||
if (!jwt) {
|
if (!jwt) {
|
||||||
this.updateAuthenticated(false);
|
this.updateAuthenticated(false, null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// /api/auth/jwt
|
// /api/auth/validate
|
||||||
this.http.get('/api/auth/jwt', {
|
this.http.get('/api/auth/validate', {
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': 'Bearer ' + jwt
|
'Authorization': 'Bearer ' + jwt
|
||||||
}
|
}
|
||||||
}).subscribe((data: any) => {
|
}).subscribe((data: any) => {
|
||||||
console.log('jwt validation', data);
|
console.log('jwt validation', data);
|
||||||
this.updateAuthenticated(data?.authenticated);
|
this.updateAuthenticated(data?.authenticated, data?.user);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateAuthenticated(value: boolean) {
|
private updateAuthenticated(authenticated: boolean, user: any) {
|
||||||
const previous = this.authenticated;
|
const previous = this.authenticated;
|
||||||
this.authenticated = value;
|
this.authenticated = authenticated;
|
||||||
|
this.user = user;
|
||||||
this.lastCheck = new Date();
|
this.lastCheck = new Date();
|
||||||
|
|
||||||
if (previous != value) {
|
if (previous != authenticated) {
|
||||||
if (value) {
|
if (authenticated) {
|
||||||
this.events.emit('login', null);
|
this.events.emit('login', null);
|
||||||
} else {
|
} else {
|
||||||
this.events.emit('logoff', null);
|
this.events.emit('logoff', null);
|
||||||
|
@ -4,8 +4,8 @@
|
|||||||
<mat-label>API Key</mat-label>
|
<mat-label>API Key</mat-label>
|
||||||
<mat-select
|
<mat-select
|
||||||
[(value)]="selected_api_key">
|
[(value)]="selected_api_key">
|
||||||
@for (key of api_keys; track key) {
|
@for (key of api_keys; track key.id) {
|
||||||
<mat-option [value]="key">{{key}}</mat-option>
|
<mat-option [value]="key.id">{{key.label}}</mat-option>
|
||||||
}
|
}
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
@ -9,6 +9,7 @@ import { HttpClient } from '@angular/common/http';
|
|||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
import { environment } from '../../environments/environment';
|
import { environment } from '../../environments/environment';
|
||||||
|
import { HermesClientService } from '../hermes-client.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'tts-login',
|
selector: 'tts-login',
|
||||||
@ -18,12 +19,12 @@ import { environment } from '../../environments/environment';
|
|||||||
styleUrl: './tts-login.component.scss'
|
styleUrl: './tts-login.component.scss'
|
||||||
})
|
})
|
||||||
export class TtsLoginComponent implements OnInit, OnDestroy {
|
export class TtsLoginComponent implements OnInit, OnDestroy {
|
||||||
api_keys: string[];
|
api_keys: { id: string, label: string }[];
|
||||||
selected_api_key: string|undefined;
|
selected_api_key: string|undefined;
|
||||||
|
|
||||||
private subscription: Subscription|undefined;
|
private subscription: Subscription|undefined;
|
||||||
|
|
||||||
constructor(private events: EventService, private http: HttpClient, private router: Router) {
|
constructor(private hermes: HermesClientService, private events: EventService, private http: HttpClient, private router: Router) {
|
||||||
this.api_keys = [];
|
this.api_keys = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,13 +33,25 @@ export class TtsLoginComponent implements OnInit, OnDestroy {
|
|||||||
headers: {
|
headers: {
|
||||||
'Authorization': 'Bearer ' + localStorage.getItem('jwt')
|
'Authorization': 'Bearer ' + localStorage.getItem('jwt')
|
||||||
}
|
}
|
||||||
}).subscribe((data: any) => this.api_keys = data.map((d: any) => d.id));
|
}).subscribe((data: any) => this.api_keys = data);
|
||||||
|
|
||||||
this.subscription = this.events.listen('tts_login_ack', _ => {
|
this.subscription = this.events.listen('tts_login_ack', _ => {
|
||||||
if (document.location.href.includes('/tts-login')) {
|
if (document.location.href.includes('/tts-login')) {
|
||||||
this.router.navigate(['/policies'])
|
this.router.navigate(['/policies'])
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
this.events.listen('tts_logoff', _ => {
|
||||||
|
this.selected_api_key = undefined;
|
||||||
|
});
|
||||||
|
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);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
@ -47,9 +60,10 @@ export class TtsLoginComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
login() {
|
login() {
|
||||||
|
console.log('api key for login', this.selected_api_key)
|
||||||
if (!this.selected_api_key)
|
if (!this.selected_api_key)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.events.emit('tts_login', this.selected_api_key);
|
this.hermes.login(this.selected_api_key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user