diff --git a/client/stillbox/src/app/app.config.ts b/client/stillbox/src/app/app.config.ts
index 0014fed..cb21be6 100644
--- a/client/stillbox/src/app/app.config.ts
+++ b/client/stillbox/src/app/app.config.ts
@@ -29,7 +29,7 @@ export function authIntercept(
next: HttpHandlerFn,
): Observable
> {
let authSvc: AuthService = inject(AuthService);
- if (authSvc.loggedIn) {
+ if (authSvc.isAuth()) {
req = req.clone({
setHeaders: {
Authorization: `Bearer ${authSvc.getToken()}`,
diff --git a/client/stillbox/src/app/login/auth.service.ts b/client/stillbox/src/app/login/auth.service.ts
index 0ec7b97..46fc5df 100644
--- a/client/stillbox/src/app/login/auth.service.ts
+++ b/client/stillbox/src/app/login/auth.service.ts
@@ -1,56 +1,92 @@
-import { Injectable } from '@angular/core';
+import {
+ Injectable,
+ signal,
+ computed,
+ effect,
+ inject,
+ DestroyRef,
+} from '@angular/core';
import { HttpClient, HttpResponse } from '@angular/common/http';
import { Router } from '@angular/router';
-import { Observable } from 'rxjs';
+import { Observable, Subject } from 'rxjs';
import { tap } from 'rxjs/operators';
+import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
export class Jwt {
constructor(public jwt: string) {}
}
+type AuthState = {
+ user: string | null;
+ token: string | null;
+ is_auth: boolean;
+};
+
@Injectable({
providedIn: 'root',
})
export class AuthService {
- loggedIn: boolean = false;
+ private _accessTokenKey = 'jwt';
+ private _storedToken = localStorage.getItem(this._accessTokenKey);
+ destroyed = inject(DestroyRef);
+
+ private _state = signal({
+ user: null,
+ token: this._storedToken,
+ is_auth: this._storedToken !== null,
+ });
+ loginFailed = signal(false);
+ token = computed(() => this._state().token);
+ isAuth = computed(() => this._state().is_auth);
+ user = computed(() => this._state().user);
constructor(
private http: HttpClient,
private _router: Router,
) {
- let ssJWT = localStorage.getItem('jwt');
- if (ssJWT) {
- this.loggedIn = true;
- }
+ effect(() => {
+ const token = this.token();
+ if (token !== null) {
+ localStorage.setItem(this._accessTokenKey, token);
+ } else {
+ localStorage.removeItem(this._accessTokenKey);
+ }
+ });
}
- login(username: string, password: string): Observable> {
+ login(username: string, password: string) {
return this.http
- .post(
- '/api/login',
- { username: username, password: password },
- { observe: 'response' },
- )
- .pipe(
- tap((event) => {
- if (event.status == 200) {
- localStorage.setItem('jwt', event.body?.jwt.toString() ?? '');
- this.loggedIn = true;
- this._router.navigateByUrl('/home');
- }
- }),
- );
+ .post('/api/login', { username: username, password: password })
+ .pipe(takeUntilDestroyed(this.destroyed))
+ .subscribe({
+ next: (res) => {
+ let state = {
+ user: username,
+ token: res.jwt,
+ is_auth: true,
+ };
+ this._state.set(state);
+ this.loginFailed.update(() => false);
+ this._router.navigateByUrl('/');
+ },
+ error: (err) => {
+ this.loginFailed.update(() => true);
+ },
+ });
+ }
+
+ _clearState() {
+ this._state.set({});
}
logout() {
- this.http
- .get('/api/logout', { withCredentials: true, observe: 'response' })
- .subscribe((event) => {
- if (event.status == 200) {
- this.loggedIn = false;
- }
- });
- localStorage.removeItem('jwt');
- this.loggedIn = false;
+ this.http.get('/api/logout', { withCredentials: true }).subscribe({
+ next: (event) => {
+ this._clearState();
+ },
+ error: (err) => {
+ this._clearState();
+ },
+ });
this._router.navigateByUrl('/login');
}
@@ -60,14 +96,20 @@ export class AuthService {
.pipe(
tap((event) => {
if (event.status == 200) {
- localStorage.setItem('jwt', event.body?.jwt.toString() ?? '');
- this.loggedIn = true;
+ let ost = this._state();
+ let tok = event.body?.jwt.toString();
+ let state = {
+ user: ost.user,
+ token: tok ? tok : null,
+ is_auth: true,
+ };
+ this._state.set(state);
}
}),
);
}
getToken(): string | null {
- return localStorage.getItem('jwt');
+ return localStorage.getItem(this._accessTokenKey);
}
}
diff --git a/client/stillbox/src/app/login/login.component.html b/client/stillbox/src/app/login/login.component.html
index 293daed..1646e47 100644
--- a/client/stillbox/src/app/login/login.component.html
+++ b/client/stillbox/src/app/login/login.component.html
@@ -28,7 +28,7 @@
diff --git a/client/stillbox/src/app/login/login.component.ts b/client/stillbox/src/app/login/login.component.ts
index 5a8c5bc..a7adc76 100644
--- a/client/stillbox/src/app/login/login.component.ts
+++ b/client/stillbox/src/app/login/login.component.ts
@@ -1,4 +1,4 @@
-import { Component, inject } from '@angular/core';
+import { Component, inject, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { AuthService } from '../login/auth.service';
import { catchError, of, Subscription } from 'rxjs';
@@ -17,31 +17,9 @@ export class LoginComponent {
router: Router = inject(Router);
username: string = '';
password: string = '';
- failed: boolean = false;
- private subscriptions = new Subscription();
+ failed = this.apiService.loginFailed;
onSubmit() {
- this.failed = false;
- this.subscriptions.add(
- this.apiService
- .login(this.username, this.password)
- .pipe(
- catchError(() => {
- this.failed = true;
- return of(null);
- }),
- )
- .subscribe((event) => {
- if (event?.status == 200) {
- this.router.navigateByUrl('/');
- } else {
- this.failed = true;
- }
- }),
- );
- }
-
- ngOnDestroy() {
- this.subscriptions.unsubscribe();
+ this.apiService.login(this.username, this.password);
}
}
diff --git a/client/stillbox/src/app/talkgroups/talkgroups.service.ts b/client/stillbox/src/app/talkgroups/talkgroups.service.ts
index 1184952..fee62a3 100644
--- a/client/stillbox/src/app/talkgroups/talkgroups.service.ts
+++ b/client/stillbox/src/app/talkgroups/talkgroups.service.ts
@@ -51,7 +51,7 @@ export class TalkgroupService {
if (sh) {
this.shareSvc.getShare(sh).subscribe(this.fetchAll);
} else {
- if (this.authSvc.loggedIn) {
+ if (this.authSvc.isAuth()) {
this.fetchAll.next(null);
}
}
@@ -59,7 +59,9 @@ export class TalkgroupService {
}
setShare(share: Share | null) {
- this.fetchAll.next(share);
+ if (!this.authSvc.isAuth() && share !== null) {
+ this.fetchAll.next(share);
+ }
}
ngOnDestroy() {