From 1447b2fa6e8478f467dd8df7e4770b010c3fc0d9 Mon Sep 17 00:00:00 2001 From: Sergiotarxz Date: Sun, 19 Nov 2023 23:14:02 +0100 Subject: [PATCH] Adding initial login support. --- Build.PL | 1 + js-src/conquer/index.ts | 77 ++++++++++++++++--- lib/BurguillosInfo.pm | 1 + lib/BurguillosInfo/Controller/Metrics.pm | 2 - lib/BurguillosInfo/Controller/UserConquer.pm | 67 ++++++++++++---- lib/BurguillosInfo/DB/Migrations.pm | 6 +- .../Schema/Result/ConquerUser.pm | 1 + public/js/bundle.js | 2 +- 8 files changed, 128 insertions(+), 29 deletions(-) diff --git a/Build.PL b/Build.PL index ea3a025..63ee73a 100755 --- a/Build.PL +++ b/Build.PL @@ -31,6 +31,7 @@ my $build = Module::Build->new( 'DBIx::Class' => 0, 'UUID::URandom' => 0, 'Crypt::Bcrypt' => 0, + 'DBIx::Class::TimeStamp' => 0, }, ); $build->create_build_script; diff --git a/js-src/conquer/index.ts b/js-src/conquer/index.ts index 401c1f9..6e02221 100644 --- a/js-src/conquer/index.ts +++ b/js-src/conquer/index.ts @@ -16,6 +16,9 @@ export default class Conquer { private conquerLoginGoToRegister: HTMLAnchorElement private conquerLoginError: HTMLParagraphElement private conquerLoginSuccess: HTMLParagraphElement + private conquerLoginUsername: HTMLInputElement + private conquerLoginPassword: HTMLInputElement + private conquerLoginSubmit: HTMLButtonElement private conquerRegisterGoToLogin: HTMLAnchorElement private conquerRegister: HTMLDivElement private conquerRegisterUsername: HTMLInputElement @@ -73,7 +76,54 @@ export default class Conquer { Conquer.fail('Unable to find conquer login success.') } this.conquerLoginSuccess = conquerLoginSuccess + const conquerLoginUsername = document.querySelector('.conquer-login-username') + if (conquerLoginUsername === null || !(conquerLoginUsername instanceof HTMLInputElement)) { + Conquer.fail('Unable to find conquer login username field.') + } + this.conquerLoginUsername = conquerLoginUsername + const conquerLoginPassword = document.querySelector('.conquer-login-password') + if (conquerLoginPassword === null || !(conquerLoginPassword instanceof HTMLInputElement)) { + Conquer.fail('Unable to find conquer login password field.') + } + this.conquerLoginPassword = conquerLoginPassword + const conquerLoginSubmit = document.querySelector('.conquer-login-submit') + if (conquerLoginSubmit === null || !(conquerLoginSubmit instanceof HTMLButtonElement)) { + Conquer.fail('Unable to find the submit button for the login.') + } + this.conquerLoginSubmit = conquerLoginSubmit + this.conquerLoginSubmit.addEventListener('click', () => { + this.onLoginRequested() + }); } + + async onLoginRequested(): Promise { + const username = this.conquerLoginUsername.value; + const password = this.conquerLoginPassword.value; + const urlUser = new URL('/conquer/user/login', window.location.protocol + '//' + window.location.hostname + ':' + window.location.port) + let responseJson + let status + try { + const response = await fetch(urlUser, { + method: 'POST', + body: JSON.stringify({ + username: username, + password: password, + }) + }) + responseJson = await response.json() + status = response.status + } catch(e) { + console.error(e) + this.addNewLoginError('El servidor ha enviado datos inesperados.') + return; + } + if (status !== 200) { + this.addNewLoginError(responseJson.error) + return + } + this.unsetLoginAndRegisterErrors() + } + async goToRegister(): Promise { const isLogged = await this.isLogged(); await this.removeLoginRegisterCombo() @@ -150,22 +200,25 @@ export default class Conquer { 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 + let status try { + const response = await fetch(urlUser, { + method: 'PUT', + body: JSON.stringify({ + username: username, + password: password, + repeat_password: repeatPassword + }) + }) responseJson = await response.json() + status = response.status } catch(e) { console.error(e) + this.addNewRegisterError('El servidor ha enviado datos inesperados.') return; } - if (response.status !== 200) { + if (status !== 200) { this.addNewRegisterError(responseJson.error) return } @@ -178,6 +231,12 @@ export default class Conquer { this.conquerLoginSuccess.classList.remove('conquer-display-none') } + addNewLoginError(error: string): void { + this.unsetLoginAndRegisterErrors() + this.conquerLoginSuccess.classList.add('conquer-display-none') + this.conquerLoginError.innerText = error + this.conquerLoginError.classList.remove('conquer-display-none') + } addNewRegisterError(error: string): void { this.unsetLoginAndRegisterErrors() this.conquerLoginSuccess.classList.add('conquer-display-none') diff --git a/lib/BurguillosInfo.pm b/lib/BurguillosInfo.pm index a7695ae..8fdf552 100644 --- a/lib/BurguillosInfo.pm +++ b/lib/BurguillosInfo.pm @@ -52,6 +52,7 @@ sub startup ($self) { $r->get('/stats')->to('Metrics#stats'); $r->get('/conquer')->to('Conquer#index'); $r->put('/conquer/user')->to('UserConquer#create'); + $r->post('/conquer/user/login')->to('UserConquer#login'); $r->get('/search.json')->to('Search#search'); $r->get('/farmacia-guardia.json')->to('FarmaciaGuardia#current'); $r->get('/<:category>.rss')->to('Page#category_rss'); diff --git a/lib/BurguillosInfo/Controller/Metrics.pm b/lib/BurguillosInfo/Controller/Metrics.pm index 7fb12e8..24fb01d 100644 --- a/lib/BurguillosInfo/Controller/Metrics.pm +++ b/lib/BurguillosInfo/Controller/Metrics.pm @@ -89,8 +89,6 @@ sub submit_login { $self->render( text => 'Server error.', status => 500 ); return; } - say $password; - say $bcrypted_pass; if ( !bcrypt_check( $password, $bcrypted_pass ) ) { $self->render( text => 'Wrong password', status => 401 ); return; diff --git a/lib/BurguillosInfo/Controller/UserConquer.pm b/lib/BurguillosInfo/Controller/UserConquer.pm index 793b7d3..a39cdf3 100644 --- a/lib/BurguillosInfo/Controller/UserConquer.pm +++ b/lib/BurguillosInfo/Controller/UserConquer.pm @@ -12,6 +12,7 @@ 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 JSON; use BurguillosInfo::Schema; @@ -21,18 +22,56 @@ 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 $input = $self->_expectJson; + if ( !defined $input ) { + return; } 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); + return $self->_createUser( $username, $password ); +} + +sub _expectJson ($self) { + my $input; + eval { $input = $self->req->json; }; + if ($@) { + say STDERR $@; + $self->_renderError(400, 'Se esperaba JSON.'); + return; + } + return $input; +} + +sub login ($self) { + my $input = $self->_expectJson; + if ( !defined $input ) { + return; + } + my $username = $input->{username}; + my $password = $input->{password}; + + my $resultset_conquer_user = + BurguillosInfo::Schema->Schema->resultset('ConquerUser'); + my @tentative_users = + $resultset_conquer_user->search( { username => $username } ); + my $tentative_user = $tentative_users[0]; + if (!defined $tentative_user) { + $self->_renderError(401, 'El usuario especificado no existe.'); + return; + } + if ( !bcrypt_check( $password, $tentative_user->encrypted_password ) ) { + $self->_renderError( 401, 'Contraseña incorrecta.' ); + return; + } + $self->render( + json => { + success => $JSON::true + }, + status => 200 + ); } sub _createUser ( $self, $username, $password ) { @@ -52,19 +91,19 @@ sub _createUser ( $self, $username, $password ) { }; if ($@) { if ( $@ =~ /Key \((.*?)\)=\((.*?)\) already exists\./ ) { - return $self->renderError( 400, - "The key $1 ($2) already exists in the database.", + return $self->_renderError( 400, + "La clave $1 ($2) ya existe en la base de datos.", ); } say STDERR $@; - return $self->renderError( 400, + return $self->_renderError( 400, 'No se pudo crear el usuario por razones desconocidas.' ); } - $self->render(status => 200, json => $user->serialize_to_owner); + $self->render( status => 200, json => $user->serialize_to_owner ); return 1; } -sub renderError ( $self, $status, $message ) { +sub _renderError ( $self, $status, $message ) { $self->render( status => $status, json => { error => $message } ); return 0; } @@ -75,7 +114,7 @@ sub _createCheckInput ( $self, $username, $password, $repeat_password ) { /^(?:\w|\d|[ÑÁÉÍÓÚñáéíóú ]){$username_minimum_chars,$username_maximum_chars}$/ ) { - return $self->renderError( 400, + 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.' ); @@ -85,7 +124,7 @@ sub _createCheckInput ( $self, $username, $password, $repeat_password ) { || $password !~ /^.{$password_minimum_chars,$password_maximum_chars}$/ || $password =~ /^\d+$/ ) { - return $self->renderError( + 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' @@ -96,7 +135,7 @@ sub _createCheckInput ( $self, $username, $password, $repeat_password ) { ); } if ( !defined $repeat_password || $password ne $repeat_password ) { - $self->renderError( + $self->_renderError( 400, 'El campo de repetir contraseña debe coincidir de forma' . ' totalmente exacta con el campo de contraseña para asegurar' diff --git a/lib/BurguillosInfo/DB/Migrations.pm b/lib/BurguillosInfo/DB/Migrations.pm index 18e29e7..e2c26a8 100644 --- a/lib/BurguillosInfo/DB/Migrations.pm +++ b/lib/BurguillosInfo/DB/Migrations.pm @@ -53,10 +53,10 @@ sub MIGRATIONS { uuid UUID NOT NULL PRIMARY KEY, username TEXT NOT NULL UNIQUE, encrypted_password TEXT NOT NULL, - last_activity TEXT NOT NULL DEFAULT NOW(), - is_admin boolean NOT NULL DEFAULT false + last_activity TIMESTAMP NOT NULL DEFAULT NOW(), + is_admin BOOLEAN NOT NULL DEFAULT false, + registration_date TIMESTAMP NOT NULL DEFAULT NOW() );', - 'ALTER TABLE conquer_user ADD COLUMN registration_date TIMESTAMP NOT NULL DEFAULT NOW();', ); } diff --git a/lib/BurguillosInfo/Schema/Result/ConquerUser.pm b/lib/BurguillosInfo/Schema/Result/ConquerUser.pm index 38e555b..6354b5d 100644 --- a/lib/BurguillosInfo/Schema/Result/ConquerUser.pm +++ b/lib/BurguillosInfo/Schema/Result/ConquerUser.pm @@ -10,6 +10,7 @@ use parent 'DBIx::Class::Core'; use feature 'signatures'; __PACKAGE__->table('conquer_user'); +__PACKAGE__->load_components("TimeStamp"); __PACKAGE__->add_columns( uuid => { diff --git a/public/js/bundle.js b/public/js/bundle.js index 3ffe040..a22a3e6 100644 --- a/public/js/bundle.js +++ b/public/js/bundle.js @@ -47,7 +47,7 @@ eval(";(function() {\n function Tablesort(el, options) {\n if (!(this instan /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ Conquer)\n/* harmony export */ });\n/* harmony import */ var ol_Map__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ol/Map */ \"./node_modules/ol/Map.js\");\n/* harmony import */ var ol_View__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ol/View */ \"./node_modules/ol/View.js\");\n/* harmony import */ var ol_proj_Projection_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ol/proj/Projection.js */ \"./node_modules/ol/proj/Projection.js\");\n/* harmony import */ var ol_layer_Tile__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ol/layer/Tile */ \"./node_modules/ol/layer/Tile.js\");\n/* harmony import */ var ol_source_OSM__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ol/source/OSM */ \"./node_modules/ol/source/OSM.js\");\n/* harmony import */ var ol_proj__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ol/proj */ \"./node_modules/ol/proj.js\");\n\n\n\n\n\n\nclass Conquer {\n static start() {\n const conquerContainer = document.querySelector(\".conquer-container\");\n if (conquerContainer !== null) {\n if (!(conquerContainer instanceof HTMLDivElement)) {\n console.error(\".conquer-container is not a div.\");\n return;\n }\n const conquer = new Conquer(conquerContainer);\n conquer.run();\n }\n }\n setCenterDisplaced(lat, lon) {\n const olCoordinates = this.realCoordinatesToOl(lat, lon);\n const size = this.map.getSize();\n if (size === undefined) {\n return;\n }\n this.map.getView().centerOn(olCoordinates, size, [size[0] / 2, size[1] - 60]);\n }\n static fail(error) {\n alert('Error de interfaz');\n throw new Error(error);\n }\n fillConquerLogin() {\n const conquerLogin = document.querySelector('.conquer-login');\n if (conquerLogin === null || !(conquerLogin instanceof HTMLDivElement)) {\n Conquer.fail('conquerLogin is invalid');\n }\n this.conquerLogin = conquerLogin;\n const conquerLoginGoToRegister = document.querySelector('.conquer-login-go-to-register');\n if (conquerLoginGoToRegister === null || !(conquerLoginGoToRegister instanceof HTMLAnchorElement)) {\n Conquer.fail('Link to go to register from login is invalid.');\n }\n this.conquerLoginGoToRegister = conquerLoginGoToRegister;\n this.conquerLoginGoToRegister.addEventListener('click', () => {\n this.goToRegister();\n });\n const conquerLoginError = document.querySelector('.conquer-login-error');\n if (conquerLoginError === null || !(conquerLoginError instanceof HTMLParagraphElement)) {\n Conquer.fail('Unable to find conquer login error.');\n }\n this.conquerLoginError = conquerLoginError;\n const conquerLoginSuccess = document.querySelector('.conquer-login-success');\n if (conquerLoginSuccess === null || !(conquerLoginSuccess instanceof HTMLParagraphElement)) {\n Conquer.fail('Unable to find conquer login success.');\n }\n this.conquerLoginSuccess = conquerLoginSuccess;\n }\n async goToRegister() {\n const isLogged = await this.isLogged();\n await this.removeLoginRegisterCombo();\n if (!isLogged) {\n this.conquerRegister.classList.remove('conquer-display-none');\n }\n }\n async goToLogin() {\n const isLogged = await this.isLogged();\n await this.removeLoginRegisterCombo();\n if (!isLogged) {\n this.conquerLogin.classList.remove('conquer-display-none');\n }\n }\n async removeLoginRegisterCombo() {\n this.conquerLogin.classList.add('conquer-display-none');\n this.conquerRegister.classList.add('conquer-display-none');\n }\n fillConquerRegister() {\n const conquerRegister = document.querySelector('.conquer-register');\n if (conquerRegister === null || !(conquerRegister instanceof HTMLDivElement)) {\n Conquer.fail('conquerRegister is invalid');\n }\n this.conquerRegister = conquerRegister;\n const conquerRegisterGoToLogin = document.querySelector('.conquer-register-go-to-login');\n if (conquerRegisterGoToLogin === null || !(conquerRegisterGoToLogin instanceof HTMLAnchorElement)) {\n Conquer.fail('Link to go to login from register is invalid.');\n }\n this.conquerRegisterGoToLogin = conquerRegisterGoToLogin;\n this.conquerRegisterGoToLogin.addEventListener('click', () => {\n this.goToLogin();\n });\n const conquerRegisterUsername = document.querySelector('.conquer-register-username');\n if (conquerRegisterUsername === null || !(conquerRegisterUsername instanceof HTMLInputElement)) {\n Conquer.fail('No username field in conquer register.');\n }\n this.conquerRegisterUsername = conquerRegisterUsername;\n const conquerRegisterPassword = document.querySelector('.conquer-register-password');\n if (conquerRegisterPassword === null || !(conquerRegisterPassword instanceof HTMLInputElement)) {\n Conquer.fail('No password field in conquer register.');\n }\n this.conquerRegisterPassword = conquerRegisterPassword;\n const conquerRegisterRepeatPassword = document.querySelector('.conquer-register-repeat-password');\n if (conquerRegisterRepeatPassword === null || !(conquerRegisterRepeatPassword instanceof HTMLInputElement)) {\n Conquer.fail('No repeat password field in conquer register.');\n }\n this.conquerRegisterRepeatPassword = conquerRegisterRepeatPassword;\n const conquerRegisterSubmit = document.querySelector('.conquer-register-submit');\n if (conquerRegisterSubmit === null || !(conquerRegisterSubmit instanceof HTMLButtonElement)) {\n Conquer.fail('No register submit button found.');\n }\n this.conquerRegisterSubmit = conquerRegisterSubmit;\n this.conquerRegisterSubmit.addEventListener('click', (event) => {\n event.preventDefault();\n this.onRegisterRequest();\n });\n const conquerRegisterError = document.querySelector('.conquer-register-error');\n if (conquerRegisterError === null || !(conquerRegisterError instanceof HTMLParagraphElement)) {\n Conquer.fail('Unable to find the conquer error element.');\n }\n this.conquerRegisterError = conquerRegisterError;\n }\n unsetLoginAndRegisterErrors() {\n this.conquerRegisterError.classList.add('conquer-display-none');\n this.conquerLoginError.classList.add('conquer-display-none');\n }\n async onRegisterRequest() {\n const username = this.conquerRegisterUsername.value;\n const password = this.conquerRegisterPassword.value;\n const repeatPassword = this.conquerRegisterRepeatPassword.value;\n const urlUser = new URL('/conquer/user', window.location.protocol + '//' + window.location.hostname + ':' + window.location.port);\n const response = await fetch(urlUser, {\n method: 'PUT',\n body: JSON.stringify({\n username: username,\n password: password,\n repeat_password: repeatPassword\n })\n });\n let responseJson;\n try {\n responseJson = await response.json();\n }\n catch (e) {\n console.error(e);\n return;\n }\n if (response.status !== 200) {\n this.addNewRegisterError(responseJson.error);\n return;\n }\n this.addNewLoginSuccessText(`Usuario registrado ${username}.`);\n this.goToLogin();\n }\n addNewLoginSuccessText(message) {\n this.unsetLoginAndRegisterErrors();\n this.conquerLoginSuccess.innerText = message;\n this.conquerLoginSuccess.classList.remove('conquer-display-none');\n }\n addNewRegisterError(error) {\n this.unsetLoginAndRegisterErrors();\n this.conquerLoginSuccess.classList.add('conquer-display-none');\n this.conquerRegisterError.innerText = error;\n this.conquerRegisterError.classList.remove('conquer-display-none');\n }\n async run() {\n this.fillConquerLogin();\n this.fillConquerRegister();\n const isLogged = await this.isLogged();\n if (!isLogged) {\n this.conquerLogin.classList.remove('conquer-display-none');\n }\n const conquerContainer = this.conquerContainer;\n //layer.on('prerender', (evt) => {\n // // return\n // if (evt.context) {\n // const context = evt.context as CanvasRenderingContext2D;\n // context.filter = 'grayscale(80%) invert(100%) ';\n // context.globalCompositeOperation = 'source-over';\n // }\n //});\n //layer.on('postrender', (evt) => {\n // if (evt.context) {\n // const context = evt.context as CanvasRenderingContext2D;\n // context.filter = 'none';\n // }\n //});\n ol_proj__WEBPACK_IMPORTED_MODULE_0__.useGeographic();\n this.map = new ol_Map__WEBPACK_IMPORTED_MODULE_1__[\"default\"]({\n target: conquerContainer,\n layers: [\n new ol_layer_Tile__WEBPACK_IMPORTED_MODULE_2__[\"default\"]({\n source: new ol_source_OSM__WEBPACK_IMPORTED_MODULE_3__[\"default\"]()\n })\n ],\n view: new ol_View__WEBPACK_IMPORTED_MODULE_4__[\"default\"]({\n zoom: 21,\n maxZoom: 22,\n }),\n });\n const proccessLocation = (location) => {\n this.currentLatitude = location.coords.latitude;\n this.currentLongitude = location.coords.longitude;\n if (location.coords.heading !== null && (this.alpha != 0 || this.beta != 0 || this.gamma != 0) && !this.disableSetRotationOffset) {\n this.disableSetRotationOffset = true;\n this.heading = location.coords.heading;\n this.rotationOffset = this.compassHeading(this.alpha, this.beta, this.gamma) + (location.coords.heading * Math.PI * 2) / 360;\n }\n this.setCenterDisplaced(this.currentLatitude, this.currentLongitude);\n };\n this.setCenterDisplaced(37.58237, -5.96766);\n navigator.geolocation.watchPosition((location) => {\n proccessLocation(location);\n }, (err) => {\n return;\n }, {\n enableHighAccuracy: true,\n maximumAge: 0\n });\n window.setInterval(() => {\n this.disableSetRotationOffset = false;\n }, 10000);\n window.setTimeout(() => {\n window.setInterval(() => {\n navigator.geolocation.getCurrentPosition((location) => {\n proccessLocation(location);\n }, () => {\n return;\n }, {\n enableHighAccuracy: true,\n maximumAge: 0\n });\n }, 1000);\n }, 1000);\n if (window.DeviceOrientationEvent) {\n window.addEventListener(\"deviceorientation\", (event) => {\n if (event.alpha !== null && event.beta !== null && event.gamma !== null) {\n this.onRotate(event.alpha, event.beta, event.gamma);\n }\n }, true);\n }\n }\n realCoordinatesToOl(lat, lon) {\n return ol_proj__WEBPACK_IMPORTED_MODULE_0__.transform([lon, lat], new ol_proj_Projection_js__WEBPACK_IMPORTED_MODULE_5__[\"default\"]({ code: \"WGS84\" }), new ol_proj_Projection_js__WEBPACK_IMPORTED_MODULE_5__[\"default\"]({ code: \"EPSG:900913\" }));\n }\n compassHeading(alpha, beta, gamma) {\n const alphaRad = alpha * (Math.PI / 180);\n return alphaRad;\n let betaRad = beta * (Math.PI / 180);\n const gammaRad = gamma * (Math.PI / 180);\n betaRad = 1.5707963268;\n const cA = Math.cos(alphaRad);\n const sA = Math.sin(alphaRad);\n const sB = Math.sin(betaRad);\n const cG = Math.cos(gammaRad);\n const sG = Math.sin(gammaRad);\n const rA = -cA * sG - sA * sB * cG;\n const rB = -sA * sG + cA * sB * cG;\n return Math.atan2(rA, rB);\n }\n logToServer(logValue) {\n const urlLog = new URL('/conquer/log', window.location.protocol + '//' + window.location.hostname + ':' + window.location.port);\n urlLog.searchParams.append('log', logValue);\n fetch(urlLog).then(() => {\n return;\n }).catch((error) => {\n console.error(error);\n });\n }\n onRotate(alpha, beta, gamma) {\n if (this.enabledOnRotate) {\n this.alpha = alpha;\n this.beta = beta;\n this.gamma = gamma;\n this.enabledOnRotate = false;\n this.map.getView().setRotation((this.compassHeading(alpha, beta, gamma) - this.rotationOffset));\n window.setTimeout(() => {\n this.enabledOnRotate = true;\n }, 10);\n }\n this.setCenterDisplaced(this.currentLatitude, this.currentLongitude);\n }\n constructor(conquerContainer) {\n this.rotationOffset = 0;\n this.heading = 0;\n this.disableSetRotationOffset = false;\n this.alpha = 0;\n this.beta = 0;\n this.gamma = 0;\n this.enabledOnRotate = true;\n this.conquerContainer = conquerContainer;\n }\n async isLogged() {\n return false;\n // return fetch(\"/conquer/user\")\n // .then(async (res): Promise => {\n // const data = await res.json();\n // if (data === null) {\n // return false;\n // }\n // return true;\n // })\n // .catch((error) => {\n // console.error(error);\n // return false;\n // });\n }\n}\n\n\n//# sourceURL=webpack://BurguillosInfo/./js-src/conquer/index.ts?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ Conquer)\n/* harmony export */ });\n/* harmony import */ var ol_Map__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ol/Map */ \"./node_modules/ol/Map.js\");\n/* harmony import */ var ol_View__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ol/View */ \"./node_modules/ol/View.js\");\n/* harmony import */ var ol_proj_Projection_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ol/proj/Projection.js */ \"./node_modules/ol/proj/Projection.js\");\n/* harmony import */ var ol_layer_Tile__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ol/layer/Tile */ \"./node_modules/ol/layer/Tile.js\");\n/* harmony import */ var ol_source_OSM__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ol/source/OSM */ \"./node_modules/ol/source/OSM.js\");\n/* harmony import */ var ol_proj__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ol/proj */ \"./node_modules/ol/proj.js\");\n\n\n\n\n\n\nclass Conquer {\n static start() {\n const conquerContainer = document.querySelector(\".conquer-container\");\n if (conquerContainer !== null) {\n if (!(conquerContainer instanceof HTMLDivElement)) {\n console.error(\".conquer-container is not a div.\");\n return;\n }\n const conquer = new Conquer(conquerContainer);\n conquer.run();\n }\n }\n setCenterDisplaced(lat, lon) {\n const olCoordinates = this.realCoordinatesToOl(lat, lon);\n const size = this.map.getSize();\n if (size === undefined) {\n return;\n }\n this.map.getView().centerOn(olCoordinates, size, [size[0] / 2, size[1] - 60]);\n }\n static fail(error) {\n alert('Error de interfaz');\n throw new Error(error);\n }\n fillConquerLogin() {\n const conquerLogin = document.querySelector('.conquer-login');\n if (conquerLogin === null || !(conquerLogin instanceof HTMLDivElement)) {\n Conquer.fail('conquerLogin is invalid');\n }\n this.conquerLogin = conquerLogin;\n const conquerLoginGoToRegister = document.querySelector('.conquer-login-go-to-register');\n if (conquerLoginGoToRegister === null || !(conquerLoginGoToRegister instanceof HTMLAnchorElement)) {\n Conquer.fail('Link to go to register from login is invalid.');\n }\n this.conquerLoginGoToRegister = conquerLoginGoToRegister;\n this.conquerLoginGoToRegister.addEventListener('click', () => {\n this.goToRegister();\n });\n const conquerLoginError = document.querySelector('.conquer-login-error');\n if (conquerLoginError === null || !(conquerLoginError instanceof HTMLParagraphElement)) {\n Conquer.fail('Unable to find conquer login error.');\n }\n this.conquerLoginError = conquerLoginError;\n const conquerLoginSuccess = document.querySelector('.conquer-login-success');\n if (conquerLoginSuccess === null || !(conquerLoginSuccess instanceof HTMLParagraphElement)) {\n Conquer.fail('Unable to find conquer login success.');\n }\n this.conquerLoginSuccess = conquerLoginSuccess;\n const conquerLoginUsername = document.querySelector('.conquer-login-username');\n if (conquerLoginUsername === null || !(conquerLoginUsername instanceof HTMLInputElement)) {\n Conquer.fail('Unable to find conquer login username field.');\n }\n this.conquerLoginUsername = conquerLoginUsername;\n const conquerLoginPassword = document.querySelector('.conquer-login-password');\n if (conquerLoginPassword === null || !(conquerLoginPassword instanceof HTMLInputElement)) {\n Conquer.fail('Unable to find conquer login password field.');\n }\n this.conquerLoginPassword = conquerLoginPassword;\n const conquerLoginSubmit = document.querySelector('.conquer-login-submit');\n if (conquerLoginSubmit === null || !(conquerLoginSubmit instanceof HTMLButtonElement)) {\n Conquer.fail('Unable to find the submit button for the login.');\n }\n this.conquerLoginSubmit = conquerLoginSubmit;\n this.conquerLoginSubmit.addEventListener('click', () => {\n this.onLoginRequested();\n });\n }\n async onLoginRequested() {\n const username = this.conquerLoginUsername.value;\n const password = this.conquerLoginPassword.value;\n const urlUser = new URL('/conquer/user/login', window.location.protocol + '//' + window.location.hostname + ':' + window.location.port);\n let responseJson;\n let status;\n try {\n const response = await fetch(urlUser, {\n method: 'POST',\n body: JSON.stringify({\n username: username,\n password: password,\n })\n });\n responseJson = await response.json();\n status = response.status;\n }\n catch (e) {\n console.error(e);\n this.addNewLoginError('El servidor ha enviado datos inesperados.');\n return;\n }\n if (status !== 200) {\n this.addNewLoginError(responseJson.error);\n return;\n }\n this.unsetLoginAndRegisterErrors();\n }\n async goToRegister() {\n const isLogged = await this.isLogged();\n await this.removeLoginRegisterCombo();\n if (!isLogged) {\n this.conquerRegister.classList.remove('conquer-display-none');\n }\n }\n async goToLogin() {\n const isLogged = await this.isLogged();\n await this.removeLoginRegisterCombo();\n if (!isLogged) {\n this.conquerLogin.classList.remove('conquer-display-none');\n }\n }\n async removeLoginRegisterCombo() {\n this.conquerLogin.classList.add('conquer-display-none');\n this.conquerRegister.classList.add('conquer-display-none');\n }\n fillConquerRegister() {\n const conquerRegister = document.querySelector('.conquer-register');\n if (conquerRegister === null || !(conquerRegister instanceof HTMLDivElement)) {\n Conquer.fail('conquerRegister is invalid');\n }\n this.conquerRegister = conquerRegister;\n const conquerRegisterGoToLogin = document.querySelector('.conquer-register-go-to-login');\n if (conquerRegisterGoToLogin === null || !(conquerRegisterGoToLogin instanceof HTMLAnchorElement)) {\n Conquer.fail('Link to go to login from register is invalid.');\n }\n this.conquerRegisterGoToLogin = conquerRegisterGoToLogin;\n this.conquerRegisterGoToLogin.addEventListener('click', () => {\n this.goToLogin();\n });\n const conquerRegisterUsername = document.querySelector('.conquer-register-username');\n if (conquerRegisterUsername === null || !(conquerRegisterUsername instanceof HTMLInputElement)) {\n Conquer.fail('No username field in conquer register.');\n }\n this.conquerRegisterUsername = conquerRegisterUsername;\n const conquerRegisterPassword = document.querySelector('.conquer-register-password');\n if (conquerRegisterPassword === null || !(conquerRegisterPassword instanceof HTMLInputElement)) {\n Conquer.fail('No password field in conquer register.');\n }\n this.conquerRegisterPassword = conquerRegisterPassword;\n const conquerRegisterRepeatPassword = document.querySelector('.conquer-register-repeat-password');\n if (conquerRegisterRepeatPassword === null || !(conquerRegisterRepeatPassword instanceof HTMLInputElement)) {\n Conquer.fail('No repeat password field in conquer register.');\n }\n this.conquerRegisterRepeatPassword = conquerRegisterRepeatPassword;\n const conquerRegisterSubmit = document.querySelector('.conquer-register-submit');\n if (conquerRegisterSubmit === null || !(conquerRegisterSubmit instanceof HTMLButtonElement)) {\n Conquer.fail('No register submit button found.');\n }\n this.conquerRegisterSubmit = conquerRegisterSubmit;\n this.conquerRegisterSubmit.addEventListener('click', (event) => {\n event.preventDefault();\n this.onRegisterRequest();\n });\n const conquerRegisterError = document.querySelector('.conquer-register-error');\n if (conquerRegisterError === null || !(conquerRegisterError instanceof HTMLParagraphElement)) {\n Conquer.fail('Unable to find the conquer error element.');\n }\n this.conquerRegisterError = conquerRegisterError;\n }\n unsetLoginAndRegisterErrors() {\n this.conquerRegisterError.classList.add('conquer-display-none');\n this.conquerLoginError.classList.add('conquer-display-none');\n }\n async onRegisterRequest() {\n const username = this.conquerRegisterUsername.value;\n const password = this.conquerRegisterPassword.value;\n const repeatPassword = this.conquerRegisterRepeatPassword.value;\n const urlUser = new URL('/conquer/user', window.location.protocol + '//' + window.location.hostname + ':' + window.location.port);\n let responseJson;\n let status;\n try {\n const response = await fetch(urlUser, {\n method: 'PUT',\n body: JSON.stringify({\n username: username,\n password: password,\n repeat_password: repeatPassword\n })\n });\n responseJson = await response.json();\n status = response.status;\n }\n catch (e) {\n console.error(e);\n this.addNewRegisterError('El servidor ha enviado datos inesperados.');\n return;\n }\n if (status !== 200) {\n this.addNewRegisterError(responseJson.error);\n return;\n }\n this.addNewLoginSuccessText(`Usuario registrado ${username}.`);\n this.goToLogin();\n }\n addNewLoginSuccessText(message) {\n this.unsetLoginAndRegisterErrors();\n this.conquerLoginSuccess.innerText = message;\n this.conquerLoginSuccess.classList.remove('conquer-display-none');\n }\n addNewLoginError(error) {\n this.unsetLoginAndRegisterErrors();\n this.conquerLoginSuccess.classList.add('conquer-display-none');\n this.conquerLoginError.innerText = error;\n this.conquerLoginError.classList.remove('conquer-display-none');\n }\n addNewRegisterError(error) {\n this.unsetLoginAndRegisterErrors();\n this.conquerLoginSuccess.classList.add('conquer-display-none');\n this.conquerRegisterError.innerText = error;\n this.conquerRegisterError.classList.remove('conquer-display-none');\n }\n async run() {\n this.fillConquerLogin();\n this.fillConquerRegister();\n const isLogged = await this.isLogged();\n if (!isLogged) {\n this.conquerLogin.classList.remove('conquer-display-none');\n }\n const conquerContainer = this.conquerContainer;\n //layer.on('prerender', (evt) => {\n // // return\n // if (evt.context) {\n // const context = evt.context as CanvasRenderingContext2D;\n // context.filter = 'grayscale(80%) invert(100%) ';\n // context.globalCompositeOperation = 'source-over';\n // }\n //});\n //layer.on('postrender', (evt) => {\n // if (evt.context) {\n // const context = evt.context as CanvasRenderingContext2D;\n // context.filter = 'none';\n // }\n //});\n ol_proj__WEBPACK_IMPORTED_MODULE_0__.useGeographic();\n this.map = new ol_Map__WEBPACK_IMPORTED_MODULE_1__[\"default\"]({\n target: conquerContainer,\n layers: [\n new ol_layer_Tile__WEBPACK_IMPORTED_MODULE_2__[\"default\"]({\n source: new ol_source_OSM__WEBPACK_IMPORTED_MODULE_3__[\"default\"]()\n })\n ],\n view: new ol_View__WEBPACK_IMPORTED_MODULE_4__[\"default\"]({\n zoom: 21,\n maxZoom: 22,\n }),\n });\n const proccessLocation = (location) => {\n this.currentLatitude = location.coords.latitude;\n this.currentLongitude = location.coords.longitude;\n if (location.coords.heading !== null && (this.alpha != 0 || this.beta != 0 || this.gamma != 0) && !this.disableSetRotationOffset) {\n this.disableSetRotationOffset = true;\n this.heading = location.coords.heading;\n this.rotationOffset = this.compassHeading(this.alpha, this.beta, this.gamma) + (location.coords.heading * Math.PI * 2) / 360;\n }\n this.setCenterDisplaced(this.currentLatitude, this.currentLongitude);\n };\n this.setCenterDisplaced(37.58237, -5.96766);\n navigator.geolocation.watchPosition((location) => {\n proccessLocation(location);\n }, (err) => {\n return;\n }, {\n enableHighAccuracy: true,\n maximumAge: 0\n });\n window.setInterval(() => {\n this.disableSetRotationOffset = false;\n }, 10000);\n window.setTimeout(() => {\n window.setInterval(() => {\n navigator.geolocation.getCurrentPosition((location) => {\n proccessLocation(location);\n }, () => {\n return;\n }, {\n enableHighAccuracy: true,\n maximumAge: 0\n });\n }, 1000);\n }, 1000);\n if (window.DeviceOrientationEvent) {\n window.addEventListener(\"deviceorientation\", (event) => {\n if (event.alpha !== null && event.beta !== null && event.gamma !== null) {\n this.onRotate(event.alpha, event.beta, event.gamma);\n }\n }, true);\n }\n }\n realCoordinatesToOl(lat, lon) {\n return ol_proj__WEBPACK_IMPORTED_MODULE_0__.transform([lon, lat], new ol_proj_Projection_js__WEBPACK_IMPORTED_MODULE_5__[\"default\"]({ code: \"WGS84\" }), new ol_proj_Projection_js__WEBPACK_IMPORTED_MODULE_5__[\"default\"]({ code: \"EPSG:900913\" }));\n }\n compassHeading(alpha, beta, gamma) {\n const alphaRad = alpha * (Math.PI / 180);\n return alphaRad;\n let betaRad = beta * (Math.PI / 180);\n const gammaRad = gamma * (Math.PI / 180);\n betaRad = 1.5707963268;\n const cA = Math.cos(alphaRad);\n const sA = Math.sin(alphaRad);\n const sB = Math.sin(betaRad);\n const cG = Math.cos(gammaRad);\n const sG = Math.sin(gammaRad);\n const rA = -cA * sG - sA * sB * cG;\n const rB = -sA * sG + cA * sB * cG;\n return Math.atan2(rA, rB);\n }\n logToServer(logValue) {\n const urlLog = new URL('/conquer/log', window.location.protocol + '//' + window.location.hostname + ':' + window.location.port);\n urlLog.searchParams.append('log', logValue);\n fetch(urlLog).then(() => {\n return;\n }).catch((error) => {\n console.error(error);\n });\n }\n onRotate(alpha, beta, gamma) {\n if (this.enabledOnRotate) {\n this.alpha = alpha;\n this.beta = beta;\n this.gamma = gamma;\n this.enabledOnRotate = false;\n this.map.getView().setRotation((this.compassHeading(alpha, beta, gamma) - this.rotationOffset));\n window.setTimeout(() => {\n this.enabledOnRotate = true;\n }, 10);\n }\n this.setCenterDisplaced(this.currentLatitude, this.currentLongitude);\n }\n constructor(conquerContainer) {\n this.rotationOffset = 0;\n this.heading = 0;\n this.disableSetRotationOffset = false;\n this.alpha = 0;\n this.beta = 0;\n this.gamma = 0;\n this.enabledOnRotate = true;\n this.conquerContainer = conquerContainer;\n }\n async isLogged() {\n return false;\n // return fetch(\"/conquer/user\")\n // .then(async (res): Promise => {\n // const data = await res.json();\n // if (data === null) {\n // return false;\n // }\n // return true;\n // })\n // .catch((error) => {\n // console.error(error);\n // return false;\n // });\n }\n}\n\n\n//# sourceURL=webpack://BurguillosInfo/./js-src/conquer/index.ts?"); /***/ }),