Adding first working sign up.
This commit is contained in:
parent
21d9f46d03
commit
3396c36529
2
Build.PL
2
Build.PL
@ -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;
|
||||
|
@ -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()
|
||||
|
111
lib/BurguillosInfo/Controller/UserConquer.pm
Normal file
111
lib/BurguillosInfo/Controller/UserConquer.pm
Normal 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;
|
@ -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();',
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user