Real-time communication with frontend
- Frontend shows heatmap of most visit places - Maximum accuracy can now be set - Fix bug where battery chart filtered values wrongly
This commit is contained in:
@@ -38,6 +38,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
companion object {
|
companion object {
|
||||||
@JvmStatic val API_URL = "http://192.168.178.26:8040"
|
@JvmStatic val API_URL = "http://192.168.178.26:8040"
|
||||||
var TOKEN = ""
|
var TOKEN = ""
|
||||||
|
var USER: User? = null
|
||||||
}
|
}
|
||||||
|
|
||||||
private var broadcastReceiver: BroadcastReceiver? = null
|
private var broadcastReceiver: BroadcastReceiver? = null
|
||||||
@@ -50,6 +51,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
val client = OkHttpClient()
|
val client = OkHttpClient()
|
||||||
val req = Request.Builder()
|
val req = Request.Builder()
|
||||||
.url("$API_URL/phone/$androidId")
|
.url("$API_URL/phone/$androidId")
|
||||||
|
.header("token", TOKEN)
|
||||||
.get()
|
.get()
|
||||||
.build()
|
.build()
|
||||||
val response = client.newCall(req).execute()
|
val response = client.newCall(req).execute()
|
||||||
@@ -65,7 +67,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
androidId,
|
androidId,
|
||||||
Build.MODEL,
|
Build.MODEL,
|
||||||
Build.PRODUCT,
|
Build.PRODUCT,
|
||||||
Build.VERSION.RELEASE,
|
Build.VERSION.BASE_OS + " " + Build.VERSION.RELEASE + " " + Build.VERSION.CODENAME,
|
||||||
System.getProperty("os.arch")
|
System.getProperty("os.arch")
|
||||||
)
|
)
|
||||||
val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
|
val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
|
||||||
@@ -91,7 +93,6 @@ class MainActivity : AppCompatActivity() {
|
|||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_main)
|
setContentView(R.layout.activity_main)
|
||||||
setSupportActionBar(findViewById(R.id.toolbar))
|
setSupportActionBar(findViewById(R.id.toolbar))
|
||||||
startService(Intent(this, TrackerService::class.java))
|
|
||||||
|
|
||||||
// Check authorization
|
// Check authorization
|
||||||
val backendChecks = Thread(Runnable {
|
val backendChecks = Thread(Runnable {
|
||||||
@@ -119,14 +120,34 @@ class MainActivity : AppCompatActivity() {
|
|||||||
this.runOnUiThread(Runnable {
|
this.runOnUiThread(Runnable {
|
||||||
findViewById<TextView>(R.id.httpStatus).setTextColor(Color.GREEN)
|
findViewById<TextView>(R.id.httpStatus).setTextColor(Color.GREEN)
|
||||||
findViewById<TextView>(R.id.httpStatus).text = "CONNECTED"
|
findViewById<TextView>(R.id.httpStatus).text = "CONNECTED"
|
||||||
|
findViewById<Button>(R.id.signin).isEnabled = false
|
||||||
|
findViewById<Button>(R.id.signin).text = "Logged in"
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Get all user information
|
||||||
|
val userinfo = Request.Builder()
|
||||||
|
.url("$API_URL/user")
|
||||||
|
.get()
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.header("token", TOKEN)
|
||||||
|
.build()
|
||||||
|
val userinfoResponse = client.newCall(userinfo).execute()
|
||||||
|
|
||||||
|
if (userinfoResponse.code == 200) {
|
||||||
|
val jsonToUser = moshi.adapter(User::class.java)
|
||||||
|
val userInfoResponseBody = userinfoResponse.body!!.string()
|
||||||
|
USER = jsonToUser.fromJson(userInfoResponseBody)
|
||||||
|
|
||||||
|
// Only start service if authentication went good.
|
||||||
|
startService(Intent(this, TrackerService::class.java))
|
||||||
|
|
||||||
Snackbar.make(findViewById<FloatingActionButton>(R.id.fab), "Login succeeded", Snackbar.LENGTH_SHORT)
|
Snackbar.make(findViewById<FloatingActionButton>(R.id.fab), "Login succeeded", Snackbar.LENGTH_SHORT)
|
||||||
.setBackgroundTint(Color.GREEN)
|
.setBackgroundTint(Color.GREEN)
|
||||||
.setActionTextColor(Color.WHITE)
|
.setActionTextColor(Color.WHITE)
|
||||||
.show()
|
.show()
|
||||||
|
|
||||||
checkIfPhoneIsRegistered()
|
checkIfPhoneIsRegistered()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Since we are in another thread, we have to get back to the UI thread.
|
// Since we are in another thread, we have to get back to the UI thread.
|
||||||
this.runOnUiThread(Runnable {
|
this.runOnUiThread(Runnable {
|
||||||
@@ -158,7 +179,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
findViewById<TextView>(R.id.rabbitStatus).setTextColor(Color.RED)
|
findViewById<TextView>(R.id.rabbitStatus).setTextColor(Color.RED)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (statusHttp == 200) {
|
/*if (statusHttp == 200) {
|
||||||
findViewById<TextView>(R.id.httpStatus).text = "ONLINE (no login)"
|
findViewById<TextView>(R.id.httpStatus).text = "ONLINE (no login)"
|
||||||
findViewById<TextView>(R.id.httpStatus).setTextColor(Color.CYAN)
|
findViewById<TextView>(R.id.httpStatus).setTextColor(Color.CYAN)
|
||||||
findViewById<Button>(R.id.signin).isEnabled = false
|
findViewById<Button>(R.id.signin).isEnabled = false
|
||||||
@@ -168,7 +189,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
findViewById<TextView>(R.id.httpStatus).setTextColor(Color.RED)
|
findViewById<TextView>(R.id.httpStatus).setTextColor(Color.RED)
|
||||||
findViewById<Button>(R.id.signin).isEnabled = true
|
findViewById<Button>(R.id.signin).isEnabled = true
|
||||||
findViewById<Button>(R.id.signin).text = "Sign in"
|
findViewById<Button>(R.id.signin).text = "Sign in"
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
registerReceiver(broadcastReceiver, IntentFilter("de.nicolasklier.livebeat"))
|
registerReceiver(broadcastReceiver, IntentFilter("de.nicolasklier.livebeat"))
|
||||||
@@ -192,6 +213,12 @@ class MainActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onStart() {
|
||||||
|
super.onStart()
|
||||||
|
|
||||||
|
registerReceiver(broadcastReceiver, IntentFilter("de.nicolasklier.livebeat"))
|
||||||
|
}
|
||||||
|
|
||||||
override fun onStop() {
|
override fun onStop() {
|
||||||
super.onStop()
|
super.onStop()
|
||||||
|
|
||||||
|
|||||||
@@ -2,25 +2,35 @@ package de.nicolasklier.livebeat
|
|||||||
|
|
||||||
import com.squareup.moshi.JsonClass
|
import com.squareup.moshi.JsonClass
|
||||||
import com.squareup.moshi.ToJson
|
import com.squareup.moshi.ToJson
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents one singe beat that will be send to the database.
|
* This class represents one singe beat that will be send to the database.
|
||||||
*/
|
*/
|
||||||
class Beat(
|
class Beat(
|
||||||
final val token: String, // Token of this phone
|
val token: String, // Token of this phone
|
||||||
final val gpsLocation: Array<Double>,
|
val gpsLocation: Array<Double>,
|
||||||
final val battery: Int?,
|
val battery: Int?,
|
||||||
final val timestamp: Long
|
val timestamp: Long
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
class Phone(
|
class Phone(
|
||||||
final val androidId: String,
|
val androidId: String,
|
||||||
final val modelName: String,
|
val modelName: String,
|
||||||
final val displayName: String,
|
val displayName: String,
|
||||||
final val operatingSystem: String,
|
val operatingSystem: String,
|
||||||
final val architecture: String
|
val architecture: String
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
class User(
|
||||||
|
val name: String,
|
||||||
|
val type: String,
|
||||||
|
val lastLogin: String,
|
||||||
|
val twoFASecret: String?,
|
||||||
|
val brokerToken: String,
|
||||||
|
val createdAt: String
|
||||||
|
)
|
||||||
|
|
||||||
class Login(
|
class Login(
|
||||||
final val token: String
|
val token: String
|
||||||
) {}
|
) {}
|
||||||
@@ -40,16 +40,12 @@ class TrackerService : Service() {
|
|||||||
// This thread only connects to RabbitMQ
|
// This thread only connects to RabbitMQ
|
||||||
val connectionThread = Thread(Runnable {
|
val connectionThread = Thread(Runnable {
|
||||||
val client = OkHttpClient()
|
val client = OkHttpClient()
|
||||||
val req = Request.Builder()
|
|
||||||
.url(MainActivity.API_URL)
|
|
||||||
.get()
|
|
||||||
.build()
|
|
||||||
|
|
||||||
val factory = ConnectionFactory()
|
val factory = ConnectionFactory()
|
||||||
factory.username = "lineage"
|
factory.username = MainActivity.USER!!.name
|
||||||
factory.password = "ZSo\$X97GQ547JXL7nGq"
|
factory.password = MainActivity.USER!!.brokerToken
|
||||||
factory.virtualHost = "/"
|
factory.virtualHost = "/"
|
||||||
factory.host = "nk-home.ddns.net"
|
factory.host = "192.168.178.26"
|
||||||
factory.port = 5672
|
factory.port = 5672
|
||||||
factory.isAutomaticRecoveryEnabled = true
|
factory.isAutomaticRecoveryEnabled = true
|
||||||
try {
|
try {
|
||||||
@@ -59,11 +55,10 @@ class TrackerService : Service() {
|
|||||||
val intent = Intent("de.nicolasklier.livebeat")
|
val intent = Intent("de.nicolasklier.livebeat")
|
||||||
val bundle = Bundle()
|
val bundle = Bundle()
|
||||||
bundle.putBoolean("statusRabbit", true)
|
bundle.putBoolean("statusRabbit", true)
|
||||||
bundle.putInt("statusHttp", client.newCall(req).execute().code)
|
|
||||||
intent.putExtras(bundle)
|
intent.putExtras(bundle)
|
||||||
this.sendBroadcast(intent)
|
this.sendBroadcast(intent)
|
||||||
|
|
||||||
channel[0]?.queueDeclare("tracker", true, false, false, null)
|
channel[0]?.queueDeclare("tracker-" + factory.username, true, false, false, null)
|
||||||
//channel[0]?.basicPublish("", "Tracker", null, "Test message".toByteArray())
|
//channel[0]?.basicPublish("", "Tracker", null, "Test message".toByteArray())
|
||||||
Log.i("RabbitMQ", "run: Published test message")
|
Log.i("RabbitMQ", "run: Published test message")
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
@@ -83,7 +78,7 @@ class TrackerService : Service() {
|
|||||||
val androidId = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)
|
val androidId = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)
|
||||||
|
|
||||||
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
|
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
|
||||||
locationManager.requestLocationUpdates("gps", 5000, 0f
|
locationManager.requestLocationUpdates("gps", 10000, 0f
|
||||||
) { location ->
|
) { location ->
|
||||||
Log.i("Location", "Location is: " + location.latitude + " | " + location.longitude)
|
Log.i("Location", "Location is: " + location.latitude + " | " + location.longitude)
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
import bodyParser = require('body-parser');
|
import bodyParser = require('body-parser');
|
||||||
import { bold } from 'chalk';
|
import { bold } from 'chalk';
|
||||||
|
import * as cors from 'cors';
|
||||||
import { config as dconfig } from 'dotenv';
|
import { config as dconfig } from 'dotenv';
|
||||||
import * as express from 'express';
|
import * as express from 'express';
|
||||||
import * as figlet from 'figlet';
|
import * as figlet from 'figlet';
|
||||||
import * as mongoose from 'mongoose';
|
import * as mongoose from 'mongoose';
|
||||||
import * as cors from 'cors';
|
|
||||||
import { exit } from 'process';
|
import { exit } from 'process';
|
||||||
import * as winston from 'winston';
|
import * as winston from 'winston';
|
||||||
import { RabbitMQ } from './lib/rabbit';
|
|
||||||
|
|
||||||
import { config } from './config';
|
import { config } from './config';
|
||||||
import { DeleteUser, GetUser, LoginUser, MW_User, PatchUser } from './endpoints/user';
|
import { GetBeat, GetBeatStats } from './endpoints/beat';
|
||||||
|
import { GetPhone, PostPhone } from './endpoints/phone';
|
||||||
|
import { DeleteUser, GetUser, LoginRabbitUser, LoginUser, MW_User, PatchUser, Resource, Topic, VHost } from './endpoints/user';
|
||||||
import { hashPassword, randomPepper, randomString } from './lib/crypto';
|
import { hashPassword, randomPepper, randomString } from './lib/crypto';
|
||||||
|
import { RabbitMQ } from './lib/rabbit';
|
||||||
import { UserType } from './models/user/user.interface';
|
import { UserType } from './models/user/user.interface';
|
||||||
import { User } from './models/user/user.model';
|
import { User } from './models/user/user.model';
|
||||||
import { GetPhone, PostPhone } from './endpoints/phone';
|
|
||||||
import { GetBeat, GetBeatStats } from './endpoints/beat';
|
|
||||||
|
|
||||||
// Load .env
|
// Load .env
|
||||||
dconfig({ debug: true, encoding: 'UTF-8' });
|
dconfig({ debug: true, encoding: 'UTF-8' });
|
||||||
@@ -107,6 +107,7 @@ async function run() {
|
|||||||
await User.create({
|
await User.create({
|
||||||
name: 'admin',
|
name: 'admin',
|
||||||
password: await hashPassword(randomPassword + salt + randomPepper()),
|
password: await hashPassword(randomPassword + salt + randomPepper()),
|
||||||
|
brokerToken: randomString(16),
|
||||||
salt,
|
salt,
|
||||||
createdAt: Date.now(),
|
createdAt: Date.now(),
|
||||||
lastLogin: 0,
|
lastLogin: 0,
|
||||||
@@ -119,13 +120,6 @@ async function run() {
|
|||||||
logger.debug("At least one admin user already exists, skip.");
|
logger.debug("At least one admin user already exists, skip.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Message broker
|
|
||||||
*/
|
|
||||||
rabbitmq = new RabbitMQ();
|
|
||||||
await rabbitmq.init();
|
|
||||||
logger.info("Connected with message broker.");
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HTTP server
|
* HTTP server
|
||||||
*/
|
*/
|
||||||
@@ -143,14 +137,22 @@ async function run() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
app.get('/', (req, res) => res.status(200).send('OK'));
|
app.get('/', (req, res) => res.status(200).send('OK'));
|
||||||
|
|
||||||
|
// User authentication
|
||||||
|
app.post('/user/login', (req, res) => LoginUser(req, res));
|
||||||
|
app.get('/user/rabbitlogin', (req, res) => LoginRabbitUser(req, res));
|
||||||
|
app.get('/user/vhost', (req, res) => VHost(req, res));
|
||||||
|
app.get('/user/resource', (req, res) => Resource(req, res));
|
||||||
|
app.get('/user/topic', (req, res) => Topic(req, res));
|
||||||
|
|
||||||
|
// Basic user actions
|
||||||
app.get('/user/', MW_User, (req, res) => GetUser(req, res));
|
app.get('/user/', MW_User, (req, res) => GetUser(req, res));
|
||||||
app.get('/user/:id', MW_User, (req, res) => GetUser(req, res));
|
app.get('/user/:id', MW_User, (req, res) => GetUser(req, res));
|
||||||
app.patch('/user/:id', MW_User, (req, res) => PatchUser(req, res));
|
app.patch('/user/:id', MW_User, (req, res) => PatchUser(req, res));
|
||||||
app.delete('/user/:id', MW_User, (req, res) => DeleteUser(req, res));
|
app.delete('/user/:id', MW_User, (req, res) => DeleteUser(req, res));
|
||||||
app.post('/user/login', (req, res) => LoginUser(req, res));
|
|
||||||
|
|
||||||
app.get('/phone', MW_User, (req, res) => GetPhone(req, res));
|
|
||||||
app.get('/phone/:id', MW_User, (req, res) => GetPhone(req, res));
|
app.get('/phone/:id', MW_User, (req, res) => GetPhone(req, res));
|
||||||
|
app.get('/phone', MW_User, (req, res) => GetPhone(req, res));
|
||||||
app.post('/phone', MW_User, (req, res) => PostPhone(req, res));
|
app.post('/phone', MW_User, (req, res) => PostPhone(req, res));
|
||||||
|
|
||||||
app.get('/beat/', MW_User, (req, res) => GetBeat(req, res));
|
app.get('/beat/', MW_User, (req, res) => GetBeat(req, res));
|
||||||
@@ -159,6 +161,13 @@ async function run() {
|
|||||||
app.listen(config.http.port, config.http.host, () => {
|
app.listen(config.http.port, config.http.host, () => {
|
||||||
logger.info(`HTTP server is running at ${config.http.host}:${config.http.port}`);
|
logger.info(`HTTP server is running at ${config.http.host}:${config.http.port}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message broker
|
||||||
|
*/
|
||||||
|
rabbitmq = new RabbitMQ();
|
||||||
|
await rabbitmq.init();
|
||||||
|
logger.info("Connected with message broker.");
|
||||||
}
|
}
|
||||||
|
|
||||||
run();
|
run();
|
||||||
@@ -35,7 +35,7 @@ export async function GetBeat(req: LivebeatRequest, res: Response) {
|
|||||||
$gte: new Date((from | 0) * 1000),
|
$gte: new Date((from | 0) * 1000),
|
||||||
$lte: new Date((to | Date.now() /1000) * 1000)
|
$lte: new Date((to | Date.now() /1000) * 1000)
|
||||||
}
|
}
|
||||||
}).sort({ _id: -1 });
|
}).sort({ _id: 1 });
|
||||||
res.status(200).send(beats);
|
res.status(200).send(beats);
|
||||||
} else {
|
} else {
|
||||||
res.status(404).send({ message: 'Phone not found' });
|
res.status(404).send({ message: 'Phone not found' });
|
||||||
|
|||||||
@@ -2,10 +2,11 @@ import { Request, Response } from "express";
|
|||||||
import { verifyPassword } from "../lib/crypto";
|
import { verifyPassword } from "../lib/crypto";
|
||||||
import { User } from "../models/user/user.model";
|
import { User } from "../models/user/user.model";
|
||||||
import { sign, decode, verify } from 'jsonwebtoken';
|
import { sign, decode, verify } from 'jsonwebtoken';
|
||||||
import { JWT_SECRET, logger } from "../app";
|
import { JWT_SECRET, logger, RABBITMQ_URI } from "../app";
|
||||||
import { LivebeatRequest } from '../lib/request';
|
import { LivebeatRequest } from '../lib/request';
|
||||||
import { SchemaTypes } from "mongoose";
|
import { SchemaTypes } from "mongoose";
|
||||||
import { Phone } from "../models/phone/phone.model";
|
import { Phone } from "../models/phone/phone.model";
|
||||||
|
import { UserType } from "../models/user/user.interface";
|
||||||
|
|
||||||
export async function GetUser(req: LivebeatRequest, res: Response) {
|
export async function GetUser(req: LivebeatRequest, res: Response) {
|
||||||
let user: any = req.user;
|
let user: any = req.user;
|
||||||
@@ -64,6 +65,130 @@ export async function LoginUser(req: Request, res: Response) {
|
|||||||
res.status(200).send({ token });
|
res.status(200).send({ token });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function handles all logins to RabbitMQ since they need a differnt type of response
|
||||||
|
* then requests from frontends (web and phone).
|
||||||
|
*/
|
||||||
|
export async function LoginRabbitUser(req: Request, res: Response) {
|
||||||
|
const username = req.query.username;
|
||||||
|
const password = req.query.password;
|
||||||
|
|
||||||
|
if (username === undefined || password === undefined) {
|
||||||
|
res.status(200).send('deny');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if request comes from backend. Basicly, we permitting ourself to connect with RabbitMQ.
|
||||||
|
if (username === "backend" && password === RABBITMQ_URI.split(':')[2].split('@')[0]) {
|
||||||
|
res.status(200).send('allow administrator');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get user from database
|
||||||
|
const user = await User.findOne({ name: username.toString() });
|
||||||
|
|
||||||
|
// If we are here, it means we have a non-admin user.
|
||||||
|
if (user === null) {
|
||||||
|
res.status(200).send('deny');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auth token for message broker is stored in plain text since it's randomly generated and only grants access to the broker.
|
||||||
|
if (user.brokerToken === password.toString()) {
|
||||||
|
if (user.type === UserType.ADMIN) {
|
||||||
|
res.status(200).send('allow administrator');
|
||||||
|
} else {
|
||||||
|
// Not an admin, grant user privilieges
|
||||||
|
res.status(200).send('allow user')
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(200).send('deny');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function basicly allows access to the root vhost if the user is known.
|
||||||
|
*/
|
||||||
|
export async function VHost(req: Request, res: Response) {
|
||||||
|
const vhost = req.query.vhost;
|
||||||
|
const username = req.query.username;
|
||||||
|
|
||||||
|
if (vhost === undefined || username === undefined) {
|
||||||
|
res.status(200).send('deny');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vhost != '/') {
|
||||||
|
res.status(200).send('deny');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if user is us
|
||||||
|
if (username === 'backend') {
|
||||||
|
res.status(200).send('allow');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await User.findOne({ name: username.toString() });
|
||||||
|
if (user === null) {
|
||||||
|
// Deny if user doesn't exist.
|
||||||
|
res.status(200).send('deny');
|
||||||
|
} else {
|
||||||
|
res.status(200).send('allow');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function Resource(req: Request, res: Response) {
|
||||||
|
const username = req.query.username;
|
||||||
|
const vhost = req.query.vhost;
|
||||||
|
const resource = req.query.resource;
|
||||||
|
const name = req.query.name;
|
||||||
|
const permission = req.query.permission;
|
||||||
|
const tags = req.query.tags;
|
||||||
|
|
||||||
|
if (username === undefined || vhost === undefined || resource === undefined || name === undefined || permission === undefined || tags === undefined) {
|
||||||
|
res.status(200).send('deny');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if it's us
|
||||||
|
if (username.toString() == 'backend') {
|
||||||
|
res.status(200).send('allow');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deny if not root vhost
|
||||||
|
if (vhost.toString() != '/') {
|
||||||
|
res.status(200).send('deny');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if user exists
|
||||||
|
const user = await User.findOne({ name: username.toString() });
|
||||||
|
if (user == null) {
|
||||||
|
res.status(200).send('deny');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tags.toString() == "administrator" && user.type != UserType.ADMIN) {
|
||||||
|
res.status(200).send('deny');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: This has to change if we want to allow users to see the realtime movement of others.
|
||||||
|
if (resource.toString().startsWith('tracker-') && resource != 'tracker-' + username) {
|
||||||
|
res.status(200).send('deny');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(200).send('allow');
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function Topic(req: Request, res: Response) {
|
||||||
|
res.status(200).send('allow');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This middleware validates any tokens that are required to access most of the endpoints.
|
* This middleware validates any tokens that are required to access most of the endpoints.
|
||||||
* Note: This validation doesn't contain any permission checking.
|
* Note: This validation doesn't contain any permission checking.
|
||||||
|
|||||||
@@ -30,9 +30,9 @@ export class RabbitMQ {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info(`New beat from ${phone.displayName} with ${msg.gpsLocation[2]} accuracy and ${msg.battery}% battery`)
|
logger.info(`New beat from ${phone.displayName} with ${msg.gpsLocation[2]} accuracy and ${msg.battery}% battery`);
|
||||||
|
|
||||||
Beat.create({
|
const newBeat = await Beat.create({
|
||||||
phone: phone._id,
|
phone: phone._id,
|
||||||
coordinate: [msg.gpsLocation[0], msg.gpsLocation[1]],
|
coordinate: [msg.gpsLocation[0], msg.gpsLocation[1]],
|
||||||
accuracy: msg.gpsLocation[2],
|
accuracy: msg.gpsLocation[2],
|
||||||
@@ -40,6 +40,8 @@ export class RabbitMQ {
|
|||||||
battery: msg.battery,
|
battery: msg.battery,
|
||||||
createdAt: msg.timestamp
|
createdAt: msg.timestamp
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.channel!.publish('amq.topic', '.', Buffer.from(JSON.stringify(newBeat.toJSON())));
|
||||||
}, { noAck: true });
|
}, { noAck: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ const schemaBeat = new Schema({
|
|||||||
createdAt: { type: SchemaTypes.Date, required: false }
|
createdAt: { type: SchemaTypes.Date, required: false }
|
||||||
}, {
|
}, {
|
||||||
timestamps: {
|
timestamps: {
|
||||||
createdAt: true
|
createdAt: true,
|
||||||
|
updatedAt: false
|
||||||
},
|
},
|
||||||
versionKey: false
|
versionKey: false
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,5 +13,6 @@ export interface IUser extends Document {
|
|||||||
type: UserType,
|
type: UserType,
|
||||||
lastLogin: Date,
|
lastLogin: Date,
|
||||||
twoFASecret?: string,
|
twoFASecret?: string,
|
||||||
|
brokerToken: string,
|
||||||
createdAt?: Date
|
createdAt?: Date
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,7 @@ const schemaUser = new Schema({
|
|||||||
salt: { type: String, required: true },
|
salt: { type: String, required: true },
|
||||||
type: { type: String, required: true, default: 'user' }, // This could be user, admin, guest
|
type: { type: String, required: true, default: 'user' }, // This could be user, admin, guest
|
||||||
twoFASecret: { type: String, required: false },
|
twoFASecret: { type: String, required: false },
|
||||||
|
brokerToken: { type: String, required: true },
|
||||||
lastLogin: { type: Date, required: true, default: Date.now },
|
lastLogin: { type: Date, required: true, default: Date.now },
|
||||||
}, {
|
}, {
|
||||||
timestamps: {
|
timestamps: {
|
||||||
|
|||||||
438
frontend/package-lock.json
generated
438
frontend/package-lock.json
generated
@@ -2718,8 +2718,7 @@
|
|||||||
"balanced-match": {
|
"balanced-match": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
|
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"base": {
|
"base": {
|
||||||
"version": "0.11.2",
|
"version": "0.11.2",
|
||||||
@@ -2790,8 +2789,7 @@
|
|||||||
"base64-js": {
|
"base64-js": {
|
||||||
"version": "1.3.1",
|
"version": "1.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
|
||||||
"integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==",
|
"integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"base64id": {
|
"base64id": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
@@ -2835,6 +2833,37 @@
|
|||||||
"integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==",
|
"integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"bl": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/bl/-/bl-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg==",
|
||||||
|
"requires": {
|
||||||
|
"buffer": "^5.5.0",
|
||||||
|
"inherits": "^2.0.4",
|
||||||
|
"readable-stream": "^3.4.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"buffer": {
|
||||||
|
"version": "5.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.1.tgz",
|
||||||
|
"integrity": "sha512-2z15UUHpS9/3tk9mY/q+Rl3rydOi7yMp5XWNQnRvoz+mJwiv8brqYwp9a+nOCtma6dwuEIxljD8W3ysVBZ05Vg==",
|
||||||
|
"requires": {
|
||||||
|
"base64-js": "^1.3.1",
|
||||||
|
"ieee754": "^1.1.13"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"readable-stream": {
|
||||||
|
"version": "3.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||||
|
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||||
|
"requires": {
|
||||||
|
"inherits": "^2.0.3",
|
||||||
|
"string_decoder": "^1.1.1",
|
||||||
|
"util-deprecate": "^1.0.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"blob": {
|
"blob": {
|
||||||
"version": "0.0.5",
|
"version": "0.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
|
||||||
@@ -2936,7 +2965,6 @@
|
|||||||
"version": "1.1.11",
|
"version": "1.1.11",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"balanced-match": "^1.0.0",
|
"balanced-match": "^1.0.0",
|
||||||
"concat-map": "0.0.1"
|
"concat-map": "0.0.1"
|
||||||
@@ -3239,6 +3267,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"callback-stream": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/callback-stream/-/callback-stream-1.1.0.tgz",
|
||||||
|
"integrity": "sha1-RwGlEmbwbgbqpx/BcjOCLYdfSQg=",
|
||||||
|
"requires": {
|
||||||
|
"inherits": "^2.0.1",
|
||||||
|
"readable-stream": "> 1.0.0 < 3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"caller-callsite": {
|
"caller-callsite": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz",
|
||||||
@@ -3646,6 +3683,22 @@
|
|||||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"commist": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/commist/-/commist-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-rraC8NXWOEjhADbZe9QBNzLAN5Q3fsTPQtBV+fEVj6xKIgDgNiEVE6ZNfHpZOqfQ21YUzfVNUXLOEZquYvQPPg==",
|
||||||
|
"requires": {
|
||||||
|
"leven": "^2.1.0",
|
||||||
|
"minimist": "^1.1.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"leven": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz",
|
||||||
|
"integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"commondir": {
|
"commondir": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
|
||||||
@@ -3723,14 +3776,12 @@
|
|||||||
"concat-map": {
|
"concat-map": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"concat-stream": {
|
"concat-stream": {
|
||||||
"version": "1.6.2",
|
"version": "1.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
|
||||||
"integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
|
"integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"buffer-from": "^1.0.0",
|
"buffer-from": "^1.0.0",
|
||||||
"inherits": "^2.0.3",
|
"inherits": "^2.0.3",
|
||||||
@@ -4271,7 +4322,6 @@
|
|||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
|
||||||
"integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
|
"integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"es5-ext": "^0.10.50",
|
"es5-ext": "^0.10.50",
|
||||||
"type": "^1.0.1"
|
"type": "^1.0.1"
|
||||||
@@ -4313,7 +4363,6 @@
|
|||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz",
|
||||||
"integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==",
|
"integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"ms": "2.1.2"
|
"ms": "2.1.2"
|
||||||
}
|
}
|
||||||
@@ -4696,7 +4745,6 @@
|
|||||||
"version": "3.7.1",
|
"version": "3.7.1",
|
||||||
"resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
|
||||||
"integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==",
|
"integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"end-of-stream": "^1.0.0",
|
"end-of-stream": "^1.0.0",
|
||||||
"inherits": "^2.0.1",
|
"inherits": "^2.0.1",
|
||||||
@@ -4785,7 +4833,6 @@
|
|||||||
"version": "1.4.4",
|
"version": "1.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
|
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
|
||||||
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
|
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"once": "^1.4.0"
|
"once": "^1.4.0"
|
||||||
}
|
}
|
||||||
@@ -4978,7 +5025,6 @@
|
|||||||
"version": "0.10.53",
|
"version": "0.10.53",
|
||||||
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz",
|
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz",
|
||||||
"integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==",
|
"integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"es6-iterator": "~2.0.3",
|
"es6-iterator": "~2.0.3",
|
||||||
"es6-symbol": "~3.1.3",
|
"es6-symbol": "~3.1.3",
|
||||||
@@ -4989,13 +5035,25 @@
|
|||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
|
||||||
"integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=",
|
"integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"d": "1",
|
"d": "1",
|
||||||
"es5-ext": "^0.10.35",
|
"es5-ext": "^0.10.35",
|
||||||
"es6-symbol": "^3.1.1"
|
"es6-symbol": "^3.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"es6-map": {
|
||||||
|
"version": "0.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz",
|
||||||
|
"integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=",
|
||||||
|
"requires": {
|
||||||
|
"d": "1",
|
||||||
|
"es5-ext": "~0.10.14",
|
||||||
|
"es6-iterator": "~2.0.1",
|
||||||
|
"es6-set": "~0.1.5",
|
||||||
|
"es6-symbol": "~3.1.1",
|
||||||
|
"event-emitter": "~0.3.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"es6-promise": {
|
"es6-promise": {
|
||||||
"version": "4.2.8",
|
"version": "4.2.8",
|
||||||
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
|
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
|
||||||
@@ -5011,11 +5069,33 @@
|
|||||||
"es6-promise": "^4.0.3"
|
"es6-promise": "^4.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"es6-set": {
|
||||||
|
"version": "0.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz",
|
||||||
|
"integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=",
|
||||||
|
"requires": {
|
||||||
|
"d": "1",
|
||||||
|
"es5-ext": "~0.10.14",
|
||||||
|
"es6-iterator": "~2.0.1",
|
||||||
|
"es6-symbol": "3.1.1",
|
||||||
|
"event-emitter": "~0.3.5"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"es6-symbol": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz",
|
||||||
|
"integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=",
|
||||||
|
"requires": {
|
||||||
|
"d": "1",
|
||||||
|
"es5-ext": "~0.10.14"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"es6-symbol": {
|
"es6-symbol": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz",
|
||||||
"integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
|
"integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"d": "^1.0.1",
|
"d": "^1.0.1",
|
||||||
"ext": "^1.1.2"
|
"ext": "^1.1.2"
|
||||||
@@ -5094,6 +5174,15 @@
|
|||||||
"resolved": "https://registry.npmjs.org/eva-icons/-/eva-icons-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/eva-icons/-/eva-icons-1.1.3.tgz",
|
||||||
"integrity": "sha512-QBSEWNbEx1H0numXP1qgxKVCZHonRaky5ft4pGzQBcO4cy7mEja6TuJ8rc7BqX2pmkvetVQWKDH+DK/8y7GTag=="
|
"integrity": "sha512-QBSEWNbEx1H0numXP1qgxKVCZHonRaky5ft4pGzQBcO4cy7mEja6TuJ8rc7BqX2pmkvetVQWKDH+DK/8y7GTag=="
|
||||||
},
|
},
|
||||||
|
"event-emitter": {
|
||||||
|
"version": "0.3.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz",
|
||||||
|
"integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=",
|
||||||
|
"requires": {
|
||||||
|
"d": "1",
|
||||||
|
"es5-ext": "~0.10.14"
|
||||||
|
}
|
||||||
|
},
|
||||||
"eventemitter3": {
|
"eventemitter3": {
|
||||||
"version": "4.0.7",
|
"version": "4.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
||||||
@@ -5261,7 +5350,6 @@
|
|||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz",
|
||||||
"integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==",
|
"integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"type": "^2.0.0"
|
"type": "^2.0.0"
|
||||||
},
|
},
|
||||||
@@ -5269,16 +5357,14 @@
|
|||||||
"type": {
|
"type": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz",
|
||||||
"integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==",
|
"integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA=="
|
||||||
"dev": true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"extend": {
|
"extend": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
|
||||||
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
|
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"extend-shallow": {
|
"extend-shallow": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
@@ -5700,8 +5786,7 @@
|
|||||||
"fs.realpath": {
|
"fs.realpath": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
|
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"fsevents": {
|
"fsevents": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
@@ -5782,7 +5867,6 @@
|
|||||||
"version": "7.1.6",
|
"version": "7.1.6",
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
|
||||||
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
|
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"fs.realpath": "^1.0.0",
|
"fs.realpath": "^1.0.0",
|
||||||
"inflight": "^1.0.4",
|
"inflight": "^1.0.4",
|
||||||
@@ -5801,6 +5885,42 @@
|
|||||||
"is-glob": "^4.0.1"
|
"is-glob": "^4.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"glob-stream": {
|
||||||
|
"version": "6.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz",
|
||||||
|
"integrity": "sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ=",
|
||||||
|
"requires": {
|
||||||
|
"extend": "^3.0.0",
|
||||||
|
"glob": "^7.1.1",
|
||||||
|
"glob-parent": "^3.1.0",
|
||||||
|
"is-negated-glob": "^1.0.0",
|
||||||
|
"ordered-read-streams": "^1.0.0",
|
||||||
|
"pumpify": "^1.3.5",
|
||||||
|
"readable-stream": "^2.1.5",
|
||||||
|
"remove-trailing-separator": "^1.0.1",
|
||||||
|
"to-absolute-glob": "^2.0.0",
|
||||||
|
"unique-stream": "^2.0.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"glob-parent": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
|
||||||
|
"integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
|
||||||
|
"requires": {
|
||||||
|
"is-glob": "^3.1.0",
|
||||||
|
"path-dirname": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"is-glob": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
|
||||||
|
"integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
|
||||||
|
"requires": {
|
||||||
|
"is-extglob": "^2.1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"globals": {
|
"globals": {
|
||||||
"version": "11.12.0",
|
"version": "11.12.0",
|
||||||
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
|
||||||
@@ -6052,6 +6172,17 @@
|
|||||||
"minimalistic-assert": "^1.0.1"
|
"minimalistic-assert": "^1.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"help-me": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/help-me/-/help-me-1.1.0.tgz",
|
||||||
|
"integrity": "sha1-jy1QjQYAtKRW2i8IZVbn5cBWo8Y=",
|
||||||
|
"requires": {
|
||||||
|
"callback-stream": "^1.0.2",
|
||||||
|
"glob-stream": "^6.1.0",
|
||||||
|
"through2": "^2.0.1",
|
||||||
|
"xtend": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"hex-color-regex": {
|
"hex-color-regex": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz",
|
||||||
@@ -6482,7 +6613,6 @@
|
|||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||||
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
|
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"once": "^1.3.0",
|
"once": "^1.3.0",
|
||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
@@ -6655,6 +6785,15 @@
|
|||||||
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
|
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"is-absolute": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==",
|
||||||
|
"requires": {
|
||||||
|
"is-relative": "^1.0.0",
|
||||||
|
"is-windows": "^1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"is-absolute-url": {
|
"is-absolute-url": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz",
|
||||||
@@ -6793,8 +6932,7 @@
|
|||||||
"is-extglob": {
|
"is-extglob": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||||
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
|
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"is-fullwidth-code-point": {
|
"is-fullwidth-code-point": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
@@ -6817,6 +6955,11 @@
|
|||||||
"integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==",
|
"integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"is-negated-glob": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI="
|
||||||
|
},
|
||||||
"is-negative-zero": {
|
"is-negative-zero": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz",
|
||||||
@@ -6887,6 +7030,14 @@
|
|||||||
"has-symbols": "^1.0.1"
|
"has-symbols": "^1.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"is-relative": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==",
|
||||||
|
"requires": {
|
||||||
|
"is-unc-path": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"is-resolvable": {
|
"is-resolvable": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz",
|
||||||
@@ -6928,11 +7079,18 @@
|
|||||||
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
|
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"is-unc-path": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==",
|
||||||
|
"requires": {
|
||||||
|
"unc-path-regex": "^0.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"is-windows": {
|
"is-windows": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
|
||||||
"integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
|
"integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"is-wsl": {
|
"is-wsl": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
@@ -7219,6 +7377,11 @@
|
|||||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"json-stable-stringify-without-jsonify": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE="
|
||||||
|
},
|
||||||
"json-stringify-safe": {
|
"json-stringify-safe": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
|
||||||
@@ -8246,7 +8409,6 @@
|
|||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"brace-expansion": "^1.1.7"
|
"brace-expansion": "^1.1.7"
|
||||||
}
|
}
|
||||||
@@ -8390,11 +8552,50 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"mqtt": {
|
||||||
|
"version": "4.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/mqtt/-/mqtt-4.2.1.tgz",
|
||||||
|
"integrity": "sha512-Iv893r+jWlo5GkNcPOfCGwW8M49IixwHiKLFFYTociEymSibUVCORVEjPXWPGzSxhn7BdlUeHicbRmWiv0Crkg==",
|
||||||
|
"requires": {
|
||||||
|
"base64-js": "^1.3.0",
|
||||||
|
"commist": "^1.0.0",
|
||||||
|
"concat-stream": "^1.6.2",
|
||||||
|
"debug": "^4.1.1",
|
||||||
|
"end-of-stream": "^1.4.1",
|
||||||
|
"es6-map": "^0.1.5",
|
||||||
|
"help-me": "^1.0.1",
|
||||||
|
"inherits": "^2.0.3",
|
||||||
|
"minimist": "^1.2.5",
|
||||||
|
"mqtt-packet": "^6.3.2",
|
||||||
|
"pump": "^3.0.0",
|
||||||
|
"readable-stream": "^2.3.6",
|
||||||
|
"reinterval": "^1.1.0",
|
||||||
|
"split2": "^3.1.0",
|
||||||
|
"ws": "^7.3.1",
|
||||||
|
"xtend": "^4.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ws": {
|
||||||
|
"version": "7.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz",
|
||||||
|
"integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mqtt-packet": {
|
||||||
|
"version": "6.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-6.6.0.tgz",
|
||||||
|
"integrity": "sha512-LvghnKMFC70hKWMVykmhJarlO5e7lT3t9s9A2qPCUx+lazL3Mq55U+eCV0eLi7/nRRQYvEUWo/2tTo89EjnCJQ==",
|
||||||
|
"requires": {
|
||||||
|
"bl": "^4.0.2",
|
||||||
|
"debug": "^4.1.1",
|
||||||
|
"process-nextick-args": "^2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"ms": {
|
"ms": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"multicast-dns": {
|
"multicast-dns": {
|
||||||
"version": "6.2.3",
|
"version": "6.2.3",
|
||||||
@@ -8464,8 +8665,7 @@
|
|||||||
"next-tick": {
|
"next-tick": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
|
||||||
"integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=",
|
"integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"ng2-charts": {
|
"ng2-charts": {
|
||||||
"version": "2.4.2",
|
"version": "2.4.2",
|
||||||
@@ -8486,6 +8686,22 @@
|
|||||||
"tslib": "^2.0.0"
|
"tslib": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ngx-mqtt": {
|
||||||
|
"version": "7.0.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/ngx-mqtt/-/ngx-mqtt-7.0.14.tgz",
|
||||||
|
"integrity": "sha512-b6TwC5m8eSGbKEF/I6dDpPTRPtMm3uKKxVKanqiF+JenOlNC5GKJ7YlXLpzCGHn+pK+OuXxSlRmtNbAegANl/w==",
|
||||||
|
"requires": {
|
||||||
|
"mqtt": "4.2.1",
|
||||||
|
"tslib": "^1.10.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": {
|
||||||
|
"version": "1.14.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||||
|
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"nice-try": {
|
"nice-try": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
|
||||||
@@ -8983,7 +9199,6 @@
|
|||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
}
|
}
|
||||||
@@ -9106,6 +9321,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ordered-read-streams": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4=",
|
||||||
|
"requires": {
|
||||||
|
"readable-stream": "^2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz",
|
||||||
@@ -9480,8 +9703,7 @@
|
|||||||
"path-dirname": {
|
"path-dirname": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
|
||||||
"integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=",
|
"integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"path-exists": {
|
"path-exists": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
@@ -9492,8 +9714,7 @@
|
|||||||
"path-is-absolute": {
|
"path-is-absolute": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||||
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
|
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"path-is-inside": {
|
"path-is-inside": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
@@ -10714,7 +10935,6 @@
|
|||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
|
||||||
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
|
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"end-of-stream": "^1.1.0",
|
"end-of-stream": "^1.1.0",
|
||||||
"once": "^1.3.1"
|
"once": "^1.3.1"
|
||||||
@@ -10724,7 +10944,6 @@
|
|||||||
"version": "1.5.1",
|
"version": "1.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz",
|
||||||
"integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==",
|
"integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"duplexify": "^3.6.0",
|
"duplexify": "^3.6.0",
|
||||||
"inherits": "^2.0.3",
|
"inherits": "^2.0.3",
|
||||||
@@ -10735,7 +10954,6 @@
|
|||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
|
||||||
"integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
|
"integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"end-of-stream": "^1.1.0",
|
"end-of-stream": "^1.1.0",
|
||||||
"once": "^1.3.1"
|
"once": "^1.3.1"
|
||||||
@@ -11149,11 +11367,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"reinterval": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/reinterval/-/reinterval-1.1.0.tgz",
|
||||||
|
"integrity": "sha1-M2Hs+jymwYKDOA3Qu5VG85D17Oc="
|
||||||
|
},
|
||||||
"remove-trailing-separator": {
|
"remove-trailing-separator": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
|
||||||
"integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=",
|
"integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"repeat-element": {
|
"repeat-element": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
@@ -12177,40 +12399,6 @@
|
|||||||
"websocket-driver": "0.6.5"
|
"websocket-driver": "0.6.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sockjs-client": {
|
|
||||||
"version": "1.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.4.0.tgz",
|
|
||||||
"integrity": "sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"debug": "^3.2.5",
|
|
||||||
"eventsource": "^1.0.7",
|
|
||||||
"faye-websocket": "~0.11.1",
|
|
||||||
"inherits": "^2.0.3",
|
|
||||||
"json3": "^3.3.2",
|
|
||||||
"url-parse": "^1.4.3"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"debug": {
|
|
||||||
"version": "3.2.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
|
|
||||||
"integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"ms": "^2.1.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"faye-websocket": {
|
|
||||||
"version": "0.11.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz",
|
|
||||||
"integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"websocket-driver": ">=0.5.1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"socks": {
|
"socks": {
|
||||||
"version": "2.3.3",
|
"version": "2.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/socks/-/socks-2.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/socks/-/socks-2.3.3.tgz",
|
||||||
@@ -12413,6 +12601,26 @@
|
|||||||
"extend-shallow": "^3.0.0"
|
"extend-shallow": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"split2": {
|
||||||
|
"version": "3.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz",
|
||||||
|
"integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==",
|
||||||
|
"requires": {
|
||||||
|
"readable-stream": "^3.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"readable-stream": {
|
||||||
|
"version": "3.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||||
|
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||||
|
"requires": {
|
||||||
|
"inherits": "^2.0.3",
|
||||||
|
"string_decoder": "^1.1.1",
|
||||||
|
"util-deprecate": "^1.0.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"sprintf-js": {
|
"sprintf-js": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||||
@@ -12514,8 +12722,7 @@
|
|||||||
"stream-shift": {
|
"stream-shift": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz",
|
||||||
"integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==",
|
"integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"streamroller": {
|
"streamroller": {
|
||||||
"version": "2.2.4",
|
"version": "2.2.4",
|
||||||
@@ -12928,12 +13135,20 @@
|
|||||||
"version": "2.0.5",
|
"version": "2.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
|
||||||
"integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
|
"integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"readable-stream": "~2.3.6",
|
"readable-stream": "~2.3.6",
|
||||||
"xtend": "~4.0.1"
|
"xtend": "~4.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"through2-filter": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==",
|
||||||
|
"requires": {
|
||||||
|
"through2": "~2.0.0",
|
||||||
|
"xtend": "~4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"thunky": {
|
"thunky": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
|
||||||
@@ -12974,6 +13189,15 @@
|
|||||||
"os-tmpdir": "~1.0.2"
|
"os-tmpdir": "~1.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"to-absolute-glob": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz",
|
||||||
|
"integrity": "sha1-GGX0PZ50sIItufFFt4z/fQ98hJs=",
|
||||||
|
"requires": {
|
||||||
|
"is-absolute": "^1.0.0",
|
||||||
|
"is-negated-glob": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"to-array": {
|
"to-array": {
|
||||||
"version": "0.1.4",
|
"version": "0.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
|
||||||
@@ -13169,8 +13393,7 @@
|
|||||||
"type": {
|
"type": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
|
||||||
"integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
|
"integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"type-detect": {
|
"type-detect": {
|
||||||
"version": "4.0.8",
|
"version": "4.0.8",
|
||||||
@@ -13210,6 +13433,11 @@
|
|||||||
"integrity": "sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ==",
|
"integrity": "sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"unc-path-regex": {
|
||||||
|
"version": "0.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz",
|
||||||
|
"integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo="
|
||||||
|
},
|
||||||
"unicode-canonical-property-names-ecmascript": {
|
"unicode-canonical-property-names-ecmascript": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz",
|
||||||
@@ -13280,6 +13508,15 @@
|
|||||||
"imurmurhash": "^0.1.4"
|
"imurmurhash": "^0.1.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"unique-stream": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==",
|
||||||
|
"requires": {
|
||||||
|
"json-stable-stringify-without-jsonify": "^1.0.1",
|
||||||
|
"through2-filter": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"universal-analytics": {
|
"universal-analytics": {
|
||||||
"version": "0.4.23",
|
"version": "0.4.23",
|
||||||
"resolved": "https://registry.npmjs.org/universal-analytics/-/universal-analytics-0.4.23.tgz",
|
"resolved": "https://registry.npmjs.org/universal-analytics/-/universal-analytics-0.4.23.tgz",
|
||||||
@@ -14273,6 +14510,15 @@
|
|||||||
"upath": "^1.1.1"
|
"upath": "^1.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"faye-websocket": {
|
||||||
|
"version": "0.11.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz",
|
||||||
|
"integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"websocket-driver": ">=0.5.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"fill-range": {
|
"fill-range": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
|
||||||
@@ -14408,6 +14654,31 @@
|
|||||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"sockjs-client": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"debug": "^3.2.5",
|
||||||
|
"eventsource": "^1.0.7",
|
||||||
|
"faye-websocket": "~0.11.1",
|
||||||
|
"inherits": "^2.0.3",
|
||||||
|
"json3": "^3.3.2",
|
||||||
|
"url-parse": "^1.4.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"debug": {
|
||||||
|
"version": "3.2.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
|
||||||
|
"integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ms": "^2.1.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"supports-color": {
|
"supports-color": {
|
||||||
"version": "6.1.0",
|
"version": "6.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
|
||||||
@@ -14599,8 +14870,7 @@
|
|||||||
"wrappy": {
|
"wrappy": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"ws": {
|
"ws": {
|
||||||
"version": "6.2.1",
|
"version": "6.2.1",
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
"moment": "^2.29.1",
|
"moment": "^2.29.1",
|
||||||
"ng2-charts": "^2.4.2",
|
"ng2-charts": "^2.4.2",
|
||||||
"ngx-mapbox-gl": "^4.8.1",
|
"ngx-mapbox-gl": "^4.8.1",
|
||||||
|
"ngx-mqtt": "^7.0.14",
|
||||||
"rxjs": "~6.6.0",
|
"rxjs": "~6.6.0",
|
||||||
"tslib": "^2.0.0",
|
"tslib": "^2.0.0",
|
||||||
"zone.js": "~0.10.2"
|
"zone.js": "~0.10.2"
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
|
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
import { MqttService } from 'ngx-mqtt';
|
||||||
import { BehaviorSubject } from 'rxjs';
|
import { BehaviorSubject } from 'rxjs';
|
||||||
|
import * as moment from 'moment';
|
||||||
|
|
||||||
export interface ILogin {
|
export interface ILogin {
|
||||||
token: string;
|
token: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IBeat {
|
export interface IBeat {
|
||||||
|
_id: string;
|
||||||
coordinate?: number[];
|
coordinate?: number[];
|
||||||
accuracy: number;
|
accuracy: number;
|
||||||
speed: number;
|
speed: number;
|
||||||
@@ -35,8 +38,7 @@ export enum UserType {
|
|||||||
|
|
||||||
export interface IUser {
|
export interface IUser {
|
||||||
name: string;
|
name: string;
|
||||||
password: string;
|
brokerToken: string;
|
||||||
salt: string;
|
|
||||||
type: UserType;
|
type: UserType;
|
||||||
lastLogin: Date;
|
lastLogin: Date;
|
||||||
twoFASecret?: string;
|
twoFASecret?: string;
|
||||||
@@ -67,8 +69,15 @@ export class APIService {
|
|||||||
private token: string;
|
private token: string;
|
||||||
|
|
||||||
username: string;
|
username: string;
|
||||||
time: ITimespan | undefined;
|
rabbitmq: any;
|
||||||
|
time: ITimespan | undefined = {
|
||||||
|
from: moment().subtract(1, 'day').unix(),
|
||||||
|
to: moment().unix()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Passthough data (not useful for api but a way for components to share data)
|
||||||
showFilter = true;
|
showFilter = true;
|
||||||
|
maxAccuracy: BehaviorSubject<number> = new BehaviorSubject(30);
|
||||||
|
|
||||||
// Cached data
|
// Cached data
|
||||||
beats: IBeat[];
|
beats: IBeat[];
|
||||||
@@ -79,9 +88,8 @@ export class APIService {
|
|||||||
phones: IPhone[];
|
phones: IPhone[];
|
||||||
user: IUser = {
|
user: IUser = {
|
||||||
name: '',
|
name: '',
|
||||||
|
brokerToken: '',
|
||||||
lastLogin: new Date(2020, 3, 1),
|
lastLogin: new Date(2020, 3, 1),
|
||||||
password: '',
|
|
||||||
salt: '',
|
|
||||||
type: UserType.GUEST,
|
type: UserType.GUEST,
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
twoFASecret: ''
|
twoFASecret: ''
|
||||||
@@ -95,7 +103,7 @@ export class APIService {
|
|||||||
|
|
||||||
API_ENDPOINT = 'http://192.168.178.26:8040';
|
API_ENDPOINT = 'http://192.168.178.26:8040';
|
||||||
|
|
||||||
constructor(private httpClient: HttpClient) { }
|
constructor(private httpClient: HttpClient, private mqtt: MqttService) {}
|
||||||
|
|
||||||
async login(username: string, password: string): Promise<ILogin> {
|
async login(username: string, password: string): Promise<ILogin> {
|
||||||
return new Promise<ILogin>(async (resolve, reject) => {
|
return new Promise<ILogin>(async (resolve, reject) => {
|
||||||
@@ -107,6 +115,25 @@ export class APIService {
|
|||||||
this.username = username;
|
this.username = username;
|
||||||
await this.getPhones();
|
await this.getPhones();
|
||||||
await this.getUserInfo();
|
await this.getUserInfo();
|
||||||
|
|
||||||
|
// Connect with RabbitMQ after we received our user information
|
||||||
|
this.mqtt.connect({
|
||||||
|
hostname: '192.168.178.26',
|
||||||
|
port: 15675,
|
||||||
|
protocol: 'ws',
|
||||||
|
path: '/ws',
|
||||||
|
username: this.user.name,
|
||||||
|
password: this.user.brokerToken
|
||||||
|
});
|
||||||
|
|
||||||
|
this.mqtt.observe('/').subscribe(message => {
|
||||||
|
if (this.beats !== undefined) {
|
||||||
|
this.beats.push(JSON.parse(message.payload.toString()) as IBeat);
|
||||||
|
this.beatsEvent.next(this.beats);
|
||||||
|
this.beatStats.totalBeats++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
await this.getBeats();
|
await this.getBeats();
|
||||||
await this.getBeatStats();
|
await this.getBeatStats();
|
||||||
this.loginEvent.next(true);
|
this.loginEvent.next(true);
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { LoginComponent } from './login/login.component';
|
|||||||
import { MapComponent } from './map/map.component';
|
import { MapComponent } from './map/map.component';
|
||||||
import { UserComponent } from './user/user.component';
|
import { UserComponent } from './user/user.component';
|
||||||
import { DashboardWidgetComponent } from './dashboard-widget/dashboard-widget.component';
|
import { DashboardWidgetComponent } from './dashboard-widget/dashboard-widget.component';
|
||||||
|
import { IMqttServiceOptions, MqttModule } from 'ngx-mqtt';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@@ -31,6 +32,7 @@ import { DashboardWidgetComponent } from './dashboard-widget/dashboard-widget.co
|
|||||||
BrowserAnimationsModule,
|
BrowserAnimationsModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
HttpClientModule,
|
HttpClientModule,
|
||||||
|
MqttModule.forRoot({}),
|
||||||
NgxMapboxGLModule.withConfig({
|
NgxMapboxGLModule.withConfig({
|
||||||
accessToken: 'pk.eyJ1IjoibW9uZGVpMSIsImEiOiJja2dsY2ZtaG0xZ2o5MnR0ZWs0Mm82OTBpIn0.NzDWN3P6jJLmci_v3MM1tA'
|
accessToken: 'pk.eyJ1IjoibW9uZGVpMSIsImEiOiJja2dsY2ZtaG0xZ2o5MnR0ZWs0Mm82OTBpIn0.NzDWN3P6jJLmci_v3MM1tA'
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { AfterViewInit, Component, OnInit } from '@angular/core';
|
import { AfterViewInit, Component, OnInit } from '@angular/core';
|
||||||
import { ChartDataSets, ChartOptions } from 'chart.js';
|
import { ChartDataSets, ChartOptions } from 'chart.js';
|
||||||
import { Label } from 'ng2-charts';
|
import { Label } from 'ng2-charts';
|
||||||
|
import * as moment from 'moment';
|
||||||
import { APIService, IBeat } from '../api.service';
|
import { APIService, IBeat } from '../api.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -51,10 +52,12 @@ export class DashboardComponent implements AfterViewInit {
|
|||||||
this.lineChartLabels = [];
|
this.lineChartLabels = [];
|
||||||
|
|
||||||
const batteryLevels: number[] = [];
|
const batteryLevels: number[] = [];
|
||||||
|
let currentLevel = 0;
|
||||||
|
|
||||||
const finalBeats = beats.filter((val, i, array) => {
|
const finalBeats = beats.filter((val, i, array) => {
|
||||||
if (batteryLevels.indexOf(val.battery) === -1) {
|
if (currentLevel !== val.battery) {
|
||||||
batteryLevels.push(val.battery);
|
batteryLevels.push(val.battery);
|
||||||
|
currentLevel = val.battery;
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@@ -63,7 +66,7 @@ export class DashboardComponent implements AfterViewInit {
|
|||||||
|
|
||||||
finalBeats.forEach((beat) => {
|
finalBeats.forEach((beat) => {
|
||||||
this.lineChartData[0].data.push(beat.battery);
|
this.lineChartData[0].data.push(beat.battery);
|
||||||
this.lineChartLabels.push(this.formatDateTime(new Date(beat.createdAt)));
|
this.lineChartLabels.push(moment(new Date(beat.createdAt)).format(this.lineChartOptions.scales.xAxes[0].time.parser.toString()));
|
||||||
});
|
});
|
||||||
|
|
||||||
let tDistance = 0;
|
let tDistance = 0;
|
||||||
@@ -83,10 +86,6 @@ export class DashboardComponent implements AfterViewInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private formatDateTime(date: Date): string {
|
|
||||||
return `${date.getMonth()}/${date.getDay()}/${date.getFullYear()} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
ngAfterViewInit(): void {
|
ngAfterViewInit(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<div id="filter" [ngClass]="{hide: !this.api.showFilter}" *ngIf="this.api.loginEvent.value">
|
<div id="filter" [ngClass]="{hide: !this.api.showFilter}" *ngIf="this.api.loginEvent.value">
|
||||||
<h3 (click)="update()" style="cursor: pointer;">Refresh</h3>
|
<h3 (click)="update()" style="cursor: pointer;">Refresh</h3>
|
||||||
|
|
||||||
|
<!-- Time range -->
|
||||||
<select (change)="update($event.target.value)" [(ngModel)]="this.presetHours">
|
<select (change)="update($event.target.value)" [(ngModel)]="this.presetHours">
|
||||||
<option value="-1">Today</option>
|
<option value="-1">Today</option>
|
||||||
<option value="3">Last 3h</option>
|
<option value="3">Last 3h</option>
|
||||||
@@ -17,4 +19,8 @@
|
|||||||
<option value="month">Months</option>
|
<option value="month">Months</option>
|
||||||
<option value="year">Years</option>
|
<option value="year">Years</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<!-- Max accuracy -->
|
||||||
|
<h4>Max accuracy</h4>
|
||||||
|
<input class="customRange" (change)="updateAccuracy($event)">
|
||||||
</div>
|
</div>
|
||||||
@@ -40,6 +40,10 @@ export class FilterComponent implements OnInit {
|
|||||||
this.refresh();
|
this.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateAccuracy(val: any): void {
|
||||||
|
this.api.maxAccuracy.next(Number(val.target.value));
|
||||||
|
}
|
||||||
|
|
||||||
async refresh(): Promise<void> {
|
async refresh(): Promise<void> {
|
||||||
await this.api.getBeats();
|
await this.api.getBeats();
|
||||||
await this.api.getBeatStats();
|
await this.api.getBeatStats();
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<mgl-map [style]="'mapbox://styles/mapbox/dark-v10'" [zoom]="[15]" [center]="[this.lastLocation[0], this.lastLocation[1]]" *ngIf="showMap">
|
<mgl-map [style]="'mapbox://styles/mapbox/dark-v10'" [zoom]="[15]" [center]="[this.lastLocation[0], this.lastLocation[1]]" *ngIf="showMap">
|
||||||
<mgl-geojson-source id="locHistory" [data]="data"></mgl-geojson-source>
|
<mgl-geojson-source id="locHistory" [data]="data"></mgl-geojson-source>
|
||||||
|
<mgl-geojson-source id="locHistoryFiltered" [data]="mostVisitData"></mgl-geojson-source>
|
||||||
<mgl-geojson-source id="lastLoc" [data]="lastLocationData"></mgl-geojson-source>
|
<mgl-geojson-source id="lastLoc" [data]="lastLocationData"></mgl-geojson-source>
|
||||||
<mgl-layer
|
<mgl-layer
|
||||||
id="locHistory"
|
id="locHistory"
|
||||||
@@ -10,6 +11,12 @@
|
|||||||
'line-width': 3
|
'line-width': 3
|
||||||
}"
|
}"
|
||||||
></mgl-layer>
|
></mgl-layer>
|
||||||
|
<mgl-layer
|
||||||
|
id="locHistoryHeatmap"
|
||||||
|
type="heatmap"
|
||||||
|
source="locHistoryFiltered"
|
||||||
|
[paint]="mostVisitPaint"
|
||||||
|
></mgl-layer>
|
||||||
<mgl-layer
|
<mgl-layer
|
||||||
id="lastLoc"
|
id="lastLoc"
|
||||||
type="circle"
|
type="circle"
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { AfterViewInit, Component, OnInit } from '@angular/core';
|
import { AfterViewInit, Component, OnInit } from '@angular/core';
|
||||||
import { Map } from 'mapbox-gl';
|
|
||||||
import { APIService } from '../api.service';
|
import { APIService } from '../api.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -7,9 +6,8 @@ import { APIService } from '../api.service';
|
|||||||
templateUrl: './map.component.html',
|
templateUrl: './map.component.html',
|
||||||
styleUrls: ['./map.component.scss']
|
styleUrls: ['./map.component.scss']
|
||||||
})
|
})
|
||||||
export class MapComponent implements AfterViewInit {
|
export class MapComponent {
|
||||||
|
|
||||||
map: Map;
|
|
||||||
lastLocation: number[] = [0, 0];
|
lastLocation: number[] = [0, 0];
|
||||||
showMap = false;
|
showMap = false;
|
||||||
|
|
||||||
@@ -21,6 +19,79 @@ export class MapComponent implements AfterViewInit {
|
|||||||
geometry: { type: 'LineString', coordinates: [] }
|
geometry: { type: 'LineString', coordinates: [] }
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
|
mostVisitData: GeoJSON.FeatureCollection<GeoJSON.LineString> = {
|
||||||
|
type: 'FeatureCollection',
|
||||||
|
features: [{
|
||||||
|
type: 'Feature',
|
||||||
|
properties: null,
|
||||||
|
geometry: { type: 'LineString', coordinates: [] }
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
mostVisitPaint = {
|
||||||
|
// Increase the heatmap weight based on frequency and property magnitude
|
||||||
|
'heatmap-weight': [
|
||||||
|
'interpolate',
|
||||||
|
['linear'],
|
||||||
|
['get', 'mag'],
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
6,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
// Increase the heatmap color weight weight by zoom level
|
||||||
|
// heatmap-intensity is a multiplier on top of heatmap-weight
|
||||||
|
'heatmap-intensity': [
|
||||||
|
'interpolate',
|
||||||
|
['linear'],
|
||||||
|
['zoom'],
|
||||||
|
0,
|
||||||
|
3,
|
||||||
|
9,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
// Color ramp for heatmap. Domain is 0 (low) to 1 (high).
|
||||||
|
// Begin color ramp at 0-stop with a 0-transparancy color
|
||||||
|
// to create a blur-like effect.
|
||||||
|
'heatmap-color': [
|
||||||
|
'interpolate',
|
||||||
|
['linear'],
|
||||||
|
['heatmap-density'],
|
||||||
|
0,
|
||||||
|
'rgba(33,102,172,0)',
|
||||||
|
0.3,
|
||||||
|
'rgb(103,169,207)',
|
||||||
|
0.4,
|
||||||
|
'rgb(209,229,240)',
|
||||||
|
0.7,
|
||||||
|
'rgb(253,219,199)',
|
||||||
|
0.95,
|
||||||
|
'rgb(239,138,98)',
|
||||||
|
1,
|
||||||
|
'rgb(178,24,43)'
|
||||||
|
],
|
||||||
|
// Adjust the heatmap radius by zoom level
|
||||||
|
'heatmap-radius': [
|
||||||
|
'interpolate',
|
||||||
|
['linear'],
|
||||||
|
['zoom'],
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
7,
|
||||||
|
10,
|
||||||
|
9,
|
||||||
|
15
|
||||||
|
],
|
||||||
|
// Transition from heatmap to circle layer by zoom level
|
||||||
|
'heatmap-opacity': [
|
||||||
|
'interpolate',
|
||||||
|
['linear'],
|
||||||
|
['zoom'],
|
||||||
|
16,
|
||||||
|
1,
|
||||||
|
17,
|
||||||
|
0
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
lastLocationData: GeoJSON.FeatureCollection<GeoJSON.Point> = {
|
lastLocationData: GeoJSON.FeatureCollection<GeoJSON.Point> = {
|
||||||
type: 'FeatureCollection', features: [
|
type: 'FeatureCollection', features: [
|
||||||
@@ -54,33 +125,44 @@ export class MapComponent implements AfterViewInit {
|
|||||||
this.api.beatsEvent.subscribe(beats => {
|
this.api.beatsEvent.subscribe(beats => {
|
||||||
if (beats.length === 0) { return; }
|
if (beats.length === 0) { return; }
|
||||||
|
|
||||||
this.lastLocationPaint['circle-radius'].stops[1][1] = this.metersToPixelsAtMaxZoom(
|
|
||||||
beats[0].accuracy, this.lastLocation[0]
|
|
||||||
);
|
|
||||||
this.update();
|
this.update();
|
||||||
|
|
||||||
|
this.lastLocationPaint['circle-radius'].stops[1][1] = this.metersToPixelsAtMaxZoom(
|
||||||
|
beats[beats.length - 1].accuracy, this.lastLocation[0]
|
||||||
|
);
|
||||||
|
this.lastLocationPaint = { ...this.lastLocationPaint };
|
||||||
|
|
||||||
|
this.api.maxAccuracy.subscribe(val => {
|
||||||
|
this.buildMap(val);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Function to draw circle with exact size by
|
/* Function to draw circle with exact size from
|
||||||
https://stackoverflow.com/questions/37599561/drawing-a-circle-with-the-radius-in-miles-meters-with-mapbox-gl-js
|
https://stackoverflow.com/questions/37599561/drawing-a-circle-with-the-radius-in-miles-meters-with-mapbox-gl-js
|
||||||
*/
|
*/
|
||||||
private metersToPixelsAtMaxZoom(meters: number, latitude: number): number {
|
private metersToPixelsAtMaxZoom(meters: number, latitude: number): number {
|
||||||
return meters / 0.075 / Math.cos(latitude * Math.PI / 180);
|
return meters / 0.075 / Math.cos(latitude * Math.PI / 180);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Function to find out if a provided point is in the area from
|
||||||
|
https://stackoverflow.com/questions/24680247/check-if-a-latitude-and-longitude-is-within-a-circle-google-maps
|
||||||
|
*/
|
||||||
|
private isPointInRadius(checkPoint: { lat: number, lng: number }, centerPoint: { lat: number, lng: number }, km: number): boolean {
|
||||||
|
const ky = 40000 / 360;
|
||||||
|
const kx = Math.cos(Math.PI * centerPoint.lat / 180.0) * ky;
|
||||||
|
const dx = Math.abs(centerPoint.lng - checkPoint.lng) * kx;
|
||||||
|
const dy = Math.abs(centerPoint.lat - checkPoint.lat) * ky;
|
||||||
|
return Math.sqrt(dx * dx + dy * dy) <= km;
|
||||||
|
}
|
||||||
|
|
||||||
async update(): Promise<void> {
|
async update(): Promise<void> {
|
||||||
this.data.features[0].geometry.coordinates = [];
|
this.data.features[0].geometry.coordinates = [];
|
||||||
|
|
||||||
// Add lines to map backwards (because it looks cool)
|
this.buildMap();
|
||||||
for (let i = this.api.beats.length - 1; i >= 0; i--) {
|
|
||||||
const beat = this.api.beats[i];
|
|
||||||
this.data.features[0].geometry.coordinates.push([beat.coordinate[1], beat.coordinate[0]]);
|
|
||||||
this.data = { ... this.data };
|
|
||||||
}
|
|
||||||
console.log('Last', this.api.beats[0]);
|
|
||||||
|
|
||||||
this.lastLocation = [ this.api.beats[0].coordinate[1],
|
this.lastLocation = [this.api.beats[this.api.beats.length - 1].coordinate[1],
|
||||||
this.api.beats[0].coordinate[0] ];
|
this.api.beats[this.api.beats.length - 1].coordinate[0]];
|
||||||
|
|
||||||
this.lastLocationData.features[0].geometry.coordinates = this.lastLocation;
|
this.lastLocationData.features[0].geometry.coordinates = this.lastLocation;
|
||||||
this.lastLocationData = { ...this.lastLocationData };
|
this.lastLocationData = { ...this.lastLocationData };
|
||||||
@@ -88,7 +170,46 @@ export class MapComponent implements AfterViewInit {
|
|||||||
this.showMap = true;
|
this.showMap = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngAfterViewInit(): Promise<void> {
|
buildMap(maxAccuracy: number = 30): void {
|
||||||
|
const mostVisit = new Map<string, number>();
|
||||||
|
this.data.features[0].geometry.coordinates = [];
|
||||||
|
this.mostVisitData.features[0].geometry.coordinates = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < this.api.beats.length; i++) {
|
||||||
|
const beat = this.api.beats[i];
|
||||||
|
if (beat.accuracy > maxAccuracy) { continue; }
|
||||||
|
this.data.features[0].geometry.coordinates.push([beat.coordinate[1], beat.coordinate[0]]);
|
||||||
|
|
||||||
|
// Get most visit points
|
||||||
|
for (let b = 0; b < this.api.beats.length; b++) {
|
||||||
|
const beat2 = this.api.beats[b];
|
||||||
|
const isNearPoint = this.isPointInRadius(
|
||||||
|
{ lat: beat2.coordinate[0], lng: beat2.coordinate[1] },
|
||||||
|
{ lat: beat.coordinate[0], lng: beat.coordinate[1] },
|
||||||
|
0.02
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isNearPoint) {
|
||||||
|
if (mostVisit.has(beat2._id)) {
|
||||||
|
mostVisit.set(beat2._id, mostVisit.get(beat2._id) + 1);
|
||||||
|
} else {
|
||||||
|
mostVisit.set(beat2._id, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [key, value] of mostVisit) {
|
||||||
|
if (value < 2) { continue; }
|
||||||
|
this.api.beats.forEach(beat => {
|
||||||
|
if (beat._id === key) {
|
||||||
|
this.mostVisitData.features[0].geometry.coordinates.push([beat.coordinate[1], beat.coordinate[0]]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.data = { ... this.data };
|
||||||
|
this.mostVisitData = { ... this.mostVisitData };
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
@import '../../styles.scss';
|
@import '../../styles.scss';
|
||||||
|
|
||||||
#user {
|
#user {
|
||||||
|
min-width: 40rem;
|
||||||
margin-top: 3rem;
|
margin-top: 3rem;
|
||||||
margin-left: 20rem;
|
margin-left: 20rem;
|
||||||
margin-right: 20rem;
|
margin-right: 20rem;
|
||||||
|
|||||||
Reference in New Issue
Block a user