Use www-form instead of json for DTO

This commit is contained in:
2025-06-19 21:34:33 -07:00
parent 2b264a75e9
commit d1ff89fb19
8 changed files with 101 additions and 58 deletions
@@ -1,7 +1,7 @@
<form class="center big-frame background-border" #accountForm="ngForm" (ngSubmit)="onSubmit()"> <form class="center big-frame background-border" #accountForm="ngForm" (ngSubmit)="onSubmit()">
<div class="frame-item"> <div class="frame-item">
<input type="text" [(ngModel)]="user.email" name="email" placeholder=" " /> <input type="text" [(ngModel)]="email" name="email" placeholder=" " />
<label>Email</label> <label>Email</label>
</div> </div>
@@ -1,5 +1,5 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http'; import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { Router, ActivatedRoute } from '@angular/router'; import { Router, ActivatedRoute } from '@angular/router';
import { Account } from '../../../models/Account'; import { Account } from '../../../models/Account';
@@ -12,7 +12,7 @@ import { CommonModule } from '@angular/common';
imports: [ FormsModule, CommonModule ] imports: [ FormsModule, CommonModule ]
}) })
export class ForgotPasswordComponent { export class ForgotPasswordComponent {
user!: Account; email: string = "";
errorMsgs: string[] = []; errorMsgs: string[] = [];
returnURL: string = '/'; returnURL: string = '/';
@@ -23,12 +23,26 @@ export class ForgotPasswordComponent {
}); });
} }
sleep(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms));
}
onSubmit() { onSubmit() {
// Clear errors
this.errorMsgs = []; this.errorMsgs = [];
// Send to server and wait for response
this.errorMsgs.push("Waiting for login response from server"); this.errorMsgs.push("Waiting for login response from server");
this.http.post<Account>( "api/account/sendresetpassword", this.user, { responseType: 'json' } ).subscribe({ const body = new HttpParams()
next: data => { .set("Email", this.email)
const headers = new HttpHeaders({
'Content-Type': 'application/x-www-form-urlencoded'
});
this.http.post<Account>( "https://mistox.com/api/account/sendresetpassword", body, { headers } ).subscribe({
next: async (data) => {
if (data.error.length === 0){ if (data.error.length === 0){
this.errorMsgs = ["Reset password send"];
await this.sleep(3000);
this.router.navigate([this.returnURL]); this.router.navigate([this.returnURL]);
}else{ }else{
this.errorMsgs = []; this.errorMsgs = [];
@@ -36,7 +50,7 @@ export class ForgotPasswordComponent {
} }
}, },
error: err => { error: err => {
this.errorMsgs.push("HTTP Error: ", err); console.log("HTTP Error Signing In: ", err);
} }
}); });
} }
@@ -2,12 +2,12 @@
<h3>Login</h3> <h3>Login</h3>
<div class="frame-item"> <div class="frame-item">
<input type="text" [(ngModel)]="user.userName" name="userName" placeholder=" " autocomplete="username" /> <input type="text" [(ngModel)]="UserName" name="userName" placeholder=" " autocomplete="username" />
<label>UserName</label> <label>UserName</label>
</div> </div>
<div class="frame-item"> <div class="frame-item">
<input type="password" [(ngModel)]="user.passwordHash" name="password" placeholder=" " autocomplete="current-password" /> <input type="password" [(ngModel)]="Password" name="password" placeholder=" " autocomplete="current-password" />
<label>Password</label> <label>Password</label>
</div> </div>
@@ -19,7 +19,7 @@
<div class="frame-forgot"> <div class="frame-forgot">
<div class="sub-frame"> <div class="sub-frame">
Stay Logged In Stay Logged In
<input type="checkbox" [(ngModel)]="user.emailVerified" name="stayLoggedIn" /> <input type="checkbox" [(ngModel)]="StayLoggedIn" name="stayLoggedIn" />
</div> </div>
<div class="sub-frame"> <div class="sub-frame">
<a routerLink="/account/forgotpassword">Forgot Password</a> <a routerLink="/account/forgotpassword">Forgot Password</a>
@@ -14,7 +14,9 @@ import { Authentication, SessionType } from '../../../services/Authentication';
standalone: true standalone: true
}) })
export class LoginComponent { export class LoginComponent {
user: Account = { id: -1, userName: "", email: "", passwordHash: "", emailVerified: false, error: "", siteData: { accountID: -1, failedPasswordLock: false, passwordAttempts: 0, role: "", currentPasswordAttempts: 0, emailToken: "" } }; UserName: string = "";
Password: string = "";
StayLoggedIn: boolean = false;
errorMsgs: string[] = []; errorMsgs: string[] = [];
returnURL: string = '/'; returnURL: string = '/';
@@ -28,26 +30,21 @@ export class LoginComponent {
onSubmit() { onSubmit() {
this.errorMsgs = []; this.errorMsgs = [];
if (!this.user.userName) { if (!this.UserName) {
this.errorMsgs.push("The 'username' field is required"); this.errorMsgs.push("The 'username' field is required");
} }
if (!this.user.passwordHash) { if (!this.Password) {
this.errorMsgs.push("The 'password' field is required"); this.errorMsgs.push("The 'password' field is required");
} }
if (this.user.passwordHash.length < 6) { if (this.Password.length < 6) {
this.errorMsgs.push("Password must be at least 6 Characters long"); this.errorMsgs.push("Password must be at least 6 Characters long");
} }
if (this.errorMsgs.length > 0) { if (this.errorMsgs.length > 0) {
return; return;
} }
let sType: SessionType = SessionType.Session; this.errorMsgs.push("Waiting for response from server");
if( this.user.emailVerified ){ this.auth.Login(this.UserName, this.Password, this.StayLoggedIn).subscribe(
sType = SessionType.Forever;
}
this.errorMsgs.push("Waiting for login response from server");
this.auth.Login(this.user, sType).subscribe(
data => { data => {
if (data.error.length === 0){ if (data.error.length === 0){
this.router.navigate([this.returnURL]); this.router.navigate([this.returnURL]);
@@ -14,7 +14,6 @@ import { Authentication } from '../../../services/Authentication';
standalone: true standalone: true
}) })
export class LogoutComponent { export class LogoutComponent {
user: Account = { id: -1, userName: "", email: "", passwordHash: "", emailVerified: false, error: "", siteData: { accountID: -1, failedPasswordLock: false, passwordAttempts: 0, role: "", currentPasswordAttempts: 0, emailToken: "" } };
errorMsgs: string[] = []; errorMsgs: string[] = [];
returnURL: string = '/'; returnURL: string = '/';
@@ -2,28 +2,28 @@
<h3>Register</h3> <h3>Register</h3>
<div class="frame-item"> <div class="frame-item">
<input type="text" [(ngModel)]="user.userName" name="userName" placeholder=" " autocomplete="username" /> <input type="text" [(ngModel)]="userName" name="userName" placeholder=" " autocomplete="username" />
<label>UserName</label> <label>UserName</label>
</div> </div>
<div class="frame-item"> <div class="frame-item">
<input type="email" [(ngModel)]="user.email" name="password" placeholder=" " autocomplete="current-password" /> <input type="email" [(ngModel)]="email" name="email" placeholder=" " autocomplete="current-password" />
<label>Email</label> <label>Email</label>
</div> </div>
<div class="frame-item"> <div class="frame-item">
<input type="password" [(ngModel)]="user.passwordHash" name="password" placeholder=" " autocomplete="current-password" /> <input type="password" [(ngModel)]="passwordHash" name="password" placeholder=" " autocomplete="current-password" />
<label>Password</label> <label>Password</label>
</div> </div>
<div class="frame-item"> <div class="frame-item">
<input type="password" [(ngModel)]="user.error" name="password" placeholder=" " autocomplete="current-password" /> <input type="password" [(ngModel)]="passwordHash2" name="repeat password" placeholder=" " autocomplete="current-password" />
<label>Password</label> <label>Repeat Password</label>
</div> </div>
<div class="flex-row"> <div class="flex-row">
<div class="frame-button"> <div class="frame-button">
<input class="submit" type="button" value="LOGIN" /> <input class="submit" type="submit" value="REGISTER" />
</div> </div>
</div> </div>
@@ -1,5 +1,5 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http'; import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { Router, ActivatedRoute } from '@angular/router'; import { Router, ActivatedRoute } from '@angular/router';
import { Account } from '../../../models/Account'; import { Account } from '../../../models/Account';
@@ -12,7 +12,12 @@ import { CommonModule } from '@angular/common';
imports: [ FormsModule, CommonModule ] imports: [ FormsModule, CommonModule ]
}) })
export class RegisterComponent { export class RegisterComponent {
user!: Account; userName: string = ""
email: string = "";
passwordHash: string = "";
passwordHash2: string = "";
error: string = "";
errorMsgs: string[] = []; errorMsgs: string[] = [];
returnURL: string = '/'; returnURL: string = '/';
@@ -23,30 +28,49 @@ export class RegisterComponent {
}); });
} }
sleep(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms));
}
onSubmit() { onSubmit() {
// Clear errors
this.errorMsgs = []; this.errorMsgs = [];
if (!this.user.userName) { // Validate data
const regex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
if (!regex.test(this.email)){
this.errorMsgs.push("A valid email is required");
}
if (!this.userName) {
this.errorMsgs.push("The 'username' field is required"); this.errorMsgs.push("The 'username' field is required");
} }
if (!this.user.passwordHash) { if (!this.passwordHash) {
this.errorMsgs.push("The 'password' field is required"); this.errorMsgs.push("The 'password' field is required");
} }
if (this.user.passwordHash.length < 6) { if (this.passwordHash.length < 6) {
this.errorMsgs.push("Password must be at least 6 Characters long"); this.errorMsgs.push("Password must be at least 6 Characters long");
} }
if (this.user.passwordHash !== this.user.error){ if (this.passwordHash !== this.passwordHash2){
this.errorMsgs.push("Passwords don't match"); this.errorMsgs.push("Passwords don't match");
} }
if (this.errorMsgs.length > 0) { if (this.errorMsgs.length > 0) {
return; return;
} }
this.user.error = ""; // Send to server and wait for response
this.errorMsgs.push("Waiting for login response from server"); this.errorMsgs.push("Waiting for response from server");
this.http.post<Account>( "api/account/register", this.user, { responseType: 'json' } ).subscribe({ const body = new HttpParams()
next: data => { .set("Email", this.email)
.set("UserName", this.userName)
.set("PasswordHash", this.passwordHash);
const headers = new HttpHeaders({
'Content-Type': 'application/x-www-form-urlencoded'
});
this.http.post<Account>( "https://mistox.com/api/account/register", body, { headers } ).subscribe({
next: async (data) => {
if (data.error.length === 0){ if (data.error.length === 0){
this.errorMsgs = ["Account Created"];
await this.sleep(3000);
this.router.navigate([this.returnURL]); this.router.navigate([this.returnURL]);
}else{ }else{
this.errorMsgs = []; this.errorMsgs = [];
@@ -54,7 +78,7 @@ export class RegisterComponent {
} }
}, },
error: err => { error: err => {
this.errorMsgs.push("HTTP Error: ", err); console.log("HTTP Error Signing In: ", err);
} }
}); });
} }
@@ -1,7 +1,7 @@
import { Injectable } from "@angular/core"; import { Injectable } from "@angular/core";
import { Account } from "../models/Account"; import { Account } from "../models/Account";
import { BehaviorSubject, Observable } from "rxjs"; import { BehaviorSubject, Observable } from "rxjs";
import { HttpClient } from "@angular/common/http"; import { HttpClient, HttpHandler, HttpHeaders, HttpParams } from "@angular/common/http";
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class Authentication{ export class Authentication{
@@ -11,13 +11,22 @@ export class Authentication{
constructor( private http: HttpClient){ } constructor( private http: HttpClient){ }
Login(LoginCredentials: Account, StayLoggedIn :SessionType): Observable<Account> { Login(UserName: string, Password: string, StayLoggedIn: boolean): Observable<Account> {
let sub = this.http.post<Account>( "https://mistox.com/api/account/login", LoginCredentials, { responseType: 'json' } );
const body = new HttpParams()
.set("UserName", UserName)
.set("PasswordHash", Password)
.set("StayLoggedIn", StayLoggedIn );
const headers = new HttpHeaders({
'Content-Type': 'application/x-www-form-urlencoded'
});
let sub = this.http.post<Account>( "https://mistox.com/api/account/login", body, { headers } );
sub.subscribe({ sub.subscribe({
next: data => { next: data => {
if (data.error.length === 0){ if (data.error.length === 0){
this._user.next(data); this._user.next(data);
this.setUserToStorage(data, StayLoggedIn); this.setUserToStorage(data, StayLoggedIn == true ? SessionType.Forever : SessionType.Session);
} }
}, },
error: err => { error: err => {