Moved folders #405

This commit is contained in:
2023-10-13 17:10:00 +02:00
parent eb32bec43c
commit f435d3dd48
807 changed files with 3801 additions and 1297 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,304 @@
import { HttpClient, HttpHeaders } from "@angular/common/http";
import { Injectable } from "@angular/core";
import { Router } from "@angular/router";
import { JwtHelperService } from "@auth0/angular-jwt";
import { BehaviorSubject, firstValueFrom, Observable, Subject, Subscription, throwError } 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";
import { DiscordAuthURL } from "../../models/auth/discord-auth-url.dto";
import { OAuthDTO } from "../../models/auth/oauth.dto";
@Injectable({
providedIn: "root"
})
export class AuthService {
isLoggedIn$ = new BehaviorSubject<boolean>(false);
constructor(
private appsettings: SettingsService,
private http: HttpClient,
private router: Router,
private jwtHelper: JwtHelperService,
private spinnerService: SpinnerService
) {
}
/* 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-by-id/${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"
})
});
}
verifyLogin(): Observable<boolean> {
return this.http.get<boolean>(`${this.appsettings.getApiURL()}/api/auth/verify-login`, {
headers: new HttpHeaders({
"Content-Type": "application/json"
})
});
}
forgotPassword(email: string): Observable<unknown> {
return this.http.post(`${this.appsettings.getApiURL()}/api/auth/forgot-password/${email}`, {
headers: new HttpHeaders({
"Content-Type": "application/json"
})
});
}
getEMailFromforgotPasswordId(id: string): Observable<EMailStringDTO> {
return this.http.post<EMailStringDTO>(`${this.appsettings.getApiURL()}/api/auth/confirm-forgot-password/${id}`, {
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"
})
});
}
deleteUserByMail(mail: string) {
return this.http.post(`${this.appsettings.getApiURL()}/api/auth/delete-user-by-mail/${mail}`, {
headers: new HttpHeaders({
"Content-Type": "application/json"
})
});
}
/* discord auth */
getDiscordAuthURL() {
return this.http.get<DiscordAuthURL>(`${this.appsettings.getApiURL()}/api/auth/discord/get-url`, {
headers: new HttpHeaders({
"Content-Type": "application/json"
})
});
}
discordLogin(code: string, state: string): Observable<TokenDTO> {
return this.http.get<TokenDTO>(`${this.appsettings.getApiURL()}/api/auth/discord/login?code=${code}&state=${state}`, {
headers: new HttpHeaders({
"Content-Type": "application/json"
})
});
}
// /api/auth/discord/register?code=
discordRegister(oAuthDTO: OAuthDTO) {
return this.http.post(`${this.appsettings.getApiURL()}/api/auth/discord/register`, oAuthDTO, {
headers: new HttpHeaders({
"Content-Type": "application/json"
})
});
}
// discordGetUser(code: string, state: string) {
// return this.http.get<AuthUserDTO>(`${this.appsettings.getApiURL()}/api/auth/discord/get-user?code=${code}&state=${state}`, {
// 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(token: TokenDTO | undefined = undefined): { [key: string]: any } | null {
if (token) {
return this.jwtHelper.decodeToken(token.token);
}
return this.jwtHelper.decodeToken(this.getToken().token);
}
logout(): Subscription | null {
const token = this.getToken();
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;
this.isLoggedIn$.next(false);
localStorage.removeItem("rjwt");
this.router.navigate(["/auth/login"]);
return throwError(() => error);
})).subscribe(() => {
this.isLoggedIn$.next(false);
localStorage.removeItem("jwt");
localStorage.removeItem("rjwt");
this.router.navigate(["/auth/login"]);
});
}
this.isLoggedIn$.next(false);
localStorage.removeItem("rjwt");
this.router.navigate(["/auth/login"]);
return null;
}
async getLoggedInUser(): Promise<AuthUserDTO | null> {
if (!await this.isUserLoggedInAsync()) {
return null;
}
const token = this.getDecodedToken();
if (!token) return null;
try {
return await firstValueFrom(this.findUserByEMail(token["email"]));
} catch (error: unknown) {
return null;
}
}
async isUserLoggedInAsync(): Promise<boolean> {
const token = this.getToken();
if (!token || !token.refreshToken) {
this.isLoggedIn$.next(false);
return false;
}
try {
const verifiedLogin = await firstValueFrom(this.verifyLogin());
if (verifiedLogin) {
this.isLoggedIn$.next(true);
return true;
}
} catch (error: unknown) {
this.isLoggedIn$.next(false);
return false;
}
this.spinnerService.showSpinner();
const resfreshedToken = await firstValueFrom(await this.refresh(token));
this.spinnerService.hideSpinner();
if (resfreshedToken) {
this.saveToken(resfreshedToken);
return true;
}
return false;
}
async hasUserPermission(role: AuthRoles): Promise<boolean> {
if (!role || !await this.isUserLoggedInAsync()) {
return false;
}
const token = this.getDecodedToken();
if (!token) return false;
return AuthRoles[token["role"]] === AuthRoles[role];
}
getEMailFromDecodedToken(token: { [key: string]: any } | null): 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,90 @@
import { HttpClient } from "@angular/common/http";
import { Injectable } from "@angular/core";
import { SettingsService } from "../settings/settings.service";
import { map, Observable } from "rxjs";
import { Variables } from "../../models/graphql/variables.model";
import { ActivatedRoute, Router } from "@angular/router";
import { Server } from "../../models/data/server.model";
import { Queries } from "../../models/graphql/queries.model";
import { Query } from "../../models/graphql/query.model";
import { SpinnerService } from "../spinner/spinner.service";
import { GraphQLResult } from "../../models/graphql/result.model";
import { ToastService } from "../toast/toast.service";
import { TranslateService } from "@ngx-translate/core";
import { ServerService } from "../server.service";
@Injectable({
providedIn: "root"
})
export class DataService {
constructor(
private appsettings: SettingsService,
private http: HttpClient,
private server: ServerService,
private spinner: SpinnerService,
private router: Router,
private toast: ToastService,
private translate: TranslateService,
) {
}
public getServerFromRoute(route: ActivatedRoute): Promise<Server> {
return new Promise((resolve, reject) => {
this.spinner.showSpinner();
if (!route.snapshot.params["serverId"] || route.snapshot.params["serverId"] == "undefined") {
this.spinner.hideSpinner();
this.router.navigate(["/dashboard"]);
reject();
}
this.query<Server>(Queries.serversQuery, {
filter: { id: route.snapshot.params["serverId"] }
},
function(data: Query) {
return data.servers.length > 0 ? data.servers[0] : null;
}
).subscribe(server => {
this.server.setServer(server);
this.spinner.hideSpinner();
resolve(server);
});
});
}
public query<T>(query: string, variables?: Variables, f?: Function): Observable<T> {
return this.http
.post<GraphQLResult>(`${this.appsettings.getApiURL()}/api/graphql`, {
query: query,
variables: variables
})
.pipe(map(d => {
if (d.errors && d.errors.length > 0) {
throw new Error(d.errors.map((x: { message: String }) => x.message).toString());
}
return d.data;
}))
.pipe(map((d) => f ? f(d) : d));
}
public mutation<T>(query: string, variables?: object, f?: Function): Observable<T> {
return this.http
.post<GraphQLResult>(`${this.appsettings.getApiURL()}/api/graphql`, {
query: query,
variables: variables
})
.pipe(map(d => {
if (d.errors && d.errors.length > 0) {
d.errors.forEach((error: Error) => {
this.toast.error(this.translate.instant("common.error"), error.message)
});
throw new Error(d.errors.toString());
}
return d.data;
}))
.pipe(map((d) => f ? f(d) : d));
}
}

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,48 @@
import { HttpErrorResponse } from "@angular/common/http";
import { ErrorHandler, Injectable } from "@angular/core";
import { Observable, throwError } from "rxjs";
import { ErrorDTO } from "src/app/models/error/error-dto";
import { ServiceErrorCode } from "src/app/models/error/service-error-code.enum";
import { AuthService } from "../auth/auth.service";
import { ToastService } from "../toast/toast.service";
@Injectable()
export class ErrorHandlerService implements ErrorHandler {
constructor(
private auth: AuthService,
private toast: ToastService,
) { }
handleError(error: HttpErrorResponse): Observable<never> {
if (error && error.error) {
let message = 'Unbekannter Fehler';
let header = 'Fehler';
const errorDto: ErrorDTO = error.error;
if (errorDto.errorCode === ServiceErrorCode.Unauthorized) {
this.auth.logout();
return throwError(() => error);
}
if (errorDto.errorCode !== undefined) {
header = 'Fehlercode: ' + errorDto.errorCode;
}
if (errorDto.message) {
message = errorDto.message;
} else if (error.message) {
message = error.message;
}
this.toast.error(header, message);
} else {
console.error(error.message);
}
if (error.status === 401) {
this.auth.logout();
}
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 { ServerService } from './server.service';
describe('ServerService', () => {
let service: ServerService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(ServerService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@@ -0,0 +1,37 @@
import { Injectable } from "@angular/core";
import { BehaviorSubject } from "rxjs";
import { Server } from "../models/data/server.model";
import { NavigationEnd, Router } from "@angular/router";
@Injectable({
providedIn: "root"
})
export class ServerService {
server$ = new BehaviorSubject<Server | undefined>(undefined);
constructor(
private router: Router
) {
this.router.events.subscribe(event => {
if (!(event instanceof NavigationEnd)) {
return;
}
if (!event.url.startsWith("/server/") && this.server$.value) {
this.setServer(undefined);
}
});
}
setServer(server: Server | undefined) {
if (!server) {
this.server$.next(undefined);
return;
}
if (server.id != this.server$.value?.id) {
this.server$.next(server);
}
}
}

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,86 @@
import { HttpClient } from "@angular/common/http";
import { Injectable } from "@angular/core";
import { forkJoin, throwError } from "rxjs";
import { catchError } from "rxjs/operators";
import { AppAndVersionSettings, AppSettings, VersionSettings } from "src/app/models/config/app-settings";
import { SoftwareVersion } from "src/app/models/config/software-version";
import { Theme } from "src/app/models/view/theme";
@Injectable({
providedIn: "root"
})
export class SettingsService {
appSettings!: AppSettings;
versionSettings!: VersionSettings;
constructor(
private http: HttpClient
) {
}
loadSettings(version: boolean = false): Promise<AppAndVersionSettings> {
return new Promise((resolve, reject) => {
forkJoin([
this.http.get<AppSettings>("../../assets/config.json")
.pipe(catchError(error => {
reject(error);
return throwError(() => error);
})),
this.http.get<VersionSettings>("../../assets/version.json")
.pipe(catchError(error => {
reject(error);
return throwError(() => error);
}))
]).subscribe(settings => {
this.appSettings = settings[0];
this.versionSettings = settings[1];
resolve({...settings[0], ...settings[1]});
});
});
}
public getApiURL(): string {
if (!this.appSettings || !this.appSettings.ApiURL) {
console.error("ApiUrl is not set!");
return "";
}
return this.appSettings.ApiURL;
}
public getPrivacyURL(): string {
if (!this.appSettings || !this.appSettings.PrivacyURL) {
console.error("PrivacyURL is not set!");
return "";
}
return this.appSettings.PrivacyURL;
}
public getImprintURL(): string {
if (!this.appSettings || !this.appSettings.ImprintURL) {
console.error("ImprintURL is not set!");
return "";
}
return this.appSettings.ImprintURL;
}
public getWebVersion(): SoftwareVersion | null {
if (!this.versionSettings || !this.versionSettings.WebVersion) {
console.error("WebVersion is not set!");
return null;
}
return new SoftwareVersion(
this.versionSettings.WebVersion.Major,
this.versionSettings.WebVersion.Minor,
this.versionSettings.WebVersion.Micro
);
}
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 { SidebarService } from './sidebar.service';
describe('SidebarService', () => {
let service: SidebarService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(SidebarService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@@ -0,0 +1,231 @@
import { Injectable } from "@angular/core";
import { MenuItem } from "primeng/api";
import { BehaviorSubject, forkJoin, Observable } from "rxjs";
import { AuthRoles } from "../../models/auth/auth-roles.enum";
import { AuthService } from "../auth/auth.service";
import { TranslateService } from "@ngx-translate/core";
import { NavigationEnd, Router } from "@angular/router";
import { ThemeService } from "../theme/theme.service";
import { Server } from "../../models/data/server.model";
import { UserDTO } from "../../models/auth/auth-user.dto";
import { ServerService } from "../server.service";
import { HasServerFeatureFlagQuery, PossibleFeatureFlagsQuery, Query } from "../../models/graphql/query.model";
import { Queries } from "../../models/graphql/queries.model";
import { DataService } from "../data/data.service";
import { FeatureFlag } from "../../models/config/feature-flags.model";
@Injectable({
providedIn: "root"
})
export class SidebarService {
isSidebarOpen: boolean = true;
menuItems$ = new BehaviorSubject<MenuItem[]>(new Array<MenuItem>());
server!: Server | undefined;
dashboard: MenuItem = {};
serverDashboard: MenuItem = {};
serverProfile: MenuItem = {};
serverMembers: MenuItem = {};
serverAutoRoles: MenuItem = {};
serverLevels: MenuItem = {};
serverAchievements: MenuItem = {};
serverShortRoleNames: MenuItem = {};
serverConfig: MenuItem = {};
serverMenu: MenuItem = {};
adminConfig: MenuItem = {};
adminUsers: MenuItem = {};
adminMenu: MenuItem = {};
featureFlags: FeatureFlag[] = [];
constructor(
private themeService: ThemeService,
private authService: AuthService,
private translateService: TranslateService,
private router: Router,
private serverService: ServerService,
private data: DataService
) {
this.themeService.isSidebarOpen$.subscribe(value => {
this.isSidebarOpen = value;
this.setMenu(true);
});
this.translateService.onLangChange.subscribe(_ => {
this.setMenu(true);
});
this.serverService.server$.subscribe(server => {
this.server = server;
if (server) {
this.setMenu(true);
} else {
this.setMenu(false);
}
});
}
async buildMenu(user: UserDTO | null, hasPermission: boolean, isTechnician: boolean = false) {
this.dashboard = {
label: this.isSidebarOpen ? this.translateService.instant("sidebar.dashboard") : "",
icon: "pi pi-th-large",
routerLink: "dashboard"
};
this.serverDashboard = {
label: this.isSidebarOpen ? this.translateService.instant("sidebar.server.dashboard") : "",
icon: "pi pi-th-large",
routerLink: `server/${this.server?.id}`
};
this.serverProfile = {
label: this.isSidebarOpen ? this.translateService.instant("sidebar.server.profile") : "",
icon: "pi pi-id-card",
routerLink: `server/${this.server?.id}/members/${user?.id}`
};
this.serverMembers = {
label: this.isSidebarOpen ? this.translateService.instant("sidebar.server.members") : "",
icon: "pi pi-users",
visible: true,
routerLink: `server/${this.server?.id}/members`
};
this.serverAutoRoles = {
label: this.isSidebarOpen ? this.translateService.instant("sidebar.server.auto_roles") : "",
icon: "pi pi-sitemap",
visible: true,
routerLink: `server/${this.server?.id}/auto-roles`
};
this.serverLevels = {
label: this.isSidebarOpen ? this.translateService.instant("sidebar.server.levels") : "",
icon: "pi pi-book",
visible: true,
routerLink: `server/${this.server?.id}/levels`
};
this.serverAchievements = {
label: this.isSidebarOpen ? this.translateService.instant("sidebar.server.achievements") : "",
icon: "pi pi-angle-double-up",
visible: true,
routerLink: `server/${this.server?.id}/achievements`
};
this.serverShortRoleNames = {
label: this.isSidebarOpen ? this.translateService.instant("sidebar.server.short_role_names") : "",
icon: "pi pi-list",
visible: true,
routerLink: `server/${this.server?.id}/short-role-names`
};
this.serverConfig = {
label: this.isSidebarOpen ? this.translateService.instant("sidebar.server.configuration") : "",
icon: "pi pi-cog",
visible: true,
routerLink: `server/${this.server?.id}/config`
};
this.serverMenu = {
label: this.isSidebarOpen ? this.server?.name : "",
icon: "pi pi-server",
visible: false,
expanded: true,
items: [this.serverDashboard, this.serverProfile, this.serverMembers, this.serverAutoRoles, this.serverLevels, this.serverAchievements, this.serverShortRoleNames, this.serverConfig]
};
this.adminConfig = {
label: this.isSidebarOpen ? this.translateService.instant("sidebar.config") : "",
visible: hasPermission || isTechnician,
icon: "pi pi-cog",
routerLink: "/admin/settings"
};
this.adminUsers = {
label: this.isSidebarOpen ? this.translateService.instant("sidebar.auth_user_list") : "",
visible: hasPermission,
icon: "pi pi-user-edit",
routerLink: "/admin/users"
};
this.adminMenu = {
label: this.isSidebarOpen ? this.translateService.instant("sidebar.administration") : "",
icon: "pi pi-cog",
visible: hasPermission || isTechnician,
expanded: true,
items: [this.adminConfig, this.adminUsers]
};
}
setMenu(build: boolean = false) {
const server = this.server;
if (server) {
this.featureFlags = [];
this.data.query<PossibleFeatureFlagsQuery>("{possibleFeatureFlags}"
).subscribe(data => {
let observables: Observable<HasServerFeatureFlagQuery>[] = [];
data.possibleFeatureFlags.forEach(flag => {
observables.push(
this.data.query<HasServerFeatureFlagQuery>(Queries.hasServerFeatureFlag, {
filter: { id: server.id },
flag: flag
},
function(data: Query) {
return data.servers[0];
}
)
);
});
forkJoin(observables).subscribe(data => {
data.forEach(flag => {
if (!flag.hasFeatureFlag.value) {
return;
}
this.featureFlags.push(flag.hasFeatureFlag);
});
this._setMenu(build);
});
});
} else {
this._setMenu(build);
}
}
private _setMenu(build: boolean = false) {
this.authService.hasUserPermission(AuthRoles.Admin).then(async hasPermission => {
let authUser = await this.authService.getLoggedInUser();
let user: UserDTO | null = authUser?.users?.find(u => u.server == this.server?.id) ?? null;
let isTechnician = (authUser?.users?.map(u => u.isTechnician).filter(u => u) ?? []).length > 0;
let isTechnicianAndFullAccessActive = this.hasFeature("TechnicianFullAccess") && isTechnician;
if (build || this.menuItems$.value.length == 0) {
await this.buildMenu(user, hasPermission, isTechnician);
}
if (this.server) {
this.serverMenu.visible = true;
this.serverMembers.visible = isTechnicianAndFullAccessActive || user?.isModerator;
this.serverAutoRoles.visible = isTechnicianAndFullAccessActive || this.hasFeature("AutoRoleModule") && user?.isModerator;
this.serverLevels.visible = isTechnicianAndFullAccessActive || this.hasFeature("LevelModule") && user?.isModerator;
this.serverAchievements.visible = isTechnicianAndFullAccessActive || this.hasFeature("AchievementsModule") && user?.isModerator;
this.serverShortRoleNames.visible = isTechnicianAndFullAccessActive || this.hasFeature("ShortRoleName") && user?.isAdmin;
this.serverConfig.visible = isTechnicianAndFullAccessActive || user?.isAdmin;
} else {
this.serverMenu.visible = false;
}
let menuItems: MenuItem[] = [
this.dashboard,
this.serverMenu,
this.adminMenu
];
this.menuItems$.next(menuItems);
});
}
public hasFeature(key: string): boolean {
const flag = this.featureFlags.filter(flag => flag.key == key);
if (flag.length == 0) {
return false;
}
return flag[0].value;
}
}

View File

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

View File

@@ -0,0 +1,74 @@
import { Injectable } from "@angular/core";
import { ToastOptions } from "src/app/models/utils/toast-options";
import { SettingsService } from "../settings/settings.service";
import { SpinnerService } from "../spinner/spinner.service";
import { ToastService } from "../toast/toast.service";
import io from "socket.io-client";
import { MessageService } from "primeng/api";
@Injectable({
providedIn: "root"
})
export class SocketService {
private socket: any;
disconnected = false;
constructor(
private settingsService: SettingsService,
private toastService: ToastService,
private spinnerService: SpinnerService,
private messageService: MessageService
) {
}
startSocket() {
this.socket = io(`${this.settingsService.getApiURL()}`, { path: "/api/socket.io" });
this.socket.on("connect", () => {
if (this.disconnected) {
this.spinnerService.hideSpinner();
const options: ToastOptions = {
closable: false
};
this.messageService.clear();
this.toastService.info("Server verbunden", "Die Verbindung zum Server konnte hergestellt werden.", options);
}
this.disconnected = false;
console.info("Connected!");
});
this.socket.on("connect_error", (err: Error) => {
if (this.disconnected) {
this.spinnerService.showSpinner();
return;
}
console.warn("Connect error!");
this.disconnected = true;
const options: ToastOptions = {
sticky: true,
closable: false
};
this.messageService.clear();
this.toastService.error("Server nicht erreichbar", "Die Verbindung zum Server konnte nicht hergestellt werden!\nLaden Sie die Seite neu.", options);
console.error(err.toString());
});
this.socket.on("disconnect", () => {
if (this.disconnected) {
this.spinnerService.showSpinner();
return;
}
console.warn("Disconnected!");
this.disconnected = true;
const options: ToastOptions = {
sticky: true,
closable: false
};
this.spinnerService.showSpinner();
this.messageService.clear();
this.toastService.error("Verbindung unterbrochen", "Die Verbindung zum Server konnte nicht hergestellt werden!\nLaden Sie die Seite neu.", options);
});
}
}

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,23 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject } from "rxjs";
@Injectable({
providedIn: 'root'
})
export class SpinnerService {
showSpinnerState$ = new BehaviorSubject<boolean>(false);
constructor() { }
showSpinner() {
this.showSpinnerState$.next(true);
}
hideSpinner() {
this.showSpinnerState$.next(false);
}
toggleSpinner() {
this.showSpinnerState$.next(!this.showSpinnerState$.value);
}
}

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,107 @@
import { Injectable } from "@angular/core";
import { BehaviorSubject } from "rxjs";
import { Themes } from "src/app/models/view/themes.enum";
import { AuthService } from "../auth/auth.service";
@Injectable({
providedIn: "root"
})
export class ThemeService {
themeName: string = Themes.Default;
isSidebarOpen = false;
themeName$ = new BehaviorSubject<string>(Themes.Default);
isSidebarOpen$ = new BehaviorSubject<boolean>(true);
constructor(
private authService: AuthService
) {
this.themeName$.subscribe(themeName => {
this.themeName = themeName;
});
this.isSidebarOpen$.subscribe(isSidebarOpen => {
this.isSidebarOpen = isSidebarOpen;
});
this.loadTheme();
}
loadTheme(): void {
const token = this.authService.getDecodedToken();
const mail = this.authService.getEMailFromDecodedToken(token);
let defaultThemeName = localStorage.getItem(`default_themeName`);
if (!defaultThemeName) {
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, isDefault: boolean = false): void {
if (isDefault) {
localStorage.setItem(`default_themeName`, name);
this.themeName$.next(name);
return;
}
if (!this.authService.isLoggedIn$.value) {
localStorage.setItem(`default_themeName`, Themes.Default);
this.themeName$.next(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$.next(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 = JSON.parse(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(isSidebarOpen: boolean): void {
this.isSidebarOpen$.next(isSidebarOpen);
}
}

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);
}
}