Archived
Private
Public Access
1
0

Finished Login / Register system

This commit is contained in:
2022-09-07 12:12:42 +02:00
parent 15f48d259f
commit 9122d513ea
24 changed files with 450 additions and 27 deletions

View File

@@ -0,0 +1,15 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="HtmlUnknownAttribute" enabled="true" level="WARNING" enabled_by_default="true">
<option name="myValues">
<value>
<list size="1">
<item index="0" class="java.lang.String" itemvalue="mat-input" />
</list>
</value>
</option>
<option name="myCustomValuesEnabled" value="true" />
</inspection_tool>
</profile>
</component>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectTasksOptions" suppressed-tasks="SCSS" />
</project>

View File

@@ -6,10 +6,30 @@
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="041c7675-58ae-4243-af88-0d29855e558f" name="Changes" comment=""> <list default="true" id="041c7675-58ae-4243-af88-0d29855e558f" name="Changes" comment="">
<change beforePath="$PROJECT_DIR$/.idea/.idea.WebDesktop 2.0/.idea/vcs.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.idea.WebDesktop 2.0/.idea/vcs.xml" afterDir="false" /> <change afterPath="$PROJECT_DIR$/.idea/.idea.WebDesktop 2.0/.idea/inspectionProfiles/Project_Default.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/.idea.WebDesktop 2.0/.idea/watcherTasks.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Frontend/src/app/sites/register/register.component.html" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Frontend/src/app/sites/register/register.component.scss" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Frontend/src/app/sites/register/register.component.ts" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Frontend/src/assets/background.png" afterDir="false" />
<change afterPath="$PROJECT_DIR$/Frontend/src/colors.scss" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/.idea.WebDesktop 2.0/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.idea.WebDesktop 2.0/.idea/workspace.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/.idea.WebDesktop 2.0/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.idea.WebDesktop 2.0/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Backend/Controllers/UserController.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Backend/Controllers/UserController.cs" afterDir="false" /> <change beforePath="$PROJECT_DIR$/Backend/Controllers/UserController.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Backend/Controllers/UserController.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Backend/Security/Permissions.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Backend/Security/Permissions.cs" afterDir="false" /> <change beforePath="$PROJECT_DIR$/Backend/appsettings.json" beforeDir="false" afterPath="$PROJECT_DIR$/Backend/appsettings.json" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Frontend/angular.json" beforeDir="false" afterPath="$PROJECT_DIR$/Frontend/angular.json" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Frontend/package-lock.json" beforeDir="false" afterPath="$PROJECT_DIR$/Frontend/package-lock.json" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Frontend/package.json" beforeDir="false" afterPath="$PROJECT_DIR$/Frontend/package.json" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Frontend/src/app/app-routing.module.ts" beforeDir="false" afterPath="$PROJECT_DIR$/Frontend/src/app/app-routing.module.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Frontend/src/app/app.component.ts" beforeDir="false" afterPath="$PROJECT_DIR$/Frontend/src/app/app.component.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Frontend/src/app/app.module.ts" beforeDir="false" afterPath="$PROJECT_DIR$/Frontend/src/app/app.module.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Frontend/src/app/entitys/user.ts" beforeDir="false" afterPath="$PROJECT_DIR$/Frontend/src/app/entitys/user.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Frontend/src/app/services/backend.service.ts" beforeDir="false" afterPath="$PROJECT_DIR$/Frontend/src/app/services/backend.service.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Frontend/src/app/services/users.service.ts" beforeDir="false" afterPath="$PROJECT_DIR$/Frontend/src/app/services/users.service.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Frontend/src/app/sites/login/login.component.html" beforeDir="false" afterPath="$PROJECT_DIR$/Frontend/src/app/sites/login/login.component.html" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Frontend/src/app/sites/login/login.component.scss" beforeDir="false" afterPath="$PROJECT_DIR$/Frontend/src/app/sites/login/login.component.scss" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Frontend/src/app/sites/login/login.component.ts" beforeDir="false" afterPath="$PROJECT_DIR$/Frontend/src/app/sites/login/login.component.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Frontend/src/index.html" beforeDir="false" afterPath="$PROJECT_DIR$/Frontend/src/index.html" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Frontend/src/styles.scss" beforeDir="false" afterPath="$PROJECT_DIR$/Frontend/src/styles.scss" afterDir="false" />
</list> </list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -20,6 +40,7 @@
<option name="RECENT_TEMPLATES"> <option name="RECENT_TEMPLATES">
<list> <list>
<option value="TypeScript File" /> <option value="TypeScript File" />
<option value="SCSS File" />
</list> </list>
</option> </option>
</component> </component>
@@ -48,6 +69,7 @@
"RunOnceActivity.ShowReadmeOnStart": "true", "RunOnceActivity.ShowReadmeOnStart": "true",
"SHARE_PROJECT_CONFIGURATION_FILES": "true", "SHARE_PROJECT_CONFIGURATION_FILES": "true",
"WebServerToolWindowFactoryState": "false", "WebServerToolWindowFactoryState": "false",
"list.type.of.created.stylesheet": "SCSS",
"nodejs_package_manager_path": "npm", "nodejs_package_manager_path": "npm",
"settings.editor.selected.configurable": "project.propVCSSupport.DirectoryMappings", "settings.editor.selected.configurable": "project.propVCSSupport.DirectoryMappings",
"ts.external.directory.path": "D:\\Programmierstuff\\Projekte\\WebDesktop 2.0\\Frontend\\node_modules\\typescript\\lib", "ts.external.directory.path": "D:\\Programmierstuff\\Projekte\\WebDesktop 2.0\\Frontend\\node_modules\\typescript\\lib",
@@ -130,6 +152,7 @@
<workItem from="1662126115999" duration="9904000" /> <workItem from="1662126115999" duration="9904000" />
<workItem from="1662204907636" duration="15449000" /> <workItem from="1662204907636" duration="15449000" />
<workItem from="1662228779224" duration="302000" /> <workItem from="1662228779224" duration="302000" />
<workItem from="1662539031966" duration="6394000" />
</task> </task>
<servers /> <servers />
</component> </component>

View File

@@ -36,7 +36,6 @@ public class UserController : ControllerBase {
} }
[HttpGet("token")] [HttpGet("token")]
[Authorized]
public ActionResult<AccessToken> GetToken() { public ActionResult<AccessToken> GetToken() {
return this.FromLogicResult(_logic.GenerateToken(_logic.GetCurrentUserRefreshToken())); return this.FromLogicResult(_logic.GenerateToken(_logic.GetCurrentUserRefreshToken()));
} }

View File

@@ -33,12 +33,12 @@
], ],
"Messages": { "Messages": {
"Users": { "Users": {
"NotFound": "This user does not exist", "NotFound": "Dieser Benutzer existiert nicht!",
"InvalidEditData": "Userdata does not match security rules", "InvalidEditData": "Benutzerdaten entsprechen nicht den Sicherheitsvorschritfen!",
"InvalidRegisterData": "Userdata does not match security rules", "InvalidRegisterData": "Benutzerdaten entsprechen nicht den Sicherheitsvorschritfen!",
"WrongPassword": "Wrong password", "WrongPassword": "Falsches Passwort!",
"UsernameOrEmailExist": "This username or email already exist", "UsernameOrEmailExist": "Der Benutzername oder die E-Mail existieren bereits",
"InvalidRefreshToken": "Invalid RefreshToken" "InvalidRefreshToken": "Dein RefreshToken ist abgelaufen!"
} }
} }
} }

View File

@@ -31,6 +31,7 @@
"src/assets" "src/assets"
], ],
"styles": [ "styles": [
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
"src/styles.scss" "src/styles.scss"
], ],
"scripts": [] "scripts": []
@@ -99,6 +100,7 @@
"src/assets" "src/assets"
], ],
"styles": [ "styles": [
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
"src/styles.scss" "src/styles.scss"
], ],
"scripts": [] "scripts": []

View File

@@ -9,10 +9,12 @@
"version": "0.0.0", "version": "0.0.0",
"dependencies": { "dependencies": {
"@angular/animations": "~13.1.0", "@angular/animations": "~13.1.0",
"@angular/cdk": "^13.3.9",
"@angular/common": "~13.1.0", "@angular/common": "~13.1.0",
"@angular/compiler": "~13.1.0", "@angular/compiler": "~13.1.0",
"@angular/core": "~13.1.0", "@angular/core": "~13.1.0",
"@angular/forms": "~13.1.0", "@angular/forms": "~13.1.0",
"@angular/material": "^13.3.9",
"@angular/platform-browser": "~13.1.0", "@angular/platform-browser": "~13.1.0",
"@angular/platform-browser-dynamic": "~13.1.0", "@angular/platform-browser-dynamic": "~13.1.0",
"@angular/router": "~13.1.0", "@angular/router": "~13.1.0",
@@ -347,6 +349,28 @@
"@angular/core": "13.1.3" "@angular/core": "13.1.3"
} }
}, },
"node_modules/@angular/cdk": {
"version": "13.3.9",
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-13.3.9.tgz",
"integrity": "sha512-XCuCbeuxWFyo3EYrgEYx7eHzwl76vaWcxtWXl00ka8d+WAOtMQ6Tf1D98ybYT5uwF9889fFpXAPw98mVnlo3MA==",
"dependencies": {
"tslib": "^2.3.0"
},
"optionalDependencies": {
"parse5": "^5.0.0"
},
"peerDependencies": {
"@angular/common": "^13.0.0 || ^14.0.0-0",
"@angular/core": "^13.0.0 || ^14.0.0-0",
"rxjs": "^6.5.3 || ^7.4.0"
}
},
"node_modules/@angular/cdk/node_modules/parse5": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz",
"integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==",
"optional": true
},
"node_modules/@angular/cli": { "node_modules/@angular/cli": {
"version": "13.1.4", "version": "13.1.4",
"resolved": "https://registry.npmjs.org/@angular/cli/-/cli-13.1.4.tgz", "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-13.1.4.tgz",
@@ -472,6 +496,23 @@
"rxjs": "^6.5.3 || ^7.4.0" "rxjs": "^6.5.3 || ^7.4.0"
} }
}, },
"node_modules/@angular/material": {
"version": "13.3.9",
"resolved": "https://registry.npmjs.org/@angular/material/-/material-13.3.9.tgz",
"integrity": "sha512-FU8lcMgo+AL8ckd27B4V097ZPoIZNRHiCe3wpgkImT1qC0YwcyXZVn0MqQTTFSdC9a/aI8wPm3AbTClJEVw5Vw==",
"dependencies": {
"tslib": "^2.3.0"
},
"peerDependencies": {
"@angular/animations": "^13.0.0 || ^14.0.0-0",
"@angular/cdk": "13.3.9",
"@angular/common": "^13.0.0 || ^14.0.0-0",
"@angular/core": "^13.0.0 || ^14.0.0-0",
"@angular/forms": "^13.0.0 || ^14.0.0-0",
"@angular/platform-browser": "^13.0.0 || ^14.0.0-0",
"rxjs": "^6.5.3 || ^7.4.0"
}
},
"node_modules/@angular/platform-browser": { "node_modules/@angular/platform-browser": {
"version": "13.1.3", "version": "13.1.3",
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-13.1.3.tgz", "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-13.1.3.tgz",
@@ -11779,6 +11820,23 @@
"tslib": "^2.3.0" "tslib": "^2.3.0"
} }
}, },
"@angular/cdk": {
"version": "13.3.9",
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-13.3.9.tgz",
"integrity": "sha512-XCuCbeuxWFyo3EYrgEYx7eHzwl76vaWcxtWXl00ka8d+WAOtMQ6Tf1D98ybYT5uwF9889fFpXAPw98mVnlo3MA==",
"requires": {
"parse5": "^5.0.0",
"tslib": "^2.3.0"
},
"dependencies": {
"parse5": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz",
"integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==",
"optional": true
}
}
},
"@angular/cli": { "@angular/cli": {
"version": "13.1.4", "version": "13.1.4",
"resolved": "https://registry.npmjs.org/@angular/cli/-/cli-13.1.4.tgz", "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-13.1.4.tgz",
@@ -11857,6 +11915,14 @@
"tslib": "^2.3.0" "tslib": "^2.3.0"
} }
}, },
"@angular/material": {
"version": "13.3.9",
"resolved": "https://registry.npmjs.org/@angular/material/-/material-13.3.9.tgz",
"integrity": "sha512-FU8lcMgo+AL8ckd27B4V097ZPoIZNRHiCe3wpgkImT1qC0YwcyXZVn0MqQTTFSdC9a/aI8wPm3AbTClJEVw5Vw==",
"requires": {
"tslib": "^2.3.0"
}
},
"@angular/platform-browser": { "@angular/platform-browser": {
"version": "13.1.3", "version": "13.1.3",
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-13.1.3.tgz", "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-13.1.3.tgz",

View File

@@ -11,10 +11,12 @@
"private": true, "private": true,
"dependencies": { "dependencies": {
"@angular/animations": "~13.1.0", "@angular/animations": "~13.1.0",
"@angular/cdk": "^13.3.9",
"@angular/common": "~13.1.0", "@angular/common": "~13.1.0",
"@angular/compiler": "~13.1.0", "@angular/compiler": "~13.1.0",
"@angular/core": "~13.1.0", "@angular/core": "~13.1.0",
"@angular/forms": "~13.1.0", "@angular/forms": "~13.1.0",
"@angular/material": "^13.3.9",
"@angular/platform-browser": "~13.1.0", "@angular/platform-browser": "~13.1.0",
"@angular/platform-browser-dynamic": "~13.1.0", "@angular/platform-browser-dynamic": "~13.1.0",
"@angular/router": "~13.1.0", "@angular/router": "~13.1.0",

View File

@@ -1,9 +1,11 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
import {LoginComponent} from "./sites/login/login.component"; import {LoginComponent} from "./sites/login/login.component";
import {RegisterComponent} from "./sites/register/register.component";
const routes: Routes = [ const routes: Routes = [
{path: "login", component: LoginComponent} {path: "login", component: LoginComponent},
{path: "register", component: RegisterComponent}
]; ];
@NgModule({ @NgModule({

View File

@@ -22,6 +22,7 @@ export class AppComponent implements OnInit {
if (await this.backend.requestToken()) this.loaded = true; if (await this.backend.requestToken()) this.loaded = true;
else await this.router.navigate(["login"]); else await this.router.navigate(["login"]);
this.loaded = true;
}, 0); }, 0);
} }
} }

View File

@@ -5,16 +5,30 @@ import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
import {HttpClientModule} from "@angular/common/http"; import {HttpClientModule} from "@angular/common/http";
import { LoginComponent } from './sites/login/login.component'; import { LoginComponent } from './sites/login/login.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import {MatFormFieldModule} from "@angular/material/form-field";
import {MatInputModule} from "@angular/material/input";
import {MatButtonModule} from "@angular/material/button";
import {MatDividerModule} from "@angular/material/divider";
import { RegisterComponent } from './sites/register/register.component';
import {ReactiveFormsModule} from "@angular/forms";
@NgModule({ @NgModule({
declarations: [ declarations: [
AppComponent, AppComponent,
LoginComponent LoginComponent,
RegisterComponent
], ],
imports: [ imports: [
BrowserModule, BrowserModule,
AppRoutingModule, AppRoutingModule,
HttpClientModule HttpClientModule,
BrowserAnimationsModule,
MatFormFieldModule,
MatInputModule,
MatButtonModule,
MatDividerModule,
ReactiveFormsModule
], ],
providers: [], providers: [],
bootstrap: [AppComponent] bootstrap: [AppComponent]

View File

@@ -1,6 +1,6 @@
export interface User extends UserEditor { export interface User extends UserEditor {
id: string; id: string;
created: Date; created: string;
} }
export interface UserLogin { export interface UserLogin {

View File

@@ -74,7 +74,7 @@ export class BackendService {
} }
} }
return {content: undefined, success: false, code: error.status, message: error.error.title}; return {content: undefined, success: false, code: error.status, message: error.error};
} }
} }

View File

@@ -32,8 +32,8 @@ export class UserApi {
return response.success; return response.success;
} }
public async getUserPermissions(id: string, includeGroupPermissions: boolean = true): Promise<string[]> { public async getUserPermissions(id: string, excludeGroupPermissions: boolean = false): Promise<string[]> {
const response = await this.backend.sendRequest<string[]>(RequestTypes.GET, "users/" + id + "/permissions" + (includeGroupPermissions ? "/raw" : ""), undefined, {authorized: true}); const response = await this.backend.sendRequest<string[]>(RequestTypes.GET, "users/" + id + "/permissions" + (excludeGroupPermissions ? "/raw" : ""), undefined, {authorized: true});
if (!response.success) return []; if (!response.success) return [];
return response.content; return response.content;
} }
@@ -48,7 +48,7 @@ export class UserApi {
return response.success; return response.success;
} }
public async login(login: UserLogin): Promise<{success: boolean, errorMessage: string}> { public async login(login: UserLogin): Promise<{success: boolean, errorMessage: string, errorCode: number}> {
const response = await this.backend.sendRequest<AccessToken>(RequestTypes.PUT, "users/login", login, {withCredentials: true}); const response = await this.backend.sendRequest<AccessToken>(RequestTypes.PUT, "users/login", login, {withCredentials: true});
if (response.success) { if (response.success) {
@@ -56,10 +56,10 @@ export class UserApi {
await this.getAuthorizedUser(); await this.getAuthorizedUser();
} }
return {success: response.success, errorMessage: response.message}; return {success: response.success, errorMessage: response.message, errorCode: response.code};
} }
public async register(register: UserEditor): Promise<{success: boolean, errorMessage: string}> { public async register(register: UserEditor): Promise<{success: boolean, errorMessage: string, errorCode: number}> {
const response = await this.backend.sendRequest<AccessToken>(RequestTypes.POST, "users/register", register, {withCredentials: true}); const response = await this.backend.sendRequest<AccessToken>(RequestTypes.POST, "users/register", register, {withCredentials: true});
if (response.success) { if (response.success) {
@@ -67,7 +67,7 @@ export class UserApi {
await this.getAuthorizedUser(); await this.getAuthorizedUser();
} }
return {success: response.success, errorMessage: response.message}; return {success: response.success, errorMessage: response.message, errorCode: response.code};
} }
public async logout(id: string): Promise<boolean> { public async logout(id: string): Promise<boolean> {

View File

@@ -1 +1,24 @@
<p>login works!</p> <section class="login">
<form class="glass" (submit)="$event.preventDefault(); login(form, username.value, password.value)" #form>
<mat-form-field appearance="fill">
<mat-label>Benutzername oder E-Mail</mat-label>
<input matInput type="text" required #username>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Passwort</mat-label>
<input matInput type="password" required #password>
</mat-form-field>
<button mat-button type="submit" [disabled]="disableLogin">Einloggen</button>
<mat-divider></mat-divider>
<div>
<span>Du besitzt keinen Account? </span>
<a routerLink="/register">Registrieren</a>
</div>
</form>
</section>
<div class="error" #error></div>

View File

@@ -0,0 +1,42 @@
@use "src/styles";
.login {
width: 100vw;
height: 100vh;
overflow: hidden;
background-image: url(styles.$background);
display: flex;
align-items: center;
justify-content: center;
form {
padding: 30px;
display: flex;
gap: 5px;
flex-direction: column;
button[type="submit"] {
width: max-content;
align-self: center;
}
mat-divider {
margin-top: 10px;
}
}
}
.error {
position: absolute;
bottom: 120px;
left: 50%;
transform: translateX(-50%);
background-color: #F00;
padding: 10px;
border-radius: 5px;
opacity: 0;
transition: 500ms;
}

View File

@@ -1,15 +1,45 @@
import { Component, OnInit } from '@angular/core'; import {Component, ElementRef, ViewChild} from '@angular/core';
import {UserApi} from "../../services/users.service";
import {UserLogin} from "../../entitys/user";
import {Router} from "@angular/router";
@Component({ @Component({
selector: 'app-login', selector: 'app-login',
templateUrl: './login.component.html', templateUrl: './login.component.html',
styleUrls: ['./login.component.scss'] styleUrls: ['./login.component.scss']
}) })
export class LoginComponent implements OnInit { export class LoginComponent {
@ViewChild('error') error: ElementRef;
public disableLogin: boolean = false;
constructor() { } constructor(private users: UserApi, private router: Router) { }
ngOnInit(): void { public async login(form: HTMLFormElement, username: string, password: string) {
if (!form.reportValidity()) return;
const login: UserLogin = {usernameOrEmail: username, password: password};
const response = await this.users.login(login);
if (!response.success) {
this.showError(response.errorMessage)
return;
}
await this.router.navigate([""]);
}
public showError(error: string) {
this.disableLogin = true;
this.error.nativeElement.innerText = error;
this.error.nativeElement.style.opacity = "1";
this.error.nativeElement.style.bottom = "150px";
setTimeout(() => {
this.error.nativeElement.style.opacity = "0";
this.error.nativeElement.style.bottom = "120px";
this.disableLogin = false;
}, 5000)
} }
} }

View File

@@ -0,0 +1,57 @@
<section class="login">
<form class="glass" (submit)="$event.preventDefault(); register(form, {
username: username.value,
password: password.value,
firstName: firstname.value,
lastName: lastname.value,
email: email.value
}, pwRepeat.value)" #form>
<table>
<tr>
<td>
<mat-form-field appearance="fill">
<mat-label>Vorname</mat-label>
<input matInput type="text" required #firstname>
</mat-form-field>
</td>
<td>
<mat-form-field appearance="fill">
<mat-label>Nachname</mat-label>
<input matInput type="text" required #lastname>
</mat-form-field>
</td>
</tr>
</table>
<mat-form-field appearance="fill">
<mat-label>Benutzername</mat-label>
<input matInput type="text" required #username>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>E-Mail</mat-label>
<input matInput type="email" required #email>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Passwort</mat-label>
<input matInput type="password" required #password>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Passwort wiederholen</mat-label>
<input matInput type="password" required #pwRepeat>
</mat-form-field>
<button mat-button [disabled]="disableRegister" type="submit">Registrieren</button>
<mat-divider></mat-divider>
<div>
<span>Du hast bereits einen Account? </span>
<a routerLink="/login">Einloggen</a>
</div>
</form>
</section>
<div class="error" #error></div>

View File

@@ -0,0 +1,42 @@
@use "src/styles";
.login {
width: 100vw;
height: 100vh;
overflow: hidden;
background-image: url(styles.$background);
display: flex;
align-items: center;
justify-content: center;
form {
padding: 30px;
display: flex;
gap: 5px;
flex-direction: column;
button[type="submit"] {
width: max-content;
align-self: center;
}
mat-divider {
margin-top: 10px;
}
}
}
.error {
position: absolute;
bottom: 120px;
left: 50%;
transform: translateX(-50%);
background-color: #F00;
padding: 10px;
border-radius: 5px;
opacity: 0;
transition: 500ms;
}

View File

@@ -0,0 +1,64 @@
import {Component, ElementRef, ViewChild} from '@angular/core';
import {UserApi} from "../../services/users.service";
import {Router} from "@angular/router";
import {UserEditor} from "../../entitys/user";
@Component({
selector: 'app-register',
templateUrl: './register.component.html',
styleUrls: ['./register.component.scss']
})
export class RegisterComponent {
@ViewChild('error') error: ElementRef;
public disableRegister: boolean = false;
constructor(private users: UserApi, private router: Router) { }
public async register(form: HTMLFormElement, register: UserEditor, pwRepeat: string) {
if (!form.reportValidity()) return;
if (register.password !== pwRepeat) {
this.showError("Passwörter stimmen nicht überein!");
return;
}
if (!register.email.includes("@") || !register.email.includes(".")) {
this.showError("Bitte gebe eine gültige E-Mail Adresse ein!");
return;
}
if (register.username.includes("@")) {
this.showError("Benutzername enthätlt ungültige Zeichen!");
return;
}
if (register.password.length < 8) {
this.showError("Dein Passwort muss mindestens 8 Zeichen lang sein!");
return;
}
const response = await this.users.register(register);
if (!response.success) {
this.showError(response.errorMessage);
return;
}
await this.router.navigate([""]);
}
public showError(error: string) {
this.disableRegister = true;
this.error.nativeElement.innerText = error;
this.error.nativeElement.style.opacity = "1";
this.error.nativeElement.style.bottom = "150px";
setTimeout(() => {
this.error.nativeElement.style.opacity = "0";
this.error.nativeElement.style.bottom = "120px";
this.disableRegister = false;
}, 5000)
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

4
Frontend/src/colors.scss Normal file
View File

@@ -0,0 +1,4 @@
$dark: #0D1321;
$medium: #1D2D44;
$light: #3E5C76;
$text: #FFFFFF;

View File

@@ -6,8 +6,11 @@
<base href="/"> <base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico"> <link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
</head> </head>
<body> <body class="mat-typography">
<app-root></app-root> <app-root></app-root>
</body> </body>
</html> </html>

View File

@@ -1 +1,31 @@
/* You can add global styles to this file, and also import other style files */ /* You can add global styles to this file, and also import other style files */
@use "colors";
html, body { height: 100%; overflow: hidden; }
body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
$background: "/assets/background.png";
* {
color: colors.$text;
}
.glass {
background: rgba(colors.$light, 0.15);
backdrop-filter: blur(10px);
border-radius: 20px;
overflow: hidden;
}
@mixin mat-select-theme() {
.mat-form-field-appearance-fill .mat-form-field-underline::before {
background-color: colors.$text;
}
.mat-divider {
border-top-color: colors.$text;
}
}
@include mat-select-theme();