diff --git a/.env b/.env index 69c917c7..a8367d68 100644 --- a/.env +++ b/.env @@ -1,5 +1,8 @@ # Docker: Environment variables in Compose +## UI +MF_UI_PORT=3000 + ## NginX MF_NGINX_HTTP_PORT=80 MF_NGINX_SSL_PORT=443 @@ -12,9 +15,6 @@ MF_NATS_URL=nats://nats:4222 ## Redis MF_REDIS_TCP_PORT=6379 -## UI -MF_UI_PORT=3000 - ## Grafana MF_GRAFANA_PORT=3000 @@ -31,6 +31,8 @@ MF_JAEGER_URL=jaeger:6831 MF_AUTHN_LOG_LEVEL=debug MF_AUTHN_HTTP_PORT=8189 MF_AUTHN_GRPC_PORT=8181 +MF_AUTHN_GRPC_URL=authn:8181 +MF_AUTHN_GRPC_TIMEOUT=1s MF_AUTHN_DB_PORT=5432 MF_AUTHN_DB_USER=mainflux MF_AUTHN_DB_PASS=mainflux @@ -63,6 +65,8 @@ MF_THINGS_LOG_LEVEL=debug MF_THINGS_HTTP_PORT=8182 MF_THINGS_AUTH_HTTP_PORT=8989 MF_THINGS_AUTH_GRPC_PORT=8183 +MF_THINGS_AUTH_GRPC_URL=things:8183 +MF_THINGS_AUTH_GRPC_TIMEOUT=1s MF_THINGS_DB_PORT=5432 MF_THINGS_DB_USER=mainflux MF_THINGS_DB_PASS=mainflux @@ -72,23 +76,21 @@ MF_THINGS_ES_URL=localhost:6379 MF_THINGS_ES_PASS= MF_THINGS_ES_DB=0 -### WS -MF_WS_ADAPTER_LOG_LEVEL=debug -MF_WS_ADAPTER_PORT=8186 - ### HTTP MF_HTTP_ADAPTER_PORT=8185 ### MQTT MF_MQTT_ADAPTER_LOG_LEVEL=debug -MF_MQTT_ADAPTER_PORT=1883 +MF_MQTT_ADAPTER_MQTT_PORT=1883 +MF_MQTT_BROKER_PORT=1883 MF_MQTT_ADAPTER_WS_PORT=8080 +MF_MQTT_BROKER_WS_PORT=8080 MF_MQTT_ADAPTER_ES_DB=0 MF_MQTT_ADAPTER_ES_PASS= -### CoAP -MF_COAP_ADAPTER_LOG_LEVEL=debug -MF_COAP_ADAPTER_PORT=5683 +### VERMEMQ +MF_DOCKER_VERNEMQ_ALLOW_ANONYMOUS=on +MF_DOCKER_VERNEMQ_LOG__CONSOLE__LEVEL=error ## Addons Services ### Bootstrap @@ -100,6 +102,30 @@ MF_BOOTSTRAP_DB_PASS=mainflux MF_BOOTSTRAP_DB=bootstrap MF_BOOTSTRAP_DB_SSL_MODE=disable + +### Provision +MF_PROVISION_CONFIG_FILE=/configs/config.toml +MF_PROVISION_LOG_LEVEL=debug +MF_PROVISION_HTTP_PORT=8190 +MF_PROVISION_ENV_CLIENTS_TLS=false +MF_PROVISION_SERVER_CERT= +MF_PROVISION_SERVER_KEY= +MF_PROVISION_MQTT_URL=tcp://localhost +MF_PROVISION_USERS_LOCATION=http://localhost:8180 +MF_PROVISION_THINGS_LOCATION=http://things:8182 +MF_PROVISION_USER= +MF_PROVISION_PASS= +MF_PROVISION_API_KEY= +MF_PROVISION_CERTS_SVC_URL=http://certs:8204 +MF_PROVISION_X509_PROVISIONING=false +MF_PROVISION_BS_SVC_URL=http://bootstrap:8202/things +MF_PROVISION_BS_SVC_WHITELIST_URL=http://bootstrap:8202/things/state +MF_PROVISION_BS_CONFIG_PROVISIONING=true +MF_PROVISION_BS_AUTO_WHITELIST=true +MF_PROVISION_BS_CONTENT= +MF_PROVISION_CERTS_RSA_BITS=4096 +MF_PROVISION_CERTS_HOURS_VALID=2400h + ### LoRa MF_LORA_ADAPTER_LOG_LEVEL=debug MF_LORA_ADAPTER_MESSAGES_URL=tcp://lora.mqtt.mainflux.io:1883 @@ -111,9 +137,6 @@ MF_LORA_ADAPTER_ROUTE_MAP_DB=0 ### OPC-UA MF_OPCUA_ADAPTER_HTTP_PORT=8188 MF_OPCUA_ADAPTER_LOG_LEVEL=debug -MF_OPCUA_ADAPTER_SERVER_URI=opc.tcp://opcua.rocks:4840 -MF_OPCUA_ADAPTER_NODE_NAMESPACE=0 -MF_OPCUA_ADAPTER_NODE_IDENTIFIER=2256 MF_OPCUA_ADAPTER_POLICY= MF_OPCUA_ADAPTER_MODE= MF_OPCUA_ADAPTER_CERT_FILE= @@ -123,73 +146,44 @@ MF_OPCUA_ADAPTER_ROUTE_MAP_PASS= MF_OPCUA_ADAPTER_ROUTE_MAP_DB=0 MF_OPCUA_ADAPTER_EVENT_CONSUMER=opcua -### Cassandra Writer -MF_CASSANDRA_WRITER_LOG_LEVEL=debug -MF_CASSANDRA_WRITER_PORT=8902 -MF_CASSANDRA_WRITER_DB_PORT=9042 -MF_CASSANDRA_WRITER_DB_CLUSTER=mainflux-cassandra -MF_CASSANDRA_WRITER_DB_KEYSPACE=mainflux - -### Cassandra Reader -MF_CASSANDRA_READER_LOG_LEVEL=debug -MF_CASSANDRA_READER_PORT=8903 -MF_CASSANDRA_READER_DB_PORT=9042 -MF_CASSANDRA_READER_DB_CLUSTER=mainflux-cassandra -MF_CASSANDRA_READER_DB_KEYSPACE=mainflux - ### InfluxDB Writer MF_INFLUX_WRITER_LOG_LEVEL=debug MF_INFLUX_WRITER_PORT=8900 MF_INFLUX_WRITER_BATCH_SIZE=5000 MF_INFLUX_WRITER_BATCH_TIMEOUT=5 MF_INFLUX_WRITER_DB_PORT=8086 -MF_INFLUX_WRITER_DB_NAME=mainflux MF_INFLUX_WRITER_DB_USER=mainflux MF_INFLUX_WRITER_DB_PASS=mainflux +MF_INFLUX_WRITER_DB=mainflux MF_INFLUX_WRITER_GRAFANA_PORT=3001 +MF_INFLUX_WRITER_CONTENT_TYPE=application/senml+json ### InfluxDB Reader MF_INFLUX_READER_LOG_LEVEL=debug MF_INFLUX_READER_PORT=8905 -MF_INFLUX_READER_DB_NAME=mainflux MF_INFLUX_READER_DB_PORT=8086 MF_INFLUX_READER_DB_USER=mainflux MF_INFLUX_READER_DB_PASS=mainflux - -### MongoDB Writer -MF_MONGO_WRITER_LOG_LEVEL=debug -MF_MONGO_WRITER_PORT=8901 -MF_MONGO_WRITER_DB_NAME=mainflux -MF_MONGO_WRITER_DB_PORT=27017 - -### MongoDB Reader -MF_MONGO_READER_LOG_LEVEL=debug -MF_MONGO_READER_PORT=8904 -MF_MONGO_READER_DB_NAME=mainflux -MF_MONGO_READER_DB_PORT=27017 - -### Postgres Writer -MF_POSTGRES_WRITER_LOG_LEVEL=debug -MF_POSTGRES_WRITER_PORT=9104 -MF_POSTGRES_WRITER_DB_PORT=5432 -MF_POSTGRES_WRITER_DB_USER=mainflux -MF_POSTGRES_WRITER_DB_PASS=mainflux -MF_POSTGRES_WRITER_DB_NAME=messages -MF_POSTGRES_WRITER_DB_SSL_MODE=disable -MF_POSTGRES_WRITER_DB_SSL_CERT="" -MF_POSTGRES_WRITER_DB_SSL_KEY="" -MF_POSTGRES_WRITER_DB_SSL_ROOT_CERT="" - -### Postgres Reader -MF_POSTGRES_READER_LOG_LEVEL=debug -MF_POSTGRES_READER_PORT=9204 -MF_POSTGRES_READER_CLIENT_TLS=false -MF_POSTGRES_READER_CA_CERTS="" -MF_POSTGRES_READER_DB_PORT=5432 -MF_POSTGRES_READER_DB_USER=mainflux -MF_POSTGRES_READER_DB_PASS=mainflux -MF_POSTGRES_READER_DB_NAME=messages -MF_POSTGRES_READER_DB_SSL_MODE=disable -MF_POSTGRES_READER_DB_SSL_CERT="" -MF_POSTGRES_READER_DB_SSL_KEY="" -MF_POSTGRES_READER_DB_SSL_ROOT_CERT="" +MF_INFLUX_READER_DB=mainflux +MF_INFLUX_READER_SERVER_CERT= +MF_INFLUX_READER_SERVER_KEY= + + + +# Twins +MF_TWINS_LOG_LEVEL=debug +MF_TWINS_HTTP_PORT=9021 +MF_TWINS_SERVER_CERT="" +MF_TWINS_SERVER_KEY="" +MF_TWINS_DB=mainflux-twins +MF_TWINS_DB_HOST=twins-db +MF_TWINS_DB_PORT=27018 +MF_TWINS_SINGLE_USER_EMAIL="" +MF_TWINS_SINGLE_USER_TOKEN="" +MF_TWINS_CLIENT_TLS="" +MF_TWINS_CA_CERTS="" +MF_TWINS_MQTT_URL=tcp://mqtt-adapter:1883 +MF_TWINS_CHANNEL_ID="" +MF_TWINS_CACHE_URL=localhost:6379 +MF_TWINS_CACHE_PASS="" +MF_TWINS_CACHE_DB=0 diff --git a/Makefile b/Makefile index a2c33c92..35173fb8 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,10 @@ ui: docker build --tag=mainflux/ui -f docker/Dockerfile . run: - docker-compose -f docker/docker-compose.yml -f docker/aedes.yml up + docker-compose -f docker/docker-compose.yml up + +clean: + docker-compose -f docker/docker-compose.yml down --rmi all -v --remove-orphans release: $(eval version = $(shell git describe --abbrev=0 --tags)) diff --git a/README.md b/README.md index 4bd49e66..b6835c3c 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,31 @@ # Mainflux IoT Admin UI based on Angular 8+ and Nebular -![dashboard][dashboard] - -![things][things] - -![details][details] - ## Install +Build Docker image: +``` +make ui +``` + +Run docker-compose: +``` +make run +``` +Run angular-cli: ``` cd ui npm install npm start ``` -Make UI Docker: -``` -make docker_ui -``` +## +![dashboard][dashboard] + +## +![things][things] + +## +![details][details] [dashboard]: https://github.com/mainflux/docs/blob/master/docs/img/ui/dashboard.png [things]: https://github.com/mainflux/docs/blob/master/docs/img/ui/things.png diff --git a/docker/aedes.yml b/docker/aedes.yml deleted file mode 100644 index 2afcbf22..00000000 --- a/docker/aedes.yml +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright (c) Mainflux -# SPDX-License-Identifier: Apache-2.0 - -version: "3.7" - -volumes: - mainflux-mqtt-redis-volume: - -services: - nginx: - depends_on: - - mqtt-adapter - - mqtt-redis: - image: redis:5.0-alpine - container_name: mainflux-mqtt-redis - restart: on-failure - networks: - - mainflux-base-net - volumes: - - mainflux-mqtt-redis-volume:/data - - mqtt-adapter: - image: mainflux/mqtt:latest - container_name: mainflux-mqtt - depends_on: - - things - - nats - - mqtt-redis - restart: on-failure - environment: - MF_MQTT_ADAPTER_LOG_LEVEL: ${MF_MQTT_ADAPTER_LOG_LEVEL} - MF_MQTT_INSTANCE_ID: mqtt-adapter-1 - MF_MQTT_ADAPTER_PORT: ${MF_MQTT_ADAPTER_PORT} - MF_MQTT_ADAPTER_WS_PORT: ${MF_MQTT_ADAPTER_WS_PORT} - MF_MQTT_ADAPTER_REDIS_HOST: mqtt-redis - MF_MQTT_ADAPTER_ES_HOST: es-redis - MF_NATS_URL: ${MF_NATS_URL} - MF_THINGS_URL: things:${MF_THINGS_AUTH_GRPC_PORT} - MF_JAEGER_URL: ${MF_JAEGER_URL} - ports: - - 18831:${MF_MQTT_ADAPTER_PORT} - - 8881:${MF_MQTT_ADAPTER_WS_PORT} - networks: - - mainflux-base-net diff --git a/docker/channels.toml b/docker/channels.toml deleted file mode 100644 index febebdd0..00000000 --- a/docker/channels.toml +++ /dev/null @@ -1,4 +0,0 @@ -# If you want to listen on all channels, just pass one element ["*"], otherwise -# pass the list of channels. -[channels] -filter = ["*"] diff --git a/docker/configs/config.toml b/docker/configs/config.toml new file mode 100644 index 00000000..c5749658 --- /dev/null +++ b/docker/configs/config.toml @@ -0,0 +1,78 @@ +[bootstrap] + [bootstrap.content] + [bootstrap.content.agent.edgex] + url = "http://localhost:48090/api/v1/" + + [bootstrap.content.agent.log] + level = "info" + + [bootstrap.content.agent.mqtt] + mtls = false + qos = 0 + retain = false + skip_tls_ver = true + url = "localhost:1883" + + [bootstrap.content.agent.server] + nats_url = "localhost:4222" + port = "9000" + + [bootstrap.content.agent.heartbeat] + interval = "30s" + + [bootstrap.content.agent.terminal] + session_timeout = "30s" + + + [bootstrap.content.export.exp] + log_level = "debug" + nats = "nats://localhost:4222" + port = "8172" + cache_url = "localhost:6379" + cache_pass = "" + cache_db = "0" + + [bootstrap.content.export.mqtt] + ca_path = "ca.crt" + cert_path = "thing.crt" + channel = "" + host = "tcp://localhost:1883" + mtls = false + password = "" + priv_key_path = "thing.key" + qos = 0 + retain = false + skip_tls_ver = false + username = "" + + [[bootstrap.content.export.routes]] + mqtt_topic = "" + nats_topic = "channels" + subtopic = "" + type = "mfx" + workers = 10 + + [[bootstrap.content.export.routes]] + mqtt_topic = "" + nats_topic = "export" + subtopic = "" + type = "default" + workers = 10 + +[[things]] + name = "thing" + + [things.metadata] + external_id = "xxxxxx" + +[[channels]] + name = "control-channel" + + [channels.metadata] + type = "control" + +[[channels]] + name = "data-channel" + + [channels.metadata] + type = "data" diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 0748e5e3..001028a6 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -11,15 +11,29 @@ volumes: mainflux-authn-db-volume: mainflux-users-db-volume: mainflux-things-db-volume: - mainflux-things-redis-volume: + mainflux-auth-redis-volume: mainflux-es-redis-volume: mainflux-influxdb-volume: mainflux-bootstrap-db-volume: mainflux-grafana-volume: mainflux-opcua-adapter-volume: mainflux-opcua-redis-volume: + mainflux-twins-db-volume: + mainflux-twins-db-configdb-volume: + mainflux-mqtt-broker-volume: services: + ui: + image: mainflux/ui:latest + container_name: mainflux-ui + restart: on-failure + ports: + - ${MF_UI_PORT}:${MF_UI_PORT} + networks: + - mainflux-base-net + environment: + MF_UI_PORT: ${MF_UI_PORT} + nginx: image: nginx:1.16.0-alpine container_name: mainflux-nginx @@ -46,16 +60,20 @@ services: depends_on: - things - users + - mqtt-adapter - http-adapter - - ws-adapter nats: image: nats:1.3.0 container_name: mainflux-nats + command: "-c /etc/nats/nats.conf" restart: on-failure + volumes: + - ./nats/:/etc/nats networks: - mainflux-base-net + ## AUTHN SERVICE authn-db: image: postgres:10.8-alpine container_name: mainflux-authn-db @@ -94,6 +112,7 @@ services: networks: - mainflux-base-net + ## USERS SERVICE users-db: image: postgres:10.8-alpine container_name: mainflux-users-db @@ -134,12 +153,14 @@ services: MF_EMAIL_FROM_NAME: ${MF_EMAIL_FROM_NAME} MF_EMAIL_TEMPLATE: ${MF_EMAIL_TEMPLATE} MF_TOKEN_RESET_ENDPOINT: ${MF_TOKEN_RESET_ENDPOINT} - MF_AUTHN_URL: authn:${MF_AUTHN_GRPC_PORT} + MF_AUTHN_GRPC_URL: ${MF_AUTHN_GRPC_URL} + MF_AUTHN_GRPC_TIMEOUT: ${MF_AUTHN_GRPC_TIMEOUT} ports: - ${MF_USERS_HTTP_PORT}:${MF_USERS_HTTP_PORT} networks: - mainflux-base-net + ## THINGS SERVICE things-db: image: postgres:10.8-alpine container_name: mainflux-things-db @@ -153,14 +174,14 @@ services: volumes: - mainflux-things-db-volume:/var/lib/postgresql/data - things-redis: + auth-redis: image: redis:5.0-alpine - container_name: mainflux-things-redis + container_name: mainflux-auth-redis restart: on-failure networks: - mainflux-base-net volumes: - - mainflux-things-redis-volume:/data + - mainflux-auth-redis-volume:/data things: image: mainflux/things:latest @@ -176,14 +197,15 @@ services: MF_THINGS_DB_USER: ${MF_THINGS_DB_USER} MF_THINGS_DB_PASS: ${MF_THINGS_DB_PASS} MF_THINGS_DB: ${MF_THINGS_DB} - MF_THINGS_CACHE_URL: things-redis:${MF_REDIS_TCP_PORT} + MF_THINGS_CACHE_URL: auth-redis:${MF_REDIS_TCP_PORT} MF_THINGS_ES_URL: es-redis:${MF_REDIS_TCP_PORT} MF_THINGS_HTTP_PORT: ${MF_THINGS_HTTP_PORT} MF_THINGS_AUTH_HTTP_PORT: ${MF_THINGS_AUTH_HTTP_PORT} MF_THINGS_AUTH_GRPC_PORT: ${MF_THINGS_AUTH_GRPC_PORT} - MF_AUTH_URL: authn:${MF_AUTHN_GRPC_PORT} MF_THINGS_SECRET: ${MF_THINGS_SECRET} MF_JAEGER_URL: ${MF_JAEGER_URL} + MF_AUTHN_GRPC_URL: ${MF_AUTHN_GRPC_URL} + MF_AUTHN_GRPC_TIMEOUT: ${MF_AUTHN_GRPC_TIMEOUT} ports: - ${MF_THINGS_HTTP_PORT}:${MF_THINGS_HTTP_PORT} - ${MF_THINGS_AUTH_HTTP_PORT}:${MF_THINGS_AUTH_HTTP_PORT} @@ -202,24 +224,45 @@ services: networks: - mainflux-base-net - ws-adapter: - image: mainflux/ws:latest - container_name: mainflux-ws + vernemq: + image: mainflux/vernemq:latest + container_name: mainflux-vernemq + restart: on-failure + environment: + DOCKER_VERNEMQ_ALLOW_ANONYMOUS: ${MF_DOCKER_VERNEMQ_ALLOW_ANONYMOUS} + DOCKER_VERNEMQ_LOG__CONSOLE__LEVEL: ${MF_DOCKER_VERNEMQ_LOG__CONSOLE__LEVEL} + networks: + - mainflux-base-net + volumes: + - mainflux-mqtt-broker-volume:/var/lib/vernemq + + ## MQTT ADAPTER + mqtt-adapter: + image: mainflux/mqtt:latest + container_name: mainflux-mqtt depends_on: + - vernemq - things - nats restart: on-failure environment: - MF_WS_ADAPTER_LOG_LEVEL: ${MF_WS_ADAPTER_LOG_LEVEL} - MF_WS_ADAPTER_PORT: ${MF_WS_ADAPTER_PORT} + MF_MQTT_ADAPTER_LOG_LEVEL: ${MF_MQTT_ADAPTER_LOG_LEVEL} + MF_MQTT_ADAPTER_MQTT_PORT: ${MF_MQTT_ADAPTER_MQTT_PORT} + MF_MQTT_ADAPTER_WS_PORT: ${MF_MQTT_ADAPTER_WS_PORT} + MF_MQTT_ADAPTER_ES_URL: es-redis:${MF_REDIS_TCP_PORT} MF_NATS_URL: ${MF_NATS_URL} - MF_THINGS_URL: things:${MF_THINGS_AUTH_GRPC_PORT} + MF_MQTT_ADAPTER_MQTT_TARGET_HOST: vernemq + MF_MQTT_ADAPTER_MQTT_TARGET_PORT: ${MF_MQTT_BROKER_PORT} + MF_MQTT_ADAPTER_WS_TARGET_HOST: vernemq + MF_MQTT_ADAPTER_WS_TARGET_PORT: ${MF_MQTT_BROKER_WS_PORT} MF_JAEGER_URL: ${MF_JAEGER_URL} - ports: - - ${MF_WS_ADAPTER_PORT}:${MF_WS_ADAPTER_PORT} + MF_THINGS_AUTH_GRPC_URL: ${MF_THINGS_AUTH_GRPC_URL} + MF_THINGS_AUTH_GRPC_TIMEOUT: ${MF_THINGS_AUTH_GRPC_TIMEOUT} + MF_AUTH_CACHE_URL: auth-redis:${MF_REDIS_TCP_PORT} networks: - mainflux-base-net + ## HTTP ADAPTER http-adapter: image: mainflux/http:latest container_name: mainflux-http @@ -231,8 +274,9 @@ services: MF_HTTP_ADAPTER_LOG_LEVEL: debug MF_HTTP_ADAPTER_PORT: ${MF_HTTP_ADAPTER_PORT} MF_NATS_URL: ${MF_NATS_URL} - MF_THINGS_URL: things:${MF_THINGS_AUTH_GRPC_PORT} MF_JAEGER_URL: ${MF_JAEGER_URL} + MF_THINGS_AUTH_GRPC_URL: ${MF_THINGS_AUTH_GRPC_URL} + MF_THINGS_AUTH_GRPC_TIMEOUT: ${MF_THINGS_AUTH_GRPC_TIMEOUT} ports: - ${MF_HTTP_ADAPTER_PORT}:${MF_HTTP_ADAPTER_PORT} networks: @@ -247,36 +291,7 @@ services: volumes: - mainflux-es-redis-volume:/data - coap-adapter: - image: mainflux/coap:latest - container_name: mainflux-coap - depends_on: - - things - - nats - restart: on-failure - environment: - MF_COAP_ADAPTER_LOG_LEVEL: ${MF_COAP_ADAPTER_LOG_LEVEL} - MF_COAP_ADAPTER_PORT: ${MF_COAP_ADAPTER_PORT} - MF_NATS_URL: ${MF_NATS_URL} - MF_THINGS_URL: things:${MF_THINGS_AUTH_GRPC_PORT} - MF_JAEGER_URL: ${MF_JAEGER_URL} - ports: - - ${MF_COAP_ADAPTER_PORT}:${MF_COAP_ADAPTER_PORT}/udp - - ${MF_COAP_ADAPTER_PORT}:${MF_COAP_ADAPTER_PORT}/tcp - networks: - - mainflux-base-net - - ui: - image: mainflux/ui:latest - container_name: mainflux-ui - restart: on-failure - ports: - - ${MF_UI_PORT}:${MF_UI_PORT} - networks: - - mainflux-base-net - environment: - MF_UI_PORT: ${MF_UI_PORT} - + ## OPC-UA ADAPTER opcua-redis: image: redis:5.0-alpine container_name: mainflux-opcua-redis @@ -296,9 +311,6 @@ services: MF_OPCUA_ADAPTER_HTTP_PORT: ${MF_OPCUA_ADAPTER_HTTP_PORT} MF_OPCUA_ADAPTER_LOG_LEVEL: ${MF_OPCUA_ADAPTER_LOG_LEVEL} MF_NATS_URL: ${MF_NATS_URL} - MF_OPCUA_ADAPTER_SERVER_URI: ${MF_OPCUA_ADAPTER_SERVER_URI} - MF_OPCUA_ADAPTER_NODE_NAMESPACE: ${MF_OPCUA_ADAPTER_NODE_NAMESPACE} - MF_OPCUA_ADAPTER_NODE_IDENTIFIER: ${MF_OPCUA_ADAPTER_NODE_IDENTIFIER} MF_OPCUA_ADAPTER_POLICY: ${MF_OPCUA_ADAPTER_POLICY} MF_OPCUA_ADAPTER_MODE: ${MF_OPCUA_ADAPTER_MODE} MF_OPCUA_ADAPTER_CERT_FILE: ${MF_OPCUA_ADAPTER_CERT_FILE} @@ -317,12 +329,13 @@ services: volumes: - mainflux-opcua-adapter-volume:/store + ## INFLUX DB influxdb: image: influxdb:1.6.4-alpine container_name: mainflux-influxdb restart: on-failure environment: - INFLUXDB_DB: ${MF_INFLUX_WRITER_DB_NAME} + INFLUXDB_DB: ${MF_INFLUX_WRITER_DB} INFLUXDB_ADMIN_USER: ${MF_INFLUX_WRITER_DB_USER} INFLUXDB_ADMIN_PASSWORD: ${MF_INFLUX_WRITER_DB_PASS} networks: @@ -345,7 +358,7 @@ services: MF_INFLUX_WRITER_PORT: ${MF_INFLUX_WRITER_PORT} MF_INFLUX_WRITER_BATCH_SIZE: ${MF_INFLUX_WRITER_BATCH_SIZE} MF_INFLUX_WRITER_BATCH_TIMEOUT: ${MF_INFLUX_WRITER_BATCH_TIMEOUT} - MF_INFLUX_WRITER_DB_NAME: ${MF_INFLUX_WRITER_DB_NAME} + MF_INFLUX_WRITER_DB: ${MF_INFLUX_WRITER_DB} MF_INFLUX_WRITER_DB_HOST: mainflux-influxdb MF_INFLUX_WRITER_DB_PORT: ${MF_INFLUX_WRITER_DB_PORT} MF_INFLUX_WRITER_DB_USER: ${MF_INFLUX_WRITER_DB_USER} @@ -355,7 +368,7 @@ services: networks: - mainflux-base-net volumes: - - ./channels.toml:/config/channels.toml + - ./subjects.toml:/config/subjects.toml grafana: image: grafana/grafana:5.1.3 @@ -376,19 +389,25 @@ services: restart: on-failure environment: MF_INFLUX_READER_LOG_LEVEL: debug - MF_THINGS_URL: things:${MF_THINGS_AUTH_GRPC_PORT} MF_INFLUX_READER_PORT: ${MF_INFLUX_READER_PORT} - MF_INFLUX_READER_DB_NAME: ${MF_INFLUX_READER_DB_NAME} + MF_INFLUX_READER_DB: ${MF_INFLUX_READER_DB} MF_INFLUX_READER_DB_HOST: mainflux-influxdb MF_INFLUX_READER_DB_PORT: ${MF_INFLUX_READER_DB_PORT} MF_INFLUX_READER_DB_USER: ${MF_INFLUX_READER_DB_USER} MF_INFLUX_READER_DB_PASS: ${MF_INFLUX_READER_DB_PASS} + MF_INFLUX_READER_SERVER_CERT: ${MF_INFLUX_READER_SERVER_CERT} + MF_INFLUX_READER_SERVER_KEY: ${MF_INFLUX_READER_SERVER_KEY} MF_JAEGER_URL: ${MF_JAEGER_URL} + MF_THINGS_AUTH_GRPC_URL: ${MF_THINGS_AUTH_GRPC_URL} + MF_THINGS_AUTH_GRPC_TIMEOUT: ${MF_THINGS_AUTH_GRPC_TIMEOUT} ports: - ${MF_INFLUX_READER_PORT}:${MF_INFLUX_READER_PORT} networks: - mainflux-base-net + volumes: + - ../../ssl/certs:/etc/ssl/certs + ## LORA ADAPTER lora-redis: image: redis:5.0-alpine container_name: mainflux-lora-redis @@ -414,6 +433,7 @@ services: networks: - mainflux-base-net + ## BOOTSTRAP bootstrap-db: image: postgres:10.2-alpine container_name: mainflux-bootstrap-db @@ -445,9 +465,86 @@ services: MF_BOOTSTRAP_DB_SSL_MODE: ${MF_BOOTSTRAP_DB_SSL_MODE} MF_BOOTSTRAP_PORT: ${MF_BOOTSTRAP_PORT} MF_SDK_BASE_URL: http://mainflux-things:${MF_THINGS_HTTP_PORT} - MF_AUTH_URL: authn:${MF_AUTHN_GRPC_PORT} MF_THINGS_ES_URL: es-redis:${MF_REDIS_TCP_PORT} MF_BOOTSTRAP_ES_URL: es-redis:${MF_REDIS_TCP_PORT} MF_JAEGER_URL: ${MF_JAEGER_URL} + MF_AUTHN_GRPC_URL: ${MF_AUTHN_GRPC_URL} + MF_AUTHN_GRPC_TIMMEOUT: ${MF_AUTHN_GRPC_TIMEOUT} + networks: + - mainflux-base-net + + # Provision + provision: + image: mainflux/provision:latest + container_name: mainflux-provision + restart: on-failure + networks: + - mainflux-base-net + ports: + - ${MF_PROVISION_HTTP_PORT}:${MF_PROVISION_HTTP_PORT} + environment: + MF_PROVISION_LOG_LEVEL: ${MF_PROVISION_LOG_LEVEL} + MF_PROVISION_HTTP_PORT: ${MF_PROVISION_HTTP_PORT} + MF_PROVISION_CONFIG_FILE: ${MF_PROVISION_CONFIG_FILE} + MF_PROVISION_LOG_LEVEL: ${MF_PROVISION_LOG_LEVEL} + MF_PROVISION_ENV_CLIENTS_TLS: ${MF_PROVISION_ENV_CLIENTS_TLS} + MF_PROVISION_SERVER_CERT: ${MF_PROVISION_SERVER_CERT} + MF_PROVISION_SERVER_KEY: ${MF_PROVISION_SERVER_KEY} + MF_PROVISION_MQTT_URL: ${MF_PROVISION_MQTT_URL} + MF_PROVISION_USERS_LOCATION: ${MF_PROVISION_USERS_LOCATION} + MF_PROVISION_THINGS_LOCATION: ${MF_PROVISION_THINGS_LOCATION} + MF_PROVISION_USER: ${MF_PROVISION_USER} + MF_PROVISION_PASS: ${MF_PROVISION_PASS} + MF_PROVISION_API_KEY: ${MF_PROVISION_API_KEY} + MF_PROVISION_CERTS_SVC_URL: ${MF_PROVISION_CERTS_SVC_URL} + MF_PROVISION_X509_PROVISIONING: ${MF_PROVISION_X509_PROVISIONING} + MF_PROVISION_BS_SVC_URL: ${MF_PROVISION_BS_SVC_URL} + MF_PROVISION_BS_SVC_WHITELIST_URL: ${MF_PROVISION_BS_SVC_WHITELIST_URL} + MF_PROVISION_BS_CONFIG_PROVISIONING: ${MF_PROVISION_BS_CONFIG_PROVISIONING} + MF_PROVISION_BS_AUTO_WHITELIST: ${MF_PROVISION_BS_AUTO_WHITELIST} + MF_PROVISION_BS_CONTENT: ${MF_PROVISION_BS_CONTENT} + MF_PROVISION_CERTS_RSA_BITS: ${MF_PROVISION_CERTS_RSA_BITS} + MF_PROVISION_CERTS_HOURS_VALID: ${MF_PROVISION_CERTS_HOURS_VALID} + volumes: + - ./configs:/configs + - ../ssl/certs/ca.key:/etc/ssl/certs/ca.key + - ../ssl/certs/ca.crt:/etc/ssl/certs/ca.crt + + ## TWINS + twins-db: + image: mongo:bionic + command: mongod --port ${MF_TWINS_DB_PORT} + container_name: mainflux-twins-db + restart: on-failure + environment: + MONGO_INITDB_DATABASE: ${MF_TWINS_DB} + ports: + - ${MF_TWINS_DB_PORT}:${MF_TWINS_DB_PORT} + networks: + mainflux-base-net: + volumes: + - mainflux-twins-db-volume:/data/db + - mainflux-twins-db-configdb-volume:/data/configdb + + twins: + image: mainflux/twins:latest + container_name: mainflux-twins + depends_on: + - twins-db + - nats + restart: on-failure + environment: + MF_TWINS_LOG_LEVEL: ${MF_TWINS_LOG_LEVEL} + MF_TWINS_HTTP_PORT: ${MF_TWINS_HTTP_PORT} + MF_TWINS_DB: ${MF_TWINS_DB} + MF_TWINS_DB_HOST: ${MF_TWINS_DB_HOST} + MF_TWINS_DB_PORT: ${MF_TWINS_DB_PORT} + MF_TWINS_CHANNEL_ID: ${MF_TWINS_CHANNEL_ID} + MF_NATS_URL: ${MF_NATS_URL} + MF_TWINS_MQTT_URL: ${MF_TWINS_MQTT_URL} + MF_AUTHN_GRPC_URL: ${MF_AUTHN_GRPC_URL} + MF_AUTHN_GRPC_TIMEOUT: ${MF_AUTHN_GRPC_TIMEOUT} + ports: + - ${MF_TWINS_HTTP_PORT}:${MF_TWINS_HTTP_PORT} networks: - mainflux-base-net diff --git a/docker/nats/nats.conf b/docker/nats/nats.conf new file mode 100644 index 00000000..6f2b26f8 --- /dev/null +++ b/docker/nats/nats.conf @@ -0,0 +1,2 @@ +# maximum payload +max_payload: 268435456 diff --git a/docker/nginx/entrypoint.sh b/docker/nginx/entrypoint.sh index 4ee5f268..b5d15cab 100755 --- a/docker/nginx/entrypoint.sh +++ b/docker/nginx/entrypoint.sh @@ -2,22 +2,21 @@ if [ -z "$MF_MQTT_CLUSTER" ] then - envsubst '${MF_MQTT_ADAPTER_PORT}' < /etc/nginx/snippets/mqtt-upstream-single.conf > /etc/nginx/snippets/mqtt-upstream.conf + envsubst '${MF_MQTT_ADAPTER_MQTT_PORT}' < /etc/nginx/snippets/mqtt-upstream-single.conf > /etc/nginx/snippets/mqtt-upstream.conf envsubst '${MF_MQTT_ADAPTER_WS_PORT}' < /etc/nginx/snippets/mqtt-ws-upstream-single.conf > /etc/nginx/snippets/mqtt-ws-upstream.conf else - envsubst '${MF_MQTT_ADAPTER_PORT}' < /etc/nginx/snippets/mqtt-upstream-cluster.conf > /etc/nginx/snippets/mqtt-upstream.conf + envsubst '${MF_MQTT_ADAPTER_MQTT_PORT}' < /etc/nginx/snippets/mqtt-upstream-cluster.conf > /etc/nginx/snippets/mqtt-upstream.conf envsubst '${MF_MQTT_ADAPTER_WS_PORT}' < /etc/nginx/snippets/mqtt-ws-upstream-cluster.conf > /etc/nginx/snippets/mqtt-ws-upstream.conf fi envsubst ' ${MF_USERS_HTTP_PORT} ${MF_THINGS_HTTP_PORT} - ${MF_THINGS_HTTP_PORT} ${MF_HTTP_ADAPTER_PORT} - ${MF_WS_ADAPTER_PORT} ${MF_UI_PORT} ${MF_INFLUX_READER_PORT} ${MF_BOOTSTRAP_PORT} + ${MF_TWINS_HTTP_PORT} ${MF_OPCUA_ADAPTER_HTTP_PORT}' < /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf exec nginx -g "daemon off;" diff --git a/docker/nginx/nginx-key.conf b/docker/nginx/nginx-key.conf index e6b0ae9c..7a84e47f 100644 --- a/docker/nginx/nginx-key.conf +++ b/docker/nginx/nginx-key.conf @@ -78,18 +78,11 @@ http { proxy_pass http://http-adapter:${MF_HTTP_ADAPTER_PORT}/; } - # Proxy pass to mainflux-ws-adapter - location /ws/ { - include snippets/proxy-headers.conf; - include snippets/ws-upgrade.conf; - proxy_pass http://ws-adapter:${MF_WS_ADAPTER_PORT}/; - } - # Proxy pass to mainflux-mqtt-adapter over WS - location /mqtt/ { + location /mqtt { include snippets/proxy-headers.conf; include snippets/ws-upgrade.conf; - proxy_pass http://mqtt_ws_cluster/; + proxy_pass http://mqtt_ws_cluster; } # Proxy pass to mainflux-influxdb-reader @@ -116,6 +109,12 @@ http { proxy_pass http://opcua-adapter:${MF_OPCUA_ADAPTER_HTTP_PORT}; } + # Proxy pass to mainflux-twins + location ~ ^/(twins|states) { + include snippets/proxy-headers.conf; + proxy_pass http://twins:${MF_TWINS_HTTP_PORT}; + } + location / { include snippets/proxy-headers.conf; proxy_pass http://ui:${MF_UI_PORT}; diff --git a/docker/nginx/nginx-x509.conf b/docker/nginx/nginx-x509.conf index 3c3670fb..bcd83f48 100644 --- a/docker/nginx/nginx-x509.conf +++ b/docker/nginx/nginx-x509.conf @@ -63,7 +63,7 @@ http { } # Proxy pass to things service - location ~ ^/(things|channels) { + location ~ ^/(things|channels|connect) { include snippets/proxy-headers.conf; add_header Access-Control-Expose-Headers Location; proxy_pass http://things:${MF_THINGS_HTTP_PORT}; @@ -86,20 +86,12 @@ http { proxy_pass http://http-adapter:${MF_HTTP_ADAPTER_PORT}/; } - # Proxy pass to mainflux-ws-adapter - location /ws/ { - include snippets/verify-ssl-client.conf; - include snippets/proxy-headers.conf; - include snippets/ws-upgrade.conf; - proxy_pass http://ws-adapter:${MF_WS_ADAPTER_PORT}/; - } - # Proxy pass to mainflux-mqtt-adapter over WS - location /mqtt/ { + location /mqtt { include snippets/verify-ssl-client.conf; include snippets/proxy-headers.conf; include snippets/ws-upgrade.conf; - proxy_pass http://mqtt_ws_cluster/; + proxy_pass http://mqtt_ws_cluster; } # Proxy pass to mainflux-influxdb-reader @@ -126,6 +118,11 @@ http { proxy_pass http://opcua-adapter:${MF_OPCUA_ADAPTER_HTTP_PORT}; } + location ~ ^/(twins|states) { + include snippets/proxy-headers.conf; + proxy_pass http://twins:${MF_TWINS_HTTP_PORT}; + } + location / { include snippets/proxy-headers.conf; proxy_pass http://ui:${MF_UI_PORT}; diff --git a/docker/nginx/snippets/mqtt-upstream-cluster.conf b/docker/nginx/snippets/mqtt-upstream-cluster.conf index 31657294..204219f1 100644 --- a/docker/nginx/snippets/mqtt-upstream-cluster.conf +++ b/docker/nginx/snippets/mqtt-upstream-cluster.conf @@ -3,7 +3,7 @@ upstream mqtt_cluster { least_conn; - server mqtt-adapter-1:${MF_MQTT_ADAPTER_PORT}; - server mqtt-adapter-2:${MF_MQTT_ADAPTER_PORT}; - server mqtt-adapter-3:${MF_MQTT_ADAPTER_PORT}; -} \ No newline at end of file + server mqtt-adapter-1:${MF_MQTT_ADAPTER_MQTT_PORT}; + server mqtt-adapter-2:${MF_MQTT_ADAPTER_MQTT_PORT}; + server mqtt-adapter-3:${MF_MQTT_ADAPTER_MQTT_PORT}; +} diff --git a/docker/nginx/snippets/mqtt-upstream-single.conf b/docker/nginx/snippets/mqtt-upstream-single.conf index 67a0828b..3c5372ba 100644 --- a/docker/nginx/snippets/mqtt-upstream-single.conf +++ b/docker/nginx/snippets/mqtt-upstream-single.conf @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 upstream mqtt_cluster { - server mqtt-adapter:${MF_MQTT_ADAPTER_PORT}; -} \ No newline at end of file + server mqtt-adapter:${MF_MQTT_ADAPTER_MQTT_PORT}; +} diff --git a/docker/ssl/Makefile b/docker/ssl/Makefile index 9cf19bd2..6119e068 100644 --- a/docker/ssl/Makefile +++ b/docker/ssl/Makefile @@ -6,7 +6,7 @@ EA = info@mainflux.com CN = localhost CRT_FILE_NAME = thing -all: clean_certs ca server_crt +all: clean_certs ca server_cert # CA name and key is "ca". ca: diff --git a/docker/ssl/authorization.js b/docker/ssl/authorization.js index 7e8968c2..408f2317 100644 --- a/docker/ssl/authorization.js +++ b/docker/ssl/authorization.js @@ -43,7 +43,7 @@ function authenticate(s) { function parsePackage(s, data) { // An explanation of MQTT packet structure can be found here: - // https://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mqtt-v3r1.html#msg-format. + // https://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mqtt-v3r1.html#msg-format. // CONNECT message is explained here: // https://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mqtt-v3r1.html#connect. @@ -54,9 +54,9 @@ function parsePackage(s, data) { +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | TYPE | RSRVD | REMAINING LEN | PROTOCOL NAME LEN | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | PROTOCOL NAME | + | PROTOCOL NAME | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-| - | VERSION | FLAGS | KEEP ALIVE | + | VERSION | FLAGS | KEEP ALIVE | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-| | Payload (if any) ... | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -65,8 +65,8 @@ function parsePackage(s, data) { Remaining Length is the length of the variable header (10 bytes) plus the length of the Payload. It is encoded in the manner described here: http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/errata01/os/mqtt-v3.1.1-errata01-os-complete.html#_Toc442180836. - - Connect flags byte looks like this: + + Connect flags byte looks like this: | 7 | 6 | 5 | 4 3 | 2 | 1 | 0 | | Username Flag | Password Flag | Will Retain | Will QoS | Will Flag | Clean Session | Reserved | @@ -76,7 +76,7 @@ function parsePackage(s, data) { 3. Will Message (2 bytes length + Will Message value) if Will Flag is 1. 4. User Name (2 bytes length + User Name value) if User Name Flag is 1. 5. Password (2 bytes length + Password value) if Password Flag is 1. - + This method extracts Password field. */ @@ -95,23 +95,23 @@ function parsePackage(s, data) { // CONTROL(1) + MSG_LEN(1-4) + PROTO_NAME_LEN(2) + PROTO_NAME(4) + PROTO_VERSION(1) var flags_pos = 1 + len_size + 2 + 4 + 1; var flags = data.codePointAt(flags_pos); - + // If there are no username and password flags (11xxxxxx), return. if (flags < 192) { s.error('MQTT username or password not provided'); return ''; } - + // FLAGS(1) + KEEP_ALIVE(2) var shift = flags_pos + 1 + 2; - + // Number of bytes to encode length. var len_bytes_num = 2; // If Wil Flag is present, Will Topic and Will Message need to be skipped as well. var shift_flags = 196 <= flags ? 5 : 3; var len_msb, len_lsb, len; - + for (var i = 0; i < shift_flags; i++) { len_msb = data.codePointAt(shift).toString(16); len_lsb = data.codePointAt(shift + 1).toString(16); @@ -138,18 +138,6 @@ function setKey(r) { return ''; } - if (r.uri.startsWith('/ws') && (!auth || !auth.length)) { - var a; - for (a in r.args) { - if (a == 'authorization' && r.args[a] === clientKey) { - return clientKey; - } - } - - r.error('Authorization param does not match certificate'); - return ''; - } - return clientKey; } diff --git a/docker/subjects.toml b/docker/subjects.toml new file mode 100644 index 00000000..5da1237c --- /dev/null +++ b/docker/subjects.toml @@ -0,0 +1,4 @@ +# If you want to listen on all subjects, just pass one element ["channel.>"], otherwise +# pass the list of subjects (e.g ["channel.", "channel..sub.topic.x", ...]). +[subjects] +filter = ["channels.>"] diff --git a/package-lock.json b/package-lock.json index d4357e6e..636d3c3d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,9 +5,9 @@ "requires": true, "dependencies": { "@agm/core": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@agm/core/-/core-1.0.0.tgz", - "integrity": "sha512-tgrobTyAHCZ3cdi21IdiBafiHLxsC5/6ncq6+f9RV+HrLg0XDuk+DA7upfdE34o2Ui3fNRDYDIgExRVFLsSPCw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@agm/core/-/core-1.1.0.tgz", + "integrity": "sha512-cMvmm3+3/uuVFurLv1FKhE0/6ssIlDvYBjQFCi8ELg7h0OY2MkIU1MXWr7z+f/xZ08E936I4eeddni6k4yUTIA==", "requires": { "tslib": "^1.9.0" } @@ -237,19 +237,19 @@ } }, "@angular-devkit/schematics": { - "version": "8.3.17", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-8.3.17.tgz", - "integrity": "sha512-1ttzYGnw5Ux7Nfr/TGyWx3C/rarq5l+dn+5SpKof/8ctvQU1gJaswF492ngPs/jDjaPq5MucKYoX+wTUJHnUhg==", + "version": "8.3.26", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-8.3.26.tgz", + "integrity": "sha512-IoZbXVFGLvVi5d0ozfssWDXuzot0/pMSKbQPzWIG8K7nCo7nNMVYpsMHrEVYUikA9EQEL5LqMCGohH36/zVPcA==", "dev": true, "requires": { - "@angular-devkit/core": "8.3.17", + "@angular-devkit/core": "8.3.26", "rxjs": "6.4.0" }, "dependencies": { "@angular-devkit/core": { - "version": "8.3.17", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-8.3.17.tgz", - "integrity": "sha512-cR2H/7OxxqagwGfzxwNWDOYN4QpOZ56Fei9JED/2p/K/5UDAowl20o3qP9mTfia/lEhFeyjMBcM8gsHxhJNYJQ==", + "version": "8.3.26", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-8.3.26.tgz", + "integrity": "sha512-b1ng9091o33s55/cwQYh1kboiJtj8y8z8xQWATDI9kRmNIQkWYVwVa/MzgPRJ4bzbEGG3zIUHCsp52A6vuGr2A==", "dev": true, "requires": { "ajv": "6.10.2", @@ -298,9 +298,9 @@ } }, "@angular/animations": { - "version": "8.2.13", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-8.2.13.tgz", - "integrity": "sha512-ZE4UZsQ6HDW1ZIj9tL45PVosCcG4Ke7ihV7eWCE1VgLZKDDxTOPbLf1UeEiszUYptMLGH3eGMNBKo85mOlkH8w==", + "version": "8.2.14", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-8.2.14.tgz", + "integrity": "sha512-3Vc9TnNpKdtvKIXcWDFINSsnwgEMiDmLzjceWg1iYKwpeZGQahUXPoesLwQazBMmxJzQiA4HOMj0TTXKZ+Jzkg==", "requires": { "tslib": "^1.9.0" } @@ -315,16 +315,16 @@ } }, "@angular/cli": { - "version": "8.3.17", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-8.3.17.tgz", - "integrity": "sha512-1R4IlPnrm6B9drhrCHTu9fBFIqCvOXK1zqYLsdobz6KfdkBcLSFqRqRfgEqADLChkxMo4tedtsftkuqY5Xinrg==", + "version": "8.3.26", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-8.3.26.tgz", + "integrity": "sha512-/dZik0ALcMSNaZdzqeG5hnFqyezrPQlWv+NXPidp1l0VTIwdEmjWmL26QpSBBvZ9bqXjY5/5SZYb+zZlGu78Kg==", "dev": true, "requires": { - "@angular-devkit/architect": "0.803.17", - "@angular-devkit/core": "8.3.17", - "@angular-devkit/schematics": "8.3.17", - "@schematics/angular": "8.3.17", - "@schematics/update": "0.803.17", + "@angular-devkit/architect": "0.803.26", + "@angular-devkit/core": "8.3.26", + "@angular-devkit/schematics": "8.3.26", + "@schematics/angular": "8.3.26", + "@schematics/update": "0.803.26", "@yarnpkg/lockfile": "1.1.0", "ansi-colors": "4.1.1", "debug": "^4.1.1", @@ -343,19 +343,19 @@ }, "dependencies": { "@angular-devkit/architect": { - "version": "0.803.17", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.803.17.tgz", - "integrity": "sha512-QbuWJhjh78Pb/9574OIjR38hhJCRBd0BtdrRRdTjP7By6liXURnA/OH6iZ9o1jA8It1NvoJQtM2flr8dQf83Mw==", + "version": "0.803.26", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.803.26.tgz", + "integrity": "sha512-mCynDvhGLElmuiaK5I6hVleMuZ1Svn7o5NnMW1ItiDlVZu1v49JWOxPS1A7C/ypGmhjl9jMorVtz2IumtLgCXw==", "dev": true, "requires": { - "@angular-devkit/core": "8.3.17", + "@angular-devkit/core": "8.3.26", "rxjs": "6.4.0" } }, "@angular-devkit/core": { - "version": "8.3.17", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-8.3.17.tgz", - "integrity": "sha512-cR2H/7OxxqagwGfzxwNWDOYN4QpOZ56Fei9JED/2p/K/5UDAowl20o3qP9mTfia/lEhFeyjMBcM8gsHxhJNYJQ==", + "version": "8.3.26", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-8.3.26.tgz", + "integrity": "sha512-b1ng9091o33s55/cwQYh1kboiJtj8y8z8xQWATDI9kRmNIQkWYVwVa/MzgPRJ4bzbEGG3zIUHCsp52A6vuGr2A==", "dev": true, "requires": { "ajv": "6.10.2", @@ -449,25 +449,25 @@ } }, "@angular/common": { - "version": "8.2.13", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-8.2.13.tgz", - "integrity": "sha512-I9cTcjUi88L+Mb/a/ZzUrdDcn3YgFFK9LubxaPjAfr6+G7IZ//MY5HuvG8Y41yKprXVVvQCbQ1yQD+GGRSCkaA==", + "version": "8.2.14", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-8.2.14.tgz", + "integrity": "sha512-Qmt+aX2quUW54kaNT7QH7WGXnFxr/cC2C6sf5SW5SdkZfDQSiz8IaItvieZfXVQUbBOQKFRJ7TlSkt0jI/yjvw==", "requires": { "tslib": "^1.9.0" } }, "@angular/compiler": { - "version": "8.2.13", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-8.2.13.tgz", - "integrity": "sha512-u2NWCvEn4SjbMvn2PG6sYcf+rR5u3aYMv3/mNQ9k+2UmCIu3yJrcuCzebjo5SdlDVqKD2vzbyMZnr8VB9OcceQ==", + "version": "8.2.14", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-8.2.14.tgz", + "integrity": "sha512-ABZO4E7eeFA1QyJ2trDezxeQM5ZFa1dXw1Mpl/+1vuXDKNjJgNyWYwKp/NwRkLmrsuV0yv4UDCDe4kJOGbPKnw==", "requires": { "tslib": "^1.9.0" } }, "@angular/compiler-cli": { - "version": "8.2.13", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-8.2.13.tgz", - "integrity": "sha512-ryW2Kozx/oHJrtdeIhgZ24RIU7Za3YIOHS9EMCQ8xMo+ZlSI+t2zOlLAXzK4PVWEjuTtQlKbT0KqilgU0QsHJg==", + "version": "8.2.14", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-8.2.14.tgz", + "integrity": "sha512-XDrTyrlIZM+0NquVT+Kbg5bn48AaWFT+B3bAT288PENrTdkuxuF9AhjFRZj8jnMdmaE4O2rioEkXBtl6z3zptA==", "dev": true, "requires": { "canonical-path": "1.0.0", @@ -491,25 +491,25 @@ } }, "@angular/core": { - "version": "8.2.13", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-8.2.13.tgz", - "integrity": "sha512-W8HN0lUQV4Sq85l17nhRIXeIfcw1ZdpgGm6to98pl0y9l/1srfzWfTnofuwCJC7gedt5AMrYJGUaNiDbByosFw==", + "version": "8.2.14", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-8.2.14.tgz", + "integrity": "sha512-zeePkigi+hPh3rN7yoNENG/YUBUsIvUXdxx+AZq+QPaFeKEA2FBSrKn36ojHFrdJUjKzl0lPMEiGC2b6a6bo6g==", "requires": { "tslib": "^1.9.0" } }, "@angular/forms": { - "version": "8.2.13", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-8.2.13.tgz", - "integrity": "sha512-l7lHD4kbWK70KY0Xp4IpSa106ZzSgPMwRYMFKd9qhYaJ7v0Y7Shh7Z/ZDCOP730maj9WULnpy5X4eeozWXgvgg==", + "version": "8.2.14", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-8.2.14.tgz", + "integrity": "sha512-zhyKL3CFIqcyHJ/TQF/h1OZztK611a6rxuPHCrt/5Sn1SuBTJJQ1pPTkOYIDy6IrCrtyANc8qB6P17Mao71DNQ==", "requires": { "tslib": "^1.9.0" } }, "@angular/http": { - "version": "7.2.15", - "resolved": "https://registry.npmjs.org/@angular/http/-/http-7.2.15.tgz", - "integrity": "sha512-TR7PEdmLWNIre3Zn8lvyb4lSrvPUJhKLystLnp4hBMcWsJqq5iK8S3bnlR4viZ9HMlf7bW7+Hm4SI6aB3tdUtw==", + "version": "7.2.16", + "resolved": "https://registry.npmjs.org/@angular/http/-/http-7.2.16.tgz", + "integrity": "sha512-yvjbNyzFSmmz4UTjCdy5M8mk0cZqf9TvSf8yN5UVIwtw4joyuUdlgJCuin0qSbQOKIf/JjHoofpO2JkPCGSNww==", "requires": { "tslib": "^1.9.0" } @@ -521,25 +521,25 @@ "dev": true }, "@angular/platform-browser": { - "version": "8.2.13", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-8.2.13.tgz", - "integrity": "sha512-1lPbeLQIbbafjq9ul3IA8s2fMJ/EXeMJ74ouTolVXoPPur9ZPRLX9FqBAO1K4QzkAWhRlyf6qIC+mDZfJILwZw==", + "version": "8.2.14", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-8.2.14.tgz", + "integrity": "sha512-MtJptptyKzsE37JZ2VB/tI4cvMrdAH+cT9pMBYZd66YSZfKjIj5s+AZo7z8ncoskQSB1o3HMfDjSK7QXGx1mLQ==", "requires": { "tslib": "^1.9.0" } }, "@angular/platform-browser-dynamic": { - "version": "8.2.13", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-8.2.13.tgz", - "integrity": "sha512-KP5psUKujAO8jZKHi6LRC+N7hE/epiGOhYZxdher1sCi81sYoZmqrEWkVZ4VKhov/4aC409CocDXcF7nmHV8tg==", + "version": "8.2.14", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-8.2.14.tgz", + "integrity": "sha512-mO2JPR5kLU/A3AQngy9+R/Q5gaF9csMStBQjwsCRI0wNtlItOIGL6+wTYpiTuh/ux+WVN1F2sLcEYU4Zf1ud9A==", "requires": { "tslib": "^1.9.0" } }, "@angular/router": { - "version": "8.2.13", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-8.2.13.tgz", - "integrity": "sha512-9CqnachtdASnEmRMtrG/R3c5nDCjjlCU4n0W/xt5+LlveyuUVvAT/CFUC38km4Df3lIvqap8mSpxzGaEzCL+wQ==", + "version": "8.2.14", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-8.2.14.tgz", + "integrity": "sha512-DHA2BhODqV7F0g6ZKgFaZgbsqzHHWRcfWchCOrOVKu2rYiKUTwwHVLBgZAhrpNeinq2pWanVYSIhMr7wy+LfEA==", "requires": { "tslib": "^1.9.0" } @@ -915,9 +915,9 @@ } }, "@fortawesome/fontawesome-free": { - "version": "5.11.2", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.11.2.tgz", - "integrity": "sha512-XiUPoS79r1G7PcpnNtq85TJ7inJWe0v+b5oZJZKb0pGHNIV6+UiNeQWiFGmuQ0aj7GEhnD/v9iqxIsjuRKtEnQ==", + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.13.0.tgz", + "integrity": "sha512-xKOeQEl5O47GPZYIMToj6uuA2syyFlq9EMSl2ui0uytjY9xbe8XS0pexNWmxrdcCyNGyDmLyYw5FtKsalBUeOg==", "dev": true }, "@mrmlnc/readdir-enhanced": { @@ -951,11 +951,11 @@ "integrity": "sha512-S4u037/cmzMJo6faZE51B+cGlXimzM4irYSfMmkCr8yeuDt394urhBzTUDE7/dhW9AYo9jRAWW4i+V+wrnhOqw==" }, "@nebular/theme": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/@nebular/theme/-/theme-4.5.0.tgz", - "integrity": "sha512-qhZyDdHeo8uzuGnDOZ2kH0rxbb2hYEPewI+svGaJyHHPHHjJROHJB2EpBRKs2qBoY4tQeuOjEyn1q/GI34y6Sw==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@nebular/theme/-/theme-4.6.0.tgz", + "integrity": "sha512-S7topzx0Hf9D7YkQxKJ8UB1G2/sEN3o4eKjkRe8Xv5n7H3WqpKQ9Uef3oW2mTf75dmICBldhKXvopdr6TA5foA==", "requires": { - "intersection-observer": "0.5.0" + "intersection-observer": "0.7.0" } }, "@ngtools/webpack": { @@ -989,19 +989,19 @@ "dev": true }, "@schematics/angular": { - "version": "8.3.17", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-8.3.17.tgz", - "integrity": "sha512-Ot++j7vpEBKUD+D6eQxIJto+PYR2HPR9hmBoshZ2nt1/mU3OPEpIMIO/OBnbVtGmuzKXdq2n2NvbPtnhlxWplA==", + "version": "8.3.26", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-8.3.26.tgz", + "integrity": "sha512-NJCykMxB9RKL+Tmr9xHftUevsivKGsQZQKjkub528wrSgwrCWoFCxGWV31iOXkT3TlBWmuibH6MZkrWbCLX4Sw==", "dev": true, "requires": { - "@angular-devkit/core": "8.3.17", - "@angular-devkit/schematics": "8.3.17" + "@angular-devkit/core": "8.3.26", + "@angular-devkit/schematics": "8.3.26" }, "dependencies": { "@angular-devkit/core": { - "version": "8.3.17", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-8.3.17.tgz", - "integrity": "sha512-cR2H/7OxxqagwGfzxwNWDOYN4QpOZ56Fei9JED/2p/K/5UDAowl20o3qP9mTfia/lEhFeyjMBcM8gsHxhJNYJQ==", + "version": "8.3.26", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-8.3.26.tgz", + "integrity": "sha512-b1ng9091o33s55/cwQYh1kboiJtj8y8z8xQWATDI9kRmNIQkWYVwVa/MzgPRJ4bzbEGG3zIUHCsp52A6vuGr2A==", "dev": true, "requires": { "ajv": "6.10.2", @@ -1050,13 +1050,13 @@ } }, "@schematics/update": { - "version": "0.803.17", - "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.803.17.tgz", - "integrity": "sha512-42CqIcm0i1BfBXYm6YuE8Q6kEEb0ATYiPYJQTgmb+bZSF2Sl9eAzPBaackW2EwVK1JP3Rdyl2S31OwGvgAk6xA==", + "version": "0.803.26", + "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.803.26.tgz", + "integrity": "sha512-r284UN3HP/UgxK80SG3MDlbF4qPS6EitEqwdSBqXizUYRlV6ovG9vHMLpNruWE0B6vfYbSAn1YvvIwW/ORL1Cw==", "dev": true, "requires": { - "@angular-devkit/core": "8.3.17", - "@angular-devkit/schematics": "8.3.17", + "@angular-devkit/core": "8.3.26", + "@angular-devkit/schematics": "8.3.26", "@yarnpkg/lockfile": "1.1.0", "ini": "1.3.5", "pacote": "9.5.5", @@ -1066,9 +1066,9 @@ }, "dependencies": { "@angular-devkit/core": { - "version": "8.3.17", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-8.3.17.tgz", - "integrity": "sha512-cR2H/7OxxqagwGfzxwNWDOYN4QpOZ56Fei9JED/2p/K/5UDAowl20o3qP9mTfia/lEhFeyjMBcM8gsHxhJNYJQ==", + "version": "8.3.26", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-8.3.26.tgz", + "integrity": "sha512-b1ng9091o33s55/cwQYh1kboiJtj8y8z8xQWATDI9kRmNIQkWYVwVa/MzgPRJ4bzbEGG3zIUHCsp52A6vuGr2A==", "dev": true, "requires": { "ajv": "6.10.2", @@ -1156,9 +1156,9 @@ } }, "@types/googlemaps": { - "version": "3.38.0", - "resolved": "https://registry.npmjs.org/@types/googlemaps/-/googlemaps-3.38.0.tgz", - "integrity": "sha512-Rp7FnrwyYGnjoxM+/q/4DRqUzvV9JKK1SBRXx8APxU6NNMtYMsWmOMsQOV2U6z8aMFzlUbRv3EDixLcDep8t9w==", + "version": "3.39.4", + "resolved": "https://registry.npmjs.org/@types/googlemaps/-/googlemaps-3.39.4.tgz", + "integrity": "sha512-R11RyCccERJhqSXlePFdoOXbWiTsY3H17Wl5e+JEpJFBXl8P+0Rm+JP8eqqSqOCtZjXDlYeKKnhE8a4VZlhtTw==", "dev": true }, "@types/jasmine": { @@ -1575,12 +1575,12 @@ "dev": true }, "ansi-escapes": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.2.1.tgz", - "integrity": "sha512-Cg3ymMAdN10wOk/VYfLV7KCQyv7EDirJ64500sU7n9UlmioEtDuU5Gd+hj73hXSU/ex7tHJSssmyftDdkMLO8Q==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", "dev": true, "requires": { - "type-fest": "^0.5.2" + "type-fest": "^0.11.0" } }, "ansi-gray": { @@ -2775,15 +2775,10 @@ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, - "charset": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/charset/-/charset-1.0.1.tgz", - "integrity": "sha512-6dVyOOYjpfFcL1Y4qChrAoQLRHvj2ziyhcm0QJlhOcAhykL/k1kTUPbeo+87MNRTRdk2OIIsIXbuF3x2wi5EXg==" - }, "chart.js": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.9.2.tgz", - "integrity": "sha512-AagP9h27gU7hhx8F64BOFpNZGV0R1Pz1nhsi0M1+KLhtniX6ElqLl0z0obKSiuGMl9tcRe6ZhruCGCJWmH6snQ==", + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.9.3.tgz", + "integrity": "sha512-+2jlOobSk52c1VU6fzkh3UwqHMdSlgH1xFv9FKMqHiNCpXsGPQa/+81AFa+i3jZ253Mq9aAycPwDjnn1XbRNNw==", "requires": { "chartjs-color": "^2.1.0", "moment": "^2.10.2" @@ -2954,9 +2949,9 @@ } }, "cli-width": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", - "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", + "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", "dev": true }, "cliui": { @@ -3015,14 +3010,14 @@ "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, "codelyzer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-5.2.0.tgz", - "integrity": "sha512-izfUfhEOOgAizszPlEDxo71DK/C4wprZw0vkY6UWcOSTQvN1JyfXf9DXwaV7WX+/JC+hH0ShXfdtGLA9Rca7LA==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-5.2.2.tgz", + "integrity": "sha512-jB4FZ1Sx7kZhvZVdf+N2BaKTdrrNZOL0Bj10RRfrhHrb3zEvXjJvvq298JPMJAiyiCS/v4zs1QlGU0ip7xGqeA==", "dev": true, "requires": { "app-root-path": "^2.2.1", "aria-query": "^3.0.0", - "axobject-query": "^2.0.2", + "axobject-query": "2.0.2", "css-selector-tokenizer": "^0.7.1", "cssauron": "^1.4.0", "damerau-levenshtein": "^1.0.4", @@ -4126,14 +4121,14 @@ } }, "css-selector-tokenizer": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.1.tgz", - "integrity": "sha512-xYL0AMZJ4gFzJQsHUKa5jiWWi2vH77WVNg7JYRyewwj6oPh4yb/y6Y9ZCw9dsj/9UauMhtuxR+ogQd//EdEVNA==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.2.tgz", + "integrity": "sha512-yj856NGuAymN6r8bn8/Jl46pR+OC3eEvAhfGYDUe7YPtTPAYrSSw4oAniZ9Y8T5B92hjhwTBLUen0/vKPxf6pw==", "dev": true, "requires": { - "cssesc": "^0.1.0", - "fastparse": "^1.1.1", - "regexpu-core": "^1.0.0" + "cssesc": "^3.0.0", + "fastparse": "^1.1.2", + "regexpu-core": "^4.6.0" } }, "css-tokenize": { @@ -4188,9 +4183,9 @@ } }, "cssesc": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", - "integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", "dev": true }, "currently-unhandled": { @@ -4223,9 +4218,9 @@ } }, "damerau-levenshtein": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.5.tgz", - "integrity": "sha512-CBCRqFnpu715iPmw1KrdOrzRqbdFwQTwAWyyyYS42+iAgHCuXZ+/TdMgQkUENPomxEz9z1BEzuQU2Xw0kUuAgA==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz", + "integrity": "sha512-JVrozIeElnj3QzfUIt8tB8YMluBJom4Vw9qTPpjGYQ9fYlB3D/rb6OordUxf3xeFB35LKWs0xqcO5U6ySvBtug==", "dev": true }, "dargs": { @@ -4812,30 +4807,11 @@ "safer-buffer": "^2.1.0" } }, - "ecstatic": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/ecstatic/-/ecstatic-4.1.2.tgz", - "integrity": "sha512-lnrAOpU2f7Ra8dm1pW0D1ucyUxQIEk8RjFrvROg1YqCV0ueVu9hzgiSEbSyROqXDDiHREdqC4w3AwOTb23P4UQ==", - "requires": { - "charset": "^1.0.1", - "he": "^1.1.1", - "mime": "^2.4.1", - "minimist": "^1.1.0", - "on-finished": "^2.3.0", - "url-join": "^4.0.0" - }, - "dependencies": { - "mime": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", - "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==" - } - } - }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true }, "electron-to-chromium": { "version": "1.3.190", @@ -5263,9 +5239,9 @@ "dev": true }, "eva-icons": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/eva-icons/-/eva-icons-1.1.2.tgz", - "integrity": "sha512-R5iD6nYcQS/qQOfC1ooP1hs8Xg+VKYfO95SXDzVWFUwYdxXrJYc7+ZnbE7tsl1amFjbRMm+c/vnH4NVaB2zrUQ==" + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/eva-icons/-/eva-icons-1.1.3.tgz", + "integrity": "sha512-QBSEWNbEx1H0numXP1qgxKVCZHonRaky5ft4pGzQBcO4cy7mEja6TuJ8rc7BqX2pmkvetVQWKDH+DK/8y7GTag==" }, "event-emitter": { "version": "0.3.5", @@ -5783,12 +5759,13 @@ "figgy-pudding": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz", - "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==" + "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==", + "dev": true }, "figures": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.1.0.tgz", - "integrity": "sha512-ravh8VRXqHuMvZt/d8GblBeqDMkdJMBdv/2KntFH+ra5MXkO7nxNKpzQ3n6QD/2da1kH0aWmNISdvhM7gl2gVg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", "dev": true, "requires": { "escape-string-regexp": "^1.0.5" @@ -7384,12 +7361,12 @@ "dev": true }, "globule": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.1.tgz", - "integrity": "sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.1.tgz", + "integrity": "sha512-OVyWOHgw29yosRHCHo7NncwR1hW5ew0W/UrvtwvjefVJeQ26q4/8r8FmPsSF1hJ93IgWkyv16pCTz6WblMzm/g==", "requires": { "glob": "~7.1.1", - "lodash": "~4.17.10", + "lodash": "~4.17.12", "minimatch": "~3.0.2" } }, @@ -7547,11 +7524,6 @@ "minimalistic-assert": "^1.0.1" } }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" - }, "help-me": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/help-me/-/help-me-1.1.0.tgz", @@ -7903,9 +7875,9 @@ "dev": true }, "in-publish": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz", - "integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E=" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.1.tgz", + "integrity": "sha512-oDM0kUSNFC31ShNxHKUyfZKy8ZeXZBWMjMdZHKLOk13uvT27VTL/QzRGfRUcevJhpkZAvlhPYuXkF7eNWrtyxQ==" }, "indent-string": { "version": "2.1.0", @@ -7975,9 +7947,9 @@ }, "dependencies": { "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", "dev": true }, "ansi-styles": { @@ -8000,21 +7972,6 @@ "supports-color": "^5.3.0" } }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, "is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -8028,14 +7985,25 @@ "dev": true }, "string-width": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.1.0.tgz", - "integrity": "sha512-NrX+1dVVh+6Y9dnQ19pR0pP4FiEIlUvdTGn8pw6CKTNq5sgib2nIhmUNT5TAmhWmvKr3WcxBcP3E8nWezuipuQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^5.2.0" + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } } }, "strip-ansi": { @@ -8045,6 +8013,14 @@ "dev": true, "requires": { "ansi-regex": "^4.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + } } }, "supports-color": { @@ -8075,9 +8051,9 @@ } }, "intersection-observer": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/intersection-observer/-/intersection-observer-0.5.0.tgz", - "integrity": "sha512-8Zgt4ijlyvIrQVTA7MPb2W9+KhoetrAbxlh0RmTGxpx0+ZsAXvy7IsbNnZIrqZ6TddAdWeQj49x7Ph7Ir6KRkA==" + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/intersection-observer/-/intersection-observer-0.7.0.tgz", + "integrity": "sha512-Id0Fij0HsB/vKWGeBe9PxeY45ttRiBmhFyyt/geBdDHBYNctMRTE3dC1U3ujzz3lap+hVXlEcVaB56kZP/eEUg==" }, "intl": { "version": "1.2.5", @@ -8375,12 +8351,6 @@ "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", "dev": true }, - "is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", - "dev": true - }, "is-regex": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", @@ -9273,9 +9243,9 @@ } }, "leaflet": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.5.1.tgz", - "integrity": "sha512-ekM9KAeG99tYisNBg0IzEywAlp0hYI5XRipsqRXyRTeuU8jcuntilpp+eFf5gaE0xubc9RuSNIVtByEKwqFV0w==" + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.6.0.tgz", + "integrity": "sha512-CPkhyqWUKZKFJ6K8umN5/D2wrJ2+/8UIpXppY7QDnUZW5bZL5+SEI2J7GBpwh4LIupOKqbNSQXgqmrEJopHVNQ==" }, "leaflet-draw": { "version": "1.0.4", @@ -9657,9 +9627,9 @@ "dev": true }, "make-fetch-happen": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-5.0.1.tgz", - "integrity": "sha512-b4dfaMvUDR67zxUq1+GN7Ke9rH5WvGRmoHuMH7l+gmUCR2tCXFP6mpeJ9Dp+jB6z8mShRopSf1vLRBhRs8Cu5w==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-5.0.2.tgz", + "integrity": "sha512-07JHC0r1ykIoruKO8ifMXu+xEU8qOXDFETylktdug6vJDACnP+HKevOu3PXyNPzFyTSlz8vrBYlBO1JZRe8Cag==", "dev": true, "requires": { "agentkeepalive": "^3.4.1", @@ -9676,9 +9646,9 @@ }, "dependencies": { "cacache": { - "version": "12.0.3", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.3.tgz", - "integrity": "sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==", + "version": "12.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", + "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", "dev": true, "requires": { "bluebird": "^3.5.5", @@ -10112,9 +10082,9 @@ "dev": true }, "moment": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", - "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" + "version": "2.25.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.25.1.tgz", + "integrity": "sha512-nRKMf9wDS4Fkyd0C9LXh2FFXinD+iwbJ5p/lh3CHitW9kZbRbJ8hCruiadiIXZVbeAqKZzqcTvHnK3mRhFjb6w==" }, "morgan": { "version": "1.9.1", @@ -10174,9 +10144,9 @@ }, "dependencies": { "readable-stream": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", - "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", + "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", @@ -10188,14 +10158,30 @@ } }, "mqtt-packet": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-6.2.1.tgz", - "integrity": "sha512-ZxG5QVb7+gMix5n4DClym9dQoCZC6DoNEqgMkMi/GMXvIU4Wsdx+/6KBavw50HHFH9kN1lBSY7phxNlAS2+jnw==", + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-6.3.2.tgz", + "integrity": "sha512-i56+2kN6F57KInGtjjfUXSl4xG8u/zOvfaXFLKFAbBXzWkXOmwcmjaSCBPayf2IQCkQU0+h+S2DizCo3CF6gQA==", "requires": { "bl": "^1.2.2", + "debug": "^4.1.1", "inherits": "^2.0.3", "process-nextick-args": "^2.0.0", "safe-buffer": "^5.1.2" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } } }, "ms": { @@ -10295,9 +10281,9 @@ } }, "ng2-ckeditor": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/ng2-ckeditor/-/ng2-ckeditor-1.2.6.tgz", - "integrity": "sha512-/bw0ZzVBvHUrFrXFeP08XKSsIX67M85rTiZkcphn0kpripAj13JvX/l/SQ74Kfk5K226Y3c+rONDj6OtfZH0kA==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/ng2-ckeditor/-/ng2-ckeditor-1.2.7.tgz", + "integrity": "sha512-TmnfhvQB9yFisl6kS3MT+LmkMU1sc5gHdrcSvzL62a/LZs+V483ZunkW4A+wz+a9EV/wu5heiqqcSZsKtnVJqQ==", "requires": { "tslib": "^1.9.0" } @@ -10316,22 +10302,20 @@ } }, "ngx-auth": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ngx-auth/-/ngx-auth-5.2.0.tgz", - "integrity": "sha512-nanlmKgLUgIgzcKhcGgKqctCKavU+dfZk8H4URZD34kfUnadecD3HV/so8nuseSYW3maoxtKhC6QavvaasWf6g==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ngx-auth/-/ngx-auth-5.3.0.tgz", + "integrity": "sha512-qBkiF+1g7ioj1tWbknfE4+jNaL+mUgXsQqYj3i8sB1XLAdpzn2B7pvvkcLD0LnXk3A0Hx2kfE61eLESwxebbLA==", "requires": { "tslib": "^1.9.0" } }, "ngx-mqtt": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/ngx-mqtt/-/ngx-mqtt-6.13.2.tgz", - "integrity": "sha512-uJDdJ3xYFazObg3s9CDMMzmf8TSdn7SWny9xgTxEhqr5/UwrowoZxrMCQqeoAzfU/0FBshWFTXV+/k/0/VdcPg==", + "version": "6.13.3", + "resolved": "https://registry.npmjs.org/ngx-mqtt/-/ngx-mqtt-6.13.3.tgz", + "integrity": "sha512-f5VUKe/5Fn+eCSWnpEq34QHWCmPcwnZhF8Ze8ykjFZiz5wPYO0TIstX29Jw0PQh1cmQhHQXoqehjpzAMZ+3a9Q==", "requires": { "@types/node": "10.3.2", - "ecstatic": ">=3.2.0", - "mqtt": "^3.0.0", - "ssri": ">=6.0.0" + "mqtt": "^3.0.0" }, "dependencies": { "@types/node": { @@ -10348,9 +10332,9 @@ "dev": true }, "node-fetch-npm": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/node-fetch-npm/-/node-fetch-npm-2.0.2.tgz", - "integrity": "sha512-nJIxm1QmAj4v3nfCvEeCrYSoVwXyxLnaPBK5W1W5DGEJwjlKuC2VEUycGw5oxk+4zZahRrB84PUJJgEmhFTDFw==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/node-fetch-npm/-/node-fetch-npm-2.0.4.tgz", + "integrity": "sha512-iOuIQDWDyjhv9qSDrj9aq/klt6F9z1p2otB3AV7v3zBDcL/x+OfGsvGQZZCcMZbUf4Ujw1xGNQkjvGnVT22cKg==", "dev": true, "requires": { "encoding": "^0.1.11", @@ -10457,9 +10441,9 @@ } }, "node-sass": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.13.0.tgz", - "integrity": "sha512-W1XBrvoJ1dy7VsvTAS5q1V45lREbTlZQqFbiHb3R3OTTCma0XBtuG6xZ6Z4506nR4lmHPTqVRwxT6KgtWC97CA==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.14.0.tgz", + "integrity": "sha512-AxqU+DFpk0lEz95sI6jO0hU0Rwyw7BXVEv6o9OItoXLyeygPeaSpiV4rwQb10JiTghHaa0gZeD21sz+OsQluaw==", "requires": { "async-foreach": "^0.1.3", "chalk": "^1.1.1", @@ -10541,9 +10525,18 @@ "integrity": "sha1-IhiMJwfJEfs608GqwGd/9oZhvqg=" }, "npm-bundled": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.6.tgz", - "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", + "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", + "dev": true, + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", "dev": true }, "npm-package-arg": { @@ -10559,13 +10552,14 @@ } }, "npm-packlist": { - "version": "1.4.6", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.6.tgz", - "integrity": "sha512-u65uQdb+qwtGvEJh/DgQgW1Xg7sqeNbmxYyrvlNznaVTjV3E5P6F/EFjM+BVHXl7JJlsdG8A64M0XI8FI/IOlg==", + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz", + "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", "dev": true, "requires": { "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" + "npm-bundled": "^1.0.1", + "npm-normalize-package-bin": "^1.0.1" } }, "npm-pick-manifest": { @@ -10580,9 +10574,9 @@ } }, "npm-registry-fetch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-4.0.2.tgz", - "integrity": "sha512-Z0IFtPEozNdeZRPh3aHHxdG+ZRpzcbQaJLthsm3VhNf6DScicTFRHZzK82u8RsJUsUHkX+QH/zcB/5pmd20H4A==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-4.0.4.tgz", + "integrity": "sha512-6jb34hX/iYNQebqWUHtU8YF6Cjb1H6ouTFPClYsyiW6lpFkljTpdeftm53rRojtja1rKAvKNIIiTS5Sjpw4wsA==", "dev": true, "requires": { "JSONStream": "^1.3.4", @@ -10797,14 +10791,85 @@ "isobject": "^3.0.0" } }, - "object.getownpropertydescriptors": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", - "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", "dev": true, "requires": { "define-properties": "^1.1.2", - "es-abstract": "^1.5.1" + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "object.getownpropertydescriptors": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", + "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", + "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "is-callable": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", + "dev": true + }, + "is-regex": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "object-inspect": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", + "dev": true + } } }, "object.omit": { @@ -10847,6 +10912,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, "requires": { "ee-first": "1.1.1" } @@ -11100,9 +11166,9 @@ }, "dependencies": { "cacache": { - "version": "12.0.3", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.3.tgz", - "integrity": "sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==", + "version": "12.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", + "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", "dev": true, "requires": { "bluebird": "^3.5.5", @@ -12336,16 +12402,16 @@ } }, "read-package-json": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.0.tgz", - "integrity": "sha512-KLhu8M1ZZNkMcrq1+0UJbR8Dii8KZUqB0Sha4mOx/bknfKI/fyrQVrG/YIt2UOtG667sD8+ee4EXMM91W9dC+A==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.1.tgz", + "integrity": "sha512-dAiqGtVc/q5doFz6096CcnXhpYk0ZN8dEKVkGLU0CsASt8SrgF6SF7OTKAYubfvFhWaqofl+Y8HK19GR8jwW+A==", "dev": true, "requires": { "glob": "^7.1.1", "graceful-fs": "^4.1.2", "json-parse-better-errors": "^1.0.1", "normalize-package-data": "^2.0.0", - "slash": "^1.0.0" + "npm-normalize-package-bin": "^1.0.0" } }, "read-package-tree": { @@ -12436,6 +12502,15 @@ "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", "dev": true }, + "regenerate-unicode-properties": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz", + "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==", + "dev": true, + "requires": { + "regenerate": "^1.4.0" + } + }, "regenerator-runtime": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", @@ -12462,26 +12537,29 @@ } }, "regexpu-core": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", - "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.0.tgz", + "integrity": "sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ==", "dev": true, "requires": { - "regenerate": "^1.2.1", - "regjsgen": "^0.2.0", - "regjsparser": "^0.1.4" + "regenerate": "^1.4.0", + "regenerate-unicode-properties": "^8.2.0", + "regjsgen": "^0.5.1", + "regjsparser": "^0.6.4", + "unicode-match-property-ecmascript": "^1.0.4", + "unicode-match-property-value-ecmascript": "^1.2.0" } }, "regjsgen": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", - "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.1.tgz", + "integrity": "sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg==", "dev": true }, "regjsparser": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", - "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.4.tgz", + "integrity": "sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw==", "dev": true, "requires": { "jsesc": "~0.5.0" @@ -12658,13 +12736,10 @@ "integrity": "sha512-ZYzRkETgBrdEGzL5JSKimvjI2CX7ioyZCkX2BpcfyjqI+079W0wHAyj5W4rIZMcDSOHgLZtgz1IdDi/vU77KEQ==" }, "run-async": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", - "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", - "dev": true, - "requires": { - "is-promise": "^2.1.0" - } + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true }, "run-queue": { "version": "1.0.3", @@ -13780,6 +13855,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "dev": true, "requires": { "figgy-pudding": "^3.5.1" } @@ -13960,6 +14036,284 @@ "function-bind": "^1.0.2" } }, + "string.prototype.trimend": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", + "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", + "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "is-callable": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", + "dev": true + }, + "is-regex": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "object-inspect": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", + "dev": true + } + } + }, + "string.prototype.trimleft": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz", + "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5", + "string.prototype.trimstart": "^1.0.0" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", + "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "is-callable": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", + "dev": true + }, + "is-regex": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "object-inspect": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", + "dev": true + } + } + }, + "string.prototype.trimright": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz", + "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5", + "string.prototype.trimend": "^1.0.0" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", + "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "is-callable": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", + "dev": true + }, + "is-regex": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "object-inspect": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", + "dev": true + } + } + }, + "string.prototype.trimstart": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", + "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", + "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "is-callable": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", + "dev": true + }, + "is-regex": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "object-inspect": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", + "dev": true + } + } + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -15138,9 +15492,9 @@ } }, "tslib": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", - "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", + "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==" }, "tslint": { "version": "5.20.1", @@ -15267,9 +15621,9 @@ } }, "type-fest": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.5.2.tgz", - "integrity": "sha512-DWkS49EQKVX//Tbupb9TFa19c7+MK1XmzkrZUR8TAktmE/DizXoaoJV6TZ/tSIPXipqNiRI6CyAe7x69Jb6RSw==", + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", "dev": true }, "type-is": { @@ -15336,6 +15690,28 @@ "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=" }, + "unicode-canonical-property-names-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", + "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", + "dev": true + }, + "unicode-match-property-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", + "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", + "dev": true, + "requires": { + "unicode-canonical-property-names-ecmascript": "^1.0.4", + "unicode-property-aliases-ecmascript": "^1.0.4" + } + }, + "unicode-match-property-value-ecmascript": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz", + "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==", + "dev": true + }, "unicode-properties": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.2.2.tgz", @@ -15346,6 +15722,12 @@ "unicode-trie": "^1.0.0" } }, + "unicode-property-aliases-ecmascript": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz", + "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==", + "dev": true + }, "unicode-trie": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-1.0.0.tgz", @@ -15533,11 +15915,6 @@ } } }, - "url-join": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", - "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==" - }, "url-parse": { "version": "1.4.7", "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", @@ -15602,9 +15979,9 @@ "dev": true }, "uuid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", - "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==" + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" }, "v8flags": { "version": "3.1.3", @@ -16088,9 +16465,9 @@ "dev": true }, "websocket-stream": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/websocket-stream/-/websocket-stream-5.5.0.tgz", - "integrity": "sha512-EXy/zXb9kNHI07TIMz1oIUIrPZxQRA8aeJ5XYg5ihV8K4kD1DuA+FY6R96HfdIHzlSzS8HiISAfrm+vVQkZBug==", + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/websocket-stream/-/websocket-stream-5.5.2.tgz", + "integrity": "sha512-8z49MKIHbGk3C4HtuHWDtYX8mYej1wWabjthC/RupM9ngeukU4IWoM46dgth1UOS/T4/IqgEdCDJuMe2039OQQ==", "requires": { "duplexify": "^3.5.1", "inherits": "^2.0.1", @@ -16432,9 +16809,9 @@ } }, "yargs-parser": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", - "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", "dev": true, "requires": { "camelcase": "^5.0.0", diff --git a/package.json b/package.json index ce7bd434..7ca72a1d 100644 --- a/package.json +++ b/package.json @@ -4,10 +4,10 @@ "license": "MIT", "repository": { "type": "git", - "url": "git+https://github.com/akveo/ngx-admin.git" + "url": "git+https://github.com/mainflux/ui.git" }, "bugs": { - "url": "https://github.com/akveo/ngx-admin/issues" + "url": "https://github.com/mainflux/ui/issues" }, "scripts": { "ng": "ng", @@ -29,91 +29,93 @@ "release:changelog": "npm run conventional-changelog -- -p angular -i CHANGELOG.md -s" }, "dependencies": { - "@agm/core": "^1.0.0", - "@angular/animations": "^8.2.13", + "@agm/core": "^1.1.0", + "@angular/animations": "^8.2.14", "@angular/cdk": "^8.2.3", - "@angular/common": "^8.2.13", - "@angular/compiler": "^8.2.13", - "@angular/core": "^8.2.13", - "@angular/forms": "^8.2.13", - "@angular/http": "^7.2.15", - "@angular/platform-browser": "^8.2.13", - "@angular/platform-browser-dynamic": "^8.2.13", - "@angular/router": "^8.2.13", + "@angular/common": "^8.2.14", + "@angular/compiler": "^8.2.14", + "@angular/core": "^8.2.14", + "@angular/forms": "^8.2.14", + "@angular/platform-browser": "^8.2.14", + "@angular/platform-browser-dynamic": "^8.2.14", + "@angular/router": "^8.2.14", "@asymmetrik/ngx-leaflet": "^6.0.1", "@asymmetrik/ngx-leaflet-draw": "^5.0.1", - "@nebular/auth": "4.1.0", - "@nebular/bootstrap": "4.1.0", - "@nebular/eva-icons": "4.1.0", - "@nebular/security": "4.1.0", - "@nebular/theme": "^4.5.0", - "angular-tree-component": "7.2.0", - "angular2-chartjs": "0.4.1", - "angular2-toaster": "^7.0.0", - "bootstrap": "4.3.1", - "chart.js": "^2.9.2", - "ckeditor": "4.12.1", + "@nebular/auth": "4.6.0", + "@nebular/bootstrap": "4.6.0", + "@nebular/eva-icons": "4.6.0", + "@nebular/security": "4.6.0", + "@nebular/theme": "^4.6.0", + "angular-tree-component": "7.2.1", + "angular2-chartjs": "0.5.1", + "angular2-toaster": "^8.0.0", + "bootstrap": "4.4.1", + "chart.js": "^2.9.3", + "ckeditor4": "4.14.0", "classlist.js": "1.1.20150312", - "core-js": "2.5.1", - "eva-icons": "^1.1.2", + "core-js": "2.6.11", + "eva-icons": "^1.1.3", + "font-awesome": "4.7.0", "intl": "1.2.5", "ionicons": "2.0.1", - "jsoneditor": "^6.4.1", + "jquery": "3.5.0", + "jsoneditor": "^8.6.6", "jwt-decode": "^2.2.0", - "leaflet": "^1.5.1", + "leaflet": "^1.6.0", "leaflet-draw": "^1.0.4", "nebular-icons": "1.1.0", "ng2-charts": "^2.3.0", "ng2-ckeditor": "^1.2.6", - "ng2-completer": "2.0.8", - "ng2-smart-table": "1.3.5", - "ngx-auth": "^5.2.0", - "ngx-mqtt": "^6.13.2", - "node-sass": "^4.13.0", - "normalize.css": "6.0.0", + "ng2-completer": "3.0.2", + "ng2-smart-table": "1.5.0", + "ngx-auth": "^5.3.0", + "ngx-mqtt": "^6.13.3", + "node-sass": "^4.14.0", + "normalize.css": "8.0.1", "pace-js": "1.0.2", - "roboto-fontface": "0.8.0", - "rxjs": "6.5.2", - "rxjs-compat": "6.3.0", + "popper.js": "1.16.0", + "roboto-fontface": "0.10.0", + "rxjs": "6.5.5", + "rxjs-compat": "6.5.5", "socicon": "3.0.5", - "tinymce": "4.9.7", - "tslib": "^1.9.0", - "typeface-exo": "0.0.22", - "uuid": "^3.3.3", + "tinymce": "4.9.10", + "tslib": "^1.11.1", + "typeface-exo": "0.0.61", + "uuid": "^8.0.0", "web-animations-js": "github:angular/web-animations-js#release_pr208", "xterm": "^3.14.5", "zone.js": "~0.9.1" }, "devDependencies": { - "@angular-devkit/build-angular": "^0.800.6", - "@angular/cli": "^8.3.17", - "@angular/compiler-cli": "^8.2.13", - "@angular/language-service": "8.0.0", + "@angular-devkit/build-angular": "^0.803.26", + "@angular/cli": "^8.3.26", + "@angular/compiler-cli": "^8.2.14", + "@angular/language-service": "8.2.14", "@compodoc/compodoc": "^1.1.11", - "@fortawesome/fontawesome-free": "^5.11.2", - "@types/d3-color": "1.0.5", - "@types/googlemaps": "^3.38.0", - "@types/jasmine": "2.5.54", - "@types/jasminewd2": "2.0.3", - "@types/node": "6.0.90", - "codelyzer": "^5.2.0", - "conventional-changelog-cli": "1.3.4", - "husky": "0.13.3", - "jasmine-core": "2.6.4", - "jasmine-spec-reporter": "4.1.1", - "karma": "1.7.1", - "karma-chrome-launcher": "2.1.1", - "karma-cli": "1.0.1", - "karma-coverage-istanbul-reporter": "1.3.0", - "karma-jasmine": "1.1.0", - "karma-jasmine-html-reporter": "0.2.2", - "npm-run-all": "4.0.2", - "protractor": "5.1.2", - "rimraf": "2.6.1", - "stylelint": "7.13.0", - "ts-node": "3.2.2", + "@fortawesome/fontawesome-free": "^5.13.0", + "@types/d3-color": "1.2.2", + "@types/googlemaps": "^3.39.4", + "@types/jasmine": "2.8.9", + "@types/jasminewd2": "2.0.8", + "@types/node": "13.13.4", + "codelyzer": "^5.2.2", + "conventional-changelog-cli": "2.0.31", + "husky": "4.2.5", + "jasmine-core": "3.5.0", + "jasmine-spec-reporter": "5.0.2", + "karma": "5.0.4", + "karma-chrome-launcher": "3.1.0", + "karma-cli": "2.0.0", + "karma-coverage-istanbul-reporter": "3.0.0", + "karma-jasmine": "3.1.1", + "karma-jasmine-html-reporter": "1.5.3", + "npm-run-all": "4.1.5", + "protractor": "5.4.4", + "rimraf": "3.0.2", + "stylelint": "13.3.3", + "ts-node": "8.10.1", "tslint": "^5.20.1", "tslint-language-service": "^0.9.9", - "typescript": "3.4.5" + "typescript": "3.5.1" } } diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 47809b8d..f1568fed 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -2,10 +2,10 @@ import { ExtraOptions, RouterModule, Routes } from '@angular/router'; import { NgModule } from '@angular/core'; import { NbAuthComponent, - NbLoginComponent, NbRequestPasswordComponent, } from '@nebular/auth'; +import { LoginComponent } from 'app/pages/login/login.component'; import { LogoutComponent } from 'app/pages/logout/logout.component'; import { ResetPasswordComponent } from 'app/pages/reset/reset.password.component'; import { RegisterComponent } from 'app/pages/register/register.component'; @@ -21,11 +21,11 @@ const routes: Routes = [ children: [ { path: '', - component: NbLoginComponent, + component: LoginComponent, }, { path: 'login', - component: NbLoginComponent, + component: LoginComponent, }, { path: 'register', diff --git a/src/app/app.module.ts b/src/app/app.module.ts index b3cca466..4c10535e 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -27,6 +27,7 @@ import { NbButtonModule, } from '@nebular/theme'; +import { LoginComponent } from './pages/login/login.component'; import { LogoutComponent } from './pages/logout/logout.component'; import { ResetPasswordComponent } from './pages/reset/reset.password.component'; import { RegisterComponent } from './pages/register/register.component'; @@ -46,6 +47,7 @@ export const MQTT_SERVICE_OPTIONS: IMqttServiceOptions = { @NgModule({ declarations: [ AppComponent, + LoginComponent, LogoutComponent, ResetPasswordComponent, RegisterComponent, diff --git a/src/app/auth/auth.token.interceptor.service.ts b/src/app/auth/auth.token.interceptor.service.ts index a76943b5..cc7825e7 100644 --- a/src/app/auth/auth.token.interceptor.service.ts +++ b/src/app/auth/auth.token.interceptor.service.ts @@ -33,7 +33,8 @@ export class TokenInterceptor implements HttpInterceptor { if (token && token.getValue() && !request.url.startsWith(environment.writerChannelsUrl) && !request.url.startsWith(environment.readerChannelsUrl) && - !request.url.startsWith(environment.bootstrapUrl) + !request.url.startsWith(environment.bootstrapUrl) && + !request.url.startsWith(environment.browseUrl) ) { request = request.clone({ setHeaders: { @@ -45,12 +46,10 @@ export class TokenInterceptor implements HttpInterceptor { resp => { }, err => { - // Forbidden - 403 / Gateway Timeout - 504 - if (err instanceof HttpErrorResponse && + // Status 403 - Forbiden + if (err instanceof HttpErrorResponse && err.status === 403 && !request.url.startsWith(environment.writerChannelsUrl) && - !request.url.startsWith(environment.readerChannelsUrl) && - !request.url.startsWith(environment.bootstrapUrl) && - (err.status === 403 || err.status === 504)) { + !request.url.startsWith(environment.readerChannelsUrl)) { localStorage.removeItem('auth_app_token'); this.router.navigateByUrl('/auth/login'); } diff --git a/src/app/common/common.module.ts b/src/app/common/common.module.ts index aabfc045..4c42204b 100644 --- a/src/app/common/common.module.ts +++ b/src/app/common/common.module.ts @@ -1,10 +1,16 @@ import { NgModule } from '@angular/core'; +import 'rxjs/add/operator/catch'; +import 'rxjs/add/observable/throw'; +import 'rxjs/add/operator/switchMap'; +import 'rxjs/add/operator/map'; + import { BootstrapService } from './services/bootstrap/bootstrap.service'; import { ChannelsService } from './services/channels/channels.service'; import { GatewaysService } from './services/gateways/gateways.service'; import { LoraService } from './services/lora/lora.service'; import { OpcuaService } from './services/opcua/opcua.service'; +import { OpcuaStore } from 'app/common/store/opcua.store'; import { MessagesService } from './services/messages/messages.service'; import { MqttManagerService } from './services/mqtt/mqtt.manager.service'; import { NotificationsService } from './services/notifications/notifications.service'; @@ -12,6 +18,7 @@ import { ThingsService } from './services/things/things.service'; import { TwinsService } from './services/twins/twins.service'; import { UsersService } from './services/users/users.service'; import { VersionsService } from './services/versions/versions.service'; +import { FsService } from './services/fs/fs.service'; import { TokenInterceptor } from 'app/auth/auth.token.interceptor.service'; import { HTTP_INTERCEPTORS } from '@angular/common/http'; @@ -23,6 +30,7 @@ import { HTTP_INTERCEPTORS } from '@angular/common/http'; GatewaysService, LoraService, OpcuaService, + OpcuaStore, MessagesService, MqttManagerService, NotificationsService, @@ -30,6 +38,7 @@ import { HTTP_INTERCEPTORS } from '@angular/common/http'; TwinsService, UsersService, VersionsService, + FsService, { provide: HTTP_INTERCEPTORS, useClass: TokenInterceptor, diff --git a/src/app/common/interfaces/bootstrap.interface.ts b/src/app/common/interfaces/bootstrap.interface.ts index 8d6fc7f1..fac08549 100644 --- a/src/app/common/interfaces/bootstrap.interface.ts +++ b/src/app/common/interfaces/bootstrap.interface.ts @@ -14,7 +14,44 @@ export interface ConfigContent { mqtt_url: string; edgex_url: string; nats_url: string; - export_config?: any; + export_config: ExportConfig; +} + +export interface ExportConfig { + file?: string; + exp: ExpConf; + mqtt: MqttConfig; + routes: Array; +} + +export interface ExpConf { + log_level?: string; + nats?: string; + port?: string; + cache_db?: string; + cache_pass?: string; + cache_url?: string; +} + +export interface Route { + mqtt_topic?: string; + nats_topic?: string; + subtopic?: string; + type?: string; +} + +export interface MqttConfig { + host?: string; + ca_path?: string; + cert_path?: string; + priv_key_path?: string; + channel?: string; + qos?: number; + mtls?: boolean; + password?: string; + username?: string; + skip_tls_ver?: boolean; + retain?: boolean; } export interface ConfigUpdate { diff --git a/src/app/common/interfaces/gateway.interface.ts b/src/app/common/interfaces/gateway.interface.ts index a60036e7..4a0ea126 100644 --- a/src/app/common/interfaces/gateway.interface.ts +++ b/src/app/common/interfaces/gateway.interface.ts @@ -1,11 +1,11 @@ export interface GatewayMetadata { - ctrlChannelID?: string; - dataChannelID?: string; - exportChannelID?: string; - exportThingID?: string; - gwPassword?: string; - mac?: string; - cfgID?: string; + ctrl_channel_id?: string; + data_channel_id?: string; + export_channel_id?: string; + export_thing_id?: string; + external_key?: string; + external_id?: string; + cfg_id?: string; type?: string; } diff --git a/src/app/common/interfaces/lora.interface.ts b/src/app/common/interfaces/lora.interface.ts index 883d2fb5..64352877 100644 --- a/src/app/common/interfaces/lora.interface.ts +++ b/src/app/common/interfaces/lora.interface.ts @@ -1,10 +1,10 @@ export interface LoraMetadata { type: string; lora: { - devEUI?: string, - appID?: string, + dev_eui?: string, + app_id?: string, }; - channelID: string; + channel_id: string; } export interface LoraDevice { @@ -12,6 +12,12 @@ export interface LoraDevice { id?: string; key?: string; metadata?: LoraMetadata; - devEUI?: string; - appID?: string; +} + +export interface LoraTableRow { + name: string; + id: string; + devEUI: string; + appID: string; + metadata: LoraMetadata; } diff --git a/src/app/common/interfaces/mainflux.interface.ts b/src/app/common/interfaces/mainflux.interface.ts index 46897f97..01693ec3 100644 --- a/src/app/common/interfaces/mainflux.interface.ts +++ b/src/app/common/interfaces/mainflux.interface.ts @@ -9,6 +9,7 @@ export interface Channel { id?: string; name?: string; metadata?: any; + type?: string; } export interface Thing { @@ -16,6 +17,7 @@ export interface Thing { key?: string; name?: string; metadata?: any; + type?: string; } export interface Attribute { @@ -29,6 +31,7 @@ export interface Definition { id: string; created: Date; attributes: Attribute[]; + delta: number; } export interface Twin { @@ -37,6 +40,7 @@ export interface Twin { id?: string; revision?: number; created?: Date; + updated?: Date; definitions?: Definition[]; definition?: any; // for request metadata?: any; diff --git a/src/app/common/interfaces/opcua.interface.ts b/src/app/common/interfaces/opcua.interface.ts index e6923f2b..7385e60c 100644 --- a/src/app/common/interfaces/opcua.interface.ts +++ b/src/app/common/interfaces/opcua.interface.ts @@ -1,10 +1,10 @@ export interface OpcuaMetadata { type?: string; opcua: { - serverURI?: string, - nodeID?: string, + server_uri?: string, + node_id?: string, }; - channelID?: string; + channel_id?: string; } export interface OpcuaNode { @@ -13,3 +13,11 @@ export interface OpcuaNode { key?: string; metadata?: OpcuaMetadata; } + +export interface OpcuaTableRow { + name: string; + id: string; + serverURI: string; + nodeID: string; + metadata: OpcuaMetadata; +} diff --git a/src/app/common/services/bootstrap/bootstrap.service.ts b/src/app/common/services/bootstrap/bootstrap.service.ts index a0fc609a..83fa67a6 100644 --- a/src/app/common/services/bootstrap/bootstrap.service.ts +++ b/src/app/common/services/bootstrap/bootstrap.service.ts @@ -1,9 +1,5 @@ import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import 'rxjs/add/operator/catch'; -import 'rxjs/add/observable/throw'; -import 'rxjs/add/operator/switchMap'; -import 'rxjs/add/operator/map'; import { environment } from 'environments/environment'; import { Gateway } from 'app/common/interfaces/gateway.interface'; @@ -14,44 +10,18 @@ import { ThingsService } from 'app/common/services/things/things.service'; @Injectable() export class BootstrapService { content: ConfigContent = { - 'log_level': 'debug', - 'http_port': '9000', - 'mqtt_url': 'localhost:1883', - 'edgex_url': 'http://localhost:48090/api/v1/', - 'nats_url': 'localhost:4222', - 'export_config': { - 'exp': { - 'log_level': 'debug', - 'nats': 'nats://localhost:4222', - 'port': '8170', + log_level: 'debug', + http_port: '9000', + mqtt_url: 'localhost:1883', + edgex_url: 'http://localhost:48090/api/v1/', + nats_url: 'localhost:4222', + export_config: { + file: `${environment.exportConfigFile}`, + exp: { + port: '8170', }, - 'mqtt': { - 'ca_path': 'ca.crt', - 'cert_path': 'thing.crt', - 'channel': '', - 'host': 'tcp://localhost:1883', - 'mtls': false, - 'password': '', - 'priv_key_path': 'thing.key', - 'qos': 0, - 'retain': false, - 'skip_tls_ver': false, - 'username': '', - }, - 'routes': [ - { - 'mqtt_topic': '', - 'nats_topic': 'adc.samples', - 'subtopic': '', - 'type': 'plain', - }, - { - 'mqtt_topic': '', - 'nats_topic': 'telegraf', - 'subtopic': '', - 'type': 'plain', - }, - ], + mqtt: {}, + routes: [{}, {}], }, }; @@ -63,18 +33,20 @@ export class BootstrapService { addConfig(gw: Gateway) { // Boostrap - this.content.export_config.mqtt.channel = gw.metadata.exportChannelID; + this.content.export_config.mqtt.channel = gw.metadata.export_channel_id; this.content.export_config.mqtt.username = gw.id; - this.content.export_config.routes[0].mqtt_topic = `channels/${gw.metadata.exportChannelID}/messages`; - this.content.export_config.routes[1].mqtt_topic = `channels/${gw.metadata.exportChannelID}/messages`; + this.content.export_config.routes[0].mqtt_topic = `channels/${gw.metadata.export_channel_id}/messages`; + this.content.export_config.routes[1].mqtt_topic = `channels/${gw.metadata.export_channel_id}/messages`; this.content.export_config.mqtt.password = gw.key; + this.content.export_config.exp.nats = this.content.nats_url; + const config: Config = { thing_id: gw.id, thing_key: gw.key, - channels: [gw.metadata.ctrlChannelID, gw.metadata.dataChannelID], - external_id: gw.metadata.mac, - external_key: gw.metadata.gwPassword, + channels: [gw.metadata.ctrl_channel_id, gw.metadata.data_channel_id], + external_id: gw.metadata.external_id, + external_key: gw.metadata.external_key, content: JSON.stringify(this.content), state: 0, }; @@ -82,8 +54,8 @@ export class BootstrapService { return this.http.post(environment.bootstrapConfigsUrl, config, { observe: 'response' }) .map( resp => { - const cfgID: string = resp.headers.get('location').replace('/things/configs/', ''); - gw.metadata.cfgID = cfgID; + const cfg_id: string = resp.headers.get('location').replace('/things/configs/', ''); + gw.metadata.cfg_id = cfg_id; this.thingsService.editThing(gw).subscribe( respEdit => { this.notificationsService.success('Gateway successfully bootstrapped', ''); @@ -105,10 +77,10 @@ export class BootstrapService { getConfig(gateway: Gateway) { const headers = new HttpHeaders({ - 'Authorization': gateway.metadata.gwPassword, + 'Authorization': gateway.metadata.external_key, }); - return this.http.get(`${environment.bootstrapUrl}/${gateway.metadata.mac}`, { headers: headers }); + return this.http.get(`${environment.bootstrapUrl}/${gateway.metadata.external_id}`, { headers: headers }); } updateConfig(configUpdate: ConfigUpdate, gateway: Gateway) { diff --git a/src/app/common/services/channels/channels.service.ts b/src/app/common/services/channels/channels.service.ts index 149e2cfb..de18d200 100644 --- a/src/app/common/services/channels/channels.service.ts +++ b/src/app/common/services/channels/channels.service.ts @@ -185,17 +185,41 @@ export class ChannelsService { connectedThings(chanID: string) { return this.http.get(`${environment.channelsUrl}/${chanID}/things`) - .map( - resp => { - return resp; - }, - ) - .catch( - err => { - this.notificationsService.error('Failed to fetch connected Things to the Channel', - `Error: ${err.status} - ${err.statusText}`); - return Observable.throw(err); + .map( + resp => { + return resp; }, - ); + ) + .catch( + err => { + this.notificationsService.error('Failed to fetch connected Things to the Channel', + `Error: ${err.status} - ${err.statusText}`); + return Observable.throw(err); + }, + ); + } + + disconnectedThings(chanID: string, offset?: number, limit?: number) { + offset = offset || 0; + limit = limit || defLimit; + + const params = new HttpParams() + .set('offset', offset.toString()) + .set('limit', limit.toString()) + .set('connected', 'false'); + + return this.http.get(`${environment.channelsUrl}/${chanID}/things`, { params }) + .map( + resp => { + return resp; + }, + ) + .catch( + err => { + this.notificationsService.error('Failed to fetch not connected Things to the Channel', + `Error: ${err.status} - ${err.statusText}`); + return Observable.throw(err); + }, + ); } } diff --git a/src/app/common/services/fs/fs.service.ts b/src/app/common/services/fs/fs.service.ts new file mode 100644 index 00000000..e62b7e27 --- /dev/null +++ b/src/app/common/services/fs/fs.service.ts @@ -0,0 +1,49 @@ +import { Injectable } from '@angular/core'; + +@Injectable() +export class FsService { + + constructor( + ) { } + + exportToCsv(filename: string, rows: any) { + if (!rows || !rows.length) { + return; + } + const separator = ','; + const keys = Object.keys(rows[0]); + const csvContent = + keys.join(separator) + + '\n' + + rows.map(row => { + return keys.map(k => { + let cell = row[k] === undefined ? '' : row[k]; + if (k === 'metadata') { + try { + cell = JSON.stringify(row[k]); + cell = cell.replace(/[\,]/g, '|'); + } catch (e) { + } + } + return cell; + }).join(separator); + }).join('\n'); + + const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }); + if (navigator.msSaveBlob) { // IE 10+ + navigator.msSaveBlob(blob, filename); + } else { + const link = document.createElement('a'); + if (link.download !== undefined) { + // Browsers that support HTML5 download attribute + const url = URL.createObjectURL(blob); + link.setAttribute('href', url); + link.setAttribute('download', filename); + link.style.visibility = 'hidden'; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + } + } + } +} diff --git a/src/app/common/services/gateways/gateways.service.ts b/src/app/common/services/gateways/gateways.service.ts index d74d763e..8978cfbf 100644 --- a/src/app/common/services/gateways/gateways.service.ts +++ b/src/app/common/services/gateways/gateways.service.ts @@ -28,11 +28,11 @@ export class GatewaysService { private notificationsService: NotificationsService, ) { } - getGateways(offset?: number, limit?: number) { + getGateways(offset?: number, limit?: number, name?: string) { offset = offset || 0; limit = limit || defLimit; - return this.thingsService.getThings(offset, limit, this.typeGateway); + return this.thingsService.getThings(offset, limit, this.typeGateway, '', name); } getCtrlChannels(offset: number, limit: number) { @@ -43,12 +43,12 @@ export class GatewaysService { return this.channelsService.getChannels(offset, limit, this.typeDataChan); } - addGateway(name: string, mac: string) { + addGateway(name: string, externalID: string) { const gateway: Gateway = { name: name, metadata: { type: this.typeGateway, - mac: mac, + external_id: externalID, }, }; @@ -94,10 +94,10 @@ export class GatewaysService { respConnectData => { this.channelsService.connectThing(exportChanID, gwID).subscribe( respConnectExport => { - gateway.metadata.ctrlChannelID = ctrlChanID; - gateway.metadata.dataChannelID = dataChanID; - gateway.metadata.exportChannelID = exportChanID; - gateway.metadata.gwPassword = uuid(); + gateway.metadata.ctrl_channel_id = ctrlChanID; + gateway.metadata.data_channel_id = dataChanID; + gateway.metadata.export_channel_id = exportChanID; + gateway.metadata.external_key = uuid(); gateway.id = gwID; this.thingsService.editThing(gateway).subscribe( @@ -147,8 +147,8 @@ export class GatewaysService { ); } - editGateway(name: string, mac: string, gateway: Gateway) { - gateway.metadata.mac = mac; + editGateway(name: string, externalID: string, gateway: Gateway) { + gateway.metadata.external_id = externalID; return this.thingsService.editThing(gateway).map( resp => { @@ -163,9 +163,9 @@ export class GatewaysService { resp => { this.notificationsService.success('Gateway successfully deleted', ''); - this.channelsService.deleteChannel(gw.metadata.ctrlChannelID).subscribe(); - this.channelsService.deleteChannel(gw.metadata.dataChannelID).subscribe(); - this.channelsService.deleteChannel(gw.metadata.exportChannelID).subscribe(); + this.channelsService.deleteChannel(gw.metadata.ctrl_channel_id).subscribe(); + this.channelsService.deleteChannel(gw.metadata.data_channel_id).subscribe(); + this.channelsService.deleteChannel(gw.metadata.export_channel_id).subscribe(); }, ); } @@ -179,7 +179,7 @@ export class GatewaysService { (resp: any) => { const gw: Gateway = resp; // TODO: remove this mocks - const topic = 'channels/' + gw.metadata.dataChannelID + '/messages'; + const topic = 'channels/' + gw.metadata.data_channel_id + '/messages'; const lon = 48 + 2 * Math.random(); const lat = 20 + 2 * Math.random(); const cmd = `[{"bn":"location-", "n":"lon", "v":${lon}}, {"n":"lat", "v":${lat}}]`; diff --git a/src/app/common/services/lora/lora.service.ts b/src/app/common/services/lora/lora.service.ts index f77c3bc6..2eb2e7e6 100644 --- a/src/app/common/services/lora/lora.service.ts +++ b/src/app/common/services/lora/lora.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; -import { LoraDevice } from 'app/common/interfaces/lora.interface'; +import { LoraDevice, LoraTableRow } from 'app/common/interfaces/lora.interface'; import { ThingsService } from 'app/common/services/things/things.service'; import { ChannelsService } from 'app/common/services/channels/channels.service'; import { MessagesService } from 'app/common/services/messages/messages.service'; @@ -35,63 +35,81 @@ export class LoraService { return this.channelsService.getChannels(offset, limit, this.typeLora); } - addDevice(row: LoraDevice) { - const chanReq = { - name: `${this.typeLoraApp}-${row.appID}`, + addDevice(row: LoraTableRow) { + // Check if a channel exist for row appID + return this.channelsService.getChannels(0, 1, 'lora', `{"app_id": "${row.appID}"}`).map( + (resp: any) => { + if (resp.total === 0) { + const chanReq = { + name: `${this.typeLoraApp}-${row.appID}`, + metadata: { + type: this.typeLora, + lora: { + app_id: row.appID, + }, + }, + }; + + this.channelsService.addChannel(chanReq).subscribe( + respChan => { + const chanID = respChan.headers.get('location').replace('/channels/', ''); + this.addAndConnect(chanID, row); + }, + ); + } else { + const chanID = resp.channels[0].id; + this.addAndConnect(chanID, row); + } + }, + ); + } + + addAndConnect(chanID: string, row: LoraTableRow) { + const devReq: LoraDevice = { + name: row.name, metadata: { type: this.typeLora, + channel_id: chanID, lora: { - appID: row.appID, + dev_eui: row.devEUI, + app_id: row.appID, }, }, }; - // TODO - Check if channel exist - return this.channelsService.addChannel(chanReq).map( - respChan => { - const chanID = respChan.headers.get('location').replace('/channels/', ''); - const devReq: LoraDevice = { - name: row.name, - metadata: { - type: this.typeLora, - channelID: chanID, - lora: { - devEUI: row.devEUI, - appID: row.appID, - }, - }, - }; + this.thingsService.addThing(devReq).subscribe( + respThing => { + const thingID = respThing.headers.get('location').replace('/things/', ''); - this.thingsService.addThing(devReq).subscribe( - respThing => { - const thingID = respThing.headers.get('location').replace('/things/', ''); + this.channelsService.connectThing(chanID, thingID).subscribe( + respCon => { + this.notificationsService.success('LoRa Device successfully created', ''); - this.channelsService.connectThing(chanID, thingID).subscribe( - respCon => { - this.notificationsService.success('LoRa Device successfully created', ''); - - // Send temperature and humidity messages - this.messagesService.sendTempMock(chanID, thingID); - }, - err => { - this.thingsService.deleteThing(thingID).subscribe(); - this.channelsService.deleteChannel(chanID).subscribe(); - }, - ); + // Send temperature and humidity messages + this.messagesService.sendTempMock(chanID, thingID); }, err => { + this.thingsService.deleteThing(thingID).subscribe(); this.channelsService.deleteChannel(chanID).subscribe(); }, ); }, + err => { + this.channelsService.deleteChannel(chanID).subscribe(); + }, ); } - editDevice(row: LoraDevice) { - row.name = row.name, - row.metadata.lora = { - devEUI: row.devEUI, - appID: row.appID, + editDevice(row: LoraTableRow) { + const devReq: LoraDevice = { + id: row.id, + name: row.name, + metadata: row.metadata, + }; + + devReq.metadata.lora = { + dev_eui: row.devEUI, + app_id: row.appID, }; return this.thingsService.editThing(row).map( @@ -101,8 +119,8 @@ export class LoraService { ); } - deleteDevice(loraDev: LoraDevice) { - const channelID = loraDev.metadata.channelID; + deleteDevice(loraDev: LoraTableRow) { + const channelID = loraDev.metadata.channel_id; return this.channelsService.deleteChannel(channelID).map( () => { this.thingsService.deleteThing(loraDev.id).subscribe( diff --git a/src/app/common/services/messages/messages.service.ts b/src/app/common/services/messages/messages.service.ts index e5b98784..76e82b3d 100644 --- a/src/app/common/services/messages/messages.service.ts +++ b/src/app/common/services/messages/messages.service.ts @@ -6,7 +6,7 @@ import { environment } from 'environments/environment'; import { ThingsService } from 'app/common/services/things/things.service'; import { NotificationsService } from 'app/common/services/notifications/notifications.service'; -const defLimit: number = 500; +const defLimit: number = 100; @Injectable() export class MessagesService { diff --git a/src/app/common/services/mqtt/mqtt.manager.service.ts b/src/app/common/services/mqtt/mqtt.manager.service.ts index bbe02570..22a40b6e 100644 --- a/src/app/common/services/mqtt/mqtt.manager.service.ts +++ b/src/app/common/services/mqtt/mqtt.manager.service.ts @@ -65,7 +65,7 @@ export class MqttManagerService { } createTopic(channel: string) { - return `channels/${channel}/messages/`; + return `channels/${channel}/messages`; } createPayload(baseName: string, name: string, valueString: string) { @@ -73,13 +73,19 @@ export class MqttManagerService { } publish(channel: string, bn: string, n: string, vs: string) { - const topic = this.createTopic(channel) + 'req'; + const topic = `${this.createTopic(channel)}/req`; + const payload = this.createPayload(bn, n, vs); + this.mqttService.publish(topic, payload).subscribe(); + } + + publishToService(channel: string, svc: string, bn: string, n: string, vs: string) { + const topic = `${this.createTopic(channel)}/services/${svc}`; const payload = this.createPayload(bn, n, vs); this.mqttService.publish(topic, payload).subscribe(); } subscribe(channel: string) { - const topic = this.createTopic(channel) + 'res'; + const topic = `${this.createTopic(channel)}/res`; const topicSub = this.mqttService.observe(topic).subscribe( (message: IMqttMessage) => { const pl = message.payload.toString(); diff --git a/src/app/common/services/opcua/opcua.service.ts b/src/app/common/services/opcua/opcua.service.ts index 7d849a53..5e0763f8 100644 --- a/src/app/common/services/opcua/opcua.service.ts +++ b/src/app/common/services/opcua/opcua.service.ts @@ -3,7 +3,7 @@ import { HttpClient, HttpParams } from '@angular/common/http'; import { Observable } from 'rxjs'; import { environment } from 'environments/environment'; -import { OpcuaNode } from 'app/common/interfaces/opcua.interface'; +import { OpcuaNode, OpcuaTableRow } from 'app/common/interfaces/opcua.interface'; import { ThingsService } from 'app/common/services/things/things.service'; import { ChannelsService } from 'app/common/services/channels/channels.service'; import { NotificationsService } from 'app/common/services/notifications/notifications.service'; @@ -36,7 +36,7 @@ export class OpcuaService { addNodes(serverURI: string, nodes: any) { // Check if a channel exist for serverURI - return this.channelsService.getChannels(0, 1, 'opcua', `{"serverURI": "${serverURI}"}`).map( + return this.channelsService.getChannels(0, 1, 'opcua', `{"server_uri": "${serverURI}"}`).map( (resp: any) => { if (resp.total === 0) { const chanReq = { @@ -44,7 +44,7 @@ export class OpcuaService { metadata: { type: this.typeOpcua, opcua: { - serverURI: serverURI, + server_uri: serverURI, }, }, }; @@ -67,14 +67,14 @@ export class OpcuaService { const nodesReq: OpcuaNode[] = []; nodes.forEach(node => { const nodeReq: OpcuaNode = { - name: `${this.typeOpcuaNode}-${node.name}`, + name: node.name, metadata: { type: this.typeOpcua, opcua: { - nodeID: node.nodeID, - serverURI: node.serverURI, + node_id: node.nodeID, + server_uri: node.serverURI, }, - channelID: chanID, + channel_id: chanID, }, }; nodesReq.push(nodeReq); @@ -98,16 +98,17 @@ export class OpcuaService { ); } - editNode(node: any) { + editNode(node: OpcuaTableRow) { const nodeReq: OpcuaNode = { id: node.id, name: node.name, metadata: { type: this.typeOpcua, opcua: { - serverURI: node.serverURI, - nodeID: node.nodeID, + server_uri: node.serverURI, + node_id: node.nodeID, }, + channel_id: node.metadata.channel_id, }, }; @@ -121,11 +122,11 @@ export class OpcuaService { deleteNode(node: any) { return this.thingsService.deleteThing(node.id).map( respThing => { - const serverURI = node.metadata.opcua.serverURI; - this.thingsService.getThings(0, 1, 'opcua', `{"serverURI": "${serverURI}"}`).subscribe( + const serverURI = node.metadata.opcua.server_uri; + this.thingsService.getThings(0, 1, 'opcua', `{"server_uri": "${serverURI}"}`).subscribe( (respChan: any) => { if (respChan.total === 0) { - const channelID = node.metadata.channelID; + const channelID = node.metadata.channel_id; this.channelsService.deleteChannel(channelID).subscribe(); } }, diff --git a/src/app/common/services/things/things.service.ts b/src/app/common/services/things/things.service.ts index c94bff35..deca32c3 100644 --- a/src/app/common/services/things/things.service.ts +++ b/src/app/common/services/things/things.service.ts @@ -133,17 +133,41 @@ export class ThingsService { connectedChannels(thingID: string) { return this.http.get(`${environment.thingsUrl}/${thingID}/channels/`) - .map( - resp => { - return resp; - }, - ) - .catch( - err => { - this.notificationsService.error('Failed to fetch connected Chanels to the Thing', - `Error: ${err.status} - ${err.statusText}`); - return Observable.throw(err); - }, - ); + .map( + resp => { + return resp; + }, + ) + .catch( + err => { + this.notificationsService.error('Failed to fetch connected Chanels to the Thing', + `Error: ${err.status} - ${err.statusText}`); + return Observable.throw(err); + }, + ); + } + + disconnectedChannels(thingID: string, offset?: number, limit?: number) { + offset = offset || 0; + limit = limit || defLimit; + + const params = new HttpParams() + .set('offset', offset.toString()) + .set('limit', limit.toString()) + .set('connected', 'false'); + + return this.http.get(`${environment.thingsUrl}/${thingID}/channels`, { params }) + .map( + resp => { + return resp; + }, + ) + .catch( + err => { + this.notificationsService.error('Failed to fetch not connected Channels to the Thing', + `Error: ${err.status} - ${err.statusText}`); + return Observable.throw(err); + }, + ); } } diff --git a/src/app/common/services/users/users.service.ts b/src/app/common/services/users/users.service.ts index 36fc649d..ef575f65 100644 --- a/src/app/common/services/users/users.service.ts +++ b/src/app/common/services/users/users.service.ts @@ -1,6 +1,8 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; +import 'rxjs/add/observable/empty'; +import { Router } from '@angular/router'; import { environment } from 'environments/environment'; import { User } from 'app/common/interfaces/mainflux.interface'; @@ -12,6 +14,7 @@ export class UsersService { constructor( private http: HttpClient, + private router: Router, private notificationsService: NotificationsService, ) { } @@ -24,9 +27,8 @@ export class UsersService { ) .catch( err => { - this.notificationsService.error('Failed to fetch User', - `Error: ${err.status} - ${err.statusText}`); - return Observable.throw(err); + this.router.navigateByUrl('/auth/login'); + return Observable.empty(); }, ); } diff --git a/src/app/common/store/gateways.store.ts b/src/app/common/store/gateways.store.ts index b3594048..538a4131 100644 --- a/src/app/common/store/gateways.store.ts +++ b/src/app/common/store/gateways.store.ts @@ -30,9 +30,9 @@ export class GatewaysStore { } @action - addGateway(name: string, mac: string) { + addGateway(name: string, externalID: string) { this.uiState.loading = true; - this.gatewaysService.addGateway(name, mac) + this.gatewaysService.addGateway(name, externalID) .subscribe(() => { this.uiState.loading = false; this.getGateways(this.offset, this.limit); @@ -43,9 +43,9 @@ export class GatewaysStore { } @action - editGateway(name: string, mac: string, gw: Gateway) { + editGateway(name: string, externalID: string, gw: Gateway) { this.uiState.loading = true; - this.gatewaysService.editGateway(name, mac, gw) + this.gatewaysService.editGateway(name, externalID, gw) .subscribe(() => { this.uiState.loading = false; this.getGateways(this.offset, this.limit); diff --git a/src/app/common/store/opcua.store.ts b/src/app/common/store/opcua.store.ts new file mode 100644 index 00000000..82d91e9e --- /dev/null +++ b/src/app/common/store/opcua.store.ts @@ -0,0 +1,21 @@ +import { Injectable } from '@angular/core'; + +@Injectable() +export class OpcuaStore { + browsedNodes = { + uri: '', + nodes: [], + }; + + constructor( + ) { } + + setBrowsedNodes(browse: any) { + this.browsedNodes.uri = browse.uri; + this.browsedNodes.nodes = browse.nodes; + } + + getBrowsedNodes() { + return this.browsedNodes; + } +} diff --git a/src/app/pages/admin/admin.module.ts b/src/app/pages/admin/admin.module.ts index 4afd794d..4b094c23 100644 --- a/src/app/pages/admin/admin.module.ts +++ b/src/app/pages/admin/admin.module.ts @@ -8,6 +8,7 @@ import { NbSelectModule, NbInputModule, NbCheckboxModule, + NbListModule, } from '@nebular/theme'; @@ -20,7 +21,6 @@ import { TwinsDetailsComponent } from './twins/details/twins.details.component'; import { TwinsStatesComponent } from './twins/states/twins.states.component'; import { TwinsPayloadComponent } from './twins/states/payload/twins.payload.component'; import { TwinsDefinitionsComponent } from './twins/definitions/twins.definitions.component'; -import { TwinsAttributesComponent } from './twins/definitions/attributes/twins.attributes.component'; import { DetailsComponent } from 'app/shared/details/details.component'; import { GrafanaComponent } from './grafana/grafana.component'; import { LoraServerComponent } from './loraserver/loraserver.component'; @@ -40,6 +40,7 @@ import { ConfirmationComponent } from 'app/shared/confirmation/confirmation.comp NbSelectModule, NbCheckboxModule, PagesModule, + NbListModule, ], declarations: [ TracingComponent, @@ -48,7 +49,6 @@ import { ConfirmationComponent } from 'app/shared/confirmation/confirmation.comp TwinsStatesComponent, TwinsPayloadComponent, TwinsDefinitionsComponent, - TwinsAttributesComponent, GrafanaComponent, LoraServerComponent, ], @@ -56,7 +56,6 @@ import { ConfirmationComponent } from 'app/shared/confirmation/confirmation.comp ConfirmationComponent, DetailsComponent, TwinsPayloadComponent, - TwinsAttributesComponent, ], }) export class AdminModule { } diff --git a/src/app/pages/admin/twins/definitions/attributes/twins.attributes.component.html b/src/app/pages/admin/twins/definitions/attributes/twins.attributes.component.html deleted file mode 100644 index 0ba3d4e7..00000000 --- a/src/app/pages/admin/twins/definitions/attributes/twins.attributes.component.html +++ /dev/null @@ -1,14 +0,0 @@ -
- {{value.created | date: 'long'}} -
- {{attr.name}} -
    -
  • Channel - {{ attr.channel }}
  • -
  • Subtopic - {{ attr.subtopic }}
  • -
  • Persisted - {{ attr.persist_state }}
  • -
-
-
diff --git a/src/app/pages/admin/twins/definitions/attributes/twins.attributes.component.scss b/src/app/pages/admin/twins/definitions/attributes/twins.attributes.component.scss deleted file mode 100644 index d7a0355f..00000000 --- a/src/app/pages/admin/twins/definitions/attributes/twins.attributes.component.scss +++ /dev/null @@ -1,3 +0,0 @@ -.col { - text-align: right; -} diff --git a/src/app/pages/admin/twins/definitions/attributes/twins.attributes.component.ts b/src/app/pages/admin/twins/definitions/attributes/twins.attributes.component.ts deleted file mode 100644 index 515e24da..00000000 --- a/src/app/pages/admin/twins/definitions/attributes/twins.attributes.component.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Component } from '@angular/core'; - -@Component({ - selector: 'ngx-twins-attributes-component', - templateUrl: './twins.attributes.component.html', - styleUrls: ['./twins.attributes.component.scss'], -}) -export class TwinsAttributesComponent { - // Depends on valuePrepareFunction - value: any; - - constructor( - ) { } -} diff --git a/src/app/pages/admin/twins/definitions/twins.definitions.component.html b/src/app/pages/admin/twins/definitions/twins.definitions.component.html index 3201f18b..2679361d 100644 --- a/src/app/pages/admin/twins/definitions/twins.definitions.component.html +++ b/src/app/pages/admin/twins/definitions/twins.definitions.component.html @@ -1,20 +1,52 @@ -
-
- - -
-
{{ definitions.length }} {{ twin.name }} definitions
-
-
- - - - - + + + {{ definitions.length }} {{ twin.name }} definitions + - - - -
-
+ + + +
+ Created +
+
+
+
+ Name +
+
+ Channels +
+
+ Subtopic +
+
+ Persisted +
+
+
+
+ +
+ {{def.created | date: 'short'}} +
+
+
+
+ {{ attr.name }} +
+
+ {{ attr.channel }} +
+
+ {{ attr.subtopic }} +
+
+ {{ attr.persist_state }} +
+
+
+
+
+
+ diff --git a/src/app/pages/admin/twins/definitions/twins.definitions.component.scss b/src/app/pages/admin/twins/definitions/twins.definitions.component.scss index feecb8c2..e8aca93a 100644 --- a/src/app/pages/admin/twins/definitions/twins.definitions.component.scss +++ b/src/app/pages/admin/twins/definitions/twins.definitions.component.scss @@ -1,3 +1,3 @@ -.col-10 { - text-align: right; +nb-list-item { + word-wrap: break-word; } diff --git a/src/app/pages/admin/twins/definitions/twins.definitions.component.ts b/src/app/pages/admin/twins/definitions/twins.definitions.component.ts index 75407a22..ca08a04e 100644 --- a/src/app/pages/admin/twins/definitions/twins.definitions.component.ts +++ b/src/app/pages/admin/twins/definitions/twins.definitions.component.ts @@ -2,8 +2,6 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { TwinsService } from 'app/common/services/twins/twins.service'; -import { LocalDataSource } from 'ng2-smart-table'; -import { TwinsAttributesComponent } from './attributes/twins.attributes.component'; import { Twin, Definition } from 'app/common/interfaces/mainflux.interface'; @Component({ @@ -12,37 +10,8 @@ import { Twin, Definition } from 'app/common/interfaces/mainflux.interface'; styleUrls: ['./twins.definitions.component.scss'], }) export class TwinsDefinitionsComponent implements OnInit { - settings = { - actions: { - add: false, - edit: false, - delete: false, - }, - columns: { - id: { - title: 'ID', - width: '3%', - filter: false, - }, - attributes: { - title: 'Attributes', - type: 'custom', - renderComponent: TwinsAttributesComponent, - valuePrepareFunction: (cell, row) => { - return row; - }, - filter: false, - }, - }, - pager: { - display: true, - perPage: 10, - }, - }; - twin: Twin = {}; definitions: Definition[] = []; - source: LocalDataSource = new LocalDataSource(); constructor( private route: ActivatedRoute, @@ -59,7 +28,6 @@ export class TwinsDefinitionsComponent implements OnInit { (resp: Twin) => { this.twin = resp; this.definitions = this.twin.definitions; - this.source.load(this.definitions); }, ); } diff --git a/src/app/pages/admin/twins/details/twins.details.component.html b/src/app/pages/admin/twins/details/twins.details.component.html index 788e2fa1..c3082a37 100644 --- a/src/app/pages/admin/twins/details/twins.details.component.html +++ b/src/app/pages/admin/twins/details/twins.details.component.html @@ -1,139 +1,223 @@ -
-
- - - Info - - -
-
Name:
{{twin.name}}
-
+ + + Twin Info + + +
+
+ Name: +
+
+ {{ twin.name }} +
+
+ Owner: +
+
+ {{ twin.owner }} +
+
-
-
Owner:
{{ twin.owner }}
-
+
+
+ ID: +
+
+ {{ twin.id }} +
+
-
-
ID:
{{ twin.id }}
-
+
+
+ Created: +
+
+ {{ twin.created }} +
+
+ Updated: +
+
+ {{ twin.updated }} +
+
-
-
Created:
{{ twin.created }}
-
+
+
+ Revision: +
+
+ {{ twin.revision }} +
+
+
+
-
-
Revision:
{{ twin.revision }}
-
-
- -
-
-
- -
-
- - - State {{ stateTime | date: 'long' }} - - - -
-
{{attr.key}}
-
{{attr.value.value}}
-
{{attr.value.time | date: 'long'}}
-
-
-
-
-
+ + + State {{ stateTime | date: 'long' }} + + + +
+
{{attr.key}}
+
{{attr.value.value}}
+
{{attr.value.time | date: 'long'}}
+
+
+
-
-
- - + + +
+
Definition {{def.created | date: 'long'}} -
+
+ delta (ms): {{this.defDelta / 1e6}} +
+
+ - - -
- {{attr.name}} -
    -
  • Channel - {{ defChans[attr.channel] }} - {{ attr.channel }}
  • -
  • Subtopic - {{ attr.subtopic }}
  • -
  • Persisted - {{ attr.persist_state }}
  • -
-
-
- -
-
+
+
+ + + + +
+ Name +
+
+ Channel +
+
+ Subtopic +
+
+ Persisted +
+
+
+
+ +
+ {{ attr.name }} +
+
+ {{ attr.channel }} - {{ defChans[attr.channel] }} +
+
+ {{ attr.subtopic }} +
+
+ {{ attr.persist_state }} +
+
+
+
+
+
+ -
-
- - + + +
+
Add definition -
+ +
+ delta (ms): +
+ +
+ - - - -
-
- {{ attr.name }} -
    -
  • Channel - {{ attr.channel }}
  • -
  • Subtopic - {{ attr.subtopic }}
  • -
  • Persisted - {{ attr.persist_state }}
  • -
-
-
- -
-
-
- - -
-
Name:
- -
+
+
+ -
Channel:
- - - {{channel.name}} - - -
+ + + +
+ Name +
+
+ Channel +
+
+ Subtopic +
+
+ Persisted +
+
+
+
+ +
+ {{ attr.name }} +
+
+ {{ attr.channel }} +
+
+ {{ attr.subtopic }} +
+
+ {{ attr.persist_state }} +
+
+ +
+
+
+
-
Subtopic:
- -
+ +
+
+ Name: +
+
+ +
-
Persisted:
- -
-
+
+ Channel: +
+
+ + + {{channel.name}} + + +
+
- - +
+
+ Subtopic: +
+
+ +
+
+ Persisted: +
+
+ +
+
-
-
-
+ + + diff --git a/src/app/pages/admin/twins/details/twins.details.component.scss b/src/app/pages/admin/twins/details/twins.details.component.scss index 57273207..19b4b54c 100644 --- a/src/app/pages/admin/twins/details/twins.details.component.scss +++ b/src/app/pages/admin/twins/details/twins.details.component.scss @@ -2,8 +2,8 @@ nb-icon { vertical-align: middle; } -.col-2 { - font-weight: bold; +.row { + margin-bottom: 10px; } .pickable:hover { @@ -11,7 +11,7 @@ nb-icon { } nb-select { - min-width: 300px; + min-width: 400px; } button { @@ -19,7 +19,6 @@ button { margin-left: 10px; } -textarea { - width: 100%; - height: 100%; +nb-list-item { + word-wrap: break-word; } diff --git a/src/app/pages/admin/twins/details/twins.details.component.ts b/src/app/pages/admin/twins/details/twins.details.component.ts index 73b46b01..0453cb8d 100644 --- a/src/app/pages/admin/twins/details/twins.details.component.ts +++ b/src/app/pages/admin/twins/details/twins.details.component.ts @@ -6,6 +6,7 @@ import { TwinsService } from 'app/common/services/twins/twins.service'; import { NotificationsService } from 'app/common/services/notifications/notifications.service'; import { MessagesService } from 'app/common/services/messages/messages.service'; import { Thing, Channel, Attribute, Definition, Twin } from 'app/common/interfaces/mainflux.interface'; +import { isNumber } from 'util'; const stateInterval: number = 5 * 1000; @@ -22,6 +23,7 @@ export class TwinsDetailsComponent implements OnInit, OnDestroy { def: Definition; defChans: {}; defAttrs: Attribute[] = []; + defDelta: number = 1e6; twinName: string; channels: Channel[] = []; @@ -64,6 +66,8 @@ export class TwinsDetailsComponent implements OnInit, OnDestroy { this.twin = resp; this.def = this.twin.definitions[this.twin.definitions.length - 1]; + + this.defDelta = this.def.delta; this.defAttrs = this.def.attributes; this.defAttrs.forEach(attr => { this.channelsService.getChannel(attr.channel).subscribe( @@ -114,13 +118,27 @@ export class TwinsDetailsComponent implements OnInit, OnDestroy { if (!msgs.messages) { return; } + + const value = this.findValue(msgs.messages[0]); + if (!value) return; + this.state[name] = this.state[name] || {}; - this.state[name].value = msgs.messages[0] && msgs.messages[0].value; + this.state[name].value = value; this.state[name].time = msgs.messages[0].time * 1000; }, ); } + findValue(message: any): any { + let value: any; + if (message.value) value = message.value; + if (message.string_value) value = message.string_value; + if (message.date_value) value = message.date_value; + if (message.bool_value) value = message.bool_value; + if (message.sum) value = message.sum; + return value; + } + showStates() { const route = this.router.routerState.snapshot.url.replace('details', 'states'); this.router.navigate([route]); @@ -170,6 +188,7 @@ export class TwinsDetailsComponent implements OnInit, OnDestroy { id: this.twin.id, definition: { attributes: this.editAttrs, + delta: this.defDelta, }, }; this.twinsService.editTwin(twin).subscribe( @@ -179,6 +198,11 @@ export class TwinsDetailsComponent implements OnInit, OnDestroy { ); } + delta($event) { + const val = +$event.srcElement.value; + this.defDelta = isNumber(val) ? val * 1e6 : this.defDelta; + } + ngOnDestroy() { window.clearInterval(this.stateIntervalID); } diff --git a/src/app/pages/admin/twins/states/payload/twins.payload.component.html b/src/app/pages/admin/twins/states/payload/twins.payload.component.html index d105310c..980095cb 100644 --- a/src/app/pages/admin/twins/states/payload/twins.payload.component.html +++ b/src/app/pages/admin/twins/states/payload/twins.payload.component.html @@ -1,6 +1,6 @@
-
{{v.key}}
+
{{v.key}}
{{v.value}}
diff --git a/src/app/pages/admin/twins/states/twins.states.component.html b/src/app/pages/admin/twins/states/twins.states.component.html index f49cb03c..f69c748f 100644 --- a/src/app/pages/admin/twins/states/twins.states.component.html +++ b/src/app/pages/admin/twins/states/twins.states.component.html @@ -3,8 +3,8 @@
-
{{ total }} states of {{ twin.name }}
-
+
{{ total }} states of {{ twin.name }}
+
from: to:
diff --git a/src/app/pages/admin/twins/states/twins.states.component.scss b/src/app/pages/admin/twins/states/twins.states.component.scss index feecb8c2..e988af47 100644 --- a/src/app/pages/admin/twins/states/twins.states.component.scss +++ b/src/app/pages/admin/twins/states/twins.states.component.scss @@ -1,3 +1,3 @@ -.col-10 { +.col-md-10 { text-align: right; } diff --git a/src/app/pages/admin/twins/states/twins.states.component.ts b/src/app/pages/admin/twins/states/twins.states.component.ts index 5383ce78..c8e402f1 100644 --- a/src/app/pages/admin/twins/states/twins.states.component.ts +++ b/src/app/pages/admin/twins/states/twins.states.component.ts @@ -145,4 +145,5 @@ export class TwinsStatesComponent implements OnInit, OnDestroy { ngOnDestroy() { window.clearInterval(this.intervalID); } + } diff --git a/src/app/pages/admin/twins/twins.component.html b/src/app/pages/admin/twins/twins.component.html index a9fe02c3..5fda3543 100644 --- a/src/app/pages/admin/twins/twins.component.html +++ b/src/app/pages/admin/twins/twins.component.html @@ -1,21 +1,36 @@ -
-
- - + + +
+
{{ twinsNumber }}   Twins - - -
-
- - +
+
+ +
+
+ +
+
+ +
+
+ + (deleteConfirm)="onDeleteConfirm($event)"> diff --git a/src/app/pages/admin/twins/twins.component.scss b/src/app/pages/admin/twins/twins.component.scss index e69de29b..e90dce65 100644 --- a/src/app/pages/admin/twins/twins.component.scss +++ b/src/app/pages/admin/twins/twins.component.scss @@ -0,0 +1,7 @@ +nb-card-header { + text-align: center; +} + +button { + width: 100%; +} diff --git a/src/app/pages/admin/twins/twins.component.ts b/src/app/pages/admin/twins/twins.component.ts index acdd023c..5134e807 100644 --- a/src/app/pages/admin/twins/twins.component.ts +++ b/src/app/pages/admin/twins/twins.component.ts @@ -8,9 +8,9 @@ import { ConfirmationComponent } from 'app/shared/confirmation/confirmation.comp import { DetailsComponent } from 'app/shared/details/details.component'; import { TwinsService } from 'app/common/services/twins/twins.service'; +import { FsService } from 'app/common/services/fs/fs.service'; import { Twin } from 'app/common/interfaces/mainflux.interface'; - @Component({ selector: 'ngx-twins', templateUrl: './twins.component.html', @@ -35,23 +35,42 @@ export class TwinsComponent implements OnInit { confirmDelete: true, }, columns: { + details: { + type: 'custom', + renderComponent: DetailsComponent, + valuePrepareFunction: (cell, row) => { + return row; + }, + editable: false, + addable: false, + filter: false, + }, name: { title: 'Name', editable: true, addable: true, + filter: false, }, - id: { - title: 'ID', + created: { + title: 'Created', editable: false, addable: false, + filter: false, + valuePrepareFunction: (cell, row) => { + return new Date(cell).toLocaleString(); + }, }, - details: { - title: 'Details', - type: 'custom', - renderComponent: DetailsComponent, + updated: { + title: 'Updated', + editable: false, + addable: false, + filter: false, valuePrepareFunction: (cell, row) => { - return row; + return new Date(cell).toLocaleString(); }, + }, + revision: { + title: 'Revision', editable: false, addable: false, filter: false, @@ -68,9 +87,12 @@ export class TwinsComponent implements OnInit { twinsNumber = 0; + searchTime = 0; + constructor( private dialogService: NbDialogService, private twinsService: TwinsService, + private fsService: FsService, ) { } ngOnInit() { @@ -104,6 +126,8 @@ export class TwinsComponent implements OnInit { onEditConfirm(event): void { // close edditable row event.confirm.resolve(); + + this.twinsService.editTwin(event.newData).subscribe(); } onDeleteConfirm(event): void { @@ -122,6 +146,13 @@ export class TwinsComponent implements OnInit { ); } - onSelection(event): void { + onSaveFile() { + this.fsService.exportToCsv('twins.csv', this.twins); + } + + onFileSelected(files: FileList) { + } + + searchThing(input) { } } diff --git a/src/app/pages/dashboard/dashboard.component.html b/src/app/pages/dashboard/dashboard.component.html index 6c8b5ca4..b0e5a8c9 100644 --- a/src/app/pages/dashboard/dashboard.component.html +++ b/src/app/pages/dashboard/dashboard.component.html @@ -10,7 +10,7 @@
- {{ thingsNumber }}   Things + {{ thingsNum }}   Things
@@ -18,7 +18,7 @@
- {{ totalChanNumber }}   Channels + {{ chansNum }}   Channels
diff --git a/src/app/pages/dashboard/dashboard.component.ts b/src/app/pages/dashboard/dashboard.component.ts index d2a5fc8b..93f721ae 100644 --- a/src/app/pages/dashboard/dashboard.component.ts +++ b/src/app/pages/dashboard/dashboard.component.ts @@ -1,6 +1,7 @@ import { Component, OnInit } from '@angular/core'; import { VersionsService } from 'app/common/services/versions/versions.service'; +import { UsersService } from 'app/common/services/users/users.service'; import { ThingsService } from 'app/common/services/things/things.service'; import { LoraService } from 'app/common/services/lora/lora.service'; import { ChannelsService } from 'app/common/services/channels/channels.service'; @@ -14,15 +15,12 @@ import { Router } from '@angular/router'; }) export class DashboardComponent implements OnInit { version: number; - addons = []; - thingsNumber = 0; + + thingsNum = 0; gatewaysNumber = 0; loraDevNumber = 0; - totalChanNumber = 0; - options: any = {}; + chansNum = 0; gateways = []; - offset = 0; - limit = 20; messages = []; messages2 = []; @@ -32,51 +30,57 @@ export class DashboardComponent implements OnInit { private thingsService: ThingsService, private loraService: LoraService, private channelsService: ChannelsService, + private usersService: UsersService, private router: Router, ) { } ngOnInit() { - this.thingsService.getThings(this.offset, this.limit).subscribe( - (resp: any) => { - this.thingsNumber = resp.total; - }, - ); + // If Token is valid + this.usersService.getUser().subscribe( + respUser => { + this.thingsService.getThings().subscribe( + (resp: any) => { + this.thingsNum = resp.total; + }, + ); - this.loraService.getDevices(this.offset, this.limit).subscribe( - (resp: any) => { - this.loraDevNumber = resp.total; - }, - ); + this.loraService.getDevices().subscribe( + (resp: any) => { + this.loraDevNumber = resp.total; + }, + ); - this.gatewaysService.getGateways(this.offset, this.limit).subscribe( - (resp: any) => { - this.gatewaysNumber = resp.total; - this.gateways = resp.things; - }, - ); + this.gatewaysService.getGateways().subscribe( + (resp: any) => { + this.gatewaysNumber = resp.total; + this.gateways = resp.things; + }, + ); - this.channelsService.getChannels(this.offset, this.limit).subscribe( - (resp: any) => { - this.totalChanNumber = resp.total; - }, - ); + this.channelsService.getChannels().subscribe( + (resp: any) => { + this.chansNum = resp.total; + }, + ); - this.versionsService.getUsersVersion().subscribe( - (resp: any) => { - this.version = resp.version; + this.versionsService.getUsersVersion().subscribe( + (resp: any) => { + this.version = resp.version; + }, + ); + + for (let i = 0; i < 61; i++) { + const vGas = 125 + 5 * Math.random(); + const vPress = 1 + 0.1 * Math.random(); + const vTemp = 25 + 1 * Math.random(); + const vHum = 45 + 20 * Math.random(); + this.messages.push({time: i, value: vTemp, publisher: 'mock', name: 'temperature'}); + this.messages.push({time: i, value: vHum, publisher: 'mock2', name: 'humidity'}); + this.messages.push({time: i, value: vPress, publisher: 'mock3', name: 'pressure'}); + this.messages.push({time: i, value: vGas, publisher: 'mock4', name: 'gas'}); + } }, ); - - for (let i = 0; i < 61; i++) { - const vGas = 125 + 5 * Math.random(); - const vPress = 1 + 0.1 * Math.random(); - const vTemp = 25 + 1 * Math.random(); - const vHum = 45 + 20 * Math.random(); - this.messages.push({time: i, value: vTemp, publisher: 'mock', name: 'temperature'}); - this.messages.push({time: i, value: vHum, publisher: 'mock2', name: 'humidity'}); - this.messages.push({time: i, value: vPress, publisher: 'mock3', name: 'pressure'}); - this.messages.push({time: i, value: vGas, publisher: 'mock4', name: 'gas'}); - } } toThingsList() { diff --git a/src/app/pages/login/login.component.html b/src/app/pages/login/login.component.html new file mode 100644 index 00000000..92d717a7 --- /dev/null +++ b/src/app/pages/login/login.component.html @@ -0,0 +1,106 @@ +

Sign In

+

Hello! Sign in with your username or email.

+ + +

Oh snap!

+
    +
  • {{ error }}
  • +
+
+ + +

Hooray!

+
    +
  • {{ message }}
  • +
+
+ +
+ +
+ + + +

+ Email is required! +

+

+ Email should be the real one! +

+
+
+ +
+ + + +

+ Password is required! +

+

+ Password should contains + from {{ getConfigValue('forms.validation.password.minLength') }} + to {{ getConfigValue('forms.validation.password.maxLength') }} + characters +

+
+
+ +
+ Remember me + Forgot Password? +
+ + +
+ + + +
+ Don't have an account? Sign Up +
diff --git a/src/app/pages/login/login.component.ts b/src/app/pages/login/login.component.ts new file mode 100644 index 00000000..39667e54 --- /dev/null +++ b/src/app/pages/login/login.component.ts @@ -0,0 +1,20 @@ +import { Component, ChangeDetectorRef, Inject } from '@angular/core'; + +import { NbAuthService, NB_AUTH_OPTIONS, NbLoginComponent } from '@nebular/auth'; +import { Router } from '@angular/router'; + +@Component({ + selector: 'ngx-login', + templateUrl: 'login.component.html', +}) +export class LoginComponent extends NbLoginComponent { + + constructor( + @Inject(NB_AUTH_OPTIONS) protected options: {}, + protected authService: NbAuthService, + protected cd: ChangeDetectorRef, + protected router: Router, + ) { + super(authService, options, cd, router); + } +} diff --git a/src/app/pages/things/channels/channels.component.html b/src/app/pages/things/channels/channels.component.html index 8ea3d17a..ebd25426 100644 --- a/src/app/pages/things/channels/channels.component.html +++ b/src/app/pages/things/channels/channels.component.html @@ -1,21 +1,21 @@
-
- {{ totalChanNumber }}   Channels +
+ {{ chansNum }}   Channels
-
+
-
+
-
+
-
+
{{con.name}}
-
+
diff --git a/src/app/pages/things/channels/details/channels.details.component.scss b/src/app/pages/things/channels/details/channels.details.component.scss index 710e4662..da771879 100644 --- a/src/app/pages/things/channels/details/channels.details.component.scss +++ b/src/app/pages/things/channels/details/channels.details.component.scss @@ -2,7 +2,7 @@ nb-icon { vertical-align: middle; } -.col-2 { +.col-md-2 { text-align: right; padding: 0; font-weight: bold; diff --git a/src/app/pages/things/channels/details/channels.details.component.ts b/src/app/pages/things/channels/details/channels.details.component.ts index e455d539..7f52a50f 100644 --- a/src/app/pages/things/channels/details/channels.details.component.ts +++ b/src/app/pages/things/channels/details/channels.details.component.ts @@ -88,28 +88,22 @@ export class ChannelsDetailsComponent implements OnInit { } findDisconnectedThings() { - this.things = []; - this.channelsService.connectedThings(this.channel.id).subscribe( (respConns: any) => { this.connections = respConns.things; - this.thingsService.getThings(this.offset, this.limit).subscribe( - (respThings: any) => { - respThings.things.forEach(thing => { - // Filter get Things resp and keep only disconnected ones. - if (!(this.connections.filter(c => c.id === thing.id).length > 0)) { - this.things.push(thing); - } - }); - - this.getchannelMessages(); - }, - ); + + this.getChannelMessages(); + }, + ); + + this.channelsService.disconnectedThings(this.channel.id).subscribe( + (respDisconns: any) => { + this.things = respDisconns.things; }, ); } - getchannelMessages() { + getChannelMessages() { if (this.connections.length) { this.messagesService.getMessages(this.channel.id, this.connections[0].key).subscribe( (respMsg: any) => { diff --git a/src/app/pages/things/devices/details/devices.details.component.html b/src/app/pages/things/devices/details/devices.details.component.html index 02b08402..a52dd291 100644 --- a/src/app/pages/things/devices/details/devices.details.component.html +++ b/src/app/pages/things/devices/details/devices.details.component.html @@ -6,9 +6,9 @@
-
Name:
{{ thing.name }}
-
ID:
{{ thing.id }}
-
Key:
{{ thing.key }}
+
Name:
{{ thing.name }}
+
ID:
{{ thing.id }}
+
Key:
{{ thing.key }}
@@ -62,11 +62,11 @@
-
+
{{con.name}}
-
+
diff --git a/src/app/pages/things/devices/details/devices.details.component.scss b/src/app/pages/things/devices/details/devices.details.component.scss index 710e4662..da771879 100644 --- a/src/app/pages/things/devices/details/devices.details.component.scss +++ b/src/app/pages/things/devices/details/devices.details.component.scss @@ -2,7 +2,7 @@ nb-icon { vertical-align: middle; } -.col-2 { +.col-md-2 { text-align: right; padding: 0; font-weight: bold; diff --git a/src/app/pages/things/devices/details/devices.details.component.ts b/src/app/pages/things/devices/details/devices.details.component.ts index 61f50bd9..ca47207a 100644 --- a/src/app/pages/things/devices/details/devices.details.component.ts +++ b/src/app/pages/things/devices/details/devices.details.component.ts @@ -87,28 +87,30 @@ export class DevicesDetailsComponent implements OnInit { ); } + getChannelMessages() { + this.connections.forEach(chan => { + this.messagesService.getMessages(chan.id, this.thing.key, this.thing.id).subscribe( + (respMsg: any) => { + this.messages = respMsg.messages || this.messages; + }, + ); + }); + } + findDisconnectedChans() { - this.channels = []; this.messages = []; this.thingsService.connectedChannels(this.thing.id).subscribe( (respConns: any) => { this.connections = respConns.channels; - this.channelsService.getChannels(this.offset, this.limit).subscribe( - (respChans: any) => { - respChans.channels.forEach(chan => { - if (!(this.connections.filter(c => c.id === chan.id).length > 0)) { - this.channels.push(chan); - } else { - this.messagesService.getMessages(chan.id, this.thing.key, this.thing.id).subscribe( - (respMsg: any) => { - this.messages = respMsg.messages || this.messages; - }, - ); - } - }); - }, - ); + + this.getChannelMessages(); + }, + ); + + this.thingsService.disconnectedChannels(this.thing.id).subscribe( + (respDisconn: any) => { + this.channels = respDisconn.channels; }, ); } diff --git a/src/app/pages/things/devices/devices.component.html b/src/app/pages/things/devices/devices.component.html index 577af27b..af7acdb7 100644 --- a/src/app/pages/things/devices/devices.component.html +++ b/src/app/pages/things/devices/devices.component.html @@ -1,21 +1,21 @@
-
- {{ thingsNumber }}   Devices +
+ {{ thingsNum }}   Devices
-
+
-
+
-
+
+
+
+ +
+
+ + + + + + diff --git a/src/app/pages/things/gateways/gateways.component.scss b/src/app/pages/things/gateways/gateways.component.scss index 1c5c5a2c..e90dce65 100644 --- a/src/app/pages/things/gateways/gateways.component.scss +++ b/src/app/pages/things/gateways/gateways.component.scss @@ -1,3 +1,7 @@ nb-card-header { text-align: center; } + +button { + width: 100%; +} diff --git a/src/app/pages/things/gateways/gateways.component.ts b/src/app/pages/things/gateways/gateways.component.ts index 954fa57d..0e796819 100644 --- a/src/app/pages/things/gateways/gateways.component.ts +++ b/src/app/pages/things/gateways/gateways.component.ts @@ -7,20 +7,19 @@ import { Gateway } from 'app/common/interfaces/gateway.interface'; import { GatewaysService } from 'app/common/services/gateways/gateways.service'; import { NotificationsService } from 'app/common/services/notifications/notifications.service'; import { MessagesService } from 'app/common/services/messages/messages.service'; +import { FsService } from 'app/common/services/fs/fs.service'; import { DetailsComponent } from 'app/shared/details/details.component'; import { ConfirmationComponent } from 'app/shared/confirmation/confirmation.component'; +const defSearchBardMs: number = 100; + @Component({ selector: 'ngx-gateways-component', templateUrl: './gateways.component.html', styleUrls: ['./gateways.component.scss'], }) export class GatewaysComponent implements OnInit { - offset = 0; - limit = 20; - total = 0; - settings = { add: { addButtonContent: '', @@ -39,25 +38,28 @@ export class GatewaysComponent implements OnInit { confirmDelete: true, }, columns: { + details: { + type: 'custom', + renderComponent: DetailsComponent, + valuePrepareFunction: (cell, row) => { + row.type = 'gateways'; + return row; + }, + editable: 'false', + addable: false, + filter: false, + }, name: { title: 'Name', type: 'string', - placeholder: 'Search name', - filter: { - placeholder: 'Search name', - }, + filter: false, }, - mac: { - title: 'MAC', - placeholder: 'Search MAC', + external_id: { + title: 'External ID', type: 'text', editable: true, addable: true, - filter: { - config: { - placeholder: 'Search MAC', - }, - }, + filter: false, }, messages: { title: 'Messages', @@ -76,7 +78,7 @@ export class GatewaysComponent implements OnInit { width: '20%', title: 'Last Seen', type: 'text', - editable: 'false', + editable: false, addable: false, filter: false, valuePrepareFunction: (cell, row) => { @@ -86,18 +88,6 @@ export class GatewaysComponent implements OnInit { return 'undefined'; }, }, - details: { - title: 'Details', - type: 'custom', - renderComponent: DetailsComponent, - valuePrepareFunction: (cell, row) => { - row.type = 'gateways'; - return row; - }, - editable: 'false', - addable: false, - filter: false, - }, }, pager: { display: true, @@ -108,48 +98,38 @@ export class GatewaysComponent implements OnInit { source: LocalDataSource = new LocalDataSource(); gateways: Gateway[]; - gwDataChanNumber = 0; - gwCtrlChanNumber = 0; prefixLength = 3; + offset = 0; + limit = 20; + total = 0; + + searchTime = 0; + constructor( private gatewaysService: GatewaysService, private messagesService: MessagesService, private notificationsService: NotificationsService, + private fsService: FsService, private dialogService: NbDialogService, ) { } ngOnInit() { this.getGateways(); - this.getGatewaysChannels(); - } - - getGatewaysChannels() { - this.gatewaysService.getCtrlChannels(this.offset, this.limit).subscribe( - (resp: any) => { - this.gwCtrlChanNumber = resp.total; - }, - ); - - this.gatewaysService.getDataChannels(this.offset, this.limit).subscribe( - (resp: any) => { - this.gwDataChanNumber = resp.total; - }, - ); } - getGateways(): void { + getGateways(name?: string): void { this.gateways = []; - this.gatewaysService.getGateways(this.offset, this.limit).subscribe( + this.gatewaysService.getGateways(this.offset, this.limit, name).subscribe( (resp: any) => { this.total = resp.total; resp.things.forEach(gw => { - gw.mac = gw.metadata.mac; + gw.external_id = gw.metadata.external_id; - const dataChannelID: string = gw.metadata ? gw.metadata.dataChannelID : ''; - this.messagesService.getMessages(dataChannelID, gw.key, gw.id).subscribe( + const data_channel_id: string = gw.metadata ? gw.metadata.data_channel_id : ''; + this.messagesService.getMessages(data_channel_id, gw.key, gw.id).subscribe( (msgResp: any) => { if (msgResp.messages) { gw.seen = msgResp.messages[0].time; @@ -160,6 +140,11 @@ export class GatewaysComponent implements OnInit { this.source.load(this.gateways); this.source.refresh(); }, + err => { + this.gateways.push(gw); + this.source.load(this.gateways); + this.source.refresh(); + }, ); }); }, @@ -168,15 +153,21 @@ export class GatewaysComponent implements OnInit { validate(row: any): boolean { + const gws = this.gateways.map(g => g.metadata.external_id); + if (gws.includes(row.external_id)) { + this.notificationsService.warn( + 'External ID already exist.', ''); + return false; + } if (row.name === '' || row.name.length > 32) { this.notificationsService.warn( 'Name is required and must be maximum 32 characters long.', ''); return false; } - if (row.mac === '' || row.mac.length < 8) { + if (row.external_id === '' || row.external_id.length < 8) { this.notificationsService.warn( - 'MAC is required and must be at least 8 characters long.', ''); + 'External ID is required and must be at least 8 characters long.', ''); return false; } @@ -193,12 +184,11 @@ export class GatewaysComponent implements OnInit { // close edditable row event.confirm.resolve(); - this.gatewaysService.addGateway(event.newData.name, event.newData.mac).subscribe( + this.gatewaysService.addGateway(event.newData.name, event.newData.external_id).subscribe( resp => { setTimeout( () => { this.getGateways(); - this.getGatewaysChannels(); }, 3000, ); }, @@ -206,6 +196,15 @@ export class GatewaysComponent implements OnInit { } onEditConfirm(event): void { + // Check if the row have been modified + const extIDs = this.gateways.map(g => g.metadata.external_id); + const names = this.gateways.map(g => g.name); + if (extIDs.includes(event.newData.external_id) && names.includes(event.newData.name)) { + // close edditable row + event.confirm.resolve(); + return; + } + // Formulaire Validator if (!this.validate(event.newData)) { return; @@ -215,11 +214,12 @@ export class GatewaysComponent implements OnInit { event.confirm.resolve(); const name = event.newData.name; - const mac = event.newData.mac; + const external_id = event.newData.external_id; const gw = event.newData; - this.gatewaysService.editGateway(name, mac, gw).subscribe( + this.gatewaysService.editGateway(name, external_id, gw).subscribe( resp => { + this.getGateways(); }, ); } @@ -242,4 +242,19 @@ export class GatewaysComponent implements OnInit { }, ); } + + searchGW(input) { + const t = new Date().getTime(); + if ((t - this.searchTime) > defSearchBardMs) { + this.getGateways(input); + this.searchTime = t; + } + } + + onClickSave() { + this.fsService.exportToCsv('gateways.csv', this.gateways); + } + + onFileSelected(files: FileList) { + } } diff --git a/src/app/pages/things/lora/details/lora.details.component.html b/src/app/pages/things/lora/details/lora.details.component.html index b34a6cca..fdf995fd 100644 --- a/src/app/pages/things/lora/details/lora.details.component.html +++ b/src/app/pages/things/lora/details/lora.details.component.html @@ -6,9 +6,9 @@
-
Name:
{{ loraDevice.name }}
-
ID:
{{ loraDevice.id }}
-
Key:
{{ loraDevice.key }}
+
Name:
{{ loraDevice.name }}
+
ID:
{{ loraDevice.id }}
+
Key:
{{ loraDevice.key }}
diff --git a/src/app/pages/things/lora/details/lora.details.component.scss b/src/app/pages/things/lora/details/lora.details.component.scss index 10851bd5..23905163 100644 --- a/src/app/pages/things/lora/details/lora.details.component.scss +++ b/src/app/pages/things/lora/details/lora.details.component.scss @@ -2,7 +2,7 @@ nb-icon { vertical-align: middle; } -.col-2 { +.col-md-2 { text-align: right; padding: 0; font-weight: bold; diff --git a/src/app/pages/things/lora/details/lora.details.component.ts b/src/app/pages/things/lora/details/lora.details.component.ts index 5cda1645..60ded387 100644 --- a/src/app/pages/things/lora/details/lora.details.component.ts +++ b/src/app/pages/things/lora/details/lora.details.component.ts @@ -30,7 +30,7 @@ export class LoraDetailsComponent implements OnInit { this.loraDevice = resp; this.messagesService.getMessages( - this.loraDevice.metadata.channelID, this.loraDevice.key, this.loraDevice.id).subscribe( + this.loraDevice.metadata.channel_id, this.loraDevice.key, this.loraDevice.id).subscribe( (msgResp: any) => { if (msgResp.messages) { this.messages = msgResp.messages; diff --git a/src/app/pages/things/lora/lora.component.html b/src/app/pages/things/lora/lora.component.html index e191bb76..ae950f5c 100644 --- a/src/app/pages/things/lora/lora.component.html +++ b/src/app/pages/things/lora/lora.component.html @@ -1,21 +1,21 @@
-
+
{{ loraDevsNumber }}   LoRa Devices
-
+
-
+
-
+
-
+
- + + + + -
- Node ID +
+ Node ID
-
- Data Type +
+ Data Type
-
- Description +
+ Description
-
- BrowseName +
+ BrowseName
-
+
+
- -
- {{ node.NodeID }} -
-
- {{ node.DataType }} -
-
- {{ node.Description }} -
-
- {{ node.BrowseName }} -
-
- -
-
- + + + + + + No browsed server + + +
+ {{ node.NodeID }} +
+
+ {{ node.DataType }} +
+
+ {{ node.Description }} +
+
+ {{ node.BrowseName }} +
+
+ +
+
+
- +
-
+
{{ opcuaNodes.length }}   OPC-UA Server Subscription
-
+
-
+
-
+