Added top bar on all pages. Simplified TTS login component. Fixed some issues. Removed redirects for now.

This commit is contained in:
Tom
2025-04-01 21:12:01 +00:00
parent d44ec50a6a
commit 055885837c
30 changed files with 402 additions and 186 deletions

View File

@ -1,6 +1,5 @@
import { NgModule } from '@angular/core';
import { LoginComponent } from './login/login.component';
import { TtsLoginComponent } from './tts-login/tts-login.component';
import { ImpersonationComponent } from './impersonation/impersonation.component';
import { UserCardComponent } from './user-card/user-card.component';
@ -8,7 +7,6 @@ import { UserCardComponent } from './user-card/user-card.component';
declarations: [],
imports: [
LoginComponent,
TtsLoginComponent,
ImpersonationComponent,
UserCardComponent,
]

View File

@ -1,9 +1,9 @@
@if (isAdmin()) {
<main>
<mat-form-field>
<mat-form-field class="mat-small"
subscriptSizing="dynamic">
<mat-label>User to impersonate</mat-label>
<mat-select (selectionChange)="onChange($event)"
[(value)]="impersonated">
<mat-select [formControl]="impersonationControl">
<mat-option>{{getUsername()}}</mat-option>
@for (user of users; track user.id) {
<mat-option [value]="user.id">{{ user.name }}</mat-option>

View File

@ -2,5 +2,4 @@ main {
display: flex;
justify-content: center;
align-items: center;
margin-top: 1em;
}

View File

@ -8,56 +8,76 @@ import { environment } from '../../../environments/environment';
import EventService from '../../shared/services/EventService';
import { HermesClientService } from '../../hermes-client.service';
import { Router } from '@angular/router';
import { timeout, first } from 'rxjs';
import ApiKey from '../../shared/models/api-key';
import { ApiKeyService } from '../../shared/services/api/api-key.service';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { User } from '../../shared/models/user';
import { UserService } from '../../shared/services/user.service';
@Component({
selector: 'impersonation',
standalone: true,
imports: [MatCardModule, MatSelectModule],
imports: [
MatCardModule,
MatSelectModule,
ReactiveFormsModule,
],
templateUrl: './impersonation.component.html',
styleUrl: './impersonation.component.scss'
})
export class ImpersonationComponent implements OnInit {
private readonly keyService = inject(ApiKeyService);
private readonly events = inject(EventService);
private readonly userService = inject(UserService);
impersonated: string | undefined;
users: { id: string, name: string }[];
impersonationControl = new FormControl<string | undefined>(undefined);
users: User[];
constructor(private client: HermesClientService, private auth: ApiAuthenticationService, private router: Router, private events: EventService, private http: HttpClient, @Inject(PLATFORM_ID) private platformId: Object) {
this.users = []
constructor(private client: HermesClientService, private auth: ApiAuthenticationService, private router: Router, private http: HttpClient, @Inject(PLATFORM_ID) private platformId: Object) {
this.users = [];
}
ngOnInit(): void {
if (!isPlatformBrowser(this.platformId) || !this.auth.isAdmin()) {
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());
this.userService.fetch().subscribe(users => {
this.users = users.filter((d: any) => d.name != this.auth.getUsername());
const id = this.auth.getImpersonatedId();
if (id && this.users.find(u => u.id == id)) {
this.impersonated = id;
this.impersonationControl.setValue(id);
}
});
this.events.listen('impersonation', (userId) => {
const url = this.router.url;
this.client.first(d => d.op == 2 && !d.d.another_client)
.subscribe(async _ =>
await setTimeout(async () =>
await this.router.navigate([url.substring(1)]), 500));
this.keyService.fetch()
.pipe(timeout(3000), first())
.subscribe(async (d: ApiKey[]) => {
if (d.length > 0)
this.client.login(d[0].id);
this.impersonationControl.valueChanges.subscribe((impersonationId) => {
if (!this.auth.isAdmin() || impersonationId == this.auth.getImpersonatedId())
return;
if (!impersonationId) {
this.http.delete(environment.API_HOST + '/admin/impersonate', {
headers: {
'Authorization': 'Bearer ' + localStorage.getItem('jwt')
},
body: {
impersonation: impersonationId
}
}).subscribe(async (data: any) => {
this.impersonationControl.setValue(undefined);
this.client.disconnect(true);
this.events.emit('impersonation', undefined);
});
} else {
this.http.put(environment.API_HOST + '/admin/impersonate', {
impersonation: impersonationId
}, {
headers: {
'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']);
});
}
});
}
@ -68,35 +88,4 @@ export class ImpersonationComponent implements OnInit {
public getUsername() {
return this.auth.getUsername();
}
public onChange(e: any) {
if (!this.auth.isAdmin())
return;
if (!e.value) {
this.http.delete(environment.API_HOST + '/admin/impersonate', {
headers: {
'Authorization': 'Bearer ' + localStorage.getItem('jwt')
},
body: {
impersonation: e.value
}
}).subscribe(async (data: any) => {
this.client.disconnect();
this.events.emit('impersonation', e.value);
});
} else {
this.http.put(environment.API_HOST + '/admin/impersonate', {
impersonation: e.value
}, {
headers: {
'Authorization': 'Bearer ' + localStorage.getItem('jwt')
}
}).subscribe(async (data: any) => {
this.client.disconnect();
this.events.emit('impersonation', e.value);
await this.router.navigate(['tts-login']);
});
}
}
}

View File

@ -1,32 +1,15 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { Component } from '@angular/core';
import { MatCardModule } from '@angular/material/card';
import { Router, RouterModule } from '@angular/router';
import { Subscription } from 'rxjs';
import { environment } from '../../../environments/environment';
@Component({
selector: 'login',
standalone: true,
imports: [MatCardModule, RouterModule],
imports: [MatCardModule],
templateUrl: './login.component.html',
styleUrl: './login.component.scss'
})
export class LoginComponent implements OnInit, OnDestroy {
subscription: Subscription | null;
constructor(private router: Router) {
this.subscription = null;
}
ngOnInit(): void {
}
ngOnDestroy(): void {
if (this.subscription)
this.subscription.unsubscribe()
}
export class LoginComponent {
login() {
document.location.replace(environment.API_HOST + '/auth');
}

View File

@ -9,7 +9,7 @@
<mat-card-content class="content">
<mat-form-field>
<mat-label>API Key</mat-label>
<mat-select [(value)]="selected_api_key">
<mat-select [formControl]="keyControl">
@for (key of api_keys; track key.id) {
<mat-option [value]="key.id">{{key.label}}</mat-option>
}

View File

@ -1,58 +1,41 @@
import { Component, inject, OnDestroy, OnInit } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { MatInputModule } from '@angular/material/input';
import { Component, inject, OnInit } from '@angular/core';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { MatSelectModule } from '@angular/material/select';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatButtonModule } from '@angular/material/button';
import EventService from '../../shared/services/EventService';
import { ActivatedRoute } from '@angular/router';
import { first, Subscription, timeout } from 'rxjs';
import { HermesClientService } from '../../hermes-client.service';
import { MatCardModule } from '@angular/material/card';
import { ApiKeyService } from '../../shared/services/api/api-key.service';
@Component({
selector: 'tts-login',
standalone: true,
imports: [MatButtonModule, MatCardModule, MatFormFieldModule, MatSelectModule, MatInputModule, FormsModule],
imports: [
MatButtonModule,
MatCardModule,
MatFormFieldModule,
MatSelectModule,
ReactiveFormsModule,
],
templateUrl: './tts-login.component.html',
styleUrl: './tts-login.component.scss'
})
export class TtsLoginComponent implements OnInit, OnDestroy {
export class TtsLoginComponent implements OnInit {
private readonly client = inject(HermesClientService);
private readonly keyService = inject(ApiKeyService);
private readonly events = inject(EventService);
private readonly route = inject(ActivatedRoute);
keyControl = new FormControl<string | null>('');
api_keys: { id: string, label: string }[] = [];
selected_api_key: string | undefined;
private subscriptions: Subscription[] = [];
ngOnInit(): void {
this.route.data.subscribe(d => this.api_keys = d['keys']);
this.subscriptions.push(this.events.listen('tts_logoff', async _ => {
this.selected_api_key = undefined;
}));
this.subscriptions.push(this.events.listen('impersonation', _ => {
this.selected_api_key = undefined;
this.keyService.fetch()
.pipe(timeout(3000), first())
.subscribe(d => this.api_keys = d);
}));
}
ngOnDestroy(): void {
this.subscriptions.forEach(s => s.unsubscribe());
}
login(): void {
if (!this.selected_api_key)
if (!this.keyControl.value)
return;
this.client.login(this.selected_api_key);
this.client.login(this.keyControl.value);
}
}