www: Add redis cache and api cookie-based i18n
This commit is contained in:
parent
4bfa6cfe16
commit
d590582a85
23
app.js
23
app.js
@ -1,3 +1,4 @@
|
|||||||
|
require('dotenv').config();
|
||||||
const createError = require('http-errors');
|
const createError = require('http-errors');
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const session = require('express-session');
|
const session = require('express-session');
|
||||||
@ -6,6 +7,13 @@ const path = require('path');
|
|||||||
const cookieParser = require('cookie-parser');
|
const cookieParser = require('cookie-parser');
|
||||||
const logger = require('morgan');
|
const logger = require('morgan');
|
||||||
const i18n = require('i18n-2');
|
const i18n = require('i18n-2');
|
||||||
|
const redis = require('redis');
|
||||||
|
|
||||||
|
const [redisHost, redisPort] = [process.env.REDIS_HOST, process.env.REDIS_PORT];
|
||||||
|
const redisClient = redis.createClient(redisPort, redisHost);
|
||||||
|
redisClient.on('error', (error) => {
|
||||||
|
console.error('Redis error:', error);
|
||||||
|
});
|
||||||
|
|
||||||
const indexRouter = require('./routes/index');
|
const indexRouter = require('./routes/index');
|
||||||
const apiRouter = require('./routes/api');
|
const apiRouter = require('./routes/api');
|
||||||
@ -45,18 +53,15 @@ i18n.expressBind(app, {
|
|||||||
app.use(function (req, res, next) {
|
app.use(function (req, res, next) {
|
||||||
req.i18n.setLocaleFromQuery();
|
req.i18n.setLocaleFromQuery();
|
||||||
req.i18n.setLocaleFromCookie();
|
req.i18n.setLocaleFromCookie();
|
||||||
next();
|
// set locale from url prefix
|
||||||
});
|
|
||||||
|
|
||||||
app.all('*', function(req, res, next) {
|
|
||||||
// set locale
|
|
||||||
var rxLocale = /^\/(fr|en)/i;
|
var rxLocale = /^\/(fr|en)/i;
|
||||||
if(rxLocale.test(req.url)){
|
if (rxLocale.test(req.url)) {
|
||||||
const arr = rxLocale.exec(req.url);
|
const arr = rxLocale.exec(req.url);
|
||||||
const locale=arr[1];
|
const locale = arr[1];
|
||||||
req.i18n.setLocale(locale);
|
req.i18n.setLocale(locale);
|
||||||
} else {
|
}
|
||||||
req.i18n.setLocale('en');
|
if (req.cookies.locale === undefined) {
|
||||||
|
res.cookie('locale', req.i18n.locale, { maxAge: 90000 });
|
||||||
}
|
}
|
||||||
// add extra logic
|
// add extra logic
|
||||||
next();
|
next();
|
||||||
|
@ -1,23 +1,20 @@
|
|||||||
const debug = require('debug')('soundbirder:api');
|
|
||||||
const debugResponses = require('debug')('soundbirder:api:responses');
|
|
||||||
require('dotenv').config();
|
require('dotenv').config();
|
||||||
const axios = require('axios');
|
const debug = require('debug')('soundbirder:api');
|
||||||
const eBird = require('@unclesamulus/ebird-api')(process.env.EBIRD_API_KEY);
|
const debugLocale = require('debug')('soundbirder:locale');
|
||||||
const XenoCanto = require('@unclesamulus/xeno-canto-api');
|
const debugResponses = require('debug')('soundbirder:api:responses');
|
||||||
const { choices, choice } = require('../utils/choices');
|
const quizzController = require('./quizz');
|
||||||
|
|
||||||
const OPENCAGE_API_KEY = process.env.OPENCAGE_API_KEY;
|
|
||||||
const OPENCAGE_API_URL = 'https://api.opencagedata.com/geocode/v1/json?q=<lat>+<lon>&key=<key>';
|
|
||||||
const QUIZZ_SIZE = process.env.QUIZZ_SIZE ? process.env.QUIZZ_SIZE : 5;
|
const QUIZZ_SIZE = process.env.QUIZZ_SIZE ? process.env.QUIZZ_SIZE : 5;
|
||||||
|
|
||||||
function check(req, res) {
|
function check(req, res) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function quizz(req, res) {
|
function quizz(req, res) {
|
||||||
debug('Generating quizz');
|
debug('Generating quizz');
|
||||||
const { lat, lng } = req.body;
|
const { lat, lng } = req.body;
|
||||||
generateQuizz({ lat, lng }, req.locale)
|
const locale = req.i18n.locale;
|
||||||
|
debugLocale("Locale:", locale);
|
||||||
|
quizzController.generateQuizz({ lat, lng }, locale, QUIZZ_SIZE)
|
||||||
.then(({ species, correct, audio }) => {
|
.then(({ species, correct, audio }) => {
|
||||||
req.session.correct = correct;
|
req.session.correct = correct;
|
||||||
res.json({ species, audio });
|
res.json({ species, audio });
|
||||||
@ -30,118 +27,11 @@ function quizz(req, res) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function generateQuizz(coordinates, locale) {
|
|
||||||
const quizz = {}
|
|
||||||
try {
|
|
||||||
const speciesSelection = await getSpeciesSelection(coordinates);
|
|
||||||
const speciesSelectionLocalized = await getLocalizedNames(speciesSelection, locale);
|
|
||||||
quizz.species = await speciesSelectionLocalized;
|
|
||||||
debug("Got species selection", quizz.species);
|
|
||||||
const answer = await choice(speciesSelectionLocalized);
|
|
||||||
debug("Got answer", answer);
|
|
||||||
quizz.correct = answer.code;
|
|
||||||
quizz.audio = await getAudio(answer.sciName);
|
|
||||||
} catch (error) {
|
|
||||||
debug("Error raised while generating quizz");
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
return quizz;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getSpeciesSelection(coordinates) {
|
|
||||||
const { lat, lng } = coordinates;
|
|
||||||
const region = await getRegion(lat, lng);
|
|
||||||
region.country_code = region.country_code.toUpperCase();
|
|
||||||
// const regionCode = await getRegionCode(region);
|
|
||||||
const regionCode = region.country_code;
|
|
||||||
const speciesList = await getSpeciesList(regionCode);
|
|
||||||
const speciesSelection = choices(speciesList, QUIZZ_SIZE);
|
|
||||||
debug("Species proposals:", speciesSelection)
|
|
||||||
return speciesSelection;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getHome(req, res) {
|
function getHome(req, res) {
|
||||||
res.render('api', {
|
res.render('api', {
|
||||||
title: "SoundBirder api",
|
title: "SoundBirder api",
|
||||||
version: 0
|
version: 0
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getRegion(lat, lon) {
|
|
||||||
const url = OPENCAGE_API_URL
|
|
||||||
.replace('<lat>', lat)
|
|
||||||
.replace('<lon>', lon)
|
|
||||||
.replace('<key>', OPENCAGE_API_KEY);
|
|
||||||
return axios.get(url).then(response => {
|
|
||||||
const { results } = response.data;
|
|
||||||
const { country_code, country, state, county } = results[0].components;
|
|
||||||
return { country_code, country, state, county };
|
|
||||||
}).catch(error => {
|
|
||||||
throw error;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getRegionCode({ country_code, country, state, county }) {
|
|
||||||
return eBird.ref.region.list('subnational1', country_code)()
|
|
||||||
.then(states => {
|
|
||||||
console.log(states);
|
|
||||||
const regionCode = states.find(region => region.name === state).code;
|
|
||||||
return regionCode;
|
|
||||||
}).catch(error => {
|
|
||||||
throw error;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getLocalizedNames(speciesCodes, locale) {
|
|
||||||
let names = [];
|
|
||||||
for (let i = 0; i < speciesCodes.length; i++) {
|
|
||||||
const code = speciesCodes[i];
|
|
||||||
const { sciName, comName } = await getLocalizedName(code, locale);
|
|
||||||
names.push({ code, sciName, comName });
|
|
||||||
}
|
|
||||||
return names;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getLocalizedName(speciesCode, locale) {
|
|
||||||
return eBird.ref.taxonomy.ebird({
|
|
||||||
fmt: 'json',
|
|
||||||
locale: locale,
|
|
||||||
species: speciesCode
|
|
||||||
}).then(
|
|
||||||
response => {
|
|
||||||
const names = response[0];
|
|
||||||
debug("Got localized species names");
|
|
||||||
debugResponses(names);
|
|
||||||
return names;
|
|
||||||
}
|
|
||||||
).catch(error => {
|
|
||||||
throw error;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSpeciesList(regionCode) {
|
|
||||||
return eBird.product.spplist.in(regionCode)()
|
|
||||||
.then(species => {
|
|
||||||
return species;
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
throw error;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAudio(speciesScientificName) {
|
|
||||||
return XenoCanto.search({
|
|
||||||
name: speciesScientificName,
|
|
||||||
quality: 'A'
|
|
||||||
}).then(response => {
|
|
||||||
debugResponses(response);
|
|
||||||
const { recordings } = response;
|
|
||||||
const randomRecord = choice(recordings);
|
|
||||||
const audio = randomRecord.file;
|
|
||||||
return audio;
|
|
||||||
}).catch(error => {
|
|
||||||
throw error;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
28
controllers/cache.js
Normal file
28
controllers/cache.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
require('dotenv').config();
|
||||||
|
const debug = require('debug')('soundbirder:cache');
|
||||||
|
const redis = require('redis');
|
||||||
|
[redisHost, redisPort] = [process.env.REDIS_HOST, process.env.REDIS_PORT];
|
||||||
|
const redisClient = redis.createClient(redisPort, redisHost);
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
redisClient.connect();
|
||||||
|
})();
|
||||||
|
|
||||||
|
function cacheResponse(request, response) {
|
||||||
|
debug("Caching response", request);
|
||||||
|
redisClient.set(request, JSON.stringify(response));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getCached(request) {
|
||||||
|
debug("Getting cached response", request);
|
||||||
|
const cached = await redisClient.get(request);
|
||||||
|
if (cached) {
|
||||||
|
return JSON.parse(cached);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
cacheResponse,
|
||||||
|
getCached
|
||||||
|
}
|
120
controllers/quizz.js
Normal file
120
controllers/quizz.js
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
require('dotenv').config();
|
||||||
|
const debug = require('debug')('soundbirder:api:quizz');
|
||||||
|
const debugResponses = require('debug')('soundbirder:api:responses');
|
||||||
|
const cache = require('./cache');
|
||||||
|
const eBird = require('@unclesamulus/ebird-api')(process.env.EBIRD_API_KEY);
|
||||||
|
const XenoCanto = require('@unclesamulus/xeno-canto-api');
|
||||||
|
const { choices, choice } = require('../utils/choices');
|
||||||
|
const { getRegion } = require('./region');
|
||||||
|
|
||||||
|
async function generateQuizz(coordinates, locale, size) {
|
||||||
|
const quizz = {}
|
||||||
|
try {
|
||||||
|
const speciesSelection = await getSpeciesSelection(coordinates, size);
|
||||||
|
debugResponses(`Species selection: ${speciesSelection}`);
|
||||||
|
const speciesSelectionLocalized = await getLocalizedNames(speciesSelection, locale);
|
||||||
|
debugResponses('Localized species selection:', speciesSelectionLocalized);
|
||||||
|
quizz.species = speciesSelectionLocalized;
|
||||||
|
debug("Got species selection", quizz.species);
|
||||||
|
const answer = await choice(speciesSelectionLocalized);
|
||||||
|
debug("Got answer", answer);
|
||||||
|
quizz.correct = answer.speciesCode;
|
||||||
|
quizz.audio = await getAudio(answer.sciName);
|
||||||
|
debug("Got audio", quizz.audio);
|
||||||
|
} catch (error) {
|
||||||
|
debug("Error raised while generating quizz");
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
return quizz;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getSpeciesSelection(coordinates, number) {
|
||||||
|
const { lat, lng } = coordinates;
|
||||||
|
const region = await getRegion(lat, lng);
|
||||||
|
// const regionCode = await getRegionCode(region);
|
||||||
|
const regionCode = region.country_code;
|
||||||
|
const speciesList = await getSpeciesList(regionCode);
|
||||||
|
const speciesSelection = choices(speciesList, number);
|
||||||
|
debug("Species proposals:", speciesSelection)
|
||||||
|
return speciesSelection;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function getLocalizedNames(speciesCodes, locale) {
|
||||||
|
let allNames = [];
|
||||||
|
for (let i = 0; i < speciesCodes.length; i++) {
|
||||||
|
const code = speciesCodes[i];
|
||||||
|
let flag = false;
|
||||||
|
try {
|
||||||
|
names = await cache.getCached(`${code}-${locale}`);
|
||||||
|
if (names) {
|
||||||
|
allNames.push(names);
|
||||||
|
flag = true;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
if (!flag) {
|
||||||
|
try {
|
||||||
|
const names = { speciesCode, sciName, comName } = await getLocalizedName(code, locale);
|
||||||
|
cache.cacheResponse(`${code}-${locale}`, names);
|
||||||
|
allNames.push(names);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return allNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLocalizedName(speciesCode, locale) {
|
||||||
|
return eBird.ref.taxonomy.ebird({
|
||||||
|
fmt: 'json',
|
||||||
|
locale: locale,
|
||||||
|
species: speciesCode
|
||||||
|
}).then(
|
||||||
|
response => {
|
||||||
|
const names = response[0];
|
||||||
|
debug("Got localized species names");
|
||||||
|
debugResponses(names);
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
).catch(error => {
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getSpeciesList(regionCode) {
|
||||||
|
const cached = await cache.getCached(`spplist-${regionCode}`);
|
||||||
|
if (cached) {
|
||||||
|
return cached;
|
||||||
|
} else {
|
||||||
|
return eBird.product.spplist.in(regionCode)()
|
||||||
|
.then(species => {
|
||||||
|
cache.cacheResponse(`spplist-${regionCode}`, species);
|
||||||
|
return species;
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAudio(speciesScientificName) {
|
||||||
|
return XenoCanto.search({
|
||||||
|
name: speciesScientificName,
|
||||||
|
quality: 'A'
|
||||||
|
}).then(response => {
|
||||||
|
debugResponses(response);
|
||||||
|
const { recordings } = response;
|
||||||
|
const randomRecord = choice(recordings);
|
||||||
|
const audio = randomRecord.file;
|
||||||
|
return audio;
|
||||||
|
}).catch(error => {
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
generateQuizz
|
||||||
|
}
|
46
controllers/region.js
Normal file
46
controllers/region.js
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
require('dotenv').config();
|
||||||
|
const debug = require('debug')('soundbirder:api:region');
|
||||||
|
const cache = require('./cache');
|
||||||
|
const axios = require('axios');
|
||||||
|
|
||||||
|
const OPENCAGE_API_KEY = process.env.OPENCAGE_API_KEY;
|
||||||
|
const OPENCAGE_API_URL = 'https://api.opencagedata.com/geocode/v1/json?q=<lat>+<lon>&key=<key>';
|
||||||
|
|
||||||
|
async function getRegion(lat, lon) {
|
||||||
|
const url = OPENCAGE_API_URL
|
||||||
|
.replace('<lat>', lat)
|
||||||
|
.replace('<lon>', lon)
|
||||||
|
.replace('<key>', OPENCAGE_API_KEY);
|
||||||
|
try {
|
||||||
|
const cached = await cache.getCached(`region-${lat}-${lon}`);
|
||||||
|
if (cached) {
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
return axios.get(url).then(response => {
|
||||||
|
const { results } = response.data;
|
||||||
|
const region = results[0].components;
|
||||||
|
region.country_code = region.country_code.toUpperCase();
|
||||||
|
cache.cacheResponse(url, region);
|
||||||
|
return { country_code, country, state, county } = region;
|
||||||
|
}).catch(error => {
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRegionCode({ country_code, country, state, county }) {
|
||||||
|
return eBird.ref.region.list('subnational1', country_code)()
|
||||||
|
.then(states => {
|
||||||
|
console.log(states);
|
||||||
|
const regionCode = states.find(region => region.name === state).code;
|
||||||
|
return regionCode;
|
||||||
|
}).catch(error => {
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getRegion,
|
||||||
|
}
|
158
package-lock.json
generated
158
package-lock.json
generated
@ -23,7 +23,8 @@
|
|||||||
"leaflet": "^1.8.0",
|
"leaflet": "^1.8.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"morgan": "~1.9.1",
|
"morgan": "~1.9.1",
|
||||||
"pug": "^3.0.2"
|
"pug": "^3.0.2",
|
||||||
|
"redis": "^4.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/helper-string-parser": {
|
"node_modules/@babel/helper-string-parser": {
|
||||||
@ -66,6 +67,59 @@
|
|||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@redis/bloom": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-EBw7Ag1hPgFzdznK2PBblc1kdlj5B5Cw3XwI9/oG7tSn85/HKy3X9xHy/8tm/eNXJYHLXHJL/pkwBpFMVVefkw==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@redis/client": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@redis/client": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@redis/client/-/client-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-XCFV60nloXAefDsPnYMjHGtvbtHR8fV5Om8cQ0JYqTNbWcQo/4AryzJ2luRj4blveWazRK/j40gES8M7Cp6cfQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"cluster-key-slot": "1.1.0",
|
||||||
|
"generic-pool": "3.8.2",
|
||||||
|
"yallist": "4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@redis/graph": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-oDE4myMCJOCVKYMygEMWuriBgqlS5FqdWerikMoJxzmmTUErnTRRgmIDa2VcgytACZMFqpAOWDzops4DOlnkfQ==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@redis/client": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@redis/json": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-4X0Qv0BzD9Zlb0edkUoau5c1bInWSICqXAGrpwEltkncUwcxJIGEcVryZhLgb0p/3PkKaLIWkjhHRtLe9yiA7Q==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@redis/client": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@redis/search": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-NyFZEVnxIJEybpy+YskjgOJRNsfTYqaPbK/Buv6W2kmFNaRk85JiqjJZA5QkRmWvGbyQYwoO5QfDi2wHskKrQQ==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@redis/client": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@redis/time-series": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-OFp0q4SGrTH0Mruf6oFsHGea58u8vS/iI5+NpYdicaM+7BgqBZH8FFvNZ8rYYLrUO/QRqMq72NpXmxLVNcdmjA==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@redis/client": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@unclesamulus/ebird-api": {
|
"node_modules/@unclesamulus/ebird-api": {
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@unclesamulus/ebird-api/-/ebird-api-0.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@unclesamulus/ebird-api/-/ebird-api-0.0.0.tgz",
|
||||||
@ -209,6 +263,14 @@
|
|||||||
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz",
|
||||||
"integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA=="
|
"integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA=="
|
||||||
},
|
},
|
||||||
|
"node_modules/cluster-key-slot": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/combined-stream": {
|
"node_modules/combined-stream": {
|
||||||
"version": "1.0.8",
|
"version": "1.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||||
@ -596,6 +658,14 @@
|
|||||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
|
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
|
||||||
},
|
},
|
||||||
|
"node_modules/generic-pool": {
|
||||||
|
"version": "3.8.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.8.2.tgz",
|
||||||
|
"integrity": "sha512-nGToKy6p3PAbYQ7p1UlWl6vSPwfwU6TMSWK7TTu+WUY4ZjyZQGniGGt2oNVvyNSpyZYSB43zMXVLcBm08MTMkg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/get-intrinsic": {
|
"node_modules/get-intrinsic": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz",
|
||||||
@ -1063,6 +1133,19 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/redis": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/redis/-/redis-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-RXRUor0iU1vizu4viHoUyLpe1ZO/RngZp0V9DyXBHTI+7tC7rEz6Wzn4Sv9v0tTJeqGAzdJ+q5YVbNKKQ5hX9A==",
|
||||||
|
"dependencies": {
|
||||||
|
"@redis/bloom": "1.0.2",
|
||||||
|
"@redis/client": "1.3.0",
|
||||||
|
"@redis/graph": "1.0.1",
|
||||||
|
"@redis/json": "1.0.3",
|
||||||
|
"@redis/search": "1.1.0",
|
||||||
|
"@redis/time-series": "1.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/resolve": {
|
"node_modules/resolve": {
|
||||||
"version": "1.22.1",
|
"version": "1.22.1",
|
||||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
|
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
|
||||||
@ -1257,6 +1340,11 @@
|
|||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 10.0.0"
|
"node": ">= 10.0.0"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"node_modules/yallist": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -1285,6 +1373,46 @@
|
|||||||
"to-fast-properties": "^2.0.0"
|
"to-fast-properties": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@redis/bloom": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-EBw7Ag1hPgFzdznK2PBblc1kdlj5B5Cw3XwI9/oG7tSn85/HKy3X9xHy/8tm/eNXJYHLXHJL/pkwBpFMVVefkw==",
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
|
"@redis/client": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@redis/client/-/client-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-XCFV60nloXAefDsPnYMjHGtvbtHR8fV5Om8cQ0JYqTNbWcQo/4AryzJ2luRj4blveWazRK/j40gES8M7Cp6cfQ==",
|
||||||
|
"requires": {
|
||||||
|
"cluster-key-slot": "1.1.0",
|
||||||
|
"generic-pool": "3.8.2",
|
||||||
|
"yallist": "4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@redis/graph": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-oDE4myMCJOCVKYMygEMWuriBgqlS5FqdWerikMoJxzmmTUErnTRRgmIDa2VcgytACZMFqpAOWDzops4DOlnkfQ==",
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
|
"@redis/json": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-4X0Qv0BzD9Zlb0edkUoau5c1bInWSICqXAGrpwEltkncUwcxJIGEcVryZhLgb0p/3PkKaLIWkjhHRtLe9yiA7Q==",
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
|
"@redis/search": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-NyFZEVnxIJEybpy+YskjgOJRNsfTYqaPbK/Buv6W2kmFNaRk85JiqjJZA5QkRmWvGbyQYwoO5QfDi2wHskKrQQ==",
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
|
"@redis/time-series": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-OFp0q4SGrTH0Mruf6oFsHGea58u8vS/iI5+NpYdicaM+7BgqBZH8FFvNZ8rYYLrUO/QRqMq72NpXmxLVNcdmjA==",
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
"@unclesamulus/ebird-api": {
|
"@unclesamulus/ebird-api": {
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@unclesamulus/ebird-api/-/ebird-api-0.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@unclesamulus/ebird-api/-/ebird-api-0.0.0.tgz",
|
||||||
@ -1404,6 +1532,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz",
|
||||||
"integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA=="
|
"integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA=="
|
||||||
},
|
},
|
||||||
|
"cluster-key-slot": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw=="
|
||||||
|
},
|
||||||
"combined-stream": {
|
"combined-stream": {
|
||||||
"version": "1.0.8",
|
"version": "1.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||||
@ -1692,6 +1825,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
|
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
|
||||||
},
|
},
|
||||||
|
"generic-pool": {
|
||||||
|
"version": "3.8.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.8.2.tgz",
|
||||||
|
"integrity": "sha512-nGToKy6p3PAbYQ7p1UlWl6vSPwfwU6TMSWK7TTu+WUY4ZjyZQGniGGt2oNVvyNSpyZYSB43zMXVLcBm08MTMkg=="
|
||||||
|
},
|
||||||
"get-intrinsic": {
|
"get-intrinsic": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz",
|
||||||
@ -2074,6 +2212,19 @@
|
|||||||
"unpipe": "1.0.0"
|
"unpipe": "1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"redis": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/redis/-/redis-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-RXRUor0iU1vizu4viHoUyLpe1ZO/RngZp0V9DyXBHTI+7tC7rEz6Wzn4Sv9v0tTJeqGAzdJ+q5YVbNKKQ5hX9A==",
|
||||||
|
"requires": {
|
||||||
|
"@redis/bloom": "1.0.2",
|
||||||
|
"@redis/client": "1.3.0",
|
||||||
|
"@redis/graph": "1.0.1",
|
||||||
|
"@redis/json": "1.0.3",
|
||||||
|
"@redis/search": "1.1.0",
|
||||||
|
"@redis/time-series": "1.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"resolve": {
|
"resolve": {
|
||||||
"version": "1.22.1",
|
"version": "1.22.1",
|
||||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
|
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
|
||||||
@ -2217,6 +2368,11 @@
|
|||||||
"assert-never": "^1.2.1",
|
"assert-never": "^1.2.1",
|
||||||
"babel-walk": "3.0.0-canary-5"
|
"babel-walk": "3.0.0-canary-5"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"yallist": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
"leaflet": "^1.8.0",
|
"leaflet": "^1.8.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"morgan": "~1.9.1",
|
"morgan": "~1.9.1",
|
||||||
"pug": "^3.0.2"
|
"pug": "^3.0.2",
|
||||||
|
"redis": "^4.3.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,6 @@ async function query(endpoint, params) {
|
|||||||
function getQuizz(lat, lng) {
|
function getQuizz(lat, lng) {
|
||||||
return query('game/quizz', { lat, lng })
|
return query('game/quizz', { lat, lng })
|
||||||
.then(data => {
|
.then(data => {
|
||||||
console.log(data);
|
|
||||||
return data;
|
return data;
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
throw error;
|
throw error;
|
||||||
|
@ -26,7 +26,10 @@ function geolocationHandler() {
|
|||||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>'
|
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>'
|
||||||
}).addTo(map);
|
}).addTo(map);
|
||||||
|
|
||||||
let marker = undefined;
|
let marker = L.marker(location, { draggable: true }).addTo(map);
|
||||||
|
marker.on('dragend', updateMarker);
|
||||||
|
marker.addTo(map);
|
||||||
|
|
||||||
// Get geolocation
|
// Get geolocation
|
||||||
function getLocation() {
|
function getLocation() {
|
||||||
if (navigator.geolocation) {
|
if (navigator.geolocation) {
|
||||||
@ -38,11 +41,7 @@ function geolocationHandler() {
|
|||||||
|
|
||||||
function setLocation(position) {
|
function setLocation(position) {
|
||||||
location = [position.coords.latitude, position.coords.longitude];
|
location = [position.coords.latitude, position.coords.longitude];
|
||||||
if (marker === undefined) {
|
if (marker !== undefined) {
|
||||||
marker = new L.marker(location, { draggable: 'true' });
|
|
||||||
marker.on('dragend', updateMarker);
|
|
||||||
marker.addTo(map);
|
|
||||||
} else {
|
|
||||||
marker.setLatLng(location);
|
marker.setLatLng(location);
|
||||||
}
|
}
|
||||||
map.setView(location, 15);
|
map.setView(location, 15);
|
||||||
|
Loading…
Reference in New Issue
Block a user