Adding first working sign up.

This commit is contained in:
Sergiotarxz 2023-11-19 19:26:59 +01:00
parent 21d9f46d03
commit 3396c36529
9 changed files with 266 additions and 20 deletions

View File

@ -29,6 +29,8 @@ my $build = Module::Build->new(
'Lingua::Stem::Snowball' => 0,
'Mojo::Redis' => 0,
'DBIx::Class' => 0,
'UUID::URandom' => 0,
'Crypt::Bcrypt' => 0,
},
);
$build->create_build_script;

View File

@ -14,17 +14,20 @@ export default class Conquer {
private disableSetRotationOffset = false
private conquerLogin: HTMLDivElement
private conquerLoginGoToRegister: HTMLAnchorElement
private conquerLoginError: HTMLParagraphElement
private conquerLoginSuccess: HTMLParagraphElement
private conquerRegisterGoToLogin: HTMLAnchorElement
private conquerRegister: HTMLDivElement
private conquerRegisterUsername: HTMLInputElement
private conquerRegisterPassword: HTMLInputElement
private conquerRegisterRepeatPassword: HTMLInputElement
private conquerRegisterSubmit: HTMLButtonElement
private conquerRegisterError: HTMLParagraphElement
private alpha = 0;
private beta = 0;
private gamma = 0;
static start() {
const conquerContainer = document.querySelector(".conquer-container");
window.onerror = function myErrorHandler(errorMsg, url, lineNumber) {
alert("Error occured: " + errorMsg);//or any message
return false;
}
if (conquerContainer !== null) {
if (!(conquerContainer instanceof HTMLDivElement)) {
console.error(".conquer-container is not a div.");
@ -60,6 +63,16 @@ export default class Conquer {
this.conquerLoginGoToRegister.addEventListener('click', () => {
this.goToRegister()
})
const conquerLoginError = document.querySelector('.conquer-login-error')
if (conquerLoginError === null || !(conquerLoginError instanceof HTMLParagraphElement)) {
Conquer.fail('Unable to find conquer login error.')
}
this.conquerLoginError = conquerLoginError
const conquerLoginSuccess = document.querySelector('.conquer-login-success')
if (conquerLoginSuccess === null || !(conquerLoginSuccess instanceof HTMLParagraphElement)) {
Conquer.fail('Unable to find conquer login success.')
}
this.conquerLoginSuccess = conquerLoginSuccess
}
async goToRegister(): Promise<void> {
const isLogged = await this.isLogged();
@ -95,6 +108,81 @@ export default class Conquer {
this.conquerRegisterGoToLogin.addEventListener('click', () => {
this.goToLogin()
})
const conquerRegisterUsername = document.querySelector('.conquer-register-username')
if (conquerRegisterUsername === null || !(conquerRegisterUsername instanceof HTMLInputElement)) {
Conquer.fail('No username field in conquer register.')
}
this.conquerRegisterUsername = conquerRegisterUsername
const conquerRegisterPassword = document.querySelector('.conquer-register-password')
if (conquerRegisterPassword === null || !(conquerRegisterPassword instanceof HTMLInputElement)) {
Conquer.fail('No password field in conquer register.')
}
this.conquerRegisterPassword = conquerRegisterPassword
const conquerRegisterRepeatPassword = document.querySelector('.conquer-register-repeat-password')
if (conquerRegisterRepeatPassword === null || !(conquerRegisterRepeatPassword instanceof HTMLInputElement)) {
Conquer.fail('No repeat password field in conquer register.')
}
this.conquerRegisterRepeatPassword = conquerRegisterRepeatPassword
const conquerRegisterSubmit = document.querySelector('.conquer-register-submit')
if (conquerRegisterSubmit === null || !(conquerRegisterSubmit instanceof HTMLButtonElement)) {
Conquer.fail('No register submit button found.')
}
this.conquerRegisterSubmit = conquerRegisterSubmit
this.conquerRegisterSubmit.addEventListener('click', (event: Event) => {
event.preventDefault()
this.onRegisterRequest()
})
const conquerRegisterError = document.querySelector('.conquer-register-error')
if (conquerRegisterError === null || !(conquerRegisterError instanceof HTMLParagraphElement)) {
Conquer.fail('Unable to find the conquer error element.')
}
this.conquerRegisterError = conquerRegisterError
}
unsetLoginAndRegisterErrors() {
this.conquerRegisterError.classList.add('conquer-display-none')
this.conquerLoginError.classList.add('conquer-display-none')
}
async onRegisterRequest(): Promise<void> {
const username = this.conquerRegisterUsername.value
const password = this.conquerRegisterPassword.value
const repeatPassword = this.conquerRegisterRepeatPassword.value
const urlUser = new URL('/conquer/user', window.location.protocol + '//' + window.location.hostname + ':' + window.location.port)
const response = await fetch(urlUser, {
method: 'PUT',
body: JSON.stringify({
username: username,
password: password,
repeat_password: repeatPassword
})
})
let responseJson
try {
responseJson = await response.json()
} catch(e) {
console.error(e)
return;
}
if (response.status !== 200) {
this.addNewRegisterError(responseJson.error)
return
}
this.addNewLoginSuccessText(`Usuario registrado ${username}.`)
this.goToLogin()
}
addNewLoginSuccessText(message: string): void {
this.unsetLoginAndRegisterErrors()
this.conquerLoginSuccess.innerText = message
this.conquerLoginSuccess.classList.remove('conquer-display-none')
}
addNewRegisterError(error: string): void {
this.unsetLoginAndRegisterErrors()
this.conquerLoginSuccess.classList.add('conquer-display-none')
this.conquerRegisterError.innerText = error
this.conquerRegisterError.classList.remove('conquer-display-none')
}
async run() {
this.fillConquerLogin()

View File

@ -0,0 +1,111 @@
package BurguillosInfo::Controller::UserConquer;
use v5.34.1;
use strict;
use warnings;
use utf8;
use Mojo::Base 'Mojolicious::Controller', '-signatures';
use UUID::URandom qw/create_uuid_string/;
use Crypt::Bcrypt qw/bcrypt bcrypt_check/;
use Crypt::URandom qw/urandom/;
use BurguillosInfo::Schema;
my $username_minimum_chars = 3;
my $username_maximum_chars = 15;
my $password_minimum_chars = 8;
my $password_maximum_chars = 4096;
sub create ($self) {
my $input;
eval { $input = $self->req->json; };
if ($@) {
say STDERR $@;
return $self->render( text => 'Expecting json', status => 400 );
}
my $username = $input->{username};
my $password = $input->{password};
my $repeat_password = $input->{repeat_password};
return
unless $self->_createCheckInput( $username, $password, $repeat_password );
return $self->_createUser($username, $password);
}
sub _createUser ( $self, $username, $password ) {
my $user;
my $uuid = create_uuid_string();
my $new_salt = urandom(16);
my $encrypted_password = bcrypt $password, '2b', 12, $new_salt;
eval {
$user = BurguillosInfo::Schema->Schema->resultset('ConquerUser')->new(
{
uuid => $uuid,
encrypted_password => $encrypted_password,
username => $username
}
);
$user->insert;
};
if ($@) {
if ( $@ =~ /Key \((.*?)\)=\((.*?)\) already exists\./ ) {
return $self->renderError( 400,
"The key $1 ($2) already exists in the database.",
);
}
say STDERR $@;
return $self->renderError( 400,
'No se pudo crear el usuario por razones desconocidas.' );
}
$self->render(status => 200, json => $user->serialize_to_owner);
return 1;
}
sub renderError ( $self, $status, $message ) {
$self->render( status => $status, json => { error => $message } );
return 0;
}
sub _createCheckInput ( $self, $username, $password, $repeat_password ) {
if ( !defined $username
|| $username !~
/^(?:\w|\d|[ÑÁÉÍÓÚñáéíóú ]){$username_minimum_chars,$username_maximum_chars}$/
)
{
return $self->renderError( 400,
"Username invalido, las reglas son tamaño entre $username_minimum_chars y $username_maximum_chars"
. ' carácteres y solo se podrán usar letras, números y espacios.'
);
}
if ( !defined $password
|| $password eq $username
|| $password !~ /^.{$password_minimum_chars,$password_maximum_chars}$/
|| $password =~ /^\d+$/ )
{
return $self->renderError(
400,
'Contraseña invalida, las reglas son la contraseña debe ser'
. ' distinta al nombre de usuario, la contraseña debe tener entre'
. " $password_minimum_chars y $password_maximum_chars carácteres"
. ' (Tu contraseña no se guardará en texto plano, el límite de'
. " $password_maximum_chars caracteres es para evitar denegaciones"
. ' de servicio), la contraseña no puede estar compuesta solo de números.',
);
}
if ( !defined $repeat_password || $password ne $repeat_password ) {
$self->renderError(
400,
'El campo de repetir contraseña debe coincidir de forma'
. ' totalmente exacta con el campo de contraseña para asegurar'
. ' que podrás recordar la contraseña y/o que no has cometido'
. ' ningún error, si pierdes el acceso a tu cuenta no podrás'
. ' recuperarlo de ningún modo.',
);
return 0;
}
return 1;
}
1;

View File

@ -56,6 +56,7 @@ sub MIGRATIONS {
last_activity TEXT NOT NULL DEFAULT NOW(),
is_admin boolean NOT NULL DEFAULT false
);',
'ALTER TABLE conquer_user ADD COLUMN registration_date TIMESTAMP NOT NULL DEFAULT NOW();',
);
}

View File

@ -7,32 +7,51 @@ use warnings;
use parent 'DBIx::Class::Core';
__PACKAGE__->table('players');
use feature 'signatures';
__PACKAGE__->table('conquer_user');
__PACKAGE__->add_columns(
uuid => {
data_type => 'uuid',
data_type => 'uuid',
is_nullable => 0,
},
username => {
data_type => 'text',
data_type => 'text',
is_nullable => 0,
},
encrypted_password => {
data_type => 'text',
data_type => 'text',
is_nullable => 0,
},
last_activity => {
data_type => 'timestamp',
is_nullable => 0,
data_type => 'timestamp',
is_nullable => 0,
default_value => \'NOW()',
},
registration_date => {
data_type => 'timestamp',
is_nullable => 0,
default_value => \'NOW()',
},
is_admin => {
data_type => 'boolean',
is_nullable => 0,
data_type => 'boolean',
is_nullable => 0,
default_value => \'0',
}
);
sub serialize_to_owner ($self) {
return {
kind => 'ConquerUser',
uuid => $self->uuid,
username => $self->username,
is_admin => $self->is_admin,
last_activity => $self->last_activity,
registration_date => $self->registration_date,
};
}
__PACKAGE__->set_primary_key('uuid');
__PACKAGE__->add_unique_constraint("unique_constraint_username", ['username']);
__PACKAGE__->add_unique_constraint( "unique_constraint_username",
['username'] );
1;

View File

@ -8,10 +8,20 @@ body {
min-height: 100%;
width: 100%;
height: 100%; }
body p.conquer-register-error, body p.conquer-login-error, body p.conquer-login-success {
color: red;
margin: 3px;
font-size: 1.3rem;
background: blanchedalmond;
padding: 3px;
border-radius: 10px;
border: solid 1px black; }
body p.conquer-login-success {
color: green; }
body div.conquer-login, body div.conquer-register {
position: fixed;
color: black;
font-size: 25px;
font-size: 1.5rem;
border-radius: 30px;
background: darkseagreen;
top: calc( 50% - 200px - 10px);
@ -26,7 +36,7 @@ body {
display: flex;
flex-direction: column;
justify-content: center; }
body div.conquer-display-none {
body .conquer-display-none {
display: none; }
body div.conquer-container {
height: 100dvh;

View File

@ -16,6 +16,18 @@ html {
}
body {
p.conquer-register-error, p.conquer-login-error, p.conquer-login-success {
color: red;
margin: 3px;
font-size: 1.3rem;
background: blanchedalmond;
padding: 3px;
border-radius: 10px;
border: solid 1px black;
}
p.conquer-login-success {
color: green;
}
div.conquer-login,div.conquer-register {
form {
width: 100%;
@ -26,7 +38,7 @@ body {
}
position: fixed;
color: black;
font-size: 25px;
font-size: 1.5rem;
border-radius: 30px;
background: darkseagreen;
top: calc( 50% - 200px - 10px );
@ -36,7 +48,7 @@ body {
width: 300px;
z-index: 1;
}
div.conquer-display-none {
.conquer-display-none {
display: none;
}
div.conquer-container {

File diff suppressed because one or more lines are too long

View File

@ -10,23 +10,26 @@
<body>
<div class="conquer-login conquer-display-none">
<form>
<p class="conquer-login-error conquer-display-none"></p>
<p class="conquer-login-success conquer-display-none"></p>
<label>Nombre de usuario</label>
<input class="conquer-login-username"/>
<label>Contraseña</label>
<input class="conquer-login-password" type="password"/>
<button class="conquer-login-sumbit">Inicia sesión</button>
<button class="conquer-login-submit">Inicia sesión</button>
<p>¿No tienes cuenta aun? <a href="#" class="conquer-login-go-to-register">Registrate</a></p>
</form>
</div>
<div class="conquer-register conquer-display-none">
<form>
<p class="conquer-register-error conquer-display-none"></p>
<label>Nombre de usuario</label>
<input class="conquer-register-username"/>
<label>Contraseña</label>
<input class="conquer-register-password" type="password"/>
<label>Repite la contraseña</label>
<input class="conquer-register-repeat-password" type="password"/>
<button class="conquer-register-sumbit">Finaliza el registro</button>
<button class="conquer-register-submit">Finaliza el registro</button>
<p>¿Ya estás registrado? <a href="#" class="conquer-register-go-to-login">Inicia Sesión</a>.</p>
</form>
</div>