diff --git a/src/app/auth/auth.component.ts b/src/app/auth/auth.component.ts index 575a07502..3f80d692e 100644 --- a/src/app/auth/auth.component.ts +++ b/src/app/auth/auth.component.ts @@ -1,28 +1,27 @@ -import { - AuthResponseData, - AuthService -} from '../core/_services/access/auth.service'; -import { environment } from './../../environments/environment'; -import { Component, Inject, OnInit } from '@angular/core'; -import { Router } from '@angular/router'; -import { NgForm } from '@angular/forms'; import { Observable } from 'rxjs'; -import { LocalStorageService } from '../core/_services/storage/local-storage.service'; -import { UnsubscribeService } from '../core/_services/unsubscribe.service'; -import { ConfigService } from '../core/_services/shared/config.service'; -import { UISettingsUtilityClass } from '../shared/utils/config'; -import { UIConfig } from '../core/_models/config-ui.model'; +import { Component, OnInit } from '@angular/core'; +import { OnDestroy } from '@angular/core'; import { FormGroup } from '@angular/forms'; import { FormControl } from '@angular/forms'; import { Validators } from '@angular/forms'; -import { OnDestroy } from '@angular/core'; -import { NgZone } from '@angular/core'; + +import { UIConfig } from '@models/config-ui.model'; + +import { AuthResponseData, AuthService } from '@services/access/auth.service'; +import { AlertService } from '@services/shared/alert.service'; +import { ConfigService } from '@services/shared/config.service'; +import { LocalStorageService } from '@services/storage/local-storage.service'; +import { UnsubscribeService } from '@services/unsubscribe.service'; + +import { UISettingsUtilityClass } from '@src/app/shared/utils/config'; +import { environment } from '@src/environments/environment'; +import { HeaderConfig } from '@src/config/default/app/config.model'; @Component({ - selector: 'app-login', - templateUrl: './auth.component.html', - standalone: false + selector: 'app-login', + templateUrl: './auth.component.html', + standalone: false }) export class AuthComponent implements OnInit, OnDestroy { /** Form group for the new SuperHashlist. */ @@ -32,7 +31,7 @@ export class AuthComponent implements OnInit, OnDestroy { protected uiSettings: UISettingsUtilityClass; public isVisible = true; - headerConfig: any; + headerConfig: HeaderConfig; isDarkMode = false; /** on loggin loading */ @@ -43,8 +42,7 @@ export class AuthComponent implements OnInit, OnDestroy { private storage: LocalStorageService, private configService: ConfigService, private authService: AuthService, - private ngZone: NgZone, - private router: Router + private alertService: AlertService ) { this.headerConfig = environment.config.header; this.uiSettings = new UISettingsUtilityClass(this.storage); @@ -77,17 +75,14 @@ export class AuthComponent implements OnInit, OnDestroy { */ buildForm(): void { this.loginForm = new FormGroup({ - username: new FormControl(null, [ - Validators.required, - Validators.minLength(2) - ]), - password: new FormControl(null, [ - Validators.required, - Validators.minLength(3) - ]) + username: new FormControl(null, [Validators.required, Validators.minLength(2)]), + password: new FormControl(null, [Validators.required, Validators.minLength(3)]) }); } + /** + * Handle login, when user submits the login form. + */ onSubmit() { if (this.loginForm.invalid) { return; @@ -97,43 +92,32 @@ export class AuthComponent implements OnInit, OnDestroy { this.isLoading = true; // Show spinner - let authObs: Observable; - authObs = this.authService.logIn(username, password); + const authObs: Observable = this.authService.logIn(username, password); - const authSubscription$ = authObs.subscribe( - (resData) => { - this.handleSuccessfulLogin(resData); + const authSubscription$ = authObs.subscribe({ + next: () => { this.loginForm.reset(); }, - (error) => { + error: () => { this.isLoading = false; this.handleError('An error occurred. Please try again later.'); }, - () => { + complete: () => { this.isLoading = false; // Hide spinner after attempting to log in } - ); + }); this.unsubscribeService.add(authSubscription$); } - private handleSuccessfulLogin(resData: AuthResponseData): void { - if (this.authService.redirectUrl) { - const redirectUrl = this.authService.redirectUrl; - this.authService.redirectUrl = ''; - this.authService.setUserLoggedIn(true); - this.router.navigate([redirectUrl]); - } else { - this.router.navigate(['/']); - } - } - + /** + * Show and log specified error message + * @param errorMessage Message to log/display + * @private + */ private handleError(errorMessage: string): void { this.errorRes = errorMessage; console.log(this.errorRes); - } - - onHandleError() { - this.errorRes = null; + this.alertService.showErrorMessage(errorMessage); } } diff --git a/src/app/core/_services/access/auth.service.ts b/src/app/core/_services/access/auth.service.ts index b6705d268..b01dc199d 100644 --- a/src/app/core/_services/access/auth.service.ts +++ b/src/app/core/_services/access/auth.service.ts @@ -12,6 +12,7 @@ import { AuthData, AuthUser } from '@models/auth-user.model'; import { PermissionService } from '@services/permission/permission.service'; import { ConfigService } from '@services/shared/config.service'; import { LocalStorageService } from '@services/storage/local-storage.service'; +import { LoginRedirectService } from '@services/access/login-redirect.service'; export interface AuthResponseData { token: string; @@ -93,6 +94,12 @@ export class AuthService { this.userAuthChanged(true); const permissionService = this.injector.get(PermissionService); return permissionService.loadPermissions(); + }), + tap(() => { + const redirectService = this.injector.get(LoginRedirectService); + const redirectUrl = this.redirectUrl; + redirectService.handlePostLoginRedirect(this.userId, redirectUrl); + this.redirectUrl = ''; }) ); } diff --git a/src/app/core/_services/access/login-redirect.service.ts b/src/app/core/_services/access/login-redirect.service.ts new file mode 100644 index 000000000..1c3399077 --- /dev/null +++ b/src/app/core/_services/access/login-redirect.service.ts @@ -0,0 +1,37 @@ +import { Injectable } from '@angular/core'; +import { Router } from '@angular/router'; + +import { LocalStorageService } from '@services/storage/local-storage.service'; + +/** + * This service redirects the user to the dashboard, if current logged-in user is another user then the last logged in + * user and uses LocalStorageService to keep track of current and last logged-in user. + * + * The service is meant to be used after successful login in the login workflow. + */ +@Injectable({ providedIn: 'root' }) +export class LoginRedirectService { + private readonly LAST_USER_KEY = 'lastLoggedInUser'; + + constructor( + private router: Router, + private localStorage: LocalStorageService + ) {} + + /** + * Redirect to redirect URL, if current and last user are the same, otherwise redirect to dashboard + * @param userId ID of current user + * @param redirectUrl Redirect URL used if current user is the same as the last logged-in user + */ + handlePostLoginRedirect(userId: string, redirectUrl: string): void { + const lastUser = this.localStorage.getItem(this.LAST_USER_KEY); + + this.localStorage.setItem(this.LAST_USER_KEY, userId, 0); + + if (lastUser === userId && redirectUrl) { + void this.router.navigate([redirectUrl]); + } else { + void this.router.navigate(['/']); + } + } +} diff --git a/src/app/core/_services/storage/local-storage.service.ts b/src/app/core/_services/storage/local-storage.service.ts index 6b0afe28d..9c5cfc5bc 100644 --- a/src/app/core/_services/storage/local-storage.service.ts +++ b/src/app/core/_services/storage/local-storage.service.ts @@ -1,5 +1,6 @@ import { Injectable } from '@angular/core'; -import { BaseStorageService, StorageWrapper } from './base-storage.service'; + +import { BaseStorageService, StorageWrapper } from '@services/storage/base-storage.service'; /** * A storage service implementation that uses browser's local storage to store and retrieve data diff --git a/src/config/default/app/config.model.ts b/src/config/default/app/config.model.ts new file mode 100644 index 000000000..88a8e1ec9 --- /dev/null +++ b/src/config/default/app/config.model.ts @@ -0,0 +1,48 @@ +// Interfaces for app config + +export interface BrandConfig { + logo: string; + logored: string; + name: string; + height: string; + width: string; + heightred: string; + widthred: string; +} + +export interface HeaderConfig { + brand: BrandConfig; +} + +export interface FooterConfig { + copyright: string; + footer_link_one: FooterLink; + footer_link_two: FooterLink; + footer_link_three: FooterLink; +} + +export interface FooterLink { + link: string; + name: string; +} + +export interface Config { + prodApiEndpoint: string; + prodApiMaxResults: number; + agentURL: string; + agentdownloadURL: string; + appName: string; + favicon: string; + header: HeaderConfig; + footer: FooterConfig; + agents: { + statusOrderBy: string; + statusOrderByName: string; + }; + tasks: { + priority: number; + maxAgents: number; + chunkSize: number; + }; + chunkSizeTUS: number; +} diff --git a/src/config/default/app/main.ts b/src/config/default/app/main.ts index 38e05d8da..c602be09b 100644 --- a/src/config/default/app/main.ts +++ b/src/config/default/app/main.ts @@ -1,4 +1,6 @@ -export const DEFAULT_CONFIG = { +import { Config } from '@src/config/default/app/config.model'; + +export const DEFAULT_CONFIG: Config = { prodApiEndpoint: 'http://localhost:8080/api/v2', prodApiMaxResults: 3000, agentURL: '/server.php', @@ -13,10 +15,10 @@ export const DEFAULT_CONFIG = { height: '60', width: '60', heightred: '60', - widthred: '60', - }, + widthred: '60' + } }, - footer:{ + footer: { copyright: 's3in!c Hashtopolis: 0.14.3', footer_link_one: { link: 'https://github.com/hashtopolis', @@ -31,15 +33,15 @@ export const DEFAULT_CONFIG = { name: 'Hashtopolis' } }, - agents:{ + agents: { statusOrderBy: 'asc', - statusOrderByName: 'agentName', + statusOrderByName: 'agentName' }, - tasks:{ + tasks: { priority: 0, maxAgents: 0, - chunkSize: 0, + chunkSize: 0 }, // File settings 10 * 1024 *1024 (5.24mb) - chunkSizeTUS: 5242880, + chunkSizeTUS: 5242880 };