Full Data Update
This commit is contained in:
+243
-58
@@ -1,4 +1,5 @@
|
||||
const express = require('express');
|
||||
const WebSocket = require('ws');
|
||||
const auth = require('express-basic-auth');
|
||||
const { createHash } = require('crypto');
|
||||
|
||||
@@ -40,6 +41,58 @@ const hazeTranslator = {
|
||||
"Pathfinder",
|
||||
"Trailblazer",
|
||||
"Maven"
|
||||
],
|
||||
tag: [
|
||||
"Vicious Dogs",
|
||||
"Biohazard",
|
||||
"Radioactive",
|
||||
"Fence",
|
||||
"Rats",
|
||||
"Still Water",
|
||||
"Barbed Wire",
|
||||
"Fire",
|
||||
"Flashlight Recommended",
|
||||
"Climbing Rope Recommended",
|
||||
"Boat",
|
||||
"Electrical Hazard",
|
||||
"Helmet Recommended",
|
||||
"Boots Recommended",
|
||||
"Falling Debris",
|
||||
"Dust Mask Recommended",
|
||||
"Asbestos",
|
||||
"Extreme Temperature",
|
||||
"Squatters",
|
||||
"Risk of Violence or Crime",
|
||||
"Emotional Distress",
|
||||
"Landmines",
|
||||
"Respirator Recommended",
|
||||
"Active Security",
|
||||
"No Trespassing Sign",
|
||||
"Private Property Sign",
|
||||
"Flood Risk",
|
||||
"Locked",
|
||||
"Dead Air",
|
||||
"Unstable Structure",
|
||||
"Black Mold",
|
||||
"Ticks",
|
||||
"Mosquitoes",
|
||||
"Razor Wire",
|
||||
"Fiberglass",
|
||||
"Bears",
|
||||
"Wolves",
|
||||
"Wildlife",
|
||||
"Broken Glass",
|
||||
"Scorpions",
|
||||
"Snakes",
|
||||
"Venomous Spiders",
|
||||
"Karens Present",
|
||||
"Gloves Recommended",
|
||||
"Boarded Up",
|
||||
"Thorns",
|
||||
"Dangerous Hights",
|
||||
"Hazmat Suit Recommended",
|
||||
"Heavy Overgrowth",
|
||||
"Bats"
|
||||
]
|
||||
};
|
||||
app.use(auth({
|
||||
@@ -48,13 +101,15 @@ app.use(auth({
|
||||
}
|
||||
}));
|
||||
|
||||
app.all('/locations.geojson', (req, res) => {
|
||||
// Assemble full Haze location data, can take a LONG time
|
||||
app.all('/locations_full.geojson', (req, res) => {
|
||||
// Verify Inputs
|
||||
if ((!req.auth.user || !req.auth.password) && (!req.query.auth)) {
|
||||
res.status(401).send('Unauthorized');
|
||||
return;
|
||||
}
|
||||
// Query Haze API Websocket
|
||||
|
||||
// Setup local variables for request
|
||||
let hazePwHash = null;
|
||||
let hazeUser = null;
|
||||
let hazeSocketCounter = 0;
|
||||
@@ -71,8 +126,64 @@ app.all('/locations.geojson', (req, res) => {
|
||||
hazePwHash = createHash('sha256').update(req.auth.password).digest('hex');
|
||||
}
|
||||
}
|
||||
const haze = new WebSocket('wss://do.ecven.com:8120/explr');
|
||||
const haze = new WebSocket('wss://do.ecven.com:8120/explr-mb');
|
||||
let locations = [];
|
||||
let failCounter = 0;
|
||||
let queryComplete = false;
|
||||
|
||||
// Socket Message Handlers, defined here to use the local variables above
|
||||
const sendComplete = () => {
|
||||
if (!queryComplete) {
|
||||
queryComplete = true;
|
||||
haze.close();
|
||||
let testTime = (new Date().getTime() - requestStartTime);
|
||||
const countExported = locations.filter(location => location && location.completed).length;
|
||||
console.log(`${(new Date().getTime() - requestStartTime)/1000}s | FAILS: ${failCounter} | TIMEOUT: ${testTime} | TOTAL LOCATIONS: ${locations.length} | EXPORTED: ${countExported} | TIMEDOUT: ${locations.length - countExported - failCounter}`);
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.end(JSON.stringify({
|
||||
...geoJsonTemplate,
|
||||
features: locations.map(location => ({
|
||||
type: "Feature",
|
||||
geometry: {
|
||||
type: "Point",
|
||||
coordinates: [location['longitude'], location['latitude']]
|
||||
},
|
||||
properties: {
|
||||
id: location['id'],
|
||||
title: location['name'],
|
||||
"marker-symbol": location['isUnconfirmedLocation'] === 1 ? "star-stroked" : "star",
|
||||
author: location['author'] || '',
|
||||
explored: location['isExplored'] || false,
|
||||
confirmed: location['isConfirmed'] || false,
|
||||
category: location['category'] || '',
|
||||
level: location['level'] || '',
|
||||
rating: location['rating'] || '',
|
||||
difficulty: location['difficulty'] || '',
|
||||
comments: location['comments'] || [],
|
||||
querySuccess: location['completed']
|
||||
}
|
||||
}))
|
||||
}));
|
||||
}
|
||||
}
|
||||
const queryLocation = (index) => {
|
||||
haze.send(locations[index]['requestQuery']);
|
||||
setTimeout(() => {
|
||||
if (locations[index]['completed'] === 0) {
|
||||
console.log(`Timeout on ${locations[index]['id']}}`);
|
||||
locations[index]['completed'] = -1; // mark as failed
|
||||
failCounter++;
|
||||
let nextIndex = locations.findIndex(location => location && location.completed === 0);
|
||||
if (locations.every(location => location && location.completed !== 0) || nextIndex === -1) {
|
||||
sendComplete();
|
||||
} else {
|
||||
queryLocation(nextIndex);
|
||||
}
|
||||
}
|
||||
}, 10000);
|
||||
}
|
||||
|
||||
// These are the socket event handlers, in order of expected use
|
||||
haze.addEventListener('message', (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
let haze_authToken = '', haze_username = '';
|
||||
@@ -103,15 +214,9 @@ app.all('/locations.geojson', (req, res) => {
|
||||
break;
|
||||
case "getMyLocationsRequest_response":
|
||||
if (data['body']['response'] === 1) {
|
||||
// locations = data['body']['locations'].map(location => {
|
||||
// return {
|
||||
// id: location['id'],
|
||||
// title: location['name'],
|
||||
// completed: false
|
||||
// };
|
||||
// });
|
||||
data['body']['locations'].forEach(location => {
|
||||
locations[hazeSocketCounter++] = {
|
||||
hazeSocketCounter++;
|
||||
locations[hazeSocketCounter] = {
|
||||
id: location['id'],
|
||||
title: location['name'],
|
||||
latitude: location['coordinates']['latitude'],
|
||||
@@ -119,21 +224,17 @@ app.all('/locations.geojson', (req, res) => {
|
||||
isExplored: location['explored'] > 0,
|
||||
isConfirmed: location['isUnconfirmedLocation'] !== 1,
|
||||
category: hazeTranslator.category[location['locationCategory']],
|
||||
completed: false
|
||||
}
|
||||
});
|
||||
|
||||
// need to make the socketIDs dynamic and handle completed status before returning final payload
|
||||
// FIX: Currently throwing response 2 "User does not have access to location."
|
||||
locations.forEach((location, index) => {
|
||||
if (location) {
|
||||
haze.send(JSON.stringify({
|
||||
rawBasicData: JSON.stringify(location),
|
||||
requestQuery: JSON.stringify({
|
||||
event: "getLocation_request",
|
||||
body: locations.map(location => location.id),
|
||||
socketMessageId: index
|
||||
}));
|
||||
body: [ location['id'] ],
|
||||
socketMessageId: hazeSocketCounter
|
||||
}),
|
||||
completed: 0
|
||||
}
|
||||
});
|
||||
queryLocation(locations.findIndex(location => location && location.completed === 0));
|
||||
|
||||
} else {
|
||||
console.dir(data);
|
||||
res.status(500).send('Failed to retrieve locations');
|
||||
@@ -143,14 +244,29 @@ app.all('/locations.geojson', (req, res) => {
|
||||
break;
|
||||
case "getLocation_response":
|
||||
if (data['body']['response'] === 1) {
|
||||
let locationTags = {};
|
||||
data['body']['location']['locationRating']['ratingItemsList'].map(tag => {
|
||||
locationTags[hazeTranslator.tag[tag['id']] ? hazeTranslator.tag[tag['id']] : tag['id']] = tag['count'];
|
||||
});
|
||||
locations[data['socketMessageId']] = {
|
||||
...locations[data['socketMessageId']],
|
||||
completed: true,
|
||||
completed: 1,
|
||||
author: `${data['body']['location']['submittorUserDatas']['username']} (${data['body']['location']['submittorUserDatas']['email']})`,
|
||||
rating: hazeTranslator.rating[data['body']['location']['locationRating']['overallRating']],
|
||||
difficulty: hazeTranslator.difficulty[data['body']['location']['locationRating']['difficultyRating']],
|
||||
level: hazeTranslator.level[data['body']['location']['locationRating']['levelRating']],
|
||||
// TODO: use body.location.locationRating.ratingItemsList for location tags
|
||||
tags: locationTags,
|
||||
images: [
|
||||
... data['body']['location']['gallery'].map(image => ({
|
||||
id: image['id'],
|
||||
mimetype: `image/${image['mediaType']}`,
|
||||
author: `${image['uploaderData']['username']} (${image['uploaderData']['email']})`,
|
||||
url: image['url'],
|
||||
thumbnail: image['thumbnailUrl'],
|
||||
likes: image['likes'],
|
||||
date: image['uploadDate'],
|
||||
}))
|
||||
],
|
||||
comments: data['body']['location']['locationCommentsAndCommunityVotes'].map(comment => ({
|
||||
id: comment['id'],
|
||||
text: comment['comment'],
|
||||
@@ -158,43 +274,111 @@ app.all('/locations.geojson', (req, res) => {
|
||||
author: comment['submittor']['username'],
|
||||
}))
|
||||
};
|
||||
let testTime = (new Date().getTime() - requestStartTime) > 30000; // 30s timeout
|
||||
if (locations.every(location => location && location.completed) || testTime) {
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
haze.close();
|
||||
res.end(JSON.stringify({
|
||||
...geoJsonTemplate,
|
||||
features: locations.map(location => ({
|
||||
type: "Feature",
|
||||
geometry: {
|
||||
type: "Point",
|
||||
coordinates: [location['longitude'], location['latitude']]
|
||||
},
|
||||
properties: {
|
||||
id: location['id'],
|
||||
title: location['name'],
|
||||
"marker-symbol": location['isUnconfirmedLocation'] === 1 ? "star-stroked" : "star",
|
||||
author: location['author'] || '',
|
||||
explored: location['isExplored'] || false,
|
||||
confirmed: location['isConfirmed'] || false,
|
||||
category: location['category'] || '',
|
||||
level: location['level'] || '',
|
||||
rating: location['rating'] || '',
|
||||
difficulty: location['difficulty'] || '',
|
||||
comments: location['comments'] || [],
|
||||
querySuccess: location['completed']
|
||||
}
|
||||
}))
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
console.dir(data);
|
||||
// res.status(500).send('Failed to retrieve locations');
|
||||
// haze.close();
|
||||
// res.end();
|
||||
console.log(`Failure on ${locations[data['socketMessageId']]['id']}: ${data['body']['responseDetails']}`);
|
||||
locations[data['socketMessageId']]['completed'] = -1; // mark as failed
|
||||
failCounter++;
|
||||
}
|
||||
// Completion Check and increment
|
||||
let nextIndex = locations.findIndex(location => location && location.completed === 0);
|
||||
if (locations.every(location => location && location.completed !== 0) || nextIndex === -1) {
|
||||
sendComplete();
|
||||
} else {
|
||||
queryLocation(nextIndex);
|
||||
}
|
||||
break;
|
||||
case "ping":
|
||||
console.log('Ping received from Haze API');
|
||||
break;
|
||||
default:
|
||||
console.log(JSON.stringify(data, null, 2));
|
||||
res.status(500).send('Unexpected response from Haze API');
|
||||
haze.close();
|
||||
res.end();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Original API, for only listing locations with minimal data
|
||||
app.all('/locations.geojson', (req, res) => {
|
||||
// Verify Inputs
|
||||
if ((!req.auth.user || !req.auth.password) && (!req.query.auth)) {
|
||||
res.status(401).send('Unauthorized');
|
||||
return;
|
||||
}
|
||||
// Query Haze API Websocket
|
||||
let hazePwHash = null;
|
||||
let hazeUser = null;
|
||||
if (req.query.auth) {
|
||||
let paramAuth = Buffer.from(req.query.auth, 'base64').toString('utf-8');
|
||||
hawUser = paramAuth.split(":")[0];
|
||||
hazePwHash = paramAuth.split(":")[1];
|
||||
} else {
|
||||
hazeUser = req.auth.user;
|
||||
if (/\b[A-Fa-f0-9]{64}\b/.test(req.auth.password)) {
|
||||
hazePwHash = req.auth.password;
|
||||
} else {
|
||||
hazePwHash = createHash('sha256').update(req.auth.password).digest('hex');
|
||||
}
|
||||
}
|
||||
const haze = new WebSocket('wss://do.ecven.com:8120/explr');
|
||||
haze.addEventListener('message', (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
let haze_authToken = '', haze_username = '';
|
||||
switch (data['event']) {
|
||||
case "connected":
|
||||
haze.send(JSON.stringify({
|
||||
"event": "authenticateAccount_request",
|
||||
"body": [
|
||||
{
|
||||
"password": hazePwHash,
|
||||
"email": hazeUser
|
||||
}
|
||||
],
|
||||
"socketMessageId": 0
|
||||
}));
|
||||
break;
|
||||
case "authenticateAccount_response":
|
||||
if (data['body']['response'] === 1) {
|
||||
haze_authToken = data['body']['authToken'];
|
||||
haze_username = data['body']['username'];
|
||||
haze.send('{"event": "getMyLocationsRequest_request","body": [{"higlightImages": true}],"socketMessageId": 0}');
|
||||
} else {
|
||||
console.dir(req.query);
|
||||
res.status(401).json({ ...data, password: req.auth.password });
|
||||
haze.close();
|
||||
res.end();
|
||||
}
|
||||
break;
|
||||
case "getMyLocationsRequest_response":
|
||||
if (data['body']['response'] === 1) {
|
||||
const hazeLocations = data['body']['locations'].map(location => {
|
||||
return {
|
||||
type: "Feature",
|
||||
geometry: {
|
||||
type: "Point",
|
||||
coordinates: [location['coordinates']['longitude'], location['coordinates']['latitude']]
|
||||
},
|
||||
properties: {
|
||||
id: location['id'],
|
||||
title: location['name'],
|
||||
"marker-symbol": location['isUnconfirmedLocation'] === 1 ? "star-stroked" : "star",
|
||||
}
|
||||
}
|
||||
});
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
haze.close();
|
||||
res.end(JSON.stringify({
|
||||
...geonJsonTemplate,
|
||||
features: hazeLocations
|
||||
}));
|
||||
} else {
|
||||
console.dir(data);
|
||||
res.status(500).send('Failed to retrieve locations');
|
||||
haze.close();
|
||||
res.end();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
console.dir(data);
|
||||
res.status(500).send('Unexpected response from Haze API');
|
||||
@@ -204,6 +388,7 @@ app.all('/locations.geojson', (req, res) => {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Server is running on http://localhost:${port}`);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user