Compare commits

..

No commits in common. "main" and "0.9.0" have entirely different histories.
main ... 0.9.0

37 changed files with 360 additions and 709 deletions

2
.gitignore vendored
View File

@ -3,8 +3,6 @@ api/personal_measure_api
api/postgres.container.id api/postgres.container.id
api/src/main/nim/personal_measure_api api/src/main/nim/personal_measure_api
api/src/main/nim/personal_measure_apipkg/db api/src/main/nim/personal_measure_apipkg/db
api/personal_measure_api.config.dev.json
api/personal_measure_api.config.prod.json
.DS_Store .DS_Store
node_modules node_modules

View File

@ -5,12 +5,14 @@ build: dist/personal-measure-api.tar.gz dist/personal-measure-web.tar.gz
clean: clean:
-rm -r dist -rm -r dist
-rm api/personal_measure_api
-rm -r web/dist -rm -r web/dist
-docker container prune
-docker image prune
update-version: dist/personal-measure-api.tar.gz:
operations/update-version.sh -mkdir dist
make -C api personal_measure_api
tar czf dist/personal-measure-api-${VERSION}.tar.gz -C api personal_measure_api
cp dist/personal-measure-api-${VERSION}.tar.gz dist/personal-measure-api.tar.gz
dist/personal-measure-web.tar.gz: dist/personal-measure-web.tar.gz:
-mkdir dist -mkdir dist
@ -18,14 +20,18 @@ dist/personal-measure-web.tar.gz:
tar czf dist/personal-measure-web-${VERSION}.tar.gz -C web/dist . tar czf dist/personal-measure-web-${VERSION}.tar.gz -C web/dist .
cp dist/personal-measure-web-${VERSION}.tar.gz dist/personal-measure-web.tar.gz cp dist/personal-measure-web-${VERSION}.tar.gz dist/personal-measure-web.tar.gz
deploy-api: deploy-api: dist/personal-measure-api.tar.gz
make -C api personal_measure_api-image push-image mkdir -p temp-deploy/personal-measure-api-${VERSION}
cd operations/terraform && terraform apply -target module.${TARGET_ENV}_env.aws_ecs_task_definition.pmapi -target module.${TARGET_ENV}_env.aws_ecs_service.pmapi tar xzf dist/personal-measure-api-${VERSION}.tar.gz -C temp-deploy/personal-measure-api-${VERSION}
-ssh pmapi@pmapi.jdb-labs.com "sudo systemctl stop personal_measure_api.$(TARGET_ENV).service"
scp temp-deploy/personal-measure-api-${VERSION}/personal_measure_api pmapi@pmapi.jdb-labs.com:/home/pmapi/$(TARGET_ENV)/personal_measure_api
ssh pmapi@pmapi.jdb-labs.com "sudo systemctl start personal_measure_api.$(TARGET_ENV).service"
rm -r temp-deploy
deploy-web: dist/personal-measure-web.tar.gz deploy-web: dist/personal-measure-web.tar.gz
mkdir -p temp-deploy/personal-measure-web-${VERSION} mkdir -p temp-deploy/personal-measure-web-${VERSION}
tar xzf dist/personal-measure-web-${VERSION}.tar.gz -C temp-deploy/personal-measure-web-${VERSION} tar xzf dist/personal-measure-web-${VERSION}.tar.gz -C temp-deploy/personal-measure-web-${VERSION}
aws s3 sync temp-deploy/personal-measure-web-${VERSION} s3://pm.jdb-software.com/$(TARGET_ENV)/webroot aws s3 sync temp-deploy/personal-measure-web-${VERSION} s3://pm.jdb-labs.com/$(TARGET_ENV)/webroot
TARGET_ENV=${TARGET_ENV} operations/invalidate-cdn-cache.sh TARGET_ENV=${TARGET_ENV} operations/invalidate-cdn-cache.sh
rm -r temp-deploy rm -r temp-deploy

View File

@ -1,22 +1,26 @@
FROM 063932952339.dkr.ecr.us-west-2.amazonaws.com/alpine-nim:nim-1.4.8 AS build FROM 063932952339.dkr.ecr.us-west-2.amazonaws.com/nim-alpine AS build
MAINTAINER jonathan@jdbernard.com MAINTAINER jonathan@jdbernard.com
# TODO: install db_migrate so we can use it below
# RUN nimble install https://git.jdb-labs.com/jdb/db-migrate.git
#RUN apt-get install -y libssl-dev
COPY personal_measure_api.nimble /pm-api/ COPY personal_measure_api.nimble /pm-api/
COPY src /pm-api/src COPY src /pm-api/src
WORKDIR /pm-api WORKDIR /pm-api
RUN nimble build -y RUN nimble build -y
FROM alpine FROM alpine
EXPOSE 80 #RUN apt-get install -y postgresql-client
RUN apk -v --update add --no-cache \ RUN apk -v --update add --no-cache \
ca-certificates \ ca-certificates \
libcrypto1.1 \ libressl2.7-libssl \
libssl1.1 \ libressl2.7-libcrypto \
pcre \ pcre \
postgresql-client postgresql-client
COPY --from=build /pm-api/personal_measure_api / COPY --from=build /pm-api/personal_measure_api /
COPY personal_measure_api.config.docker.json /personal_measure_api.config.json COPY personal_measure_api.config.prod.json /personal_measure_api.config.json
CMD ["/personal_measure_api", "serve"] CMD ["/personal_measure_api", "serve"]
# TODO: replace the above with something like: # TODO: replace the above with something like:

View File

@ -1,123 +1,31 @@
PGSQL_CONTAINER_ID=`cat postgres.container.id` PGSQL_CONTAINER_ID=`cat postgres.container.id`
DB_NAME="personal_measure"
SOURCES=$(wildcard src/main/nim/*.nim) $(wildcard src/main/nim/personal_measure_apipkg/*.nim) SOURCES=$(wildcard src/main/nim/*.nim) $(wildcard src/main/nim/personal_measure_apipkg/*.nim)
# Variables that can be overriden serve: personal_measure_api start-postgres
# -------------------------------
# AWS Account URL for the ECR repository
ECR_ACCOUNT_URL ?= 063932952339.dkr.ecr.us-west-2.amazonaws.com
# The version number that will be tagged the container image. You might want to
# override this when doing local development to create local versions that are
# reflect changes not yet committed.
VERSION ?= `git describe`
# The port on the host machine (not the container)
PORT ?= 8100
# The name of the database (used then creating a local Postgres container)
DB_NAME ?= personal_measure
# The database connection string. You would change this to point the API at a
# different database server (default is the local Postgres container).
DB_CONN_STRING ?= host=localhost dbname=$(DB_NAME) user=postgres password=password port=5500
# The API authentication secret (used for hashing passwords, etc.)
AUTH_SECRET ?= 123abc
default: start-postgres serve-docker
# Building and deploying the API container image
# ----------------------------------------------
personal_measure_api-image: $(SOURCES)
# Build the container image.
docker image build -t $(ECR_ACCOUNT_URL)/personal_measure_api:$(VERSION) .
push-image: personal_measure_api-image
# Push the container image to the private AWS ECR
docker push $(ECR_ACCOUNT_URL)/personal_measure_api:$(VERSION)
# Running the API locally on bare metal
# -------------------------------------
personal_measure_api: $(SOURCES)
# Build the API
nimble build
serve: personal_measure_api
# Run the API on this machine. Note that configuration is taken by default
# from the `personal_measure_api.config.json` file, but environment variables
# specified when running make can be used to override these (to change the
# DB_CONN_STRING, for example).
./personal_measure_api serve ./personal_measure_api serve
# Running the API locally in a container
# --------------------------------------
serve-docker: personal_measure_api-image
# Run the API in a docker container. Note that the configuration loaded into
# the Docker container defines very little of the actual configuration as
# environment variables are used in the deployed environments. Accordingly,
# we must specify them explicitly here.
docker run \
-e AUTH_SECRET=$(AUTH_SECRET) \
-e PORT=80 \
-e "DB_CONN_STRING=$(DB_CONN_STRING)" \
-e 'KNOWN_ORIGINS=["https://curl.localhost"]' \
-p 127.0.0.1:$(PORT):80/tcp \
$(ECR_ACCOUNT_URL)/personal_measure_api:$(VERSION)
# Managing Postgres in a local container
# --------------------------------------
#
# This supports local development on this machine. These commands rely on a
# file named `postgres.container.id` to track the existing and ID of the
# local Postgres instance.
postgres.container.id: postgres.container.id:
# This creates a new local Postegres container and initializes the PM API
# database scheme.
docker run --name postgres-$(DB_NAME) -e POSTGRES_PASSWORD=password -p 5500:5432 -d postgres > postgres.container.id docker run --name postgres-$(DB_NAME) -e POSTGRES_PASSWORD=password -p 5500:5432 -d postgres > postgres.container.id
sleep 5 sleep 5
PGPASSWORD=password psql -p 5500 -U postgres -h localhost -c "CREATE DATABASE $(DB_NAME);" PGPASSWORD=password psql -p 5500 -U postgres -h localhost -c "CREATE DATABASE $(DB_NAME);"
db_migrate up -c database-local.json db_migrate up -c database-local.json
start-postgres: postgres.container.id start-postgres: postgres.container.id
# Start the existing local Postgres container
docker start $(PGSQL_CONTAINER_ID) docker start $(PGSQL_CONTAINER_ID)
sleep 1 sleep 1
db_migrate up -c database-local.json db_migrate up -c database-local.json
stop-postgres: postgres.container.id stop-postgres: postgres.container.id
# Stop the existing local Postgres container
docker stop $(PGSQL_CONTAINER_ID) docker stop $(PGSQL_CONTAINER_ID)
delete-postgres-container: delete-postgres-container:
# Delete the local Postgres container. Note that this will destroy any data
# in this database instance.
-docker stop $(PGSQL_CONTAINER_ID) -docker stop $(PGSQL_CONTAINER_ID)
docker container rm $(PGSQL_CONTAINER_ID) docker container rm $(PGSQL_CONTAINER_ID)
rm postgres.container.id rm postgres.container.id
connect-postgres: connect:
# Connect to the Postgres instance running in the local container
PGPASSWORD=password psql -p 5500 -U postgres -h localhost ${DB_NAME} PGPASSWORD=password psql -p 5500 -U postgres -h localhost ${DB_NAME}
personal_measure_api: $(SOURCES)
# Utility nimble build
# -------
ecr-auth:
# Authenticate docker to the AWS private elastic container repository.
aws ecr get-login-password --region us-west-2 | docker login --username AWS --password-stdin 063932952339.dkr.ecr.us-west-2.amazonaws.com
echo-vars:
@echo \
" ECR_ACCOUNT_URL=$(ECR_ACCOUNT_URL)\n" \
"VERSION=$(VERSION)\n" \
"PORT=$(PORT)\n" \
"DB_NAME=$(DB_NAME)\n" \
"DB_CONN_STRING=$(DB_CONN_STRING)\n" \
"AUTH_SECRET=$(AUTH_SECRET)\n"

View File

@ -1,30 +0,0 @@
## Local Development
Examples of different local development & testing scenarios:
- Bare-metal API server, local Postgres container
make start-postgres
make serve
- Bare-metal API server, different Postgres server
DB_CONN_STRING="host=<db-hostname> user=pmapi password=<pwd>" make serve
- Docker API Server, local Postgres container
make start-postgres
VERSION=0.X.0-alpha make serve-docker
- Docker API server, different Postgres server
DB_CONN_STRING="host=<db-hostname> user=pmapi password=<pwd>" \
VERSION=0.X.0-alpha \
make serve-docker
All of the available `make` targets are documented inline; see the
[Makefile](./Makefile) for more details.
### Using the API CLI wrapper
The API CLI wrapper

View File

@ -1,5 +1,5 @@
{ {
"driver": "postgres", "driver": "postgres",
"connectionString": "host=localhost port=5432 dbname=personal_measure_dev user=pmapi", "connectionString": "host=localhost port=5999 dbname=personal_measure_dev user=postgres",
"sqlDir": "src/main/sql/migrations" "sqlDir": "src/main/sql/migrations"
} }

View File

@ -1,5 +1,5 @@
{ {
"driver": "postgres", "driver": "postgres",
"connectionString": "host=localhost port=5432 dbname=personal_measure user=pmapi", "connectionString": "host=localhost port=5999 dbname=personal_measure user=postgres",
"sqlDir": "src/main/sql/migrations" "sqlDir": "src/main/sql/migrations"
} }

View File

@ -1,4 +0,0 @@
{
"debug":false,
"pwdCost":11
}

View File

@ -2,7 +2,7 @@
"authSecret":"bifekHuffIs3", "authSecret":"bifekHuffIs3",
"dbConnString":"host=localhost port=5500 dbname=personal_measure user=postgres password=password", "dbConnString":"host=localhost port=5500 dbname=personal_measure user=postgres password=password",
"debug":true, "debug":true,
"port":8100, "port":8081,
"pwdCost":11, "pwdCost":11,
"knownOrigins": [ "https://curl.localhost" ] "knownOrigins": [ "https://curl.localhost" ]
} }

View File

@ -0,0 +1,6 @@
{
"debug":false,
"port":80,
"pwdCost":11,
"knownOrigins": [ "https://pm.jdb-labs.com" ]
}

View File

@ -2,7 +2,7 @@
include "src/main/nim/personal_measure_apipkg/version.nim" include "src/main/nim/personal_measure_apipkg/version.nim"
version = "0.11.0" version = "0.9.0"
author = "Jonathan Bernard" author = "Jonathan Bernard"
description = "JDB\'s Personal Measures API" description = "JDB\'s Personal Measures API"
license = "MIT" license = "MIT"
@ -16,6 +16,6 @@ skipExt = @["nim"]
requires @["nim >= 0.19.4", "bcrypt", "docopt >= 0.6.8", "isaac >= 0.1.3", requires @["nim >= 0.19.4", "bcrypt", "docopt >= 0.6.8", "isaac >= 0.1.3",
"jester >= 0.4.3", "jwt", "tempfile", "uuids >= 0.1.10" ] "jester >= 0.4.3", "jwt", "tempfile", "uuids >= 0.1.10" ]
requires "https://git.jdb-software.com/jdb/nim-cli-utils.git >= 0.6.3" requires "https://git.jdb-labs.com/jdb/nim-cli-utils.git >= 0.6.3"
requires "https://git.jdb-software.com/jdb/nim-time-utils.git >= 0.5.2" requires "https://git.jdb-labs.com/jdb/nim-time-utils.git >= 0.5.2"
requires "https://git.jdb-software.com/jdb-software/fiber-orm-nim.git >= 0.3.2" requires "https://git.jdb-labs.com/jdb-labs/fiber-orm-nim.git >= 0.3.0"

View File

@ -27,22 +27,26 @@ proc loadConfig*(args: Table[string, docopt.Value] = initTable[string, docopt.Va
try: json = parseFile(filePath) try: json = parseFile(filePath)
except: except:
json = %DEFAULT_CONFIG json = %DEFAULT_CONFIG
if not fileExists(filePath): if not existsFile(filePath):
info "created new configuration file \"" & filePath & "\"" info "created new configuration file \"" & filePath & "\""
filePath.writeFile($json) filePath.writeFile($json)
else: else:
warn "Cannot read configuration file \"" & filePath & "\":\n\t" & warn "Cannot read configuration file \"" & filePath & "\":\n\t" &
getCurrentExceptionMsg() getCurrentExceptionMsg()
let knownOriginsArray =
if json.hasKey("knownOrigins"): json["knownOrigins"]
else: newJArray()
let cfg = CombinedConfig(docopt: args, json: json) let cfg = CombinedConfig(docopt: args, json: json)
result = PMApiConfig( result = PMApiConfig(
authSecret: cfg.getVal("auth-secret"), authSecret: cfg.getVal("auth-secret"),
dbConnString: cfg.getVal("db-conn-string"), dbConnString: cfg.getVal("db-conn-string"),
debug: "true".startsWith(cfg.getVal("debug", "false").toLower()), debug: "true".startsWith(cfg.getVal("debug", "false").toLower()),
port: parseInt(cfg.getVal("port", "8100")), port: parseInt(cfg.getVal("port", "8080")),
pwdCost: cast[int8](parseInt(cfg.getVal("pwd-cost", "11"))), pwdCost: cast[int8](parseInt(cfg.getVal("pwd-cost", "11"))),
knownOrigins: cfg.getVal("known-origins")[1..^2].split(',').mapIt(it[1..^2])) knownOrigins: toSeq(knownOriginsArray).mapIt(it.getStr))
proc initContext(args: Table[string, docopt.Value]): PMApiContext = proc initContext(args: Table[string, docopt.Value]): PMApiContext =
@ -110,6 +114,6 @@ Options:
if args["serve"]: start(ctx) if args["serve"]: start(ctx)
except: except:
fatal "personal_measure_api: " & getCurrentExceptionMsg() fatal "pit: " & getCurrentExceptionMsg()
#raise getCurrentException() #raise getCurrentException()
quit(QuitFailure) quit(QuitFailure)

View File

@ -1,6 +1,5 @@
import asyncdispatch, base64, jester, json, jwt, logging, options, sequtils, import asyncdispatch, base64, jester, json, jwt, logging, options, sequtils,
times, uuids times, uuids
from httpcore import HttpMethod
from unicode import capitalize from unicode import capitalize
import strutils except capitalize import strutils except capitalize
import timeutils import timeutils
@ -59,29 +58,6 @@ template jsonResp(code: HttpCode, body: string = "", headersToSend: RawHeaders =
body body
) )
template optionsResp(allowedMethods: seq[HttpMethod]) =
let reqOrigin =
if request.headers.hasKey("Origin"): $(request.headers["Origin"])
else: ""
let corsHeaders =
if ctx.cfg.knownOrigins.contains(reqOrigin):
@{
"Access-Control-Allow-Origin": reqOrigin,
"Access-Control-Allow-Credentials": "true",
"Access-Control-Allow-Methods": allowedMethods.mapIt($it).join(", "),
"Access-Control-Allow-Headers": "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization"
}
else: @{:}
halt(
Http200,
corsHeaders,
""
)
template jsonResp(body: string) = jsonResp(Http200, body) template jsonResp(body: string) = jsonResp(Http200, body)
template statusResp(code: HttpCode, details: string = "", headersToSend: RawHeaders = @{:} ) = template statusResp(code: HttpCode, details: string = "", headersToSend: RawHeaders = @{:} ) =
@ -121,7 +97,7 @@ proc fromJWT*(ctx: PMApiContext, strTok: string): Session =
## Validate a given JWT and extract the session data. ## Validate a given JWT and extract the session data.
let jwt = toJWT(strTok) let jwt = toJWT(strTok)
var secret = ctx.cfg.authSecret var secret = ctx.cfg.authSecret
if not jwt.verify(secret, HS256): raiseEx "Unable to verify auth token." if not jwt.verify(secret): raiseEx "Unable to verify auth token."
jwt.verifyTimeClaims() jwt.verifyTimeClaims()
# Find the user record (if authenticated) # Find the user record (if authenticated)
@ -236,13 +212,9 @@ proc start*(ctx: PMApiContext): void =
routes: routes:
options "/version": optionsResp(@[HttpGet])
get "/version": get "/version":
jsonResp($(%("personal_measure_api v" & PM_API_VERSION))) jsonResp($(%("personal_measure_api v" & PM_API_VERSION)))
options "/auth-token": optionsResp(@[HttpPost])
post "/auth-token": post "/auth-token":
try: try:
@ -254,8 +226,6 @@ proc start*(ctx: PMApiContext): void =
except JsonParsingError: statusResp(Http400, getCurrentExceptionMsg()) except JsonParsingError: statusResp(Http400, getCurrentExceptionMsg())
except: statusResp(Http401, getCurrentExceptionMsg()) except: statusResp(Http401, getCurrentExceptionMsg())
options "/change-pwd": optionsResp(@[HttpPost])
post "/change-pwd": post "/change-pwd":
checkAuth() checkAuth()
@ -277,8 +247,6 @@ proc start*(ctx: PMApiContext): void =
error "internal error changing password: " & getCurrentExceptionMsg() error "internal error changing password: " & getCurrentExceptionMsg()
statusResp(Http500) statusResp(Http500)
options "/change-pwd/@userId": optionsResp(@[HttpPost])
post "/change-pwd/@userId": post "/change-pwd/@userId":
checkAuth(true) checkAuth(true)
@ -300,8 +268,6 @@ proc start*(ctx: PMApiContext): void =
error "internal error changing password: " & getCurrentExceptionMsg() error "internal error changing password: " & getCurrentExceptionMsg()
statusResp(Http500) statusResp(Http500)
options "/user": optionsResp(@[HttpGet, HttpPut])
get "/user": get "/user":
checkAuth() checkAuth()
@ -326,8 +292,6 @@ proc start*(ctx: PMApiContext): void =
error "Could not update user information:\n\t" & getCurrentExceptionMsg() error "Could not update user information:\n\t" & getCurrentExceptionMsg()
statusResp(Http500) statusResp(Http500)
options "/users": optionsResp(@[HttpGet, HttpPost])
get "/users": get "/users":
checkAuth(true) checkAuth(true)
@ -356,8 +320,6 @@ proc start*(ctx: PMApiContext): void =
error "Could not create new user:\n\t" & getCurrentExceptionMsg() error "Could not create new user:\n\t" & getCurrentExceptionMsg()
statusResp(Http500) statusResp(Http500)
options "/users/@userId": optionsResp(@[HttpGet, HttpDelete])
get "/users/@userId": get "/users/@userId":
checkAuth(true) checkAuth(true)
@ -378,8 +340,6 @@ proc start*(ctx: PMApiContext): void =
except: statusResp(Http500, getCurrentExceptionMsg()) except: statusResp(Http500, getCurrentExceptionMsg())
options "/api-tokens": optionsResp(@[HttpGet, HttpPost])
get "/api-tokens": get "/api-tokens":
checkAuth() checkAuth()
@ -414,8 +374,6 @@ proc start*(ctx: PMApiContext): void =
debug getCurrentExceptionMsg() debug getCurrentExceptionMsg()
statusResp(Http500) statusResp(Http500)
options "/api-tokens/@tokenId": optionsResp(@[HttpGet, HttpDelete])
get "/api-tokens/@tokenId": get "/api-tokens/@tokenId":
checkAuth() checkAuth()
@ -436,8 +394,6 @@ proc start*(ctx: PMApiContext): void =
# Measure # Measure
options "/measures": optionsResp(@[HttpGet, HttpPost])
get "/measures": get "/measures":
checkAuth() checkAuth()
@ -482,8 +438,6 @@ proc start*(ctx: PMApiContext): void =
error "unable to create new measure:\n\t" & getCurrentExceptionMsg() error "unable to create new measure:\n\t" & getCurrentExceptionMsg()
statusResp(Http500) statusResp(Http500)
options "/measures/@slug": optionsResp(@[HttpGet, HttpPost, HttpDelete])
get "/measures/@slug": get "/measures/@slug":
checkAuth() checkAuth()
@ -537,9 +491,6 @@ proc start*(ctx: PMApiContext): void =
statusResp(Http500) statusResp(Http500)
# Measurements # Measurements
options "/measurements/@slug": optionsResp(@[HttpGet, HttpPost])
get "/measurements/@slug": get "/measurements/@slug":
checkAuth() checkAuth()
@ -577,8 +528,6 @@ proc start*(ctx: PMApiContext): void =
error "unable to add measurement:\n\t" & getCurrentExceptionMsg() error "unable to add measurement:\n\t" & getCurrentExceptionMsg()
statusResp(Http500) statusResp(Http500)
options "/measurements/@slug/@id": optionsResp(@[HttpGet, HttpPut, HttpDelete])
get "/measurements/@slug/@id": get "/measurements/@slug/@id":
checkAuth() checkAuth()
@ -631,8 +580,6 @@ proc start*(ctx: PMApiContext): void =
error "unable to delete measurement:\n\t" & getCurrentExceptionMsg() error "unable to delete measurement:\n\t" & getCurrentExceptionMsg()
statusResp(Http500) statusResp(Http500)
options "/log": optionsResp(@[HttpPost])
post "/log": post "/log":
checkAuth() checkAuth()
@ -650,8 +597,6 @@ proc start*(ctx: PMApiContext): void =
except BadRequestError: statusResp(Http400, getCurrentExceptionMsg()) except BadRequestError: statusResp(Http400, getCurrentExceptionMsg())
except: statusResp(Http500, getCurrentExceptionMsg()) except: statusResp(Http500, getCurrentExceptionMsg())
options "/log/batch": optionsResp(@[HttpPost])
post "/log/batch": post "/log/batch":
checkAuth() checkAuth()

View File

@ -1,4 +1,4 @@
import db_postgres, fiber_orm, sequtils, uuids import db_postgres, fiber_orm, uuids
import ./models import ./models

View File

@ -1 +1 @@
const PM_API_VERSION* = "0.11.0" const PM_API_VERSION* = "0.9.0"

View File

@ -1,37 +1,24 @@
#!/bin/bash #!/bin/bash
api_base_url="${PM_API_BASE_URL:-http://localhost:8100/v0}" api_base_url="${PM_API_BASE_URL:-http://localhost:8081}"
if [ $# -eq 1 ]; then if [ $# -eq 1 ]; then
url="$1" url="$1"
method="GET" method="GET"
data="" data=""
elif [ $# -eq 2 ]; then elif [ $# -eq 2 ]; then
if [ $1 == "auth-token" ]; then
curl -s -X POST \
-H "Origin: https://curl.localhost" \
"${api_base_url}/auth-token" \
-d "$2" \
| xargs printf "Bearer %s" \
> credential
exit 0
else
method="$1" method="$1"
url="$2" url="$2"
data="" data=""
fi
else else
method="$1" method="$1"
url="$2" url="$2"
data="$3" data="$3"
fi fi
if [[ ! $url = /* ]]; then url="/$url"; fi
curl -s -X "$method" \ curl -s -X "$method" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-H "Authorization: $(cat credential)" \ -H "Authorization: $(cat credential)" \
-H "Origin: https://curl.localhost" \ -H "Origin: https://curl.localhost" \
"${api_base_url}$url" \ "${api_base_url}/api/$url" \
-d "$data" \ -d "$data" \
| jq . -v

View File

@ -1 +1 @@
010 007

View File

@ -1 +0,0 @@
### Support rolling averages in graph displays.

View File

@ -1,3 +0,0 @@
### Toggle Measure Visibility
Allow the user to choose whether a measure should be visible or hidden by default.

View File

@ -1,3 +0,0 @@
### Grouped Measures
Create a measure type that is just a grouping of several other measures. For example, it would be nice to be able to group all workout-related measures into one group. The graph could show an overlay of all the different measures on one graph.

View File

@ -0,0 +1,10 @@
<RoutingRules>
<RoutingRule>
<Condition>
<KeyPrefixEquals>api</KeyPrefixEquals>
</Condition>
<Redirect>
<HostName>https://pmapi.jdbernard.com</HostName>
</Redirect>
</RoutingRule>
</RoutingRules>

View File

@ -0,0 +1,33 @@
server {
listen 80;
server_name pmapi-dev.jdb-labs.com;
return 301 https://pmapi-dev.jdb-labs.com$request_uri;
}
server {
listen 443;
server_name pmapi-dev.jdb-labs.com;
ssl on;
location / {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' 'https://pm-dev.jdb-labs.com';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
return 204;
}
proxy_pass http://localhost:8281;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}

View File

@ -0,0 +1,33 @@
server {
listen 80;
server_name pmapi.jdb-labs.com;
return 301 https://pmapi.jdb-labs.com$request_uri;
}
server {
listen 443;
server_name pmapi.jdb-labs.com;
ssl on;
location / {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' 'https://pm.jdb-labs.com';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
return 204;
}
proxy_pass http://localhost:8280;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}

View File

@ -7,5 +7,5 @@ variable "aws_region" {
variable "app_root_url" { variable "app_root_url" {
description = "Name of the S3 bucket to store deployed artifacts, logs, etc." description = "Name of the S3 bucket to store deployed artifacts, logs, etc."
default = "pm.jdb-software.com" default = "pm.jdb-labs.com"
} }

View File

@ -1,25 +0,0 @@
resource "aws_route53_record" "app_domain" {
zone_id = data.terraform_remote_state.jdbsoft.outputs.aws_route53_zone_jdbsoft.zone_id
name = local.app_domain_name
type = "A"
alias {
name = aws_cloudfront_distribution.s3_distribution.domain_name
zone_id = aws_cloudfront_distribution.s3_distribution.hosted_zone_id
evaluate_target_health = false
}
depends_on = [aws_cloudfront_distribution.s3_distribution ]
}
resource "aws_route53_record" "api_domain" {
zone_id = data.terraform_remote_state.jdbsoft.outputs.aws_route53_zone_jdbsoft.zone_id
name = local.api_domain_name
type = "A"
alias {
name = data.terraform_remote_state.jdbsoft.outputs.aws_lb_jdbsoft.dns_name
zone_id = data.terraform_remote_state.jdbsoft.outputs.aws_lb_jdbsoft.zone_id
evaluate_target_health = false
}
}

View File

@ -1,75 +0,0 @@
resource "aws_secretsmanager_secret" "pmapi" {
name = "${local.environment_name}-Config"
tags = { Environment = local.environment_name }
}
resource "aws_ecs_task_definition" "pmapi" {
family = local.environment_name
network_mode = "bridge"
requires_compatibilities = ["EC2"]
execution_role_arn = aws_iam_role.ecs_task.arn
# See https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_ContainerDefinition.html
container_definitions = jsonencode([
{
name = local.environment_name
image = "${var.ecr_repo.repository_url}:${data.external.git_describe.result.version}"
cpu = 128
memory = 128
memoryReservation = 32
environment = [
{
name = "PORT"
value = "80"
}
]
portMappings = [
{
protocol = "tcp"
containerPort = 80
}
]
secrets = [
{
name = "AUTH_SECRET"
description = "Auth secret used to hash and salt passwords."
valueFrom = "${aws_secretsmanager_secret.pmapi.arn}:authSecret::"
},
{
name = "DB_CONN_STRING"
description = "Connection string with user credentials."
valueFrom = "${aws_secretsmanager_secret.pmapi.arn}:dbConnString::"
},
{
name = "KNOWN_ORIGINS"
description = "Connection string with user credentials."
valueFrom = "${aws_secretsmanager_secret.pmapi.arn}:knownOrigins::"
}
]
}
])
tags = {
Name = local.api_domain_name
Environment = local.environment_name
}
}
resource "aws_ecs_service" "pmapi" {
name = local.environment_name
cluster = data.terraform_remote_state.jdbsoft.outputs.aws_ecs_cluster_ortis.id
task_definition = aws_ecs_task_definition.pmapi.arn
desired_count = 1
launch_type = "EC2"
load_balancer {
target_group_arn = aws_lb_target_group.pmapi.arn
container_name = local.environment_name
container_port = 80
}
tags = {
Name = local.api_domain_name
Environment = local.environment_name
}
}

View File

@ -1,69 +0,0 @@
resource "aws_iam_role" "ecs_task" {
name = "${local.environment_name}-EcsTaskRole"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Sid = ""
Principal = {
Service = "ecs-tasks.amazonaws.com"
}
}
]
})
inline_policy {
name = "AllowSecretsAccessForPmApiTasks"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"secretsmanager:GetSecretValue",
"kms:Decrypt"
]
Resource = [
aws_secretsmanager_secret.pmapi.arn
]
}
]
})
}
inline_policy {
name = "AllowAccessToEcrForPmApiTasks"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"ecr:GetAuthorizationToken"
]
Resource = [ "*" ]
},
{
Effect = "Allow"
Action = [
"ecr:BatchGetImage",
"ecr:BatchCheckLayerAvailability",
"ecr:DescribeImages",
"ecr:GetDownloadUrlForLayer"
]
Resource = [
var.ecr_repo.arn
]
}
]
})
}
tags = {
Name = "PersonalMeasure-EcsTaskRole"
Environment = local.environment_name
}
}

View File

@ -1,43 +0,0 @@
resource "aws_lb_target_group" "pmapi" {
name = "${local.environment_name}-${substr(uuid(), 0, 2)}"
port = 80
protocol = "HTTP"
target_type = "instance"
vpc_id = data.terraform_remote_state.jdbsoft.outputs.aws_vpc_jdbsoft.id
health_check {
enabled = true
matcher = "200"
path = "/v0/version"
}
lifecycle {
create_before_destroy = true
ignore_changes = [name]
}
tags = {
Name = local.api_domain_name
Environment = local.environment_name
}
}
resource "aws_lb_listener_rule" "pmapi" {
listener_arn = data.terraform_remote_state.jdbsoft.outputs.aws_lb_listener_https.arn
action {
type = "forward"
target_group_arn = aws_lb_target_group.pmapi.arn
}
condition {
host_header {
values = [ local.api_domain_name ]
}
}
tags = {
Name = "${local.api_domain_name} HTTPS"
Environment = local.environment_name
}
}

View File

@ -6,18 +6,18 @@ data "aws_iam_policy_document" "bucket_access_policy" {
principals { principals {
type = "AWS" type = "AWS"
identifiers = [ aws_cloudfront_origin_access_identity.origin_access_identity.iam_arn ] identifiers = [ "${aws_cloudfront_origin_access_identity.origin_access_identity.iam_arn}" ]
} }
} }
statement { statement {
actions = [ "s3:ListBucket" ] actions = [ "s3:ListBucket" ]
effect = "Allow" effect = "Allow"
resources = [ var.artifact_bucket.arn ] resources = [ "${var.artifact_bucket.arn}" ]
principals { principals {
type = "AWS" type = "AWS"
identifiers = [ aws_cloudfront_origin_access_identity.origin_access_identity.iam_arn ] identifiers = [ "${aws_cloudfront_origin_access_identity.origin_access_identity.iam_arn}" ]
} }
} }
} }
@ -26,18 +26,22 @@ output "oai_access_policy" {
value = data.aws_iam_policy_document.bucket_access_policy value = data.aws_iam_policy_document.bucket_access_policy
} }
locals {
env_domain_name = "pm${var.environment == "prod" ? "" : "-${var.environment}"}.jdb-labs.com"
}
resource "aws_cloudfront_origin_access_identity" "origin_access_identity" { resource "aws_cloudfront_origin_access_identity" "origin_access_identity" {
comment = "OAI for Personal Measure {$var.environment} environment." comment = "OAI for Personal Measure {$var.environment} environment."
} }
resource "aws_cloudfront_distribution" "s3_distribution" { resource "aws_cloudfront_distribution" "s3_distribution" {
origin { origin {
domain_name = var.artifact_bucket.bucket_regional_domain_name domain_name = "${var.artifact_bucket.bucket_regional_domain_name}"
origin_id = "S3-PersonalMeasure-${var.environment}" origin_id = "S3-PersonalMeasure-${var.environment}"
origin_path = "/${var.environment}/webroot" origin_path = "/${var.environment}/webroot"
s3_origin_config { s3_origin_config {
origin_access_identity = aws_cloudfront_origin_access_identity.origin_access_identity.cloudfront_access_identity_path origin_access_identity = "${aws_cloudfront_origin_access_identity.origin_access_identity.cloudfront_access_identity_path}"
} }
} }
@ -48,11 +52,11 @@ resource "aws_cloudfront_distribution" "s3_distribution" {
logging_config { logging_config {
include_cookies = false include_cookies = false
bucket = var.artifact_bucket.bucket_domain_name bucket = "${var.artifact_bucket.bucket_domain_name}"
prefix = "${var.environment}/logs/cloudfront" prefix = "${var.environment}/logs/cloudfront"
} }
aliases = [local.app_domain_name] aliases = ["${local.env_domain_name}"]
default_cache_behavior { default_cache_behavior {
allowed_methods = ["GET", "HEAD", "OPTIONS"] allowed_methods = ["GET", "HEAD", "OPTIONS"]
@ -88,11 +92,11 @@ resource "aws_cloudfront_distribution" "s3_distribution" {
} }
} }
tags = { tags = {
Environment = local.environment_name Environment = "${var.environment}"
} }
viewer_certificate { viewer_certificate {
acm_certificate_arn = data.terraform_remote_state.jdbsoft.outputs.aws_acm_certificate_jdbsoft_us_east_1.arn acm_certificate_arn = "${var.cloudfront_ssl_certificate_arn}"
ssl_support_method = "sni-only" ssl_support_method = "sni-only"
} }
} }

View File

@ -8,27 +8,6 @@ variable "artifact_bucket" {
description = "The aws_s3_bucket object representing the artifact bucket where deployed artifacts, logs, etc. live." description = "The aws_s3_bucket object representing the artifact bucket where deployed artifacts, logs, etc. live."
} }
variable "ecr_repo" { variable "cloudfront_ssl_certificate_arn" {
description = "ECR repository information." description = "ARN of the managed SSL certificate to use for this environment."
}
locals {
environment_name = "PersonalMeasure-${var.environment}"
app_domain_name = "pm${var.environment == "prod" ? "" : "-${var.environment}"}.jdb-software.com"
api_domain_name = "pmapi${var.environment == "prod" ? "" : "-${var.environment}"}.jdb-software.com"
}
data "external" "git_describe" {
program = ["sh", "-c", "git describe | xargs printf '{\"version\": \"%s\"}'"]
}
data "terraform_remote_state" "jdbsoft" {
backend = "s3"
config = {
bucket = "operations.jdb-software.com"
region = "us-west-2"
key = "terraform/operations.tfstate"
dynamodb_table = "terraform-state-lock.jdb-software.com"
}
} }

View File

@ -1,8 +0,0 @@
resource "aws_ecr_repository" "personal_measure_api" {
name = "personal_measure_api"
image_tag_mutability = "IMMUTABLE"
image_scanning_configuration {
scan_on_push = true
}
}

View File

@ -3,16 +3,32 @@ provider "aws" {
} }
resource "aws_s3_bucket" "personal_measure" { resource "aws_s3_bucket" "personal_measure" {
bucket = var.app_root_url bucket = "${var.app_root_url}"
acl = "log-delivery-write" acl = "log-delivery-write"
} }
resource "aws_dynamodb_table" "dynamodb_terraform-state-lock" {
name = "terraform-state-lock.${var.app_root_url}"
hash_key = "LockID"
read_capacity = 20
write_capacity = 20
attribute {
name = "LockID"
type = "S"
}
tags = {
Name = "Terraform DynamoDB State Lock Table"
}
}
module "dev_env" { module "dev_env" {
source = "./deployed_env" source = "./deployed_env"
environment = "dev" environment = "dev"
artifact_bucket = aws_s3_bucket.personal_measure artifact_bucket = aws_s3_bucket.personal_measure
ecr_repo = aws_ecr_repository.personal_measure_api cloudfront_ssl_certificate_arn = "arn:aws:acm:us-east-1:063932952339:certificate/48fe3ce0-4700-4eaa-b433-bb634f47934c"
} }
module "prod_env" { module "prod_env" {
@ -20,7 +36,7 @@ module "prod_env" {
environment = "prod" environment = "prod"
artifact_bucket = aws_s3_bucket.personal_measure artifact_bucket = aws_s3_bucket.personal_measure
ecr_repo = aws_ecr_repository.personal_measure_api cloudfront_ssl_certificate_arn = "arn:aws:acm:us-east-1:063932952339:certificate/48fe3ce0-4700-4eaa-b433-bb634f47934c"
} }
data "aws_iam_policy_document" "cloudfront_access_policy" { data "aws_iam_policy_document" "cloudfront_access_policy" {
@ -29,6 +45,6 @@ data "aws_iam_policy_document" "cloudfront_access_policy" {
} }
resource "aws_s3_bucket_policy" "personal_measure" { resource "aws_s3_bucket_policy" "personal_measure" {
bucket = aws_s3_bucket.personal_measure.id bucket = "${aws_s3_bucket.personal_measure.id}"
policy = data.aws_iam_policy_document.cloudfront_access_policy.json policy = "${data.aws_iam_policy_document.cloudfront_access_policy.json}"
} }

View File

@ -1,8 +1,8 @@
terraform { terraform {
backend "s3" { backend "s3" {
bucket = "pm.jdb-software.com" bucket = "pm.jdb-labs.com"
region = "us-west-2" region = "us-west-2"
key = "terraform.tfstate" key = "terraform.tfstate"
dynamodb_table = "terraform-state-lock.jdb-software.com" dynamodb_table = "terraform-state-lock.pm.jdb-labs.com"
} }
} }

View File

@ -1,4 +1,4 @@
NODE_ENV=production NODE_ENV=production
VUE_APP_PM_API_BASE=https://pmapi-dev.jdb-software.com/v0 VUE_APP_PM_API_BASE=https://pmapi-dev.jdb-labs.com/v0
VUE_APP_LOG_LEVEL=INFO VUE_APP_LOG_LEVEL=INFO
VUE_APP_API_LOG_LEVEL=ERROR VUE_APP_API_LOG_LEVEL=ERROR

View File

@ -1,3 +1,3 @@
VUE_APP_PM_API_BASE=https://pmapi.jdb-software.com/v0 VUE_APP_PM_API_BASE=https://pmapi.jdb-labs.com/v0
VUE_APP_LOG_LEVEL=INFO VUE_APP_LOG_LEVEL=INFO
VUE_APP_API_LOG_LEVEL=ERROR VUE_APP_API_LOG_LEVEL=ERROR

334
web/package-lock.json generated
View File

@ -1,6 +1,6 @@
{ {
"name": "personal-measure-web", "name": "personal-measure-web",
"version": "0.11.0", "version": "0.9.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@ -829,30 +829,30 @@
} }
}, },
"@fortawesome/fontawesome-common-types": { "@fortawesome/fontawesome-common-types": {
"version": "0.2.31", "version": "0.2.27",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.31.tgz", "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.27.tgz",
"integrity": "sha512-xfnPyH6NN5r/h1/qDYoGB0BlHSID902UkQzxR8QsoKDh55KAPr8ruAoie12WQEEQT8lRE2wtV7LoUllJ1HqCag==" "integrity": "sha512-97GaByGaXDGMkzcJX7VmR/jRJd8h1mfhtA7RsxDBN61GnWE/PPCZhOdwG/8OZYktiRUF0CvFOr+VgRkJrt6TWg=="
}, },
"@fortawesome/fontawesome-svg-core": { "@fortawesome/fontawesome-svg-core": {
"version": "1.2.31", "version": "1.2.27",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.31.tgz", "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.27.tgz",
"integrity": "sha512-lqUWRK+ylHQJG5Kiez4XrAZAfc7snxCc+X59quL3xPfMnxzfyf1lt+/hD7X1ZL4KlzAH2KFzMuEVrolo/rAkog==", "integrity": "sha512-sOD3DKynocnHYpuw2sLPnTunDj7rLk91LYhi2axUYwuGe9cPCw7Bsu9EWtVdNJP+IYgTCZIbyARKXuy5K/nv+Q==",
"requires": { "requires": {
"@fortawesome/fontawesome-common-types": "^0.2.31" "@fortawesome/fontawesome-common-types": "^0.2.27"
} }
}, },
"@fortawesome/free-solid-svg-icons": { "@fortawesome/free-solid-svg-icons": {
"version": "5.15.0", "version": "5.12.1",
"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.0.tgz", "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.12.1.tgz",
"integrity": "sha512-4dGRsOnGBPM7c0fd3LuiU6LgRSLn01rw1LJ370yC2iFMLUcLCLLynZhQbMhsiJmMwQM/YmPQblAdyHKVCgsIAA==", "integrity": "sha512-k3MwRFFUhyL4cuCJSaHDA0YNYMELDXX0h8JKtWYxO5XD3Dn+maXOMrVAAiNGooUyM2v/wz/TOaM0jxYVKeXX7g==",
"requires": { "requires": {
"@fortawesome/fontawesome-common-types": "^0.2.31" "@fortawesome/fontawesome-common-types": "^0.2.27"
} }
}, },
"@fortawesome/vue-fontawesome": { "@fortawesome/vue-fontawesome": {
"version": "0.1.10", "version": "0.1.9",
"resolved": "https://registry.npmjs.org/@fortawesome/vue-fontawesome/-/vue-fontawesome-0.1.10.tgz", "resolved": "https://registry.npmjs.org/@fortawesome/vue-fontawesome/-/vue-fontawesome-0.1.9.tgz",
"integrity": "sha512-b2+SLF31h32LSepVcXe+BQ63yvbq5qmTCy4KfFogCYm2bn68H5sDWUnX+U7MBqnM2aeEk9M7xSoqGnu+wSdY6w==" "integrity": "sha512-h/emhmZz+DfB2zOGLWawNwXq82UYhn9waTfUjLLmeaIqtnIyNt6kYlpQT/vzJjLZRDRvY2IEJAh1di5qKpKVpA=="
}, },
"@hapi/address": { "@hapi/address": {
"version": "2.1.4", "version": "2.1.4",
@ -1012,9 +1012,9 @@
"dev": true "dev": true
}, },
"@types/js-cookie": { "@types/js-cookie": {
"version": "2.2.6", "version": "2.2.4",
"resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-2.2.6.tgz", "resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-2.2.4.tgz",
"integrity": "sha512-+oY0FDTO2GYKEV0YPvSshGq9t7YozVkgvXLty7zogQNuCxBhT9/3INX9Q7H1aRZ4SUDRXAKlJuA4EA5nTt7SNw==" "integrity": "sha512-WTfSE1Eauak/Nrg6cA9FgPTFvVawejsai6zXoq0QYTQ3mxONeRtGhKxa7wMlUzWWmzrmTeV+rwLjHgsCntdrsA=="
}, },
"@types/jwt-decode": { "@types/jwt-decode": {
"version": "2.2.1", "version": "2.2.1",
@ -1508,9 +1508,9 @@
"dev": true "dev": true
}, },
"@vue/test-utils": { "@vue/test-utils": {
"version": "1.1.0", "version": "1.0.0-beta.31",
"resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-1.0.0-beta.31.tgz",
"integrity": "sha512-M+3jtVqNYIrvzO5gaxogre5a5+96h0hN/dXw+5Lj0t+dp6fAhYcUjpLrC9j9cEEkl2Rcuh/gKYRUmR5N4vcqPw==", "integrity": "sha512-IlhSx5hyEVnbvDZ3P98R1jNmy88QAd/y66Upn4EcvxSD5D4hwOutl3dIdfmSTSXs4b9DIMDnEVjX7t00cvOnvg==",
"dev": true, "dev": true,
"requires": { "requires": {
"dom-event-types": "^1.0.0", "dom-event-types": "^1.0.0",
@ -1880,16 +1880,16 @@
"dev": true "dev": true
}, },
"apexcharts": { "apexcharts": {
"version": "3.21.0", "version": "3.15.6",
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.21.0.tgz", "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.15.6.tgz",
"integrity": "sha512-yeulUZCTG57swbJ5oIJIjgfRdIsvmC/2WJanrZxNGhjtZf2B9NaT95pEtbrml1BILJKtMn4VbpXVZp+8Tzmydg==", "integrity": "sha512-8mZqg7eTZGU2zvjYUUOf+sTqgfmutipHU9lNgkqzZPtwIVGwR5PwXTBNKRJSI3AeSoQ8VZGYfzTJWoUDfGAeBw==",
"requires": { "requires": {
"svg.draggable.js": "^2.2.2", "svg.draggable.js": "^2.2.2",
"svg.easing.js": "^2.0.0", "svg.easing.js": "^2.0.0",
"svg.filter.js": "^2.0.2", "svg.filter.js": "^2.0.2",
"svg.pathmorphing.js": "^0.1.3", "svg.pathmorphing.js": "^0.1.3",
"svg.resize.js": "^1.4.3", "svg.resize.js": "^1.4.3",
"svg.select.js": "^3.0.1" "svg.select.js": "^2.1.2"
} }
}, },
"append-transform": { "append-transform": {
@ -6984,9 +6984,9 @@
} }
}, },
"globule": { "globule": {
"version": "1.3.2", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/globule/-/globule-1.3.2.tgz", "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.0.tgz",
"integrity": "sha512-7IDTQTIu2xzXkT+6mlluidnWo+BypnbSoEVVQCGfzqnl5Ik8d3e1d4wycb8Rj9tWW+Z39uPWsdlquqiqPCd/pA==", "integrity": "sha512-YlD4kdMqRCQHrhVdonet4TdRtv1/sZKepvoxNT4Nrhrp5HI8XFfc8kFlGlBn2myBo80aGp8Eft259mbcUJhgSg==",
"dev": true, "dev": true,
"requires": { "requires": {
"glob": "~7.1.1", "glob": "~7.1.1",
@ -7598,9 +7598,9 @@
"dev": true "dev": true
}, },
"in-publish": { "in-publish": {
"version": "2.0.1", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.1.tgz", "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz",
"integrity": "sha512-oDM0kUSNFC31ShNxHKUyfZKy8ZeXZBWMjMdZHKLOk13uvT27VTL/QzRGfRUcevJhpkZAvlhPYuXkF7eNWrtyxQ==", "integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E=",
"dev": true "dev": true
}, },
"indent-string": { "indent-string": {
@ -7674,6 +7674,12 @@
"loose-envify": "^1.0.0" "loose-envify": "^1.0.0"
} }
}, },
"invert-kv": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
"integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=",
"dev": true
},
"ip": { "ip": {
"version": "1.1.5", "version": "1.1.5",
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
@ -9270,9 +9276,9 @@
} }
}, },
"js-base64": { "js-base64": {
"version": "2.6.4", "version": "2.5.1",
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.1.tgz",
"integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==", "integrity": "sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==",
"dev": true "dev": true
}, },
"js-beautify": { "js-beautify": {
@ -9465,9 +9471,9 @@
"integrity": "sha1-fYa9VmefWM5qhHBKZX3TkruoGnk=" "integrity": "sha1-fYa9VmefWM5qhHBKZX3TkruoGnk="
}, },
"keen-ui": { "keen-ui": {
"version": "1.3.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/keen-ui/-/keen-ui-1.3.1.tgz", "resolved": "https://registry.npmjs.org/keen-ui/-/keen-ui-1.2.1.tgz",
"integrity": "sha512-2EAZy2YFdthCRtZvDHXvMZUTwvHda70WcjbEUaJKM1oH5q9rgecL80VBsTmmcIvfuratIEisBBiteojw3XEa5g==", "integrity": "sha512-SyF3orIjl098Du4b1UoXNDmdASxn/hsn7NO0JSoDI4LKmFUs8dP9uywfK+QEnDCev73jSZ3tdJELJKOjV/dl3Q==",
"requires": { "requires": {
"autosize": "^3.0.20", "autosize": "^3.0.20",
"deepmerge": "^2.0.1", "deepmerge": "^2.0.1",
@ -9520,6 +9526,15 @@
"launch-editor": "^2.2.1" "launch-editor": "^2.2.1"
} }
}, },
"lcid": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
"integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
"dev": true,
"requires": {
"invert-kv": "^1.0.0"
}
},
"left-pad": { "left-pad": {
"version": "1.3.0", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz",
@ -10483,9 +10498,9 @@
} }
}, },
"moment": { "moment": {
"version": "2.29.0", "version": "2.24.0",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.0.tgz", "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
"integrity": "sha512-z6IJ5HXYiuxvFTI6eiQ9dm77uE0gyy1yXNApVHqTcnIKfY9tIwEjlzsZ6u1LQXvVgKeTnv9Xm7NDvJ7lso3MtA==" "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="
}, },
"morgan": { "morgan": {
"version": "1.9.1", "version": "1.9.1",
@ -10771,9 +10786,9 @@
} }
}, },
"node-sass": { "node-sass": {
"version": "4.14.1", "version": "4.13.1",
"resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.14.1.tgz", "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.13.1.tgz",
"integrity": "sha512-sjCuOlvGyCJS40R8BscF5vhVlQjNN069NtQ1gSxyK1u9iqvn6tf7O1R4GNowVZfiZUCRt5MmMs1xd+4V/7Yr0g==", "integrity": "sha512-TTWFx+ZhyDx1Biiez2nB0L3YrCZ/8oHagaDalbuBSlqXgUPsdkUSzJsVxeDO9LtPB49+Fh3WQl3slABo6AotNw==",
"dev": true, "dev": true,
"requires": { "requires": {
"async-foreach": "^0.1.3", "async-foreach": "^0.1.3",
@ -10790,7 +10805,7 @@
"node-gyp": "^3.8.0", "node-gyp": "^3.8.0",
"npmlog": "^4.0.0", "npmlog": "^4.0.0",
"request": "^2.88.0", "request": "^2.88.0",
"sass-graph": "2.2.5", "sass-graph": "^2.2.4",
"stdout-stream": "^1.4.0", "stdout-stream": "^1.4.0",
"true-case-path": "^1.0.2" "true-case-path": "^1.0.2"
}, },
@ -10841,9 +10856,9 @@
} }
}, },
"nan": { "nan": {
"version": "2.14.1", "version": "2.14.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
"integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==", "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==",
"dev": true "dev": true
}, },
"strip-ansi": { "strip-ansi": {
@ -12715,9 +12730,9 @@
} }
}, },
"register-service-worker": { "register-service-worker": {
"version": "1.7.1", "version": "1.6.2",
"resolved": "https://registry.npmjs.org/register-service-worker/-/register-service-worker-1.7.1.tgz", "resolved": "https://registry.npmjs.org/register-service-worker/-/register-service-worker-1.6.2.tgz",
"integrity": "sha512-IdTfUZ4u8iJL8o1w8es8l6UMGPmkwHolUdT+UmM1UypC80IB4KbpuIlvwWVj8UDS7eJwkEYRcKRgfRX+oTmJsw==" "integrity": "sha512-I8L87fX2TK29LDx+wgyOUh2BJ3rDIRC1FtRZEHeP3rivzDv6p1DDZLGGtPucqjEkm45+2crtFIFssEWv56+9Wg=="
}, },
"regjsgen": { "regjsgen": {
"version": "0.5.1", "version": "0.5.1",
@ -13051,137 +13066,118 @@
} }
}, },
"sass-graph": { "sass-graph": {
"version": "2.2.5", "version": "2.2.4",
"resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.5.tgz", "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz",
"integrity": "sha512-VFWDAHOe6mRuT4mZRd4eKE+d8Uedrk6Xnh7Sh9b4NGufQLQjOrvf/MQoOdx+0s92L89FeyUUNfU597j/3uNpag==", "integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=",
"dev": true, "dev": true,
"requires": { "requires": {
"glob": "^7.0.0", "glob": "^7.0.0",
"lodash": "^4.0.0", "lodash": "^4.0.0",
"scss-tokenizer": "^0.2.3", "scss-tokenizer": "^0.2.3",
"yargs": "^13.3.2" "yargs": "^7.0.0"
}, },
"dependencies": { "dependencies": {
"ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
"dev": true
},
"camelcase": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
"integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=",
"dev": true
},
"cliui": { "cliui": {
"version": "5.0.0", "version": "3.2.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
"integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
"dev": true, "dev": true,
"requires": { "requires": {
"string-width": "^3.1.0", "string-width": "^1.0.1",
"strip-ansi": "^5.2.0", "strip-ansi": "^3.0.1",
"wrap-ansi": "^5.1.0" "wrap-ansi": "^2.0.0"
} }
}, },
"emoji-regex": { "is-fullwidth-code-point": {
"version": "7.0.3", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
"integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
"dev": true
},
"find-up": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
"dev": true, "dev": true,
"requires": { "requires": {
"locate-path": "^3.0.0" "number-is-nan": "^1.0.0"
} }
}, },
"get-caller-file": { "os-locale": {
"version": "2.0.5", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
"dev": true
},
"locate-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
"integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
"dev": true, "dev": true,
"requires": { "requires": {
"p-locate": "^3.0.0", "lcid": "^1.0.0"
"path-exists": "^3.0.0"
} }
}, },
"p-limit": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"dev": true,
"requires": {
"p-try": "^2.0.0"
}
},
"p-locate": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
"integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
"dev": true,
"requires": {
"p-limit": "^2.0.0"
}
},
"p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"dev": true
},
"require-main-filename": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
"dev": true
},
"string-width": { "string-width": {
"version": "3.1.0", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
"dev": true, "dev": true,
"requires": { "requires": {
"emoji-regex": "^7.0.1", "code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^2.0.0", "is-fullwidth-code-point": "^1.0.0",
"strip-ansi": "^5.1.0" "strip-ansi": "^3.0.0"
} }
}, },
"wrap-ansi": { "strip-ansi": {
"version": "5.1.0", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true, "dev": true,
"requires": { "requires": {
"ansi-styles": "^3.2.0", "ansi-regex": "^2.0.0"
"string-width": "^3.0.0",
"strip-ansi": "^5.0.0"
} }
}, },
"which-module": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz",
"integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=",
"dev": true
},
"y18n": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
"integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=",
"dev": true
},
"yargs": { "yargs": {
"version": "13.3.2", "version": "7.1.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz",
"integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=",
"dev": true, "dev": true,
"requires": { "requires": {
"cliui": "^5.0.0", "camelcase": "^3.0.0",
"find-up": "^3.0.0", "cliui": "^3.2.0",
"get-caller-file": "^2.0.1", "decamelize": "^1.1.1",
"get-caller-file": "^1.0.1",
"os-locale": "^1.4.0",
"read-pkg-up": "^1.0.1",
"require-directory": "^2.1.1", "require-directory": "^2.1.1",
"require-main-filename": "^2.0.0", "require-main-filename": "^1.0.1",
"set-blocking": "^2.0.0", "set-blocking": "^2.0.0",
"string-width": "^3.0.0", "string-width": "^1.0.2",
"which-module": "^2.0.0", "which-module": "^1.0.0",
"y18n": "^4.0.0", "y18n": "^3.2.1",
"yargs-parser": "^13.1.2" "yargs-parser": "^5.0.0"
} }
}, },
"yargs-parser": { "yargs-parser": {
"version": "13.1.2", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz",
"integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=",
"dev": true, "dev": true,
"requires": { "requires": {
"camelcase": "^5.0.0", "camelcase": "^3.0.0"
"decamelize": "^1.2.0"
} }
} }
} }
@ -13419,12 +13415,6 @@
} }
} }
}, },
"servor": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/servor/-/servor-4.0.2.tgz",
"integrity": "sha512-MlmQ5Ntv4jDYUN060x/KEmN7emvIqKMZ9OkM+nY8Bf2+KkyLmGsTqWLyAN2cZr5oESAcH00UanUyyrlS1LRjFw==",
"dev": true
},
"set-blocking": { "set-blocking": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
@ -14289,8 +14279,8 @@
"requires": { "requires": {
"svg.js": "^2.6.5", "svg.js": "^2.6.5",
"svg.select.js": "^2.1.2" "svg.select.js": "^2.1.2"
}
}, },
"dependencies": {
"svg.select.js": { "svg.select.js": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-2.1.2.tgz", "resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-2.1.2.tgz",
@ -14298,16 +14288,6 @@
"requires": { "requires": {
"svg.js": "^2.2.5" "svg.js": "^2.2.5"
} }
}
}
},
"svg.select.js": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-3.0.1.tgz",
"integrity": "sha512-h5IS/hKkuVCbKSieR9uQCj9w+zLHoPh+ce19bBYyqF53g6mnPB8sAtIbe1s9dh2S2fCmYX2xel1Ln3PJBbK4kw==",
"requires": {
"svg.js": "^2.6.5"
}
}, },
"svgo": { "svgo": {
"version": "1.3.2", "version": "1.3.2",
@ -14910,9 +14890,9 @@
"dev": true "dev": true
}, },
"typescript": { "typescript": {
"version": "3.9.7", "version": "3.7.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.5.tgz",
"integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==", "integrity": "sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==",
"dev": true "dev": true
}, },
"uglify-js": { "uglify-js": {
@ -15226,14 +15206,14 @@
"dev": true "dev": true
}, },
"vue": { "vue": {
"version": "2.6.12", "version": "2.6.11",
"resolved": "https://registry.npmjs.org/vue/-/vue-2.6.12.tgz", "resolved": "https://registry.npmjs.org/vue/-/vue-2.6.11.tgz",
"integrity": "sha512-uhmLFETqPPNyuLLbsKz6ioJ4q7AZHzD8ZVFNATNyICSZouqP2Sz0rotWQC8UNBF6VGSCs5abnKJoStA6JbCbfg==" "integrity": "sha512-VfPwgcGABbGAue9+sfrD4PuwFar7gPb1yl1UK1MwXoQPAw0BKSqWfoYCT/ThFrdEVWoI51dBuyCoiNU9bZDZxQ=="
}, },
"vue-apexcharts": { "vue-apexcharts": {
"version": "1.6.0", "version": "1.5.2",
"resolved": "https://registry.npmjs.org/vue-apexcharts/-/vue-apexcharts-1.6.0.tgz", "resolved": "https://registry.npmjs.org/vue-apexcharts/-/vue-apexcharts-1.5.2.tgz",
"integrity": "sha512-sT6tuVTLBwfH3TA7azecDNS/W70bmz14ZJI7aE7QIqcG9I6OywyH7x3hcOeY1v1DxttI8Svc5RuYj4Dd+A5F4g==" "integrity": "sha512-m7IIyql4yU6cLTu5RODx3DcdxCekmNRzUh7lEoybq2MXcgabmBPhUn8qgXNx1HucWiMNOdXfwq/L6TfCbKnfMw=="
}, },
"vue-class-component": { "vue-class-component": {
"version": "6.3.2", "version": "6.3.2",
@ -15295,9 +15275,9 @@
} }
}, },
"vue-router": { "vue-router": {
"version": "3.4.5", "version": "3.1.5",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.4.5.tgz", "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.1.5.tgz",
"integrity": "sha512-ioRY5QyDpXM9TDjOX6hX79gtaMXSVDDzSlbIlyAmbHNteIL81WIVB2e+jbzV23vzxtoV0krdS2XHm+GxFg+Nxg==" "integrity": "sha512-BszkPvhl7I9h334GjckCh7sVFyjTPMMJFJ4Bsrem/Ik+B/9gt5tgrk8k4gGLO4ZpdvciVdg7O41gW4DisQWurg=="
}, },
"vue-style-loader": { "vue-style-loader": {
"version": "4.1.2", "version": "4.1.2",
@ -15310,9 +15290,9 @@
} }
}, },
"vue-template-compiler": { "vue-template-compiler": {
"version": "2.6.12", "version": "2.6.11",
"resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.12.tgz", "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.11.tgz",
"integrity": "sha512-OzzZ52zS41YUbkCBfdXShQTe69j1gQDZ9HIX8miuC9C3rBCk9wIRjLiZZLrmX9V+Ftq/YEyv1JaVr5Y/hNtByg==", "integrity": "sha512-KIq15bvQDrcCjpGjrAhx4mUlyyHfdmTaoNfeoATHLAiWB+MU3cx4lOzMwrnUh9cCxy0Lt1T11hAFY6TQgroUAA==",
"dev": true, "dev": true,
"requires": { "requires": {
"de-indent": "^1.0.2", "de-indent": "^1.0.2",
@ -15334,9 +15314,9 @@
} }
}, },
"vuex": { "vuex": {
"version": "3.5.1", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/vuex/-/vuex-3.5.1.tgz", "resolved": "https://registry.npmjs.org/vuex/-/vuex-3.1.2.tgz",
"integrity": "sha512-w7oJzmHQs0FM9LXodfskhw9wgKBiaB+totOdb8sNzbTB2KDCEEwEs29NzBZFh/lmEK1t5tDmM1vtsO7ubG1DFw==" "integrity": "sha512-ha3jNLJqNhhrAemDXcmMJMKf1Zu4sybMPr9KxJIuOpVcsDQlTBYLLladav2U+g1AvdYDG5Gs0xBTb0M5pXXYFQ=="
}, },
"vuex-module-decorators": { "vuex-module-decorators": {
"version": "0.9.11", "version": "0.9.11",

View File

@ -1,43 +1,43 @@
{ {
"name": "personal-measure-web", "name": "personal-measure-web",
"version": "0.11.0", "version": "0.9.0",
"private": true, "private": true,
"scripts": { "scripts": {
"serve": "npx servor dist", "serve": "vue-cli-service serve",
"build-prod": "vue-cli-service build --mode production", "build-prod": "vue-cli-service build --mode production",
"build-dev": "vue-cli-service build --mode development", "build-dev": "vue-cli-service build --mode development",
"lint": "vue-cli-service lint", "lint": "vue-cli-service lint",
"test:unit": "vue-cli-service test:unit" "test:unit": "vue-cli-service test:unit"
}, },
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-svg-core": "^1.2.31", "@fortawesome/fontawesome-svg-core": "^1.2.27",
"@fortawesome/free-solid-svg-icons": "^5.15.0", "@fortawesome/free-solid-svg-icons": "^5.12.1",
"@fortawesome/vue-fontawesome": "^0.1.10", "@fortawesome/vue-fontawesome": "^0.1.9",
"@types/js-cookie": "^2.2.6", "@types/js-cookie": "^2.2.4",
"@types/jwt-decode": "^2.2.1", "@types/jwt-decode": "^2.2.1",
"@types/lodash.assign": "^4.2.6", "@types/lodash.assign": "^4.2.6",
"@types/lodash.findindex": "^4.6.6", "@types/lodash.findindex": "^4.6.6",
"@types/lodash.merge": "^4.6.6", "@types/lodash.merge": "^4.6.6",
"@types/lodash.omit": "^4.5.6", "@types/lodash.omit": "^4.5.6",
"apexcharts": "^3.21.0", "apexcharts": "^3.15.6",
"axios": "^0.18.1", "axios": "^0.18.1",
"js-cookie": "^2.2.1", "js-cookie": "^2.2.1",
"jwt-decode": "^2.2.0", "jwt-decode": "^2.2.0",
"keen-ui": "^1.3.1", "keen-ui": "^1.2.1",
"lodash.assign": "^4.2.0", "lodash.assign": "^4.2.0",
"lodash.findindex": "^4.6.0", "lodash.findindex": "^4.6.0",
"lodash.keyby": "^4.6.0", "lodash.keyby": "^4.6.0",
"lodash.merge": "^4.6.2", "lodash.merge": "^4.6.2",
"lodash.omit": "^4.5.0", "lodash.omit": "^4.5.0",
"moment": "^2.29.0", "moment": "^2.24.0",
"register-service-worker": "^1.7.1", "register-service-worker": "^1.5.2",
"vue": "^2.6.12", "vue": "^2.6.11",
"vue-apexcharts": "^1.6.0", "vue-apexcharts": "^1.5.2",
"vue-class-component": "^6.0.0", "vue-class-component": "^6.0.0",
"vue-property-decorator": "^7.0.0", "vue-property-decorator": "^7.0.0",
"vue-router": "^3.4.5", "vue-router": "^3.1.5",
"vuejs-smart-table": "0.0.3", "vuejs-smart-table": "0.0.3",
"vuex": "^3.5.1", "vuex": "^3.1.2",
"vuex-module-decorators": "^0.9.11" "vuex-module-decorators": "^0.9.11"
}, },
"devDependencies": { "devDependencies": {
@ -48,17 +48,16 @@
"@vue/cli-plugin-typescript": "^3.12.1", "@vue/cli-plugin-typescript": "^3.12.1",
"@vue/cli-plugin-unit-jest": "^3.12.1", "@vue/cli-plugin-unit-jest": "^3.12.1",
"@vue/cli-service": "^3.12.1", "@vue/cli-service": "^3.12.1",
"@vue/test-utils": "^1.1.0", "@vue/test-utils": "^1.0.0-beta.31",
"babel-core": "7.0.0-bridge.0", "babel-core": "7.0.0-bridge.0",
"lint-staged": "^8.2.1", "lint-staged": "^8.2.1",
"live-server": "^1.2.1", "live-server": "^1.2.1",
"node-sass": "^4.14.1", "node-sass": "^4.13.1",
"sass-loader": "^7.3.1", "sass-loader": "^7.3.1",
"servor": "^4.0.2",
"ts-jest": "^23.0.0", "ts-jest": "^23.0.0",
"typescript": "^3.9.7", "typescript": "^3.7.5",
"vue-cli-plugin-webpack-bundle-analyzer": "^1.4.0", "vue-cli-plugin-webpack-bundle-analyzer": "^1.4.0",
"vue-template-compiler": "^2.6.12" "vue-template-compiler": "^2.6.11"
}, },
"gitHooks": { "gitHooks": {
"pre-commit": "lint-staged" "pre-commit": "lint-staged"