14 Commits

Author SHA1 Message Date
3416d2b85b Update package version to 0.10.0 2021-07-05 02:01:16 -05:00
f29b1a0967 operations: Update primary Makefile to reflect new API ECS-based build and deploy process. 2021-07-05 02:00:49 -05:00
e3f214d0da api: Update API to support Options requests for CORS. 2021-07-05 01:59:46 -05:00
c987d66504 api: Update Dockerfile and Makefile to support building and pushing to ECR. 2021-07-05 01:59:16 -05:00
bc06fc54bb operations: Complete migration to AWS ECS. 2021-07-05 01:57:39 -05:00
99a4c1fc94 web: Update environment configurations for jdb-software. 2021-07-05 00:17:17 -05:00
87ce9cc4d4 operations: WIP continuing definition for ECS-based API deployment. 2021-07-03 03:36:41 -05:00
c2c4c8473d Update Makefile to disable obsolete API deployment flow. 2021-07-03 03:35:59 -05:00
bb89f519e0 operations: WIP moving API to run as an ECS task. 2021-07-03 01:30:51 -05:00
20e0a0b09e api: Clean up Dockerfile, rebase onto Nim 1.4.8. 2021-07-03 01:30:26 -05:00
1449e1ffdd api: Updates for Nim 1.4.x. 2021-07-03 01:26:56 -05:00
526419afb3 api: Change dependencies from jdb-labs.com -> jdb-software.com 2021-07-03 01:26:24 -05:00
327c64f45a web: Update dependencies. 2021-03-07 17:53:32 -06:00
06e3bb5ea3 Switch to servor instead of the build-in vue-cli-service server for development tools. 2020-07-06 18:37:13 -05:00
28 changed files with 559 additions and 336 deletions

View File

@ -5,14 +5,9 @@ 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
dist/personal-measure-api.tar.gz: -docker image prune
-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
@ -20,18 +15,14 @@ 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: dist/personal-measure-api.tar.gz deploy-api:
mkdir -p temp-deploy/personal-measure-api-${VERSION} make -C api personal_measure_api-image push-image
tar xzf dist/personal-measure-api-${VERSION}.tar.gz -C 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
-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-labs.com/$(TARGET_ENV)/webroot aws s3 sync temp-deploy/personal-measure-web-${VERSION} s3://pm.jdb-software.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,21 +1,17 @@
FROM 063932952339.dkr.ecr.us-west-2.amazonaws.com/nim-alpine AS build FROM 063932952339.dkr.ecr.us-west-2.amazonaws.com/alpine-nim:nim-1.4.8 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
#RUN apt-get install -y postgresql-client EXPOSE 80
RUN apk -v --update add --no-cache \ RUN apk -v --update add --no-cache \
ca-certificates \ ca-certificates \
libressl2.7-libssl \ libcrypto1.1 \
libressl2.7-libcrypto \ libssl1.1 \
pcre \ pcre \
postgresql-client postgresql-client

View File

@ -1,10 +1,15 @@
PGSQL_CONTAINER_ID=`cat postgres.container.id` PGSQL_CONTAINER_ID=`cat postgres.container.id`
ECR_ACCOUNT_URL=063932952339.dkr.ecr.us-west-2.amazonaws.com
DB_NAME="personal_measure" DB_NAME="personal_measure"
VERSION=`git describe`
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)
serve: personal_measure_api start-postgres serve-local: personal_measure_api start-postgres
./personal_measure_api serve ./personal_measure_api serve
serve-docker: personal_measure_api-image start-postgres
docker run -e AUTH_SECRET=abc123 -e "DB_CONN_STRING=host=host.docker.internal port=5500 user=postgres password=password dbname=personal_measure" -e PORT=80 -p 127.0.0.1:8100:80/tcp $(ECR_ACCOUNT_URL)/personal_measure_api:$(VERSION)
postgres.container.id: postgres.container.id:
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
@ -27,5 +32,14 @@ delete-postgres-container:
connect: connect:
PGPASSWORD=password psql -p 5500 -U postgres -h localhost ${DB_NAME} PGPASSWORD=password psql -p 5500 -U postgres -h localhost ${DB_NAME}
ecr-auth:
aws ecr get-login-password --region us-west-2 | docker login --username AWS --password-stdin 063932952339.dkr.ecr.us-west-2.amazonaws.com
personal_measure_api: $(SOURCES) personal_measure_api: $(SOURCES)
nimble build nimble build
personal_measure_api-image: $(SOURCES)
docker image build -t $(ECR_ACCOUNT_URL)/personal_measure_api:$(VERSION) .
push-image: personal_measure_api-image
docker push $(ECR_ACCOUNT_URL)/personal_measure_api:$(VERSION)

View File

@ -1,6 +1,5 @@
{ {
"debug":false, "debug":false,
"port":80,
"pwdCost":11, "pwdCost":11,
"knownOrigins": [ "https://pm.jdb-labs.com" ] "knownOrigins": [ "https://pm.jdb-software.com", "https://pm-dev.jdb-software.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.9.0" version = "0.10.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-labs.com/jdb/nim-cli-utils.git >= 0.6.3" requires "https://git.jdb-software.com/jdb/nim-cli-utils.git >= 0.6.3"
requires "https://git.jdb-labs.com/jdb/nim-time-utils.git >= 0.5.2" requires "https://git.jdb-software.com/jdb/nim-time-utils.git >= 0.5.2"
requires "https://git.jdb-labs.com/jdb-labs/fiber-orm-nim.git >= 0.3.0" requires "https://git.jdb-software.com/jdb-software/fiber-orm-nim.git >= 0.3.1"

View File

@ -27,7 +27,7 @@ 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 existsFile(filePath): if not fileExists(filePath):
info "created new configuration file \"" & filePath & "\"" info "created new configuration file \"" & filePath & "\""
filePath.writeFile($json) filePath.writeFile($json)
else: else:
@ -114,6 +114,6 @@ Options:
if args["serve"]: start(ctx) if args["serve"]: start(ctx)
except: except:
fatal "pit: " & getCurrentExceptionMsg() fatal "personal_measure_api: " & getCurrentExceptionMsg()
#raise getCurrentException() #raise getCurrentException()
quit(QuitFailure) quit(QuitFailure)

View File

@ -1,5 +1,6 @@
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
@ -58,6 +59,29 @@ 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 = @{:} ) =
@ -97,7 +121,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): raiseEx "Unable to verify auth token." if not jwt.verify(secret, HS256): raiseEx "Unable to verify auth token."
jwt.verifyTimeClaims() jwt.verifyTimeClaims()
# Find the user record (if authenticated) # Find the user record (if authenticated)
@ -212,9 +236,13 @@ 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:
@ -226,6 +254,8 @@ 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()
@ -247,6 +277,8 @@ 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)
@ -268,6 +300,8 @@ 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()
@ -292,6 +326,8 @@ 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)
@ -320,6 +356,8 @@ 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)
@ -340,6 +378,8 @@ 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()
@ -374,6 +414,8 @@ 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()
@ -394,6 +436,8 @@ proc start*(ctx: PMApiContext): void =
# Measure # Measure
options "/measures": optionsResp(@[HttpGet, HttpPost])
get "/measures": get "/measures":
checkAuth() checkAuth()
@ -438,6 +482,8 @@ 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()
@ -491,6 +537,9 @@ 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()
@ -528,6 +577,8 @@ 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()
@ -580,6 +631,8 @@ 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()
@ -597,6 +650,8 @@ 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, uuids import db_postgres, fiber_orm, sequtils, uuids
import ./models import ./models
@ -8,7 +8,7 @@ type
PMApiDb* = ref object PMApiDb* = ref object
conn: DbConn conn: DbConn
proc connect*(connString: string): PMApiDb = proc connect*(connString: string): PMApiDb =
result = PMApiDb(conn: open("", "", "", connString)) result = PMApiDb(conn: open("", "", "", connString))

View File

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

View File

@ -1 +1 @@
007 008

View File

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

View File

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

View File

@ -1,33 +0,0 @@
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

@ -1,33 +0,0 @@
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-labs.com" default = "pm.jdb-software.com"
} }

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,22 +26,18 @@ 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
} }
} }
@ -52,11 +48,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.env_domain_name}"] aliases = [local.app_domain_name]
default_cache_behavior { default_cache_behavior {
allowed_methods = ["GET", "HEAD", "OPTIONS"] allowed_methods = ["GET", "HEAD", "OPTIONS"]
@ -92,11 +88,11 @@ resource "aws_cloudfront_distribution" "s3_distribution" {
} }
} }
tags = { tags = {
Environment = "${var.environment}" Environment = local.environment_name
} }
viewer_certificate { viewer_certificate {
acm_certificate_arn = "${var.cloudfront_ssl_certificate_arn}" acm_certificate_arn = data.terraform_remote_state.jdbsoft.outputs.aws_acm_certificate_jdbsoft_us_east_1.arn
ssl_support_method = "sni-only" ssl_support_method = "sni-only"
} }
} }

View File

@ -0,0 +1,25 @@
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

@ -0,0 +1,75 @@
resource "aws_secretsmanager_secret" "pmapi_auth" {
name = "${local.environment_name}-AuthSecret"
tags = { Environment = local.environment_name }
}
resource "aws_secretsmanager_secret" "pmapi_db_conn_string" {
name = "${local.environment_name}-DbConnString"
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_auth.arn
},
{
name = "DB_CONN_STRING"
description = "Connection string with user credentials."
valueFrom = aws_secretsmanager_secret.pmapi_db_conn_string.arn
}
]
}
])
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

@ -0,0 +1,70 @@
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_auth.arn,
aws_secretsmanager_secret.pmapi_db_conn_string.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

@ -0,0 +1,43 @@
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

@ -8,6 +8,27 @@ 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 "cloudfront_ssl_certificate_arn" { variable "ecr_repo" {
description = "ARN of the managed SSL certificate to use for this environment." description = "ECR repository information."
}
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

@ -0,0 +1,8 @@
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,40 +3,24 @@ 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
cloudfront_ssl_certificate_arn = "arn:aws:acm:us-east-1:063932952339:certificate/48fe3ce0-4700-4eaa-b433-bb634f47934c" ecr_repo = aws_ecr_repository.personal_measure_api
} }
module "prod_env" { module "prod_env" {
source = "./deployed_env" source = "./deployed_env"
environment = "prod" environment = "prod"
artifact_bucket = aws_s3_bucket.personal_measure artifact_bucket = aws_s3_bucket.personal_measure
cloudfront_ssl_certificate_arn = "arn:aws:acm:us-east-1:063932952339:certificate/48fe3ce0-4700-4eaa-b433-bb634f47934c" ecr_repo = aws_ecr_repository.personal_measure_api
} }
data "aws_iam_policy_document" "cloudfront_access_policy" { data "aws_iam_policy_document" "cloudfront_access_policy" {
@ -45,6 +29,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-labs.com" bucket = "pm.jdb-software.com"
region = "us-west-2" region = "us-west-2"
key = "terraform.tfstate" key = "terraform.tfstate"
dynamodb_table = "terraform-state-lock.pm.jdb-labs.com" dynamodb_table = "terraform-state-lock.jdb-software.com"
} }
} }

View File

@ -1,4 +1,4 @@
NODE_ENV=production NODE_ENV=production
VUE_APP_PM_API_BASE=https://pmapi-dev.jdb-labs.com/v0 VUE_APP_PM_API_BASE=https://pmapi-dev.jdb-software.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-labs.com/v0 VUE_APP_PM_API_BASE=https://pmapi.jdb-software.com/v0
VUE_APP_LOG_LEVEL=INFO VUE_APP_LOG_LEVEL=INFO
VUE_APP_API_LOG_LEVEL=ERROR VUE_APP_API_LOG_LEVEL=ERROR

340
web/package-lock.json generated
View File

@ -1,6 +1,6 @@
{ {
"name": "personal-measure-web", "name": "personal-measure-web",
"version": "0.9.0", "version": "0.10.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.27", "version": "0.2.31",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.27.tgz", "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.31.tgz",
"integrity": "sha512-97GaByGaXDGMkzcJX7VmR/jRJd8h1mfhtA7RsxDBN61GnWE/PPCZhOdwG/8OZYktiRUF0CvFOr+VgRkJrt6TWg==" "integrity": "sha512-xfnPyH6NN5r/h1/qDYoGB0BlHSID902UkQzxR8QsoKDh55KAPr8ruAoie12WQEEQT8lRE2wtV7LoUllJ1HqCag=="
}, },
"@fortawesome/fontawesome-svg-core": { "@fortawesome/fontawesome-svg-core": {
"version": "1.2.27", "version": "1.2.31",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.27.tgz", "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.31.tgz",
"integrity": "sha512-sOD3DKynocnHYpuw2sLPnTunDj7rLk91LYhi2axUYwuGe9cPCw7Bsu9EWtVdNJP+IYgTCZIbyARKXuy5K/nv+Q==", "integrity": "sha512-lqUWRK+ylHQJG5Kiez4XrAZAfc7snxCc+X59quL3xPfMnxzfyf1lt+/hD7X1ZL4KlzAH2KFzMuEVrolo/rAkog==",
"requires": { "requires": {
"@fortawesome/fontawesome-common-types": "^0.2.27" "@fortawesome/fontawesome-common-types": "^0.2.31"
} }
}, },
"@fortawesome/free-solid-svg-icons": { "@fortawesome/free-solid-svg-icons": {
"version": "5.12.1", "version": "5.15.0",
"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.12.1.tgz", "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.0.tgz",
"integrity": "sha512-k3MwRFFUhyL4cuCJSaHDA0YNYMELDXX0h8JKtWYxO5XD3Dn+maXOMrVAAiNGooUyM2v/wz/TOaM0jxYVKeXX7g==", "integrity": "sha512-4dGRsOnGBPM7c0fd3LuiU6LgRSLn01rw1LJ370yC2iFMLUcLCLLynZhQbMhsiJmMwQM/YmPQblAdyHKVCgsIAA==",
"requires": { "requires": {
"@fortawesome/fontawesome-common-types": "^0.2.27" "@fortawesome/fontawesome-common-types": "^0.2.31"
} }
}, },
"@fortawesome/vue-fontawesome": { "@fortawesome/vue-fontawesome": {
"version": "0.1.9", "version": "0.1.10",
"resolved": "https://registry.npmjs.org/@fortawesome/vue-fontawesome/-/vue-fontawesome-0.1.9.tgz", "resolved": "https://registry.npmjs.org/@fortawesome/vue-fontawesome/-/vue-fontawesome-0.1.10.tgz",
"integrity": "sha512-h/emhmZz+DfB2zOGLWawNwXq82UYhn9waTfUjLLmeaIqtnIyNt6kYlpQT/vzJjLZRDRvY2IEJAh1di5qKpKVpA==" "integrity": "sha512-b2+SLF31h32LSepVcXe+BQ63yvbq5qmTCy4KfFogCYm2bn68H5sDWUnX+U7MBqnM2aeEk9M7xSoqGnu+wSdY6w=="
}, },
"@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.4", "version": "2.2.6",
"resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-2.2.4.tgz", "resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-2.2.6.tgz",
"integrity": "sha512-WTfSE1Eauak/Nrg6cA9FgPTFvVawejsai6zXoq0QYTQ3mxONeRtGhKxa7wMlUzWWmzrmTeV+rwLjHgsCntdrsA==" "integrity": "sha512-+oY0FDTO2GYKEV0YPvSshGq9t7YozVkgvXLty7zogQNuCxBhT9/3INX9Q7H1aRZ4SUDRXAKlJuA4EA5nTt7SNw=="
}, },
"@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.0.0-beta.31", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-1.0.0-beta.31.tgz", "resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-1.1.0.tgz",
"integrity": "sha512-IlhSx5hyEVnbvDZ3P98R1jNmy88QAd/y66Upn4EcvxSD5D4hwOutl3dIdfmSTSXs4b9DIMDnEVjX7t00cvOnvg==", "integrity": "sha512-M+3jtVqNYIrvzO5gaxogre5a5+96h0hN/dXw+5Lj0t+dp6fAhYcUjpLrC9j9cEEkl2Rcuh/gKYRUmR5N4vcqPw==",
"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.15.6", "version": "3.21.0",
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.15.6.tgz", "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.21.0.tgz",
"integrity": "sha512-8mZqg7eTZGU2zvjYUUOf+sTqgfmutipHU9lNgkqzZPtwIVGwR5PwXTBNKRJSI3AeSoQ8VZGYfzTJWoUDfGAeBw==", "integrity": "sha512-yeulUZCTG57swbJ5oIJIjgfRdIsvmC/2WJanrZxNGhjtZf2B9NaT95pEtbrml1BILJKtMn4VbpXVZp+8Tzmydg==",
"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": "^2.1.2" "svg.select.js": "^3.0.1"
} }
}, },
"append-transform": { "append-transform": {
@ -6984,9 +6984,9 @@
} }
}, },
"globule": { "globule": {
"version": "1.3.0", "version": "1.3.2",
"resolved": "https://registry.npmjs.org/globule/-/globule-1.3.0.tgz", "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.2.tgz",
"integrity": "sha512-YlD4kdMqRCQHrhVdonet4TdRtv1/sZKepvoxNT4Nrhrp5HI8XFfc8kFlGlBn2myBo80aGp8Eft259mbcUJhgSg==", "integrity": "sha512-7IDTQTIu2xzXkT+6mlluidnWo+BypnbSoEVVQCGfzqnl5Ik8d3e1d4wycb8Rj9tWW+Z39uPWsdlquqiqPCd/pA==",
"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.0", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz", "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.1.tgz",
"integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E=", "integrity": "sha512-oDM0kUSNFC31ShNxHKUyfZKy8ZeXZBWMjMdZHKLOk13uvT27VTL/QzRGfRUcevJhpkZAvlhPYuXkF7eNWrtyxQ==",
"dev": true "dev": true
}, },
"indent-string": { "indent-string": {
@ -7674,12 +7674,6 @@
"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",
@ -9276,9 +9270,9 @@
} }
}, },
"js-base64": { "js-base64": {
"version": "2.5.1", "version": "2.6.4",
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.1.tgz", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz",
"integrity": "sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==", "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==",
"dev": true "dev": true
}, },
"js-beautify": { "js-beautify": {
@ -9471,9 +9465,9 @@
"integrity": "sha1-fYa9VmefWM5qhHBKZX3TkruoGnk=" "integrity": "sha1-fYa9VmefWM5qhHBKZX3TkruoGnk="
}, },
"keen-ui": { "keen-ui": {
"version": "1.2.1", "version": "1.3.1",
"resolved": "https://registry.npmjs.org/keen-ui/-/keen-ui-1.2.1.tgz", "resolved": "https://registry.npmjs.org/keen-ui/-/keen-ui-1.3.1.tgz",
"integrity": "sha512-SyF3orIjl098Du4b1UoXNDmdASxn/hsn7NO0JSoDI4LKmFUs8dP9uywfK+QEnDCev73jSZ3tdJELJKOjV/dl3Q==", "integrity": "sha512-2EAZy2YFdthCRtZvDHXvMZUTwvHda70WcjbEUaJKM1oH5q9rgecL80VBsTmmcIvfuratIEisBBiteojw3XEa5g==",
"requires": { "requires": {
"autosize": "^3.0.20", "autosize": "^3.0.20",
"deepmerge": "^2.0.1", "deepmerge": "^2.0.1",
@ -9526,15 +9520,6 @@
"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",
@ -10498,9 +10483,9 @@
} }
}, },
"moment": { "moment": {
"version": "2.24.0", "version": "2.29.0",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.0.tgz",
"integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" "integrity": "sha512-z6IJ5HXYiuxvFTI6eiQ9dm77uE0gyy1yXNApVHqTcnIKfY9tIwEjlzsZ6u1LQXvVgKeTnv9Xm7NDvJ7lso3MtA=="
}, },
"morgan": { "morgan": {
"version": "1.9.1", "version": "1.9.1",
@ -10786,9 +10771,9 @@
} }
}, },
"node-sass": { "node-sass": {
"version": "4.13.1", "version": "4.14.1",
"resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.13.1.tgz", "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.14.1.tgz",
"integrity": "sha512-TTWFx+ZhyDx1Biiez2nB0L3YrCZ/8oHagaDalbuBSlqXgUPsdkUSzJsVxeDO9LtPB49+Fh3WQl3slABo6AotNw==", "integrity": "sha512-sjCuOlvGyCJS40R8BscF5vhVlQjNN069NtQ1gSxyK1u9iqvn6tf7O1R4GNowVZfiZUCRt5MmMs1xd+4V/7Yr0g==",
"dev": true, "dev": true,
"requires": { "requires": {
"async-foreach": "^0.1.3", "async-foreach": "^0.1.3",
@ -10805,7 +10790,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.4", "sass-graph": "2.2.5",
"stdout-stream": "^1.4.0", "stdout-stream": "^1.4.0",
"true-case-path": "^1.0.2" "true-case-path": "^1.0.2"
}, },
@ -10856,9 +10841,9 @@
} }
}, },
"nan": { "nan": {
"version": "2.14.0", "version": "2.14.1",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz",
"integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==",
"dev": true "dev": true
}, },
"strip-ansi": { "strip-ansi": {
@ -12730,9 +12715,9 @@
} }
}, },
"register-service-worker": { "register-service-worker": {
"version": "1.6.2", "version": "1.7.1",
"resolved": "https://registry.npmjs.org/register-service-worker/-/register-service-worker-1.6.2.tgz", "resolved": "https://registry.npmjs.org/register-service-worker/-/register-service-worker-1.7.1.tgz",
"integrity": "sha512-I8L87fX2TK29LDx+wgyOUh2BJ3rDIRC1FtRZEHeP3rivzDv6p1DDZLGGtPucqjEkm45+2crtFIFssEWv56+9Wg==" "integrity": "sha512-IdTfUZ4u8iJL8o1w8es8l6UMGPmkwHolUdT+UmM1UypC80IB4KbpuIlvwWVj8UDS7eJwkEYRcKRgfRX+oTmJsw=="
}, },
"regjsgen": { "regjsgen": {
"version": "0.5.1", "version": "0.5.1",
@ -13066,118 +13051,137 @@
} }
}, },
"sass-graph": { "sass-graph": {
"version": "2.2.4", "version": "2.2.5",
"resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz", "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.5.tgz",
"integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=", "integrity": "sha512-VFWDAHOe6mRuT4mZRd4eKE+d8Uedrk6Xnh7Sh9b4NGufQLQjOrvf/MQoOdx+0s92L89FeyUUNfU597j/3uNpag==",
"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": "^7.0.0" "yargs": "^13.3.2"
}, },
"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": "3.2.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
"integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
"dev": true, "dev": true,
"requires": { "requires": {
"string-width": "^1.0.1", "string-width": "^3.1.0",
"strip-ansi": "^3.0.1", "strip-ansi": "^5.2.0",
"wrap-ansi": "^2.0.0" "wrap-ansi": "^5.1.0"
} }
}, },
"is-fullwidth-code-point": { "emoji-regex": {
"version": "1.0.0", "version": "7.0.3",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
"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": {
"number-is-nan": "^1.0.0" "locate-path": "^3.0.0"
} }
}, },
"os-locale": { "get-caller-file": {
"version": "1.4.0", "version": "2.0.5",
"resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"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": {
"lcid": "^1.0.0" "p-locate": "^3.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": "1.0.2", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
"dev": true, "dev": true,
"requires": { "requires": {
"code-point-at": "^1.0.0", "emoji-regex": "^7.0.1",
"is-fullwidth-code-point": "^1.0.0", "is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^3.0.0" "strip-ansi": "^5.1.0"
} }
}, },
"strip-ansi": { "wrap-ansi": {
"version": "3.0.1", "version": "5.1.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
"dev": true, "dev": true,
"requires": { "requires": {
"ansi-regex": "^2.0.0" "ansi-styles": "^3.2.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": "7.1.0", "version": "13.3.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
"integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
"dev": true, "dev": true,
"requires": { "requires": {
"camelcase": "^3.0.0", "cliui": "^5.0.0",
"cliui": "^3.2.0", "find-up": "^3.0.0",
"decamelize": "^1.1.1", "get-caller-file": "^2.0.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": "^1.0.1", "require-main-filename": "^2.0.0",
"set-blocking": "^2.0.0", "set-blocking": "^2.0.0",
"string-width": "^1.0.2", "string-width": "^3.0.0",
"which-module": "^1.0.0", "which-module": "^2.0.0",
"y18n": "^3.2.1", "y18n": "^4.0.0",
"yargs-parser": "^5.0.0" "yargs-parser": "^13.1.2"
} }
}, },
"yargs-parser": { "yargs-parser": {
"version": "5.0.0", "version": "13.1.2",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
"integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
"dev": true, "dev": true,
"requires": { "requires": {
"camelcase": "^3.0.0" "camelcase": "^5.0.0",
"decamelize": "^1.2.0"
} }
} }
} }
@ -13415,6 +13419,12 @@
} }
} }
}, },
"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",
@ -14279,14 +14289,24 @@
"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": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-2.1.2.tgz",
"integrity": "sha512-tH6ABEyJsAOVAhwcCjF8mw4crjXSI1aa7j2VQR8ZuJ37H2MBUbyeqYr5nEO7sSN3cy9AR9DUwNg0t/962HlDbQ==",
"requires": {
"svg.js": "^2.2.5"
}
}
} }
}, },
"svg.select.js": { "svg.select.js": {
"version": "2.1.2", "version": "3.0.1",
"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-3.0.1.tgz",
"integrity": "sha512-tH6ABEyJsAOVAhwcCjF8mw4crjXSI1aa7j2VQR8ZuJ37H2MBUbyeqYr5nEO7sSN3cy9AR9DUwNg0t/962HlDbQ==", "integrity": "sha512-h5IS/hKkuVCbKSieR9uQCj9w+zLHoPh+ce19bBYyqF53g6mnPB8sAtIbe1s9dh2S2fCmYX2xel1Ln3PJBbK4kw==",
"requires": { "requires": {
"svg.js": "^2.2.5" "svg.js": "^2.6.5"
} }
}, },
"svgo": { "svgo": {
@ -14890,9 +14910,9 @@
"dev": true "dev": true
}, },
"typescript": { "typescript": {
"version": "3.7.5", "version": "3.9.7",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.5.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz",
"integrity": "sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==", "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==",
"dev": true "dev": true
}, },
"uglify-js": { "uglify-js": {
@ -15206,14 +15226,14 @@
"dev": true "dev": true
}, },
"vue": { "vue": {
"version": "2.6.11", "version": "2.6.12",
"resolved": "https://registry.npmjs.org/vue/-/vue-2.6.11.tgz", "resolved": "https://registry.npmjs.org/vue/-/vue-2.6.12.tgz",
"integrity": "sha512-VfPwgcGABbGAue9+sfrD4PuwFar7gPb1yl1UK1MwXoQPAw0BKSqWfoYCT/ThFrdEVWoI51dBuyCoiNU9bZDZxQ==" "integrity": "sha512-uhmLFETqPPNyuLLbsKz6ioJ4q7AZHzD8ZVFNATNyICSZouqP2Sz0rotWQC8UNBF6VGSCs5abnKJoStA6JbCbfg=="
}, },
"vue-apexcharts": { "vue-apexcharts": {
"version": "1.5.2", "version": "1.6.0",
"resolved": "https://registry.npmjs.org/vue-apexcharts/-/vue-apexcharts-1.5.2.tgz", "resolved": "https://registry.npmjs.org/vue-apexcharts/-/vue-apexcharts-1.6.0.tgz",
"integrity": "sha512-m7IIyql4yU6cLTu5RODx3DcdxCekmNRzUh7lEoybq2MXcgabmBPhUn8qgXNx1HucWiMNOdXfwq/L6TfCbKnfMw==" "integrity": "sha512-sT6tuVTLBwfH3TA7azecDNS/W70bmz14ZJI7aE7QIqcG9I6OywyH7x3hcOeY1v1DxttI8Svc5RuYj4Dd+A5F4g=="
}, },
"vue-class-component": { "vue-class-component": {
"version": "6.3.2", "version": "6.3.2",
@ -15275,9 +15295,9 @@
} }
}, },
"vue-router": { "vue-router": {
"version": "3.1.5", "version": "3.4.5",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.1.5.tgz", "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.4.5.tgz",
"integrity": "sha512-BszkPvhl7I9h334GjckCh7sVFyjTPMMJFJ4Bsrem/Ik+B/9gt5tgrk8k4gGLO4ZpdvciVdg7O41gW4DisQWurg==" "integrity": "sha512-ioRY5QyDpXM9TDjOX6hX79gtaMXSVDDzSlbIlyAmbHNteIL81WIVB2e+jbzV23vzxtoV0krdS2XHm+GxFg+Nxg=="
}, },
"vue-style-loader": { "vue-style-loader": {
"version": "4.1.2", "version": "4.1.2",
@ -15290,9 +15310,9 @@
} }
}, },
"vue-template-compiler": { "vue-template-compiler": {
"version": "2.6.11", "version": "2.6.12",
"resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.11.tgz", "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.12.tgz",
"integrity": "sha512-KIq15bvQDrcCjpGjrAhx4mUlyyHfdmTaoNfeoATHLAiWB+MU3cx4lOzMwrnUh9cCxy0Lt1T11hAFY6TQgroUAA==", "integrity": "sha512-OzzZ52zS41YUbkCBfdXShQTe69j1gQDZ9HIX8miuC9C3rBCk9wIRjLiZZLrmX9V+Ftq/YEyv1JaVr5Y/hNtByg==",
"dev": true, "dev": true,
"requires": { "requires": {
"de-indent": "^1.0.2", "de-indent": "^1.0.2",
@ -15314,9 +15334,9 @@
} }
}, },
"vuex": { "vuex": {
"version": "3.1.2", "version": "3.5.1",
"resolved": "https://registry.npmjs.org/vuex/-/vuex-3.1.2.tgz", "resolved": "https://registry.npmjs.org/vuex/-/vuex-3.5.1.tgz",
"integrity": "sha512-ha3jNLJqNhhrAemDXcmMJMKf1Zu4sybMPr9KxJIuOpVcsDQlTBYLLladav2U+g1AvdYDG5Gs0xBTb0M5pXXYFQ==" "integrity": "sha512-w7oJzmHQs0FM9LXodfskhw9wgKBiaB+totOdb8sNzbTB2KDCEEwEs29NzBZFh/lmEK1t5tDmM1vtsO7ubG1DFw=="
}, },
"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.9.0", "version": "0.10.0",
"private": true, "private": true,
"scripts": { "scripts": {
"serve": "vue-cli-service serve", "serve": "npx servor dist",
"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.27", "@fortawesome/fontawesome-svg-core": "^1.2.31",
"@fortawesome/free-solid-svg-icons": "^5.12.1", "@fortawesome/free-solid-svg-icons": "^5.15.0",
"@fortawesome/vue-fontawesome": "^0.1.9", "@fortawesome/vue-fontawesome": "^0.1.10",
"@types/js-cookie": "^2.2.4", "@types/js-cookie": "^2.2.6",
"@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.15.6", "apexcharts": "^3.21.0",
"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.2.1", "keen-ui": "^1.3.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.24.0", "moment": "^2.29.0",
"register-service-worker": "^1.5.2", "register-service-worker": "^1.7.1",
"vue": "^2.6.11", "vue": "^2.6.12",
"vue-apexcharts": "^1.5.2", "vue-apexcharts": "^1.6.0",
"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.1.5", "vue-router": "^3.4.5",
"vuejs-smart-table": "0.0.3", "vuejs-smart-table": "0.0.3",
"vuex": "^3.1.2", "vuex": "^3.5.1",
"vuex-module-decorators": "^0.9.11" "vuex-module-decorators": "^0.9.11"
}, },
"devDependencies": { "devDependencies": {
@ -48,16 +48,17 @@
"@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.0.0-beta.31", "@vue/test-utils": "^1.1.0",
"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.13.1", "node-sass": "^4.14.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.7.5", "typescript": "^3.9.7",
"vue-cli-plugin-webpack-bundle-analyzer": "^1.4.0", "vue-cli-plugin-webpack-bundle-analyzer": "^1.4.0",
"vue-template-compiler": "^2.6.11" "vue-template-compiler": "^2.6.12"
}, },
"gitHooks": { "gitHooks": {
"pre-commit": "lint-staged" "pre-commit": "lint-staged"