Fixed issues with impersonation. Show warning or error depending on connection's remaining time before expiry. Replaced use of /api/... in urls.
This commit is contained in:
parent
70e0e9bf71
commit
3e9a9f9dc5
@ -47,8 +47,8 @@
|
|||||||
"budgets": [
|
"budgets": [
|
||||||
{
|
{
|
||||||
"type": "initial",
|
"type": "initial",
|
||||||
"maximumWarning": "1024kB",
|
"maximumWarning": "3MB",
|
||||||
"maximumError": "1MB"
|
"maximumError": "5MB"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "anyComponentStyle",
|
"type": "anyComponentStyle",
|
||||||
@ -93,7 +93,7 @@
|
|||||||
},
|
},
|
||||||
"defaultConfiguration": "development",
|
"defaultConfiguration": "development",
|
||||||
"options": {
|
"options": {
|
||||||
"allowedHosts": ["*"]
|
"allowedHosts": ["beta.tomtospeech.com"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"extract-i18n": {
|
"extract-i18n": {
|
||||||
|
10
package-lock.json
generated
10
package-lock.json
generated
@ -22,6 +22,7 @@
|
|||||||
"@angular/ssr": "^19.2.5",
|
"@angular/ssr": "^19.2.5",
|
||||||
"angular-oauth2-oidc": "^17.0.2",
|
"angular-oauth2-oidc": "^17.0.2",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
|
"moment": "^2.30.1",
|
||||||
"ngx-socket-io": "^4.7.0",
|
"ngx-socket-io": "^4.7.0",
|
||||||
"rxjs": "~7.8.0",
|
"rxjs": "~7.8.0",
|
||||||
"rxjs-websockets": "^9.0.0",
|
"rxjs-websockets": "^9.0.0",
|
||||||
@ -10344,6 +10345,15 @@
|
|||||||
"mkdirp": "bin/cmd.js"
|
"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": {
|
"node_modules/mrmime": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
|
||||||
|
@ -2,8 +2,7 @@
|
|||||||
"name": "hermes-web-angular",
|
"name": "hermes-web-angular",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"ng": "ng",
|
"start": "ng serve -c development --host 0.0.0.0 --watch false",
|
||||||
"start": "ng serve -c production --host 0.0.0.0 --watch false",
|
|
||||||
"build": "ng build",
|
"build": "ng build",
|
||||||
"watch": "ng serve -c development --host 0.0.0.0 --disable-host-check",
|
"watch": "ng serve -c development --host 0.0.0.0 --disable-host-check",
|
||||||
"test": "ng test",
|
"test": "ng test",
|
||||||
@ -25,6 +24,7 @@
|
|||||||
"@angular/ssr": "^19.2.5",
|
"@angular/ssr": "^19.2.5",
|
||||||
"angular-oauth2-oidc": "^17.0.2",
|
"angular-oauth2-oidc": "^17.0.2",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
|
"moment": "^2.30.1",
|
||||||
"ngx-socket-io": "^4.7.0",
|
"ngx-socket-io": "^4.7.0",
|
||||||
"rxjs": "~7.8.0",
|
"rxjs": "~7.8.0",
|
||||||
"rxjs-websockets": "^9.0.0",
|
"rxjs-websockets": "^9.0.0",
|
||||||
|
@ -8,7 +8,6 @@ import EventService from './shared/services/EventService';
|
|||||||
import { ApiAuthenticationService } from './shared/services/api/api-authentication.service';
|
import { ApiAuthenticationService } from './shared/services/api/api-authentication.service';
|
||||||
import { AuthModule } from './auth/auth.module';
|
import { AuthModule } from './auth/auth.module';
|
||||||
import { ApiKeyService } from './shared/services/api/api-key.service';
|
import { ApiKeyService } from './shared/services/api/api-key.service';
|
||||||
import ApiKey from './shared/models/api-key';
|
|
||||||
import { ThemeService } from './shared/services/theme.service';
|
import { ThemeService } from './shared/services/theme.service';
|
||||||
import { OverlayContainer } from '@angular/cdk/overlay';
|
import { OverlayContainer } from '@angular/cdk/overlay';
|
||||||
import { MatIconModule } from '@angular/material/icon';
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
@ -16,6 +15,7 @@ import { MatToolbarModule } from '@angular/material/toolbar';
|
|||||||
import { MatButtonModule } from '@angular/material/button';
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
import { SidebarComponent } from "./navigation/sidebar/sidebar.component";
|
import { SidebarComponent } from "./navigation/sidebar/sidebar.component";
|
||||||
import { Topbar as TopbarComponent } from "./navigation/topbar/topbar.component";
|
import { Topbar as TopbarComponent } from "./navigation/topbar/topbar.component";
|
||||||
|
import ApiKey from './shared/models/api-key';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
@ -38,7 +38,6 @@ export class AppComponent implements OnInit, OnDestroy {
|
|||||||
private readonly overlayContainer = inject(OverlayContainer);
|
private readonly overlayContainer = inject(OverlayContainer);
|
||||||
private readonly themeService = inject(ThemeService);
|
private readonly themeService = inject(ThemeService);
|
||||||
|
|
||||||
private isBrowser: boolean;
|
|
||||||
private ngZone: NgZone;
|
private ngZone: NgZone;
|
||||||
private subscriptions: Subscription[];
|
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) {
|
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.ngZone = ngZone;
|
||||||
this.isBrowser = isPlatformBrowser(this.platformId);
|
|
||||||
this.subscriptions = [];
|
this.subscriptions = [];
|
||||||
|
|
||||||
this.subscriptions.push(this.events.listen('tts_login_ack', async _ => {
|
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('tts_logoff', async _ => await this.router.navigate(['tts-login'])));
|
||||||
this.subscriptions.push(this.events.listen('toggle_sidebar', () => this.isSidebarOpen = !this.isSidebarOpen))
|
this.subscriptions.push(this.events.listen('toggle_sidebar', () => this.isSidebarOpen = !this.isSidebarOpen))
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
if (!this.isBrowser)
|
if (!isPlatformBrowser(this.platformId))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.auth.update();
|
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) => {
|
this.addSubscription(this.events.listen('logoff', async (message) => {
|
||||||
localStorage.removeItem('jwt');
|
localStorage.removeItem('jwt');
|
||||||
if (!document.location.href.includes('/login')) {
|
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;
|
let currentTheme = localStorage.getItem('ui-theme') ?? this.themeService.theme;
|
||||||
if (currentTheme == 'light' || currentTheme == 'dark') {
|
if (currentTheme == 'light' || currentTheme == 'dark') {
|
||||||
this.themeService.theme = currentTheme;
|
this.themeService.theme = currentTheme;
|
||||||
|
@ -60,9 +60,8 @@ export class ImpersonationComponent implements OnInit {
|
|||||||
impersonation: impersonationId
|
impersonation: impersonationId
|
||||||
}
|
}
|
||||||
}).subscribe(async (data: any) => {
|
}).subscribe(async (data: any) => {
|
||||||
this.impersonationControl.setValue(undefined);
|
|
||||||
this.client.disconnect(true);
|
this.client.disconnect(true);
|
||||||
this.events.emit('impersonation', undefined);
|
this.events.emit('impersonation', impersonationId);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.http.put(environment.API_HOST + '/admin/impersonate', {
|
this.http.put(environment.API_HOST + '/admin/impersonate', {
|
||||||
@ -72,7 +71,6 @@ export class ImpersonationComponent implements OnInit {
|
|||||||
'Authorization': 'Bearer ' + localStorage.getItem('jwt')
|
'Authorization': 'Bearer ' + localStorage.getItem('jwt')
|
||||||
}
|
}
|
||||||
}).subscribe(async (data: any) => {
|
}).subscribe(async (data: any) => {
|
||||||
this.impersonationControl.setValue(impersonationId);
|
|
||||||
this.client.disconnect(true);
|
this.client.disconnect(true);
|
||||||
this.events.emit('impersonation', impersonationId);
|
this.events.emit('impersonation', impersonationId);
|
||||||
await this.router.navigate(['tts-login']);
|
await this.router.navigate(['tts-login']);
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
<mat-card-actions>
|
<mat-card-actions>
|
||||||
<button mat-raised-button
|
<button mat-raised-button
|
||||||
|
[disabled]="disabled"
|
||||||
(click)="login()">Log In</button>
|
(click)="login()">Log In</button>
|
||||||
</mat-card-actions>
|
</mat-card-actions>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
|
@ -32,13 +32,17 @@ export class TtsLoginComponent implements OnInit, OnDestroy {
|
|||||||
keyControl = new FormControl<string | null>('');
|
keyControl = new FormControl<string | null>('');
|
||||||
api_keys: { id: string, label: string }[] = [];
|
api_keys: { id: string, label: string }[] = [];
|
||||||
subscriptions: (Subscription | null)[] = [];
|
subscriptions: (Subscription | null)[] = [];
|
||||||
|
disabled: boolean = false;
|
||||||
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.route.data.subscribe(d => this.api_keys = d['keys']);
|
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('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 {
|
ngOnDestroy(): void {
|
||||||
@ -57,7 +61,11 @@ export class TtsLoginComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private reset() {
|
private reset() {
|
||||||
|
this.disabled = true;
|
||||||
this.api_keys = [];
|
this.api_keys = [];
|
||||||
this.keyService.fetch().subscribe(keys => this.api_keys = keys);
|
this.keyService.fetch().subscribe(keys => {
|
||||||
|
this.api_keys = keys;
|
||||||
|
this.disabled = false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import { MatIconModule } from '@angular/material/icon';
|
|||||||
import { MatInputModule } from '@angular/material/input';
|
import { MatInputModule } from '@angular/material/input';
|
||||||
import { MatSelectModule } from '@angular/material/select';
|
import { MatSelectModule } from '@angular/material/select';
|
||||||
import { DOCUMENT } from '@angular/common';
|
import { DOCUMENT } from '@angular/common';
|
||||||
|
import { environment } from '../../../environments/environment';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'connection-item-edit',
|
selector: 'connection-item-edit',
|
||||||
@ -54,7 +55,7 @@ export class ConnectionItemEditComponent {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.http.post('/api/auth/connections', {
|
this.http.post(environment.API_HOST + '/auth/connections', {
|
||||||
name: this.nameControl.value,
|
name: this.nameControl.value,
|
||||||
type: this.typeControl.value,
|
type: this.typeControl.value,
|
||||||
client_id: this.clientIdControl.value,
|
client_id: this.clientIdControl.value,
|
||||||
|
@ -2,6 +2,14 @@
|
|||||||
[class.nightbot]="connection().type == 'nightbot'">
|
[class.nightbot]="connection().type == 'nightbot'">
|
||||||
{{connection().name}}
|
{{connection().name}}
|
||||||
|
|
||||||
|
@if (isExpired) {
|
||||||
|
<mat-icon matTooltip="Connection has expired."
|
||||||
|
class="danger">error</mat-icon>
|
||||||
|
} @else if (isExpiringSoon) {
|
||||||
|
<mat-icon matTooltip="Connection is soon going to expire."
|
||||||
|
class="warning">warning</mat-icon>
|
||||||
|
}
|
||||||
|
|
||||||
<article class="right">
|
<article class="right">
|
||||||
<button mat-button
|
<button mat-button
|
||||||
class="neutral"
|
class="neutral"
|
||||||
|
@ -3,10 +3,13 @@ import { Connection } from '../../shared/models/connection';
|
|||||||
import { MatButtonModule } from '@angular/material/button';
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
import { MatIconModule } from '@angular/material/icon';
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
|
import {MatTooltipModule} from '@angular/material/tooltip';
|
||||||
import { ReactiveFormsModule } from '@angular/forms';
|
import { ReactiveFormsModule } from '@angular/forms';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { DOCUMENT } from '@angular/common';
|
import { DOCUMENT } from '@angular/common';
|
||||||
import { HermesClientService } from '../../hermes-client.service';
|
import { HermesClientService } from '../../hermes-client.service';
|
||||||
|
import { environment } from '../../../environments/environment';
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'connection-item',
|
selector: 'connection-item',
|
||||||
@ -14,6 +17,7 @@ import { HermesClientService } from '../../hermes-client.service';
|
|||||||
MatButtonModule,
|
MatButtonModule,
|
||||||
MatIconModule,
|
MatIconModule,
|
||||||
MatFormFieldModule,
|
MatFormFieldModule,
|
||||||
|
MatTooltipModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
],
|
],
|
||||||
templateUrl: './connection-item.component.html',
|
templateUrl: './connection-item.component.html',
|
||||||
@ -27,13 +31,25 @@ export class ConnectionItemComponent {
|
|||||||
|
|
||||||
constructor(@Inject(DOCUMENT) private document: Document) { }
|
constructor(@Inject(DOCUMENT) private document: Document) { }
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
console.log('coonnn', this.connection())
|
||||||
|
}
|
||||||
|
|
||||||
delete() {
|
delete() {
|
||||||
this.client.deleteConnection(this.connection().name);
|
this.client.deleteConnection(this.connection().name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isExpired() {
|
||||||
|
return moment(this.connection().expires_at).toDate().getTime() < new Date().getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
get isExpiringSoon() {
|
||||||
|
return moment(this.connection().expires_at).toDate().getTime() < moment.now() + moment.duration(7, 'd').asMilliseconds();
|
||||||
|
}
|
||||||
|
|
||||||
renew() {
|
renew() {
|
||||||
const conn = this.connection();
|
const conn = this.connection();
|
||||||
this.http.post('/api/auth/connections', {
|
this.http.post(environment.API_HOST + '/auth/connections', {
|
||||||
name: conn.name,
|
name: conn.name,
|
||||||
type: conn.type,
|
type: conn.type,
|
||||||
client_id: conn.client_id,
|
client_id: conn.client_id,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { OnInit, Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { webSocket, WebSocketSubject } from 'rxjs/webSocket';
|
import { webSocket, WebSocketSubject } from 'rxjs/webSocket';
|
||||||
import { catchError, first, timeout } from 'rxjs/operators';
|
import { catchError, first, timeout } from 'rxjs/operators';
|
||||||
import { environment } from '../environments/environment';
|
import { environment } from '../environments/environment';
|
||||||
@ -8,13 +8,9 @@ import { EMPTY, Observable, Observer, throwError } from 'rxjs';
|
|||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class HermesSocketService implements OnInit {
|
export class HermesSocketService {
|
||||||
private socket: WebSocketSubject<any> | undefined = undefined
|
private socket: WebSocketSubject<any> | undefined = undefined
|
||||||
|
|
||||||
constructor() { }
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
}
|
|
||||||
|
|
||||||
public connect(): void {
|
public connect(): void {
|
||||||
if (!this.socket || this.socket.closed) {
|
if (!this.socket || this.socket.closed) {
|
||||||
@ -22,9 +18,10 @@ export class HermesSocketService implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public first(predicate: (data: any) => boolean): Observable<any> {
|
public first<T>(predicate: (data: T) => boolean): Observable<T> {
|
||||||
if (!this.socket || this.socket.closed)
|
if (!this.socket || this.socket.closed) {
|
||||||
return new Observable().pipe(timeout(3000), catchError((e) => throwError(() => 'No response after 3 seconds.')));
|
throw new Error('Socket is ' + (this.socket ? 'closed' : 'null') + '.');
|
||||||
|
}
|
||||||
|
|
||||||
return this.socket.pipe(timeout(3000), catchError((e) => throwError(() => 'No response after 3 seconds.')), first(predicate));
|
return this.socket.pipe(timeout(3000), catchError((e) => throwError(() => 'No response after 3 seconds.')), first(predicate));
|
||||||
}
|
}
|
||||||
@ -43,7 +40,11 @@ export class HermesSocketService implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public get$(): Observable<any> | undefined {
|
public get$(): Observable<any> | undefined {
|
||||||
return this.socket?.asObservable().pipe(catchError(_ => EMPTY));
|
if (!this.socket || this.socket.closed) {
|
||||||
|
throw new Error('Socket is ' + (this.socket ? 'closed' : 'null') + '.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.socket.asObservable().pipe(catchError(_ => EMPTY));
|
||||||
}
|
}
|
||||||
|
|
||||||
public subscribe(subscriptions: Partial<Observer<any>> | ((value: any) => void)) {
|
public subscribe(subscriptions: Partial<Observer<any>> | ((value: any) => void)) {
|
||||||
|
@ -9,6 +9,7 @@ import { MatIconModule } from '@angular/material/icon';
|
|||||||
import { MatInputModule } from '@angular/material/input';
|
import { MatInputModule } from '@angular/material/input';
|
||||||
import { MatSelectModule } from '@angular/material/select';
|
import { MatSelectModule } from '@angular/material/select';
|
||||||
import EventService from '../../shared/services/EventService';
|
import EventService from '../../shared/services/EventService';
|
||||||
|
import { environment } from '../../../environments/environment';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'key-item-edit',
|
selector: 'key-item-edit',
|
||||||
@ -50,7 +51,7 @@ export class KeyItemEditComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const label = this.labelControl.value;
|
const label = this.labelControl.value;
|
||||||
this.http.post('/api/keys', { label },
|
this.http.post(environment.API_HOST + '/keys', { label },
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': 'Bearer ' + localStorage.getItem('jwt')
|
'Authorization': 'Bearer ' + localStorage.getItem('jwt')
|
||||||
|
@ -6,6 +6,7 @@ import { ReactiveFormsModule } from '@angular/forms';
|
|||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import ApiKey from '../../shared/models/api-key';
|
import ApiKey from '../../shared/models/api-key';
|
||||||
import EventService from '../../shared/services/EventService';
|
import EventService from '../../shared/services/EventService';
|
||||||
|
import { environment } from '../../../environments/environment';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'key-item',
|
selector: 'key-item',
|
||||||
@ -32,7 +33,7 @@ export class KeyItemComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const key_id = this.key().id;
|
const key_id = this.key().id;
|
||||||
this.http.delete('/api/keys',
|
this.http.delete(environment.API_HOST + '/keys',
|
||||||
{
|
{
|
||||||
body: {
|
body: {
|
||||||
key: key_id,
|
key: key_id,
|
||||||
|
@ -5,8 +5,8 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<span>Tom-to-Speech</span>
|
<span>Tom-to-Speech</span>
|
||||||
@if (isLoggedIn) {
|
|
||||||
<span class="spacer"></span>
|
<span class="spacer"></span>
|
||||||
|
@if (isLoggedIn) {
|
||||||
<div class="links">
|
<div class="links">
|
||||||
@if (showImpersonation) {
|
@if (showImpersonation) {
|
||||||
<impersonation />
|
<impersonation />
|
||||||
|
@ -51,7 +51,7 @@ export class Topbar implements OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get isAdminLoggedIn() {
|
get isAdminLoggedIn() {
|
||||||
return this.auth.isAuthenticated() && this.auth.isAdmin();
|
return this.auth.isAdmin();
|
||||||
}
|
}
|
||||||
|
|
||||||
get username() {
|
get username() {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import EventService from '../EventService';
|
import EventService from '../EventService';
|
||||||
|
import { environment } from '../../../../environments/environment';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@ -50,26 +51,28 @@ export class ApiAuthenticationService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// /api/auth/validate
|
this.http.get(environment.API_HOST + '/auth/validate', {
|
||||||
this.http.get('/api/auth/validate', {
|
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': 'Bearer ' + jwt
|
'Authorization': 'Bearer ' + jwt
|
||||||
}
|
},
|
||||||
}).subscribe((data: any) => {
|
withCredentials: true
|
||||||
this.updateAuthenticated(data?.authenticated, data?.user);
|
}).subscribe({
|
||||||
|
next: (data: any) => this.updateAuthenticated(data?.authenticated, data?.user),
|
||||||
|
error: () => this.updateAuthenticated(false, null)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateAuthenticated(authenticated: boolean, user: any) {
|
private updateAuthenticated(authenticated: boolean, user: any) {
|
||||||
const previous = this.authenticated;
|
const previous = this.authenticated;
|
||||||
this.authenticated = authenticated;
|
|
||||||
this.user = user;
|
this.user = user;
|
||||||
|
this.authenticated = authenticated;
|
||||||
this.lastCheck = new Date();
|
this.lastCheck = new Date();
|
||||||
|
|
||||||
if (previous != authenticated) {
|
if (previous != authenticated) {
|
||||||
if (authenticated) {
|
if (authenticated) {
|
||||||
this.events.emit('login', null);
|
this.events.emit('login', null);
|
||||||
} else {
|
} else {
|
||||||
|
localStorage.removeItem('jwt');
|
||||||
this.events.emit('logoff', null);
|
this.events.emit('logoff', null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import { isPlatformBrowser } from '@angular/common';
|
import { isPlatformBrowser } from '@angular/common';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { Component, Inject, OnInit, PLATFORM_ID } from '@angular/core';
|
import { Component, inject, Inject, OnDestroy, OnInit, PLATFORM_ID } from '@angular/core';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { ApiAuthenticationService } from '../shared/services/api/api-authentication.service';
|
import { ApiAuthenticationService } from '../shared/services/api/api-authentication.service';
|
||||||
import { environment } from '../../environments/environment';
|
import { environment } from '../../environments/environment';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-twitch-auth-callback',
|
selector: 'app-twitch-auth-callback',
|
||||||
@ -12,20 +13,26 @@ import { environment } from '../../environments/environment';
|
|||||||
templateUrl: './twitch-auth-callback.component.html',
|
templateUrl: './twitch-auth-callback.component.html',
|
||||||
styleUrl: './twitch-auth-callback.component.scss'
|
styleUrl: './twitch-auth-callback.component.scss'
|
||||||
})
|
})
|
||||||
export class TwitchAuthCallbackComponent implements OnInit {
|
export class TwitchAuthCallbackComponent implements OnInit, OnDestroy {
|
||||||
private isBrowser: boolean;
|
private readonly auth = inject(ApiAuthenticationService);
|
||||||
|
private readonly http = inject(HttpClient);
|
||||||
|
private readonly route = inject(ActivatedRoute);
|
||||||
|
private readonly router = inject(Router);
|
||||||
|
private readonly subscriptions: (Subscription | null)[] = [];
|
||||||
|
|
||||||
constructor(private http: HttpClient, private auth: ApiAuthenticationService, private route: ActivatedRoute, private router: Router, @Inject(PLATFORM_ID) private platformId: Object) {
|
constructor(@Inject(PLATFORM_ID) private platformId: Object) { }
|
||||||
this.isBrowser = isPlatformBrowser(this.platformId)
|
|
||||||
}
|
|
||||||
|
|
||||||
async ngOnInit(): Promise<any> {
|
async ngOnInit(): Promise<any> {
|
||||||
if (!this.isBrowser) {
|
if (!isPlatformBrowser(this.platformId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.auth.isAuthenticated()) {
|
if (!this.auth.isAuthenticated() && localStorage.getItem('jwt')) {
|
||||||
await this.router.navigate(['tts-login']);
|
localStorage.removeItem('jwt');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.auth.isAuthenticated() || localStorage.getItem('jwt')) {
|
||||||
|
await this.router.navigate(['policies']);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,20 +50,24 @@ export class TwitchAuthCallbackComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.http.post(environment.API_HOST + '/auth/twitch/callback', { code, scope, state })
|
this.http.post(environment.API_HOST + '/auth/twitch/callback', { code, scope, state })
|
||||||
.subscribe(async (response: any) => {
|
.subscribe({
|
||||||
if (!response?.authenticated) {
|
next: async (response: any) => {
|
||||||
await this.router.navigate(['login'], {
|
console.log('twitch api callback response:', response);
|
||||||
queryParams: {
|
localStorage.setItem('jwt', response.token);
|
||||||
error: 'callback_issue'
|
this.auth.update();
|
||||||
}
|
},
|
||||||
});
|
error: async () => await this.router.navigate(['login'], {
|
||||||
return;
|
queryParams: {
|
||||||
}
|
error: 'callback_issue_twitch'
|
||||||
|
}
|
||||||
localStorage.setItem('jwt', response.token);
|
}),
|
||||||
this.auth.update();
|
|
||||||
|
|
||||||
await this.router.navigate(['tts-login']);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
for (let subscription of this.subscriptions) {
|
||||||
|
if (subscription)
|
||||||
|
subscription.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import { Group } from '../../shared/models/group';
|
|||||||
import { MatInputModule } from '@angular/material/input';
|
import { MatInputModule } from '@angular/material/input';
|
||||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
import { MatIconModule } from '@angular/material/icon';
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { environment } from '../../../environments/environment';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-twitch-user-item-add',
|
selector: 'app-twitch-user-item-add',
|
||||||
@ -49,7 +50,7 @@ export class TwitchUserItemAddComponent implements OnInit {
|
|||||||
this.responseError = undefined;
|
this.responseError = undefined;
|
||||||
|
|
||||||
const username = this.usernameControl.value!.toLowerCase();
|
const username = this.usernameControl.value!.toLowerCase();
|
||||||
this.http.get('/api/auth/twitch/users?login=' + username, {
|
this.http.get(environment.API_HOST + '/auth/twitch/users?login=' + username, {
|
||||||
headers: {
|
headers: {
|
||||||
'x-api-key': this.client.api_key,
|
'x-api-key': this.client.api_key,
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user