Added angular frontend #70

This commit is contained in:
2022-10-16 00:13:03 +02:00
parent ee49bde961
commit 8c3cd1fae7
151 changed files with 29104 additions and 0 deletions

View File

@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { AuthService } from './auth.service';
describe('AuthService', () => {
let service: AuthService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(AuthService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@@ -0,0 +1,243 @@
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { JwtHelperService } from '@auth0/angular-jwt';
import { Observable, Subscription } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { AdminUpdateUserDTO } from 'src/app/models/auth/admin-update-user.dto';
import { AuthRoles } from 'src/app/models/auth/auth-roles.enum';
import { AuthUserDTO } from 'src/app/models/auth/auth-user.dto';
import { EMailStringDTO } from 'src/app/models/auth/email-string.dto';
import { ResetPasswordDTO } from 'src/app/models/auth/reset-password.dto';
import { TokenDTO } from 'src/app/models/auth/token.dto';
import { UpdateUserDTO } from 'src/app/models/auth/update-user.dto';
import { AuthUserSelectCriterion } from 'src/app/models/selection/auth-user/auth-user-select-criterion.dto';
import { GetFilteredAuthUsersResultDTO } from 'src/app/models/selection/auth-user/get-filtered-auth-users-result.dto';
import { SettingsService } from '../settings/settings.service';
import { SpinnerService } from '../spinner/spinner.service';
@Injectable({
providedIn: 'root'
})
export class AuthService {
invalidLogin!: boolean;
isLoggedIn!: boolean;
constructor(
private appsettings: SettingsService,
private http: HttpClient,
private router: Router,
private jwtHelper: JwtHelperService,
private spinnerService: SpinnerService
) {
this.isUserLoggedInAsync();
}
/* data requests */
getAllUsers(): Observable<Array<AuthUserDTO>> {
return this.http.get<Array<AuthUserDTO>>(`${this.appsettings.getApiURL()}/api/auth/users`, {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
});
}
getFilteredUsers(selectCriterions: AuthUserSelectCriterion): Observable<GetFilteredAuthUsersResultDTO> {
return this.http.post<GetFilteredAuthUsersResultDTO>(`${this.appsettings.getApiURL()}/api/auth/users/get/filtered`, selectCriterions, {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
});
}
getUserByEMail(email: string): Observable<AuthUserDTO> {
return this.http.get<AuthUserDTO>(`${this.appsettings.getApiURL()}/api/auth/users/get/${email}`, {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
});
}
findUserByEMail(email: string): Observable<AuthUserDTO> {
return this.http.get<AuthUserDTO>(`${this.appsettings.getApiURL()}/api/auth/users/find/${email}`, {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
});
}
/* auth requsts */
register(user: AuthUserDTO): Observable<unknown> {
return this.http.post<TokenDTO>(`${this.appsettings.getApiURL()}/api/auth/register`, user, {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
});
}
confirmEMail(id: string): Observable<boolean> {
return this.http.post<boolean>(`${this.appsettings.getApiURL()}/api/auth/register/${id}`, {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
});
}
login(user: AuthUserDTO): Observable<TokenDTO> {
return this.http.post<TokenDTO>(`${this.appsettings.getApiURL()}/api/auth/login`, user, {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
});
}
forgotPassword(email: string): Observable<unknown> {
const emailJson = JSON.stringify(email);
return this.http.post(`${this.appsettings.getApiURL()}/api/auth/forgot-password`, emailJson, {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
});
}
getEMailFromforgotPasswordId(id: string): Observable<EMailStringDTO> {
const idJson = JSON.stringify(id);
return this.http.post<EMailStringDTO>(`${this.appsettings.getApiURL()}/api/auth/confirm-forgot-password`, idJson, {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
});
}
resetPassword(resetPasswordDTO: ResetPasswordDTO): Observable<unknown> {
return this.http.post(`${this.appsettings.getApiURL()}/api/auth/reset-password`, resetPasswordDTO, {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
});
}
updateUser(updateUserDTO: UpdateUserDTO): Observable<unknown> {
return this.http.post(`${this.appsettings.getApiURL()}/api/auth/update-user`, updateUserDTO, {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
});
}
updateUserAsAdmin(updateUserDTO: AdminUpdateUserDTO): Observable<unknown> {
return this.http.post(`${this.appsettings.getApiURL()}/api/auth/update-user-as-admin`, updateUserDTO, {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
});
}
refresh(token: TokenDTO): Observable<TokenDTO> {
return this.http.post<TokenDTO>(`${this.appsettings.getApiURL()}/api/auth/refresh`, token, {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
});
}
logout(): Subscription | null {
const token = this.getToken();
this.isLoggedIn = false;
localStorage.removeItem('jwt');
localStorage.removeItem('rjwt');
this.router.navigate(['/auth/login']);
if (token && token.token && token.refreshToken) {
return this.http.post<TokenDTO>(`${this.appsettings.getApiURL()}/api/auth/revoke`, token, {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
}).pipe(catchError((error: any) => {
error.error = null;
throw error;
})).subscribe();
}
return null
}
deleteUserByMail(mail: string) {
return this.http.post(`${this.appsettings.getApiURL()}/api/auth/delete-user-by-mail/${mail}`, {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
});
}
/* utils */
saveToken(token: TokenDTO): void {
localStorage.setItem('jwt', token.token);
localStorage.setItem('rjwt', token.refreshToken);
if (this.router.url.startsWith('/auth')) {
this.router.navigate(['/dashboard']);
}
}
getToken(): TokenDTO {
return {
token: localStorage.getItem('jwt') ?? '',
refreshToken: localStorage.getItem('rjwt') ?? ''
};
}
getDecodedToken(): { [key: string]: any } {
return this.jwtHelper.decodeToken(this.getToken().token);
}
async isUserLoggedInAsync(): Promise<boolean> {
this.isLoggedIn = await this._isUserLoggedInAsync();
return this.isLoggedIn;
}
private async _isUserLoggedInAsync(): Promise<boolean> {
const token = this.getToken();
if (!token || !token.refreshToken) {
this.isLoggedIn = false;
return false;
}
if (token.token && !this.jwtHelper.isTokenExpired(token.token)) {
this.isLoggedIn = true;
return true;
}
if (this.isLoggedIn !== false) {
this.spinnerService.showSpinner();
const resfreshedToken = await this.refresh(token)
.pipe(catchError((err: Error) => {
this.logout();
this.spinnerService.hideSpinner();
throw err;
}))
.toPromise();
this.spinnerService.hideSpinner();
if (resfreshedToken) {
this.saveToken(resfreshedToken);
return true;
}
}
this.isLoggedIn = false;
return false;
}
async hasUserPermission(role: AuthRoles): Promise<boolean> {
if (!role || !await this.isUserLoggedInAsync()) {
return false;
}
const token = this.getDecodedToken();
return AuthRoles[token['role']] === AuthRoles[role];
}
getEMailFromDecodedToken(token: { [key: string]: any }): string | null {
if (!token) {
return null;
}
return token['email'];
}
}

View File

@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { ConfirmationDialogService } from './confirmation-dialog.service';
describe('ConfirmationDialogService', () => {
let service: ConfirmationDialogService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(ConfirmationDialogService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@@ -0,0 +1,53 @@
import { Injectable } from '@angular/core';
import { ConfirmationService } from 'primeng/api';
import { ConfirmationDialog } from 'src/app/models/utils/confirmation-dialog';
@Injectable({
providedIn: 'root'
})
export class ConfirmationDialogService {
constructor(
private confirmationService: ConfirmationService
) { }
errorDialog(header: string, message: string) {
this.confirmationService.confirm({
key: 'errorConfirmationDialog',
header: header,
message: message
});
}
confirmDialog(header: string, message: string, accept?: () => void, reject?: () => void) {
let options: ConfirmationDialog = {
key: 'confirmConfirmationDialog',
header: header,
message: message
};
if (accept) {
options.accept = accept;
}
if (reject) {
options.reject = reject;
}
this.confirmationService.confirm(options);
}
warningDialog(header: string, message: string, accept?: () => void) {
let options: ConfirmationDialog = {
key: 'warningConfirmationDialog',
header: header,
message: message
};
if (accept) {
options.accept = accept;
}
this.confirmationService.confirm(options);
}
}

View File

@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { DataService } from './data.service';
describe('DataService', () => {
let service: DataService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(DataService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@@ -0,0 +1,14 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { SettingsService } from '../settings/settings.service';
@Injectable({
providedIn: 'root'
})
export class DataService {
constructor(
private appsettings: SettingsService,
private http: HttpClient,
) { }
}

View File

@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { ErrorHandlerService } from './error-handler.service';
describe('ErrorHandlerService', () => {
let service: ErrorHandlerService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(ErrorHandlerService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@@ -0,0 +1,34 @@
import { HttpErrorResponse } from '@angular/common/http';
import { ErrorHandler, Injectable, Injector } from '@angular/core';
import { Observable, throwError } from 'rxjs';
import { ErrorDTO } from 'src/app/models/error/error-dto';
import { ToastService } from '../toast/toast.service';
@Injectable()
export class ErrorHandlerService implements ErrorHandler {
constructor(
private injector: Injector
) { }
handleError(error: HttpErrorResponse): Observable<never> {
if (error && error.error) {
let message = 'Unbekannter Fehler';
let header = 'Fehler';
const errorDto: ErrorDTO = error.error;
if (errorDto.errorCode !== undefined) {
header = 'Fehlercode: ' + errorDto.errorCode;
}
if (errorDto.message) {
message = errorDto.message;
} else if (error.message) {
message = error.message;
}
this.injector.get(ToastService).error(header, message);
}
return throwError(error);
}
}

View File

@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { GuiService } from './gui.service';
describe('GuiService', () => {
let service: GuiService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(GuiService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@@ -0,0 +1,41 @@
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, Subscribable } from 'rxjs';
import { SettingsDTO } from 'src/app/models/config/settings.dto';
import { SoftwareVersionDTO } from 'src/app/models/config/software-version.dto';
import { SettingsService } from '../settings/settings.service';
@Injectable({
providedIn: 'root'
})
export class GuiService {
constructor(
private appsettings: SettingsService,
private http: HttpClient,
) { }
getApiVersion(): Observable<SoftwareVersionDTO> {
return this.http.get<SoftwareVersionDTO>(`${this.appsettings.getApiURL()}/api/gui/api-version`, {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
});
}
getSettings(): Observable<SettingsDTO> {
return this.http.get<SettingsDTO>(`${this.appsettings.getApiURL()}/api/gui/settings`, {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
});
}
sendTestMail(mail: string): Observable<unknown> {
return this.http.post(`${this.appsettings.getApiURL()}/api/gui/send-test-mail/${mail}`, {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
});
}
}

View File

@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { SettingsService } from './settings.service';
describe('SettingsService', () => {
let service: SettingsService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(SettingsService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@@ -0,0 +1,62 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { Appsettings } from 'src/app/models/config/appsettings';
import { SoftwareVersion } from 'src/app/models/config/software-version';
import { Theme } from 'src/app/models/view/theme';
import { Themes } from 'src/app/models/view/themes.enum';
@Injectable({
providedIn: 'root'
})
export class SettingsService {
appsettings!: Appsettings;
constructor(
private http: HttpClient
) { }
loadSettings(): Promise<Appsettings> {
return new Promise((resolve, reject) => {
this.http.get<Appsettings>('../../assets/config.json')
.pipe(catchError(error => {
reject(error);
return throwError(error);
})).subscribe(settings => {
this.appsettings = settings;
resolve(settings);
});
});
}
public getApiURL(): string {
if (!this.appsettings || !this.appsettings.ApiURL) {
console.error('ApiURL is not set!');
return '';
}
return this.appsettings.ApiURL;
}
public getWebVersion(): SoftwareVersion | null {
if (!this.appsettings || !this.appsettings.WebVersion) {
console.error('WebVersion is not set!');
return null;
}
const webVersion = new SoftwareVersion(
this.appsettings.WebVersion.Major,
this.appsettings.WebVersion.Minor,
this.appsettings.WebVersion.Micro
);
return webVersion;
}
public getThemes(): Theme[] | null {
if (!this.appsettings || !this.appsettings.Themes) {
console.error('Themes is not set!');
return null;
}
return this.appsettings.Themes;
}
}

View File

@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { SpinnerService } from './spinner.service';
describe('SpinnerService', () => {
let service: SpinnerService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(SpinnerService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@@ -0,0 +1,22 @@
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class SpinnerService {
showSpinnerState = false;
constructor() { }
showSpinner() {
this.showSpinnerState = true;
}
hideSpinner() {
this.showSpinnerState = false;
}
toggleSpinner() {
this.showSpinnerState = !this.showSpinnerState;
}
}

View File

@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { ThemeService } from './theme.service';
describe('ThemeService', () => {
let service: ThemeService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(ThemeService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@@ -0,0 +1,98 @@
import { Injectable } from '@angular/core';
import { Themes } from 'src/app/models/view/themes.enum';
import { AuthService } from '../auth/auth.service';
@Injectable({
providedIn: 'root'
})
export class ThemeService {
themeName: string | null;
sidebarWidth = '150px';
isSidebarOpen = false;
hasLangChanged = false;
constructor(
private authService: AuthService
) {
this.loadTheme();
this.loadMenu();
this.themeName = null;
}
loadTheme(): void {
const token = this.authService.getDecodedToken();
const mail = this.authService.getEMailFromDecodedToken(token);
let defaultThemeName = localStorage.getItem(`default_themeName`);
if (!defaultThemeName || defaultThemeName != Themes.Default) {
defaultThemeName = Themes.Default;
}
if (!mail) {
this.setTheme(defaultThemeName);
return;
}
let userThemeName = localStorage.getItem(`${mail}_themeName`);
if (!userThemeName) {
this.setTheme(defaultThemeName);
return;
}
this.setTheme(userThemeName);
}
setTheme(name: string): void {
this.authService.isUserLoggedInAsync().then(result => {
if (!result) {
localStorage.setItem(`default_themeName`, Themes.Default);
this.themeName = Themes.Default;
}
const token = this.authService.getDecodedToken();
const mail = this.authService.getEMailFromDecodedToken(token);
if (mail) {
localStorage.setItem(`${mail}_themeName`, name);
}
localStorage.setItem(`default_themeName`, name);
this.themeName = name;
});
}
loadMenu() {
const token = this.authService.getDecodedToken();
const mail = this.authService.getEMailFromDecodedToken(token);
if (!mail) {
this.setSideWidth(true);
return;
}
let isMenuOpen = true;
let isMenuOpenStr = localStorage.getItem(`${mail}_isMenuOpen`);
if (isMenuOpenStr) {
isMenuOpen = Boolean(isMenuOpenStr);
}
this.setIsMenuOpen(isMenuOpen);
}
setIsMenuOpen(isMenuOpen: boolean) {
const token = this.authService.getDecodedToken();
const mail = this.authService.getEMailFromDecodedToken(token);
if (!mail) {
this.setSideWidth(true);
return;
}
localStorage.setItem(`${mail}_isMenuOpen`, isMenuOpen + "");
this.setSideWidth(isMenuOpen);
}
setSideWidth($event: any): void {
this.sidebarWidth = $event ? '150px' : '50px';
this.isSidebarOpen = $event;
}
}

View File

@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { ToastService } from './toast.service';
describe('ToastService', () => {
let service: ToastService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(ToastService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@@ -0,0 +1,40 @@
import { Injectable } from '@angular/core';
import { MessageService } from 'primeng/api';
import { ToastOptions } from 'src/app/models/utils/toast-options';
@Injectable({
providedIn: 'root'
})
export class ToastService {
constructor(
private messageService: MessageService
) { }
private toast(type: string, summary: string, detail: string, options?: ToastOptions) {
this.messageService.add({
severity: type,
summary: summary,
detail: detail,
life: options?.life,
sticky: options?.sticky,
closable: options?.closable
});
}
success(summary: string, detail: string, options?: ToastOptions) {
this.toast('success', summary, detail, options);
}
info(summary: string, detail: string, options?: ToastOptions) {
this.toast('info', summary, detail, options);
}
warn(summary: string, detail: string, options?: ToastOptions) {
this.toast('warn', summary, detail, options);
}
error(summary: string, detail: string, options?: ToastOptions) {
this.toast('error', summary, detail, options);
}
}