From fc021122958b4ebee9f31a8edf824e047bc17a9d Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Fri, 15 Sep 2017 22:38:45 -0500 Subject: [PATCH 1/4] Looks promising The proof of concept has been proven --- .gitignore | 4 +- .../20170104023341_InitialDb.fs | 0 src/api/App.fs | 195 ----- src/api/Data.fs | 57 -- src/api/Dependencies.fs | 48 - src/api/Entities.fs | 131 --- src/api/Extensions.fs | 16 - .../Migrations/DataContextModelSnapshot.fs | 61 -- src/api/MyPrayerJournal.fsproj | 30 - src/api/db/index.js | 18 + src/api/db/request.js | 15 + src/api/index.js | 60 ++ src/api/package.json | 21 + src/api/routes/index.js | 7 + src/api/routes/journal.js | 15 + src/api/yarn.lock | 826 ++++++++++++++++++ src/app/config/index.js | 4 +- src/app/src/api/index.js | 8 +- src/app/src/store/index.js | 5 +- 19 files changed, 974 insertions(+), 547 deletions(-) rename src/{api/Migrations => }/20170104023341_InitialDb.fs (100%) delete mode 100644 src/api/App.fs delete mode 100644 src/api/Data.fs delete mode 100644 src/api/Dependencies.fs delete mode 100644 src/api/Entities.fs delete mode 100644 src/api/Extensions.fs delete mode 100644 src/api/Migrations/DataContextModelSnapshot.fs delete mode 100644 src/api/MyPrayerJournal.fsproj create mode 100644 src/api/db/index.js create mode 100644 src/api/db/request.js create mode 100644 src/api/index.js create mode 100644 src/api/package.json create mode 100644 src/api/routes/index.js create mode 100644 src/api/routes/journal.js create mode 100644 src/api/yarn.lock diff --git a/.gitignore b/.gitignore index 1b13f4b..3814ec6 100644 --- a/.gitignore +++ b/.gitignore @@ -253,7 +253,7 @@ paket-files/ *.sln.iml # Compiled files / application -src/api/wwwroot/index.html -src/api/wwwroot/static +src/api/public/index.html +src/api/public/static src/api/appsettings.json build/ \ No newline at end of file diff --git a/src/api/Migrations/20170104023341_InitialDb.fs b/src/20170104023341_InitialDb.fs similarity index 100% rename from src/api/Migrations/20170104023341_InitialDb.fs rename to src/20170104023341_InitialDb.fs diff --git a/src/api/App.fs b/src/api/App.fs deleted file mode 100644 index e868f5c..0000000 --- a/src/api/App.fs +++ /dev/null @@ -1,195 +0,0 @@ -/// Main server module for myPrayerJournal -module MyPrayerJournal.App - -open Microsoft.EntityFrameworkCore -open Newtonsoft.Json -open Newtonsoft.Json.Linq -open System -open System.IO -open Suave -open Suave.Filters -open Suave.Operators - -// --- Types --- - -/// Auth0 settings -type Auth0Config = { - /// The domain used with Auth0 - Domain : string - /// The client Id - ClientId : string - /// The base64-encoded client secret - ClientSecret : string - /// The URL-safe base64-encoded client secret - ClientSecretJwt : string - } -with - /// An empty set of Auth0 settings - static member empty = - { Domain = "" - ClientId = "" - ClientSecret = "" - ClientSecretJwt = "" - } - -/// Application configuration -type AppConfig = { - /// PostgreSQL connection string - Conn : string - /// Auth0 settings - Auth0 : Auth0Config - } -with - static member empty = - { Conn = "" - Auth0 = Auth0Config.empty - } - -/// A JSON response as a data property -type JsonOkResponse<'a> = { - data : 'a - } - -/// A JSON response indicating an error occurred -type JsonErrorResponse = { - error : string -} - - -/// Configuration instances -module Config = - - /// Application configuration - let app = - try - use sr = File.OpenText "appsettings.json" - use tr = new JsonTextReader (sr) - let settings = JToken.ReadFrom tr - let secret = settings.["auth0"].["client-secret"].ToObject() - { Conn = settings.["conn"].ToObject() - Auth0 = - { Domain = settings.["auth0"].["domain"].ToObject() - ClientId = settings.["auth0"].["client-id"].ToObject() - ClientSecret = secret - ClientSecretJwt = secret.TrimEnd('=').Replace("-", "+").Replace("_", "/") - } - } - with _ -> AppConfig.empty - - /// Custom Suave configuration - let suave = - { defaultConfig with - homeFolder = Some (Path.GetFullPath "./wwwroot/") - serverKey = Text.Encoding.UTF8.GetBytes("12345678901234567890123456789012") - bindings = [ HttpBinding.createSimple HTTP "127.0.0.1" 8084 ] - } - - -/// Authorization functions -module Auth = - - /// Shorthand for Console.WriteLine - let cw (x : string) = Console.WriteLine x - - /// Convert microtime to ticks, add difference from 1/1/1 to 1/1/1970 - let jsDate jsTicks = - DateTime(jsTicks * 10000000L).AddTicks(DateTime(1970, 1, 1).Ticks) - - /// Get the user Id (sub) from a JSON Web Token - let getIdFromToken jwt = - try - let payload = Jose.JWT.Decode(jwt, Config.app.Auth0.ClientSecretJwt) - let tokenExpires = jsDate (payload.["exp"].ToObject()) - match tokenExpires > DateTime.UtcNow with - | true -> Some (payload.["sub"].ToObject()) - | _ -> None - with ex -> - sprintf "Token Deserialization Exception - %s" (ex.GetType().FullName) |> cw - sprintf "Message - %s" ex.Message |> cw - ex.StackTrace |> cw - None - - /// Add the logged on user Id to the context if it exists - let loggedOn = - warbler (fun ctx -> - match ctx.request.header "Authorization" with - | Choice1Of2 bearer -> Writers.setUserData "user" (getIdFromToken <| bearer.Split(' ').[1]) - | _ -> Writers.setUserData "user" None) - - -// --- Support --- - -/// Get the scheme, host, and port of the URL -let schemeHostPort (req : HttpRequest) = - sprintf "%s://%s" req.url.Scheme (req.headers |> List.filter (fun x -> fst x = "host") |> List.head |> snd) - -/// Serialize an object to JSON -let toJson = JsonConvert.SerializeObject - -/// Read an item from the user state, downcast to the expected type -let read ctx key : 'value = - ctx.userState |> Map.tryFind key |> Option.map (fun x -> x :?> 'value) |> Option.get - -/// Create a new data context -let dataCtx () = - new DataContext (((DbContextOptionsBuilder()).UseNpgsql Config.app.Conn).Options) - -/// Ensure the EF context is created in the right format -let ensureDatabase () = - async { - use data = dataCtx () - do! data.Database.MigrateAsync () - } - |> Async.RunSynchronously - - -/// URL routes for myPrayerJournal -module Route = - - /// /api/journal ~ All active prayer requests for a user - let journal = "/api/journal" - -/// All WebParts that compose the public API -module WebParts = - - let jsonMimeType = - warbler (fun ctx -> Writers.setMimeType "application/json; charset=utf8") - - /// WebPart to return a JSON response - let JSON payload = - jsonMimeType - >=> Successful.OK (toJson { data = payload }) - - /// WebPart to return an JSON error response - let errorJSON code error = - jsonMimeType - >=> Response.response code ((toJson >> Text.Encoding.UTF8.GetBytes) { error = error }) - - /// Journal page - let viewJournal = - context (fun ctx -> - use dataCtx = dataCtx () - let reqs = Data.Requests.allForUser (defaultArg (read ctx "user") "") dataCtx - JSON reqs) - - /// API-specific routes - let apiRoutes = - choose [ - GET >=> path Route.journal >=> viewJournal - errorJSON HttpCode.HTTP_404 "Page not found" - ] - - /// Suave application - let app = - Auth.loggedOn - >=> choose [ - path "/api" >=> apiRoutes - Files.browseHome - Files.browseFileHome "index.html" - ] - -[] -let main argv = - ensureDatabase () - startWebServer Config.suave WebParts.app - 0 diff --git a/src/api/Data.fs b/src/api/Data.fs deleted file mode 100644 index ce9e79c..0000000 --- a/src/api/Data.fs +++ /dev/null @@ -1,57 +0,0 @@ -namespace MyPrayerJournal - -open Microsoft.EntityFrameworkCore -open System.Linq -open System.Runtime.CompilerServices - -/// Data context for myPrayerJournal -type DataContext = - inherit DbContext - - (*--- CONSTRUCTORS ---*) - - new () = { inherit DbContext () } - new (options : DbContextOptions) = { inherit DbContext (options) } - - (*--- DbSet FIELDS ---*) - - [] - val mutable private requests : DbSet - [] - val mutable private history : DbSet - - (*--- DbSet PROPERTIES ---*) - - /// Prayer Requests - member this.Requests with get () = this.requests and set v = this.requests <- v - - /// History - member this.History with get () = this.history and set v = this.history <- v - - override this.OnModelCreating (modelBuilder) = - base.OnModelCreating modelBuilder - - modelBuilder.HasDefaultSchema "mpj" - |> Request.ConfigureEF - |> History.ConfigureEF - |> ignore - -/// Data access -module Data = - - /// Data access for prayer requests - module Requests = - - /// Get all prayer requests for a user - let allForUser userId (ctx : DataContext) = - query { - for req in ctx.Requests do - where (req.UserId = userId) - select req - } - |> Seq.sortBy - (fun req -> - match req.History |> Seq.sortBy (fun hist -> hist.AsOf) |> Seq.tryLast with - | Some hist -> hist.AsOf - | _ -> 0L) - |> List.ofSeq diff --git a/src/api/Dependencies.fs b/src/api/Dependencies.fs deleted file mode 100644 index 1eea244..0000000 --- a/src/api/Dependencies.fs +++ /dev/null @@ -1,48 +0,0 @@ -namespace MyPrayerJournal - -//open RethinkDb.Driver.Net - -// -- begin code lifted from #er demo -- -type ReaderM<'d, 'out> = 'd -> 'out - -module Reader = - // basic operations - let run dep (rm : ReaderM<_,_>) = rm dep - let constant (c : 'c) : ReaderM<_,'c> = fun _ -> c - // lifting of functions and state - let lift1 (f : 'd -> 'a -> 'out) : 'a -> ReaderM<'d, 'out> = fun a dep -> f dep a - let lift2 (f : 'd -> 'a -> 'b -> 'out) : 'a -> 'b -> ReaderM<'d, 'out> = fun a b dep -> f dep a b - let lift3 (f : 'd -> 'a -> 'b -> 'c -> 'out) : 'a -> 'b -> 'c -> ReaderM<'d, 'out> = fun a b c dep -> f dep a b c - let liftDep (proj : 'd2 -> 'd1) (rm : ReaderM<'d1, 'output>) : ReaderM<'d2, 'output> = proj >> rm - // functor - let fmap (f : 'a -> 'b) (g : 'c -> 'a) : ('c -> 'b) = g >> f - let map (f : 'a -> 'b) (rm : ReaderM<'d, 'a>) : ReaderM<'d,'b> = rm >> f - let () = map - // applicative-functor - let apply (f : ReaderM<'d, 'a->'b>) (rm : ReaderM<'d, 'a>) : ReaderM<'d, 'b> = - fun dep -> - let f' = run dep f - let a = run dep rm - f' a - let (<*>) = apply - // monad - let bind (rm : ReaderM<'d, 'a>) (f : 'a -> ReaderM<'d,'b>) : ReaderM<'d, 'b> = - fun dep -> - f (rm dep) - |> run dep - let (>>=) = bind - type ReaderMBuilder internal () = - member __.Bind(m, f) = m >>= f - member __.Return(v) = constant v - member __.ReturnFrom(v) = v - member __.Delay(f) = f () - let reader = ReaderMBuilder() -// -- end code lifted from #er demo -- - -(*type IDependencies = - abstract Conn : IConnection - -[] -module DependencyExtraction = - - let getConn (deps : IDependencies) = deps.Conn*) diff --git a/src/api/Entities.fs b/src/api/Entities.fs deleted file mode 100644 index 0c271df..0000000 --- a/src/api/Entities.fs +++ /dev/null @@ -1,131 +0,0 @@ -namespace MyPrayerJournal - -open Microsoft.EntityFrameworkCore; -open Newtonsoft.Json -open System -open System.Collections.Generic - -/// A prayer request -[] -type Request() = - /// The history collection (can be overridden) - let mutable historyCollection : ICollection = upcast List () - - /// The Id of the prayer request - member val RequestId = Guid.Empty with get, set - /// The Id of the user to whom the request belongs - member val UserId = "" with get, set - /// The ticks when the request was entered - member val EnteredOn = 0L with get, set - - /// The history for the prayer request - abstract History : ICollection with get, set - default this.History - with get () = historyCollection - and set v = historyCollection <- v - - static member ConfigureEF (mb : ModelBuilder) = - mb.Entity().ToTable "Request" - |> ignore - mb - - -/// A historial update to a prayer request -and [] History() = - /// The request to which this entry applies (may be overridden) - let mutable request = null - - /// The Id of the request to which this update applies - member val RequestId = Guid.Empty with get, set - /// The ticks when this entry was made - member val AsOf = 0L with get, set - /// The status of the request as of this history entry - member val Status = "" with get, set - /// The text of this history entry - member val Text = "" with get, set - - /// The request to which this entry belongs - abstract Request : Request with get, set - default this.Request - with get () = request - and set v = request <- v - - static member ConfigureEF (mb : ModelBuilder) = - mb.Entity().ToTable("History") - |> ignore - mb.Entity().HasKey(fun e -> (e.RequestId, e.AsOf) :> obj) - |> ignore - mb - -(* -/// A user -type Userr = { - /// The Id of the user - [] - Id : string - /// The user's e-mail address - Email : string - /// The user's name - Name : string - /// The time zone in which the user resides - TimeZone : string - /// The last time the user logged on - LastSeenOn : int64 -} - with - /// An empty User - static member Empty = - { Id = "" - Email = "" - Name = "" - TimeZone = "" - LastSeenOn = int64 0 } - - -/// Request history entry -type Historyy = { - /// The instant at which the update was made - AsOf : int64 - /// The action that was taken on the request - Action : string list - /// The status of the request (filled if it changed) - Status : string option - /// The text of the request (filled if it changed) - Text : string option -} - -/// A prayer request -type Requestt = { - /// The Id of the request - [] - Id : string - /// The Id of the user to whom this request belongs - UserId : string - /// The instant this request was entered - EnteredOn : int64 - /// The history for this request - History : Historyy list -} - with - /// The current status of the prayer request - member this.Status = - this.History - |> List.sortBy (fun item -> -item.AsOf) - |> List.map (fun item -> item.Status) - |> List.filter Option.isSome - |> List.map Option.get - |> List.head - /// The current text of the prayer request - member this.Text = - this.History - |> List.sortBy (fun item -> -item.AsOf) - |> List.map (fun item -> item.Text) - |> List.filter Option.isSome - |> List.map Option.get - |> List.head - member this.LastActionOn = - this.History - |> List.sortBy (fun item -> -item.AsOf) - |> List.map (fun item -> item.AsOf) - |> List.head -*) \ No newline at end of file diff --git a/src/api/Extensions.fs b/src/api/Extensions.fs deleted file mode 100644 index 8b0b7d3..0000000 --- a/src/api/Extensions.fs +++ /dev/null @@ -1,16 +0,0 @@ -[] -module MyPrayerJournal.Extensions - -open System.Threading.Tasks - -// H/T: Suave -type AsyncBuilder with - /// An extension method that overloads the standard 'Bind' of the 'async' builder. The new overload awaits on - /// a standard .NET task - member x.Bind(t : Task<'T>, f:'T -> Async<'R>) : Async<'R> = async.Bind (Async.AwaitTask t, f) - - /// An extension method that overloads the standard 'Bind' of the 'async' builder. The new overload awaits on - /// a standard .NET task which does not commpute a value - member x.Bind(t : Task, f : unit -> Async<'R>) : Async<'R> = async.Bind (Async.AwaitTask t, f) - - member x.ReturnFrom(t : Task<'T>) : Async<'T> = Async.AwaitTask t diff --git a/src/api/Migrations/DataContextModelSnapshot.fs b/src/api/Migrations/DataContextModelSnapshot.fs deleted file mode 100644 index fb05713..0000000 --- a/src/api/Migrations/DataContextModelSnapshot.fs +++ /dev/null @@ -1,61 +0,0 @@ -namespace MyPrayerJournal.Migrations - -open System -open Microsoft.EntityFrameworkCore -open Microsoft.EntityFrameworkCore.Infrastructure -open Microsoft.EntityFrameworkCore.Metadata -open Microsoft.EntityFrameworkCore.Migrations -open MyPrayerJournal - -[)>] -type DataContextModelSnapshot () = - inherit ModelSnapshot () - override this.BuildModel modelBuilder = - modelBuilder - .HasDefaultSchema("mpj") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn) - .HasAnnotation("ProductVersion", "1.1.0-rtm-22752") - |> ignore - - modelBuilder.Entity("MyPrayerJournal.History", - fun b -> - b.Property("RequestId") - |> ignore - b.Property("AsOf") - |> ignore - b.Property("Status") - |> ignore - b.Property("Text") - |> ignore - b.HasKey("RequestId", "AsOf") - |> ignore - b.ToTable("History") - |> ignore - ) - |> ignore - - modelBuilder.Entity("MyPrayerJournal.Request", - fun b -> - b.Property("RequestId") - .ValueGeneratedOnAdd() - |> ignore - b.Property("EnteredOn") - |> ignore - b.Property("UserId") - |> ignore - b.HasKey("RequestId") - |> ignore - b.ToTable("Request") - |> ignore - ) - |> ignore - - modelBuilder.Entity("MyPrayerJournal.History", - fun b -> - b.HasOne("MyPrayerJournal.Request", "Request") - .WithMany("History") - .HasForeignKey("RequestId") - .OnDelete(DeleteBehavior.Cascade) - |> ignore - ) - |> ignore \ No newline at end of file diff --git a/src/api/MyPrayerJournal.fsproj b/src/api/MyPrayerJournal.fsproj deleted file mode 100644 index e7dee42..0000000 --- a/src/api/MyPrayerJournal.fsproj +++ /dev/null @@ -1,30 +0,0 @@ - - - Exe - netcoreapp2.0 - myPrayerJournal - MyPrayerJournal - 0.8.1 - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/api/db/index.js b/src/api/db/index.js new file mode 100644 index 0000000..8c3116e --- /dev/null +++ b/src/api/db/index.js @@ -0,0 +1,18 @@ +'use strict' + +const { Pool } = require('pg') +const config = require('../appsettings.json') + +const pool = new Pool(config.pgPool) + +/** + * Run a SQL query + * @param {string} text The SQL command + * @param {*[]} params The parameters for the query + */ +const query = (text, params) => pool.query(text, params) + +module.exports = { + query: query, + request: require('./request')(query) +} diff --git a/src/api/db/request.js b/src/api/db/request.js new file mode 100644 index 0000000..0a664b1 --- /dev/null +++ b/src/api/db/request.js @@ -0,0 +1,15 @@ +'use strict' + +const { Pool } = require('pg') + +module.exports = query => { + return { + /** + * Get the current requests for a user (i.e., their complete current journal) + * @param {string} userId The Id of the user + * @return The requests that make up the current journal + */ + journal: async userId => + (await query('SELECT "RequestId" FROM "Request" WHERE "UserId" = $1', [userId])).rows + } +} diff --git a/src/api/index.js b/src/api/index.js new file mode 100644 index 0000000..5831078 --- /dev/null +++ b/src/api/index.js @@ -0,0 +1,60 @@ +'use strict' + +const express = require('express') + +/** Configuration for the application */ +const config = require('./appsettings.json') + +/** Express app */ +const app = express() + +const jwt = require('express-jwt') +const jwksRsa = require('jwks-rsa') + +// Authentication middleware. When used, the +// access token must exist and be verified against +// the Auth0 JSON Web Key Set +const checkJwt = jwt({ + // Dynamically provide a signing key + // based on the kid in the header and + // the singing keys provided by the JWKS endpoint. + secret: jwksRsa.expressJwtSecret({ + cache: true, + rateLimit: true, + jwksRequestsPerMinute: 5, + jwksUri: `https://${config.auth0.domain}/.well-known/jwks.json` + }), + + // Validate the audience and the issuer. + audience: config.auth0.clientId, + issuer: `https://${config.auth0.domain}/`, + algorithms: ['RS256'] +}) + +// Serve the Vue files from /public +app.use(express.static('public')) + +// Logging FTW! +app.use(require('morgan')('dev')) + +// Tie in all the API routes +require('./routes').mount(app, checkJwt) + +// Send the index.html file for what would normally get a 404 +app.use(async (req, res, next) => { + const options = { + root: __dirname + '/public/', + dotfiles: 'deny' + } + try { + await res.sendFile('index.html', options) + } + catch (err) { + return next(err) + } +}) + +// Start it up! +app.listen(config.port, () => { + console.log(`Listening on port ${config.port}`) +}) diff --git a/src/api/package.json b/src/api/package.json new file mode 100644 index 0000000..6f5fd9d --- /dev/null +++ b/src/api/package.json @@ -0,0 +1,21 @@ +{ + "name": "my-prayer-journal-api", + "private": true, + "version": "0.8.0", + "description": "Server API for myPrayerJournal", + "main": "index.js", + "author": "Daniel J. Summers ", + "license": "MIT", + "dependencies": { + "express": "^4.15.4", + "express-jwt": "^5.3.0", + "express-promise-router": "^2.0.0", + "jwks-rsa": "^1.2.0", + "morgan": "^1.8.2", + "pg": "^7.3.0" + }, + "scripts": { + "start": "node index.js", + "vue": "cd ../app && node build/build.js prod && cd ../api && node index.js" + } +} diff --git a/src/api/routes/index.js b/src/api/routes/index.js new file mode 100644 index 0000000..28c5b62 --- /dev/null +++ b/src/api/routes/index.js @@ -0,0 +1,7 @@ +'use strict' + +module.exports = { + mount: (app, checkJwt) => { + app.use('/api/journal', require('./journal')(checkJwt)) + } +} diff --git a/src/api/routes/journal.js b/src/api/routes/journal.js new file mode 100644 index 0000000..173097f --- /dev/null +++ b/src/api/routes/journal.js @@ -0,0 +1,15 @@ +'use strict' + +const Router = require('express-promise-router') +const db = require('../db') + +module.exports = checkJwt => { + let router = new Router() + + router.get('/', checkJwt, async (req, res) => { + const reqs = await db.request.journal(req.user.sub) + res.json(reqs) + }) + return router +} + diff --git a/src/api/yarn.lock b/src/api/yarn.lock new file mode 100644 index 0000000..4399873 --- /dev/null +++ b/src/api/yarn.lock @@ -0,0 +1,826 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@types/express-jwt@0.0.34": + version "0.0.34" + resolved "https://registry.yarnpkg.com/@types/express-jwt/-/express-jwt-0.0.34.tgz#fdbee4c6af5c0a246ef2a933f5519973c7717f02" + dependencies: + "@types/express" "*" + "@types/express-unless" "*" + +"@types/express-serve-static-core@*": + version "4.0.52" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.0.52.tgz#77ab67fffc402ff6e480c87c71d799ba79880223" + dependencies: + "@types/node" "*" + +"@types/express-unless@*": + version "0.0.31" + resolved "https://registry.yarnpkg.com/@types/express-unless/-/express-unless-0.0.31.tgz#cb6fa8a2e4d31a2ac4762d7aa00f5a258a701cb0" + dependencies: + "@types/express" "*" + +"@types/express@*": + version "4.0.37" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.0.37.tgz#625ac3765169676e01897ca47011c26375784971" + dependencies: + "@types/express-serve-static-core" "*" + "@types/serve-static" "*" + +"@types/mime@*": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.1.tgz#2cf42972d0931c1060c7d5fa6627fce6bd876f2f" + +"@types/node@*": + version "8.0.28" + resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.28.tgz#86206716f8d9251cf41692e384264cbd7058ad60" + +"@types/serve-static@*": + version "1.7.32" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.7.32.tgz#0f6732e4dab0813771dd8fc8fe14940f34728b4c" + dependencies: + "@types/express-serve-static-core" "*" + "@types/mime" "*" + +accepts@~1.3.3: + version "1.3.4" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.4.tgz#86246758c7dd6d21a6474ff084a4740ec05eb21f" + dependencies: + mime-types "~2.1.16" + negotiator "0.6.1" + +ajv@^4.9.1: + version "4.11.8" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" + dependencies: + co "^4.6.0" + json-stable-stringify "^1.0.1" + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + +asn1@~0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + +assert-plus@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" + +async@^1.5.0: + version "1.5.2" + resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + +aws-sign2@~0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f" + +aws4@^1.2.1: + version "1.6.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" + +base64url@2.0.0, base64url@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/base64url/-/base64url-2.0.0.tgz#eac16e03ea1438eff9423d69baa36262ed1f70bb" + +basic-auth@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-1.1.0.tgz#45221ee429f7ee1e5035be3f51533f1cdfd29884" + +bcrypt-pbkdf@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d" + dependencies: + tweetnacl "^0.14.3" + +boom@2.x.x: + version "2.10.1" + resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" + dependencies: + hoek "2.x.x" + +buffer-equal-constant-time@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + +buffer-writer@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-writer/-/buffer-writer-1.0.1.tgz#22a936901e3029afcd7547eb4487ceb697a3bf08" + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + +combined-stream@^1.0.5, combined-stream@~1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009" + dependencies: + delayed-stream "~1.0.0" + +content-disposition@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" + +content-type@~1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + +cookie@0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" + +core-util-is@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + +cryptiles@2.x.x: + version "2.0.5" + resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" + dependencies: + boom "2.x.x" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + dependencies: + assert-plus "^1.0.0" + +debug@2.6.8, debug@^2.2.0: + version "2.6.8" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc" + dependencies: + ms "2.0.0" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + +depd@1.1.1, depd@~1.1.0, depd@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359" + +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + +ecc-jsbn@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" + dependencies: + jsbn "~0.1.0" + +ecdsa-sig-formatter@1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.9.tgz#4bc926274ec3b5abb5016e7e1d60921ac262b2a1" + dependencies: + base64url "^2.0.0" + safe-buffer "^5.0.1" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + +encodeurl@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.1.tgz#79e3d58655346909fe6f0f45a5de68103b294d20" + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + +etag@~1.8.0: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + +express-jwt@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/express-jwt/-/express-jwt-5.3.0.tgz#3d90cd65802e6336252f19e6a3df3e149e0c5ea0" + dependencies: + async "^1.5.0" + express-unless "^0.3.0" + jsonwebtoken "^7.3.0" + lodash.set "^4.0.0" + +express-promise-router@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/express-promise-router/-/express-promise-router-2.0.0.tgz#dd8c5894b40c061c8dcc6591d2145690e99e9551" + dependencies: + is-promise "^2.1.0" + lodash "^4.13.1" + methods "^1.0.0" + +express-unless@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/express-unless/-/express-unless-0.3.1.tgz#2557c146e75beb903e2d247f9b5ba01452696e20" + +express@^4.15.4: + version "4.15.4" + resolved "https://registry.yarnpkg.com/express/-/express-4.15.4.tgz#032e2253489cf8fce02666beca3d11ed7a2daed1" + dependencies: + accepts "~1.3.3" + array-flatten "1.1.1" + content-disposition "0.5.2" + content-type "~1.0.2" + cookie "0.3.1" + cookie-signature "1.0.6" + debug "2.6.8" + depd "~1.1.1" + encodeurl "~1.0.1" + escape-html "~1.0.3" + etag "~1.8.0" + finalhandler "~1.0.4" + fresh "0.5.0" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.1" + path-to-regexp "0.1.7" + proxy-addr "~1.1.5" + qs "6.5.0" + range-parser "~1.2.0" + send "0.15.4" + serve-static "1.12.4" + setprototypeof "1.0.3" + statuses "~1.3.1" + type-is "~1.6.15" + utils-merge "1.0.0" + vary "~1.1.1" + +extend@~3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" + +extsprintf@1.3.0, extsprintf@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + +finalhandler@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.0.4.tgz#18574f2e7c4b98b8ae3b230c21f201f31bdb3fb7" + dependencies: + debug "2.6.8" + encodeurl "~1.0.1" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.1" + statuses "~1.3.1" + unpipe "~1.0.0" + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + +form-data@~2.1.1: + version "2.1.4" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1" + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.5" + mime-types "^2.1.12" + +forwarded@~0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" + +fresh@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.0.tgz#f474ca5e6a9246d6fd8e0953cfa9b9c805afa78e" + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + dependencies: + assert-plus "^1.0.0" + +har-schema@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e" + +har-validator@~4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a" + dependencies: + ajv "^4.9.1" + har-schema "^1.0.5" + +hawk@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" + dependencies: + boom "2.x.x" + cryptiles "2.x.x" + hoek "2.x.x" + sntp "1.x.x" + +hoek@2.x.x: + version "2.16.3" + resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" + +http-errors@~1.6.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736" + dependencies: + depd "1.1.1" + inherits "2.0.3" + setprototypeof "1.0.3" + statuses ">= 1.3.1 < 2" + +http-signature@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" + dependencies: + assert-plus "^0.2.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + +ipaddr.js@1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.4.0.tgz#296aca878a821816e5b85d0a285a99bcff4582f0" + +is-promise@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" + +is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + +isemail@1.x.x: + version "1.2.0" + resolved "https://registry.yarnpkg.com/isemail/-/isemail-1.2.0.tgz#be03df8cc3e29de4d2c5df6501263f1fa4595e9a" + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + +joi@^6.10.1: + version "6.10.1" + resolved "https://registry.yarnpkg.com/joi/-/joi-6.10.1.tgz#4d50c318079122000fe5f16af1ff8e1917b77e06" + dependencies: + hoek "2.x.x" + isemail "1.x.x" + moment "2.x.x" + topo "1.x.x" + +js-string-escape@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1068c587ae4137ef" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + +json-stable-stringify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" + dependencies: + jsonify "~0.0.0" + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + +jsonify@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + +jsonwebtoken@^7.3.0: + version "7.4.3" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-7.4.3.tgz#77f5021de058b605a1783fa1283e99812e645638" + dependencies: + joi "^6.10.1" + jws "^3.1.4" + lodash.once "^4.0.0" + ms "^2.0.0" + xtend "^4.0.1" + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + +jwa@^1.1.4: + version "1.1.5" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.1.5.tgz#a0552ce0220742cd52e153774a32905c30e756e5" + dependencies: + base64url "2.0.0" + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.9" + safe-buffer "^5.0.1" + +jwks-rsa@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/jwks-rsa/-/jwks-rsa-1.2.0.tgz#b7e925b9eac0722ad8634ad6a7e97c7daed3d4c8" + dependencies: + "@types/express-jwt" "0.0.34" + debug "^2.2.0" + limiter "^1.1.0" + lru-memoizer "^1.6.0" + ms "^2.0.0" + request "^2.73.0" + +jws@^3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/jws/-/jws-3.1.4.tgz#f9e8b9338e8a847277d6444b1464f61880e050a2" + dependencies: + base64url "^2.0.0" + jwa "^1.1.4" + safe-buffer "^5.0.1" + +limiter@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/limiter/-/limiter-1.1.2.tgz#229d8055891c8b11af9e0ee5200e8e09bb3dcbeb" + +lock@~0.1.2: + version "0.1.4" + resolved "https://registry.yarnpkg.com/lock/-/lock-0.1.4.tgz#fec7deaef17e7c3a0a55e1da042803e25d91745d" + +lodash.once@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + +lodash.set@^4.0.0: + version "4.3.2" + resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23" + +lodash@^4.13.1: + version "4.17.4" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" + +lodash@~4.5.1: + version "4.5.1" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.5.1.tgz#80e8a074ca5f3893a6b1c10b2a636492d710c316" + +lru-cache@~4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.0.2.tgz#1d17679c069cda5d040991a09dbc2c0db377e55e" + dependencies: + pseudomap "^1.0.1" + yallist "^2.0.0" + +lru-memoizer@^1.6.0: + version "1.11.1" + resolved "https://registry.yarnpkg.com/lru-memoizer/-/lru-memoizer-1.11.1.tgz#0693f6100593914c02e192bf9b8d93884cbf50d3" + dependencies: + lock "~0.1.2" + lodash "~4.5.1" + lru-cache "~4.0.0" + very-fast-args "^1.1.0" + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + +methods@^1.0.0, methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + +mime-db@~1.30.0: + version "1.30.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01" + +mime-types@^2.1.12, mime-types@~2.1.15, mime-types@~2.1.16, mime-types@~2.1.7: + version "2.1.17" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.17.tgz#09d7a393f03e995a79f8af857b70a9e0ab16557a" + dependencies: + mime-db "~1.30.0" + +mime@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53" + +moment@2.x.x: + version "2.18.1" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.18.1.tgz#c36193dd3ce1c2eed2adb7c802dbbc77a81b1c0f" + +morgan@^1.8.2: + version "1.8.2" + resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.8.2.tgz#784ac7734e4a453a9c6e6e8680a9329275c8b687" + dependencies: + basic-auth "~1.1.0" + debug "2.6.8" + depd "~1.1.0" + on-finished "~2.3.0" + on-headers "~1.0.1" + +ms@2.0.0, ms@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + +negotiator@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" + +oauth-sign@~0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + dependencies: + ee-first "1.1.1" + +on-headers@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.1.tgz#928f5d0f470d49342651ea6794b0857c100693f7" + +packet-reader@0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/packet-reader/-/packet-reader-0.3.1.tgz#cd62e60af8d7fea8a705ec4ff990871c46871f27" + +parseurl@~1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + +performance-now@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5" + +pg-connection-string@0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-0.1.3.tgz#da1847b20940e42ee1492beaf65d49d91b245df7" + +pg-pool@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-2.0.3.tgz#c022032c8949f312a4f91fb6409ce04076be3257" + +pg-types@~1.12.1: + version "1.12.1" + resolved "https://registry.yarnpkg.com/pg-types/-/pg-types-1.12.1.tgz#d64087e3903b58ffaad279e7595c52208a14c3d2" + dependencies: + postgres-array "~1.0.0" + postgres-bytea "~1.0.0" + postgres-date "~1.0.0" + postgres-interval "^1.1.0" + +pg@^7.3.0: + version "7.3.0" + resolved "https://registry.yarnpkg.com/pg/-/pg-7.3.0.tgz#275e27466e54a645f6b4a16f6acadf6b849ad83b" + dependencies: + buffer-writer "1.0.1" + js-string-escape "1.0.1" + packet-reader "0.3.1" + pg-connection-string "0.1.3" + pg-pool "~2.0.3" + pg-types "~1.12.1" + pgpass "1.x" + semver "4.3.2" + +pgpass@1.x: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pgpass/-/pgpass-1.0.2.tgz#2a7bb41b6065b67907e91da1b07c1847c877b306" + dependencies: + split "^1.0.0" + +postgres-array@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-1.0.2.tgz#8e0b32eb03bf77a5c0a7851e0441c169a256a238" + +postgres-bytea@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/postgres-bytea/-/postgres-bytea-1.0.0.tgz#027b533c0aa890e26d172d47cf9ccecc521acd35" + +postgres-date@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/postgres-date/-/postgres-date-1.0.3.tgz#e2d89702efdb258ff9d9cee0fe91bd06975257a8" + +postgres-interval@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/postgres-interval/-/postgres-interval-1.1.1.tgz#acdb0f897b4b1c6e496d9d4e0a853e1c428f06f0" + dependencies: + xtend "^4.0.0" + +proxy-addr@~1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-1.1.5.tgz#71c0ee3b102de3f202f3b64f608d173fcba1a918" + dependencies: + forwarded "~0.1.0" + ipaddr.js "1.4.0" + +pseudomap@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + +punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + +qs@6.5.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.0.tgz#8d04954d364def3efc55b5a0793e1e2c8b1e6e49" + +qs@~6.4.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" + +range-parser@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" + +request@^2.73.0: + version "2.81.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" + dependencies: + aws-sign2 "~0.6.0" + aws4 "^1.2.1" + caseless "~0.12.0" + combined-stream "~1.0.5" + extend "~3.0.0" + forever-agent "~0.6.1" + form-data "~2.1.1" + har-validator "~4.2.1" + hawk "~3.1.3" + http-signature "~1.1.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.7" + oauth-sign "~0.8.1" + performance-now "^0.2.0" + qs "~6.4.0" + safe-buffer "^5.0.1" + stringstream "~0.0.4" + tough-cookie "~2.3.0" + tunnel-agent "^0.6.0" + uuid "^3.0.0" + +safe-buffer@^5.0.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" + +semver@4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.2.tgz#c7a07158a80bedd052355b770d82d6640f803be7" + +send@0.15.4: + version "0.15.4" + resolved "https://registry.yarnpkg.com/send/-/send-0.15.4.tgz#985faa3e284b0273c793364a35c6737bd93905b9" + dependencies: + debug "2.6.8" + depd "~1.1.1" + destroy "~1.0.4" + encodeurl "~1.0.1" + escape-html "~1.0.3" + etag "~1.8.0" + fresh "0.5.0" + http-errors "~1.6.2" + mime "1.3.4" + ms "2.0.0" + on-finished "~2.3.0" + range-parser "~1.2.0" + statuses "~1.3.1" + +serve-static@1.12.4: + version "1.12.4" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.12.4.tgz#9b6aa98eeb7253c4eedc4c1f6fdbca609901a961" + dependencies: + encodeurl "~1.0.1" + escape-html "~1.0.3" + parseurl "~1.3.1" + send "0.15.4" + +setprototypeof@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04" + +sntp@1.x.x: + version "1.0.9" + resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" + dependencies: + hoek "2.x.x" + +split@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9" + dependencies: + through "2" + +sshpk@^1.7.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.1.tgz#512df6da6287144316dc4c18fe1cf1d940739be3" + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + dashdash "^1.12.0" + getpass "^0.1.1" + optionalDependencies: + bcrypt-pbkdf "^1.0.0" + ecc-jsbn "~0.1.1" + jsbn "~0.1.0" + tweetnacl "~0.14.0" + +"statuses@>= 1.3.1 < 2", statuses@~1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e" + +stringstream@~0.0.4: + version "0.0.5" + resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" + +through@2: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + +topo@1.x.x: + version "1.1.0" + resolved "https://registry.yarnpkg.com/topo/-/topo-1.1.0.tgz#e9d751615d1bb87dc865db182fa1ca0a5ef536d5" + dependencies: + hoek "2.x.x" + +tough-cookie@~2.3.0: + version "2.3.2" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.2.tgz#f081f76e4c85720e6c37a5faced737150d84072a" + dependencies: + punycode "^1.4.1" + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + +type-is@~1.6.15: + version "1.6.15" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.15.tgz#cab10fb4909e441c82842eafe1ad646c81804410" + dependencies: + media-typer "0.3.0" + mime-types "~2.1.15" + +unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + +utils-merge@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.0.tgz#0294fb922bb9375153541c4f7096231f287c8af8" + +uuid@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04" + +vary@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.1.tgz#67535ebb694c1d52257457984665323f587e8d37" + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +very-fast-args@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/very-fast-args/-/very-fast-args-1.1.0.tgz#e16d1d1faf8a6e596a246421fd90a77963d0b396" + +xtend@^4.0.0, xtend@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" + +yallist@^2.0.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" diff --git a/src/app/config/index.js b/src/app/config/index.js index aa74b4f..2333cd3 100644 --- a/src/app/config/index.js +++ b/src/app/config/index.js @@ -4,8 +4,8 @@ var path = require('path') module.exports = { build: { env: require('./prod.env'), - index: path.resolve(__dirname, '../../api/wwwroot/index.html'), - assetsRoot: path.resolve(__dirname, '../../api/wwwroot'), + index: path.resolve(__dirname, '../../api/public/index.html'), + assetsRoot: path.resolve(__dirname, '../../api/public'), assetsSubDirectory: 'static', assetsPublicPath: '/', productionSourceMap: true, diff --git a/src/app/src/api/index.js b/src/app/src/api/index.js index 77516c9..91b7732 100644 --- a/src/app/src/api/index.js +++ b/src/app/src/api/index.js @@ -1,7 +1,7 @@ import axios from 'axios' const http = axios.create({ - baseURL: 'http://localhost:8084/api' + baseURL: 'http://localhost:3000/api/' }) /** @@ -13,16 +13,16 @@ export default { * Set the bearer token for all future requests * @param {string} token The token to use to identify the user to the server */ - setBearer: token => { http.defaults.headers.common['Authentication'] = `Bearer ${token}` }, + setBearer: token => { http.defaults.headers.common['authorization'] = `Bearer ${token}` }, /** * Remove the bearer token */ - removeBearer: () => delete http.defaults.headers.common['Authentication'], + removeBearer: () => delete http.defaults.headers.common['authorization'], /** * Get all prayer requests and their most recent updates */ - journal: () => http.get('/journal') + journal: () => http.get('journal/') } diff --git a/src/app/src/store/index.js b/src/app/src/store/index.js index b608063..a9cb43c 100644 --- a/src/app/src/store/index.js +++ b/src/app/src/store/index.js @@ -41,10 +41,12 @@ export default new Vuex.Store({ [types.USER_LOGGED_ON] (state, user) { localStorage.setItem('user_profile', JSON.stringify(user)) state.user = user + api.setBearer(localStorage.getItem('id_token')) state.isAuthenticated = true }, [types.USER_LOGGED_OFF] (state) { state.user = {} + api.removeBearer() state.isAuthenticated = false }, [types.LOADING_JOURNAL] (state, flag) { @@ -58,10 +60,11 @@ export default new Vuex.Store({ [actions.LOAD_JOURNAL] ({ commit }) { commit(types.LOADED_JOURNAL, {}) commit(types.LOADING_JOURNAL, true) + api.setBearer(localStorage.getItem('id_token')) api.journal() .then(jrnl => { commit(types.LOADING_JOURNAL, false) - commit(types.LOADED_JOURNAL, jrnl) + commit(types.LOADED_JOURNAL, jrnl.data) }) .catch(err => { commit(types.LOADING_JOURNAL, false) -- 2.45.1 From 270ac45e8c7c389103b2e38dc2e99de71fccd6fa Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Sat, 16 Sep 2017 13:16:02 -0500 Subject: [PATCH 2/4] Create tables/indexes on startup Ready to create a request --- src/20170104023341_InitialDb.fs | 87 --------------------------------- src/api/db/ddl.js | 70 ++++++++++++++++++++++++++ src/api/db/index.js | 7 +-- src/api/db/request.js | 2 +- src/api/index.js | 43 ++++------------ src/api/package.json | 1 + src/api/routes/index.js | 26 +++++++++- src/api/yarn.lock | 20 ++++++++ 8 files changed, 130 insertions(+), 126 deletions(-) delete mode 100644 src/20170104023341_InitialDb.fs create mode 100644 src/api/db/ddl.js diff --git a/src/20170104023341_InitialDb.fs b/src/20170104023341_InitialDb.fs deleted file mode 100644 index 575f0a7..0000000 --- a/src/20170104023341_InitialDb.fs +++ /dev/null @@ -1,87 +0,0 @@ -namespace MyPrayerJournal.Migrations - -open System -open System.Collections.Generic -open Microsoft.EntityFrameworkCore -open Microsoft.EntityFrameworkCore.Infrastructure -open Microsoft.EntityFrameworkCore.Metadata -open Microsoft.EntityFrameworkCore.Migrations -open Microsoft.EntityFrameworkCore.Migrations.Operations -open Microsoft.EntityFrameworkCore.Migrations.Operations.Builders -open MyPrayerJournal - -type RequestTable = { - RequestId : OperationBuilder - EnteredOn : OperationBuilder - UserId : OperationBuilder - } - -type HistoryTable = { - RequestId : OperationBuilder - AsOf : OperationBuilder - Status : OperationBuilder - Text : OperationBuilder - } - -[)>] -[] -type InitialDb () = - inherit Migration () - - override this.Up migrationBuilder = - migrationBuilder.EnsureSchema( - name = "mpj") - |> ignore - - migrationBuilder.CreateTable( - name = "Request", - schema = "mpj", - columns = - (fun table -> - { RequestId = table.Column(nullable = false) - EnteredOn = table.Column(nullable = false) - UserId = table.Column(nullable = false) - } - ), - constraints = - fun table -> - table.PrimaryKey("PK_Request", fun x -> x.RequestId :> obj) |> ignore - ) - |> ignore - - migrationBuilder.CreateTable( - name = "History", - schema = "mpj", - columns = - (fun table -> - { RequestId = table.Column(nullable = false) - AsOf = table.Column(nullable = false) - Status = table.Column(nullable = true) - Text = table.Column(nullable = true) - } - ), - constraints = - fun table -> - table.PrimaryKey("PK_History", fun x -> (x.RequestId, x.AsOf) :> obj) - |> ignore - table.ForeignKey( - name = "FK_History_Request_RequestId", - column = (fun x -> x.RequestId :> obj), - principalSchema = "mpj", - principalTable = "Request", - principalColumn = "RequestId", - onDelete = ReferentialAction.Cascade) - |> ignore - ) - |> ignore - - override this.Down migrationBuilder = - migrationBuilder.DropTable( - name = "History", - schema = "mpj") - |> ignore - - migrationBuilder.DropTable( - name = "Request", - schema = "mpj") - |> ignore diff --git a/src/api/db/ddl.js b/src/api/db/ddl.js new file mode 100644 index 0000000..5b6416e --- /dev/null +++ b/src/api/db/ddl.js @@ -0,0 +1,70 @@ +'use strict' + +const { Pool } = require('pg') + +/** + * SQL to check the existence of a table in the mpj schema + * @param {string} table The name of the table whose existence should be checked + */ +const tableSql = table => `SELECT 1 FROM pg_tables WHERE schemaname='mpj' AND tablename='${table}'` + +/** + * SQL to determine if an index exists + * @param {string} table The name of the table which the given index indexes + * @param {string} index The name of the index + */ +const indexSql = (table, index) => +`SELECT 1 FROM pg_indexes WHERE schemaname='mpj' AND tablename='${table}' AND indexname='${index}'` + +const ddl = [ + { + name: 'myPrayerJournal Schema', + check: `SELECT 1 FROM pg_namespace WHERE nspname='mpj'`, + fix: ` + CREATE SCHEMA mpj; + COMMENT ON SCHEMA mpj IS 'myPrayerJournal data'` + }, + { + name: 'request Table', + check: tableSql('request'), + fix: ` + CREATE TABLE mpj.request ( + "requestId" varchar(25) PRIMARY KEY, + "enteredOn" bigint NOT NULL, + "userId" varchar(100) NOT NULL); + COMMENT ON TABLE mpj.request IS 'Requests'` + }, + { + name: 'history Table', + check: tableSql('history'), + fix: ` + CREATE TABLE mpj.history ( + "requestId" varchar(25) NOT NULL REFERENCES mpj.request, + "asOf" bigint NOT NULL, + "status" varchar(25), + "text" text, + PRIMARY KEY ("requestId", "asOf")); + COMMENT ON TABLE mpj.history IS 'Request update history'` + }, + { + name: 'request.userId Index', + check: indexSql('request', 'idx_request_userId'), + fix: ` + CREATE INDEX "idx_request_userId" ON mpj.request ("userId"); + COMMENT ON INDEX "idx_request_userId" IS 'Requests are retrieved by user'` + } +] + +module.exports = query => { + return { + /** + * Ensure that the database schema, tables, and indexes exist + */ + ensureDatabase: async () => { + for (let item of ddl) { + const result = await query(item.check, []) + if (1 > result.rowCount) await query(item.fix, []) + } + } + } +} diff --git a/src/api/db/index.js b/src/api/db/index.js index 8c3116e..07f7f2c 100644 --- a/src/api/db/index.js +++ b/src/api/db/index.js @@ -1,9 +1,9 @@ 'use strict' const { Pool } = require('pg') -const config = require('../appsettings.json') -const pool = new Pool(config.pgPool) +/** Pooled PostgreSQL instance */ +const pool = new Pool(require('../appsettings.json').pgPool) /** * Run a SQL query @@ -14,5 +14,6 @@ const query = (text, params) => pool.query(text, params) module.exports = { query: query, - request: require('./request')(query) + request: require('./request')(query), + verify: require('./ddl')(query).ensureDatabase } diff --git a/src/api/db/request.js b/src/api/db/request.js index 0a664b1..0b3ba9e 100644 --- a/src/api/db/request.js +++ b/src/api/db/request.js @@ -10,6 +10,6 @@ module.exports = query => { * @return The requests that make up the current journal */ journal: async userId => - (await query('SELECT "RequestId" FROM "Request" WHERE "UserId" = $1', [userId])).rows + (await query('SELECT "requestId" FROM request WHERE "userId" = $1', [userId])).rows } } diff --git a/src/api/index.js b/src/api/index.js index 5831078..8d5fc99 100644 --- a/src/api/index.js +++ b/src/api/index.js @@ -3,34 +3,11 @@ const express = require('express') /** Configuration for the application */ -const config = require('./appsettings.json') +const appConfig = require('./appsettings.json') /** Express app */ const app = express() -const jwt = require('express-jwt') -const jwksRsa = require('jwks-rsa') - -// Authentication middleware. When used, the -// access token must exist and be verified against -// the Auth0 JSON Web Key Set -const checkJwt = jwt({ - // Dynamically provide a signing key - // based on the kid in the header and - // the singing keys provided by the JWKS endpoint. - secret: jwksRsa.expressJwtSecret({ - cache: true, - rateLimit: true, - jwksRequestsPerMinute: 5, - jwksUri: `https://${config.auth0.domain}/.well-known/jwks.json` - }), - - // Validate the audience and the issuer. - audience: config.auth0.clientId, - issuer: `https://${config.auth0.domain}/`, - algorithms: ['RS256'] -}) - // Serve the Vue files from /public app.use(express.static('public')) @@ -38,23 +15,21 @@ app.use(express.static('public')) app.use(require('morgan')('dev')) // Tie in all the API routes -require('./routes').mount(app, checkJwt) +require('./routes').mount(app) // Send the index.html file for what would normally get a 404 app.use(async (req, res, next) => { - const options = { - root: __dirname + '/public/', - dotfiles: 'deny' - } try { - await res.sendFile('index.html', options) + await res.sendFile('index.html', { root: __dirname + '/public/', dotfiles: 'deny' }) } catch (err) { return next(err) } }) -// Start it up! -app.listen(config.port, () => { - console.log(`Listening on port ${config.port}`) -}) +// Ensure the database exists... +require('./db').verify().then(() => + // ...and start it up! + app.listen(appConfig.port, () => { + console.log(`Listening on port ${appConfig.port}`) + })) diff --git a/src/api/package.json b/src/api/package.json index 6f5fd9d..66fe3a7 100644 --- a/src/api/package.json +++ b/src/api/package.json @@ -7,6 +7,7 @@ "author": "Daniel J. Summers ", "license": "MIT", "dependencies": { + "cuid": "^1.3.8", "express": "^4.15.4", "express-jwt": "^5.3.0", "express-promise-router": "^2.0.0", diff --git a/src/api/routes/index.js b/src/api/routes/index.js index 28c5b62..188f179 100644 --- a/src/api/routes/index.js +++ b/src/api/routes/index.js @@ -1,7 +1,31 @@ 'use strict' +const jwt = require('express-jwt') +const jwksRsa = require('jwks-rsa') +const config = require('../appsettings.json') + +// Authentication middleware. When used, the +// access token must exist and be verified against +// the Auth0 JSON Web Key Set +const checkJwt = jwt({ + // Dynamically provide a signing key + // based on the kid in the header and + // the singing keys provided by the JWKS endpoint. + secret: jwksRsa.expressJwtSecret({ + cache: true, + rateLimit: true, + jwksRequestsPerMinute: 5, + jwksUri: `https://${config.auth0.domain}/.well-known/jwks.json` + }), + + // Validate the audience and the issuer. + audience: config.auth0.clientId, + issuer: `https://${config.auth0.domain}/`, + algorithms: ['RS256'] +}) + module.exports = { - mount: (app, checkJwt) => { + mount: app => { app.use('/api/journal', require('./journal')(checkJwt)) } } diff --git a/src/api/yarn.lock b/src/api/yarn.lock index 4399873..96cf70f 100644 --- a/src/api/yarn.lock +++ b/src/api/yarn.lock @@ -109,6 +109,10 @@ boom@2.x.x: dependencies: hoek "2.x.x" +browser-fingerprint@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/browser-fingerprint/-/browser-fingerprint-0.0.1.tgz#8df3cdca25bf7d5b3542d61545d730053fce604a" + buffer-equal-constant-time@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" @@ -147,6 +151,10 @@ cookie@0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" +core-js@^1.1.1: + version "1.2.7" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" + core-util-is@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -157,6 +165,14 @@ cryptiles@2.x.x: dependencies: boom "2.x.x" +cuid@^1.3.8: + version "1.3.8" + resolved "https://registry.yarnpkg.com/cuid/-/cuid-1.3.8.tgz#4b875e0969bad764f7ec0706cf44f5fb0831f6b7" + dependencies: + browser-fingerprint "0.0.1" + core-js "^1.1.1" + node-fingerprint "0.0.2" + dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" @@ -545,6 +561,10 @@ negotiator@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" +node-fingerprint@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/node-fingerprint/-/node-fingerprint-0.0.2.tgz#31cbabeb71a67ae7dd5a7dc042e51c3c75868501" + oauth-sign@~0.8.1: version "0.8.2" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" -- 2.45.1 From 794a365f0871812290329ce990afd99dbd18a402 Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Tue, 19 Sep 2017 22:20:17 -0500 Subject: [PATCH 3/4] Many changes - Express -> Koa - Babel for transforming 'import' into 'require' - SQL done for adding request; just need to get the app to call it --- src/api/app.js | 22 + src/api/db/ddl.js | 6 +- src/api/db/index.js | 14 +- src/api/db/request.js | 25 +- src/api/index.js | 58 +- src/api/package.json | 35 +- src/api/routes/index.js | 38 +- src/api/routes/journal.js | 17 +- src/api/routes/request.js | 18 + src/api/yarn.lock | 1204 ++++++++++++++--- src/app/src/components/Dashboard.vue | 9 +- src/app/src/components/request/NewRequest.vue | 26 + 12 files changed, 1179 insertions(+), 293 deletions(-) create mode 100644 src/api/app.js create mode 100644 src/api/routes/request.js create mode 100644 src/app/src/components/request/NewRequest.vue diff --git a/src/api/app.js b/src/api/app.js new file mode 100644 index 0000000..9ba2d29 --- /dev/null +++ b/src/api/app.js @@ -0,0 +1,22 @@ +'use strict' + +const chalk = require('chalk') + +const env = process.env.NODE_ENV || 'dev' + +if ('dev' === env) require('babel-register') + +const fullEnv = ('dev' === env) ? 'Development' : 'Production' + +/** Configuration for the application */ +const appConfig = require('./appsettings.json') + +/** Express app */ +const app = require('./index').default + +// Ensure the database exists... +require('./db').default.verify().then(() => + // ...and start it up! + app.listen(appConfig.port, () => { + console.log(chalk`{reset myPrayerJournal | Port: {bold ${appConfig.port}} | Mode: {bold ${fullEnv}}}`) + })) diff --git a/src/api/db/ddl.js b/src/api/db/ddl.js index 5b6416e..46c3455 100644 --- a/src/api/db/ddl.js +++ b/src/api/db/ddl.js @@ -1,6 +1,6 @@ 'use strict' -const { Pool } = require('pg') +import { Pool } from 'pg' /** * SQL to check the existence of a table in the mpj schema @@ -14,7 +14,7 @@ const tableSql = table => `SELECT 1 FROM pg_tables WHERE schemaname='mpj' AND ta * @param {string} index The name of the index */ const indexSql = (table, index) => -`SELECT 1 FROM pg_indexes WHERE schemaname='mpj' AND tablename='${table}' AND indexname='${index}'` + `SELECT 1 FROM pg_indexes WHERE schemaname='mpj' AND tablename='${table}' AND indexname='${index}'` const ddl = [ { @@ -55,7 +55,7 @@ const ddl = [ } ] -module.exports = query => { +export default function (query) { return { /** * Ensure that the database schema, tables, and indexes exist diff --git a/src/api/db/index.js b/src/api/db/index.js index 07f7f2c..0b5b349 100644 --- a/src/api/db/index.js +++ b/src/api/db/index.js @@ -1,9 +1,13 @@ 'use strict' -const { Pool } = require('pg') +import { Pool } from 'pg' + +import appConfig from '../appsettings.json' +import ddl from './ddl' +import request from './request' /** Pooled PostgreSQL instance */ -const pool = new Pool(require('../appsettings.json').pgPool) +const pool = new Pool(appConfig.pgPool) /** * Run a SQL query @@ -12,8 +16,8 @@ const pool = new Pool(require('../appsettings.json').pgPool) */ const query = (text, params) => pool.query(text, params) -module.exports = { +export default { query: query, - request: require('./request')(query), - verify: require('./ddl')(query).ensureDatabase + request: request(query), + verify: ddl(query).ensureDatabase } diff --git a/src/api/db/request.js b/src/api/db/request.js index 0b3ba9e..c25f2f0 100644 --- a/src/api/db/request.js +++ b/src/api/db/request.js @@ -1,8 +1,9 @@ 'use strict' -const { Pool } = require('pg') +import { Pool } from 'pg' +import cuid from 'cuid' -module.exports = query => { +export default function (query) { return { /** * Get the current requests for a user (i.e., their complete current journal) @@ -10,6 +11,24 @@ module.exports = query => { * @return The requests that make up the current journal */ journal: async userId => - (await query('SELECT "requestId" FROM request WHERE "userId" = $1', [userId])).rows + (await query('SELECT "requestId" FROM request WHERE "userId" = $1', [userId])).rows, + + /** + * Add a new prayer request + * @param {string} userId The Id of the user + * @param {string} requestText The text of the request + * @return {string} The Id of the created request + */ + addNew: async (userId, requestText) => { + const id = cuid() + const enteredOn = Date.now() + await query(` + BEGIN; + INSERT INTO request ("requestId", "enteredOn", "userId") VALUES ($1, $2, $3); + INSERT INTO history ("requestId", "asOf", "status", "text") VALUES ($1, $2, 'Created', $4); + COMMIT;`, + [ id, enteredOn, userId, requestText ]) + return id + } } } diff --git a/src/api/index.js b/src/api/index.js index 8d5fc99..a467651 100644 --- a/src/api/index.js +++ b/src/api/index.js @@ -1,35 +1,33 @@ 'use strict' -const express = require('express') +import Koa from 'koa' +import morgan from 'koa-morgan' +import send from 'koa-send' +import serveFrom from 'koa-static' -/** Configuration for the application */ -const appConfig = require('./appsettings.json') +import appConfig from './appsettings.json' +import router from './routes' -/** Express app */ -const app = express() +/** Koa app */ +const app = new Koa() -// Serve the Vue files from /public -app.use(express.static('public')) - -// Logging FTW! -app.use(require('morgan')('dev')) - -// Tie in all the API routes -require('./routes').mount(app) - -// Send the index.html file for what would normally get a 404 -app.use(async (req, res, next) => { - try { - await res.sendFile('index.html', { root: __dirname + '/public/', dotfiles: 'deny' }) - } - catch (err) { - return next(err) - } -}) - -// Ensure the database exists... -require('./db').verify().then(() => - // ...and start it up! - app.listen(appConfig.port, () => { - console.log(`Listening on port ${appConfig.port}`) - })) +export default app + // Logging FTW! + .use(morgan('dev')) + // Serve the Vue files from /public + .use(serveFrom('public')) + // Tie in all the routes + .use(router.routes()) + .use(router.allowedMethods()) + // Send the index.html file for what would normally get a 404 + .use(async (ctx, next) => { + if (ctx.url.indexOf('/api') === -1) { + try { + await send(ctx, 'index.html', { root: __dirname + '/public/' }) + } + catch (err) { + return await next(err) + } + } + return await next() + }) diff --git a/src/api/package.json b/src/api/package.json index 66fe3a7..a0e0ecc 100644 --- a/src/api/package.json +++ b/src/api/package.json @@ -7,16 +7,37 @@ "author": "Daniel J. Summers ", "license": "MIT", "dependencies": { + "chalk": "^2.1.0", "cuid": "^1.3.8", - "express": "^4.15.4", - "express-jwt": "^5.3.0", - "express-promise-router": "^2.0.0", - "jwks-rsa": "^1.2.0", - "morgan": "^1.8.2", + "jwks-rsa-koa": "^1.1.3", + "koa": "^2.3.0", + "koa-bodyparser": "^4.2.0", + "koa-jwt": "^3.2.2", + "koa-morgan": "^1.0.1", + "koa-router": "^7.2.1", + "koa-send": "^4.1.0", + "koa-static": "^4.0.1", "pg": "^7.3.0" }, "scripts": { - "start": "node index.js", - "vue": "cd ../app && node build/build.js prod && cd ../api && node index.js" + "start": "node app.js", + "vue": "cd ../app && node build/build.js prod && cd ../api && node app.js" + }, + "devDependencies": { + "babel": "^6.23.0", + "babel-preset-env": "^1.6.0", + "babel-register": "^6.26.0" + }, + "babel": { + "presets": [ + [ + "env", + { + "targets": { + "node": "current" + } + } + ] + ] } } diff --git a/src/api/routes/index.js b/src/api/routes/index.js index 188f179..50fc8da 100644 --- a/src/api/routes/index.js +++ b/src/api/routes/index.js @@ -1,31 +1,39 @@ 'use strict' -const jwt = require('express-jwt') -const jwksRsa = require('jwks-rsa') -const config = require('../appsettings.json') +import jwt from 'koa-jwt' +import jwksRsa from 'jwks-rsa-koa' +import Router from 'koa-router' -// Authentication middleware. When used, the -// access token must exist and be verified against -// the Auth0 JSON Web Key Set +import appConfig from '../appsettings.json' +import journal from './journal' +import request from './request' + +/** Authentication middleware to verify the access token against the Auth0 JSON Web Key Set */ const checkJwt = jwt({ // Dynamically provide a signing key // based on the kid in the header and // the singing keys provided by the JWKS endpoint. - secret: jwksRsa.expressJwtSecret({ + secret: jwksRsa.koaJwt2Key({ cache: true, rateLimit: true, jwksRequestsPerMinute: 5, - jwksUri: `https://${config.auth0.domain}/.well-known/jwks.json` + jwksUri: `https://${appConfig.auth0.domain}/.well-known/jwks.json` }), // Validate the audience and the issuer. - audience: config.auth0.clientId, - issuer: `https://${config.auth0.domain}/`, + audience: appConfig.auth0.clientId, + issuer: `https://${appConfig.auth0.domain}/`, algorithms: ['RS256'] }) -module.exports = { - mount: app => { - app.use('/api/journal', require('./journal')(checkJwt)) - } -} +/** /api/journal routes */ +const journalRoutes = journal(checkJwt) +/** /api/request routes */ +const requestRoutes = request(checkJwt) + +/** Combined router */ +const router = new Router({ prefix: '/api' }) +router.use('/journal', journalRoutes.routes(), journalRoutes.allowedMethods()) +router.use('/request', requestRoutes.routes(), requestRoutes.allowedMethods()) + +export default router diff --git a/src/api/routes/journal.js b/src/api/routes/journal.js index 173097f..1cc17be 100644 --- a/src/api/routes/journal.js +++ b/src/api/routes/journal.js @@ -1,15 +1,16 @@ 'use strict' -const Router = require('express-promise-router') -const db = require('../db') +import Router from 'koa-router' +import db from '../db' -module.exports = checkJwt => { - let router = new Router() +const router = new Router() - router.get('/', checkJwt, async (req, res) => { - const reqs = await db.request.journal(req.user.sub) - res.json(reqs) +export default function (checkJwt) { + + router.get('/', checkJwt, async (ctx, next) => { + const reqs = await db.request.journal(ctx.state.user.sub) + ctx.body = reqs + return await next() }) return router } - diff --git a/src/api/routes/request.js b/src/api/routes/request.js new file mode 100644 index 0000000..b08edf4 --- /dev/null +++ b/src/api/routes/request.js @@ -0,0 +1,18 @@ +'use strict' + +import Router from 'koa-router' +import db from '../db' + +const router = new Router() + +export default function (checkJwt) { + + router.post('/', checkJwt, async (ctx, next) => { + const newId = await db.request.addNew(ctx.state.user.sub, ctx.body.requestText) + ctx.body = { id: newId } + await next() + }) + + return router +} + diff --git a/src/api/yarn.lock b/src/api/yarn.lock index 96cf70f..9c15ed3 100644 --- a/src/api/yarn.lock +++ b/src/api/yarn.lock @@ -2,48 +2,7 @@ # yarn lockfile v1 -"@types/express-jwt@0.0.34": - version "0.0.34" - resolved "https://registry.yarnpkg.com/@types/express-jwt/-/express-jwt-0.0.34.tgz#fdbee4c6af5c0a246ef2a933f5519973c7717f02" - dependencies: - "@types/express" "*" - "@types/express-unless" "*" - -"@types/express-serve-static-core@*": - version "4.0.52" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.0.52.tgz#77ab67fffc402ff6e480c87c71d799ba79880223" - dependencies: - "@types/node" "*" - -"@types/express-unless@*": - version "0.0.31" - resolved "https://registry.yarnpkg.com/@types/express-unless/-/express-unless-0.0.31.tgz#cb6fa8a2e4d31a2ac4762d7aa00f5a258a701cb0" - dependencies: - "@types/express" "*" - -"@types/express@*": - version "4.0.37" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.0.37.tgz#625ac3765169676e01897ca47011c26375784971" - dependencies: - "@types/express-serve-static-core" "*" - "@types/serve-static" "*" - -"@types/mime@*": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.1.tgz#2cf42972d0931c1060c7d5fa6627fce6bd876f2f" - -"@types/node@*": - version "8.0.28" - resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.28.tgz#86206716f8d9251cf41692e384264cbd7058ad60" - -"@types/serve-static@*": - version "1.7.32" - resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.7.32.tgz#0f6732e4dab0813771dd8fc8fe14940f34728b4c" - dependencies: - "@types/express-serve-static-core" "*" - "@types/mime" "*" - -accepts@~1.3.3: +accepts@^1.2.2: version "1.3.4" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.4.tgz#86246758c7dd6d21a6474ff084a4740ec05eb21f" dependencies: @@ -57,9 +16,23 @@ ajv@^4.9.1: co "^4.6.0" json-stable-stringify "^1.0.1" -array-flatten@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + +ansi-styles@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.0.tgz#c159b8d5be0f9e5a6f346dab94f16ce022161b88" + dependencies: + color-convert "^1.9.0" + +any-promise@^1.0.0, any-promise@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" asn1@~0.2.3: version "0.2.3" @@ -73,10 +46,6 @@ assert-plus@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" -async@^1.5.0: - version "1.5.2" - resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" - asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -89,6 +58,472 @@ aws4@^1.2.1: version "1.6.0" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" +babel-code-frame@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" + dependencies: + chalk "^1.1.3" + esutils "^2.0.2" + js-tokens "^3.0.2" + +babel-core@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.26.0.tgz#af32f78b31a6fcef119c87b0fd8d9753f03a0bb8" + dependencies: + babel-code-frame "^6.26.0" + babel-generator "^6.26.0" + babel-helpers "^6.24.1" + babel-messages "^6.23.0" + babel-register "^6.26.0" + babel-runtime "^6.26.0" + babel-template "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + convert-source-map "^1.5.0" + debug "^2.6.8" + json5 "^0.5.1" + lodash "^4.17.4" + minimatch "^3.0.4" + path-is-absolute "^1.0.1" + private "^0.1.7" + slash "^1.0.0" + source-map "^0.5.6" + +babel-generator@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.0.tgz#ac1ae20070b79f6e3ca1d3269613053774f20dc5" + dependencies: + babel-messages "^6.23.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + detect-indent "^4.0.0" + jsesc "^1.3.0" + lodash "^4.17.4" + source-map "^0.5.6" + trim-right "^1.0.1" + +babel-helper-builder-binary-assignment-operator-visitor@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz#cce4517ada356f4220bcae8a02c2b346f9a56664" + dependencies: + babel-helper-explode-assignable-expression "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-call-delegate@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz#ece6aacddc76e41c3461f88bfc575bd0daa2df8d" + dependencies: + babel-helper-hoist-variables "^6.24.1" + babel-runtime "^6.22.0" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-define-map@^6.24.1: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz#a5f56dab41a25f97ecb498c7ebaca9819f95be5f" + dependencies: + babel-helper-function-name "^6.24.1" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + lodash "^4.17.4" + +babel-helper-explode-assignable-expression@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz#f25b82cf7dc10433c55f70592d5746400ac22caa" + dependencies: + babel-runtime "^6.22.0" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-function-name@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz#d3475b8c03ed98242a25b48351ab18399d3580a9" + dependencies: + babel-helper-get-function-arity "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-get-function-arity@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz#8f7782aa93407c41d3aa50908f89b031b1b6853d" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-hoist-variables@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz#1ecb27689c9d25513eadbc9914a73f5408be7a76" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-optimise-call-expression@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz#f7a13427ba9f73f8f4fa993c54a97882d1244257" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-regex@^6.24.1: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz#325c59f902f82f24b74faceed0363954f6495e72" + dependencies: + babel-runtime "^6.26.0" + babel-types "^6.26.0" + lodash "^4.17.4" + +babel-helper-remap-async-to-generator@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz#5ec581827ad723fecdd381f1c928390676e4551b" + dependencies: + babel-helper-function-name "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-replace-supers@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz#bf6dbfe43938d17369a213ca8a8bf74b6a90ab1a" + dependencies: + babel-helper-optimise-call-expression "^6.24.1" + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helpers@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2" + dependencies: + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-messages@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-check-es2015-constants@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz#35157b101426fd2ffd3da3f75c7d1e91835bbf8a" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-syntax-async-functions@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95" + +babel-plugin-syntax-exponentiation-operator@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz#9ee7e8337290da95288201a6a57f4170317830de" + +babel-plugin-syntax-trailing-function-commas@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz#ba0360937f8d06e40180a43fe0d5616fff532cf3" + +babel-plugin-transform-async-to-generator@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz#6536e378aff6cb1d5517ac0e40eb3e9fc8d08761" + dependencies: + babel-helper-remap-async-to-generator "^6.24.1" + babel-plugin-syntax-async-functions "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-arrow-functions@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz#452692cb711d5f79dc7f85e440ce41b9f244d221" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-block-scoped-functions@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz#bbc51b49f964d70cb8d8e0b94e820246ce3a6141" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-block-scoping@^6.23.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz#d70f5299c1308d05c12f463813b0a09e73b1895f" + dependencies: + babel-runtime "^6.26.0" + babel-template "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + lodash "^4.17.4" + +babel-plugin-transform-es2015-classes@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz#5a4c58a50c9c9461e564b4b2a3bfabc97a2584db" + dependencies: + babel-helper-define-map "^6.24.1" + babel-helper-function-name "^6.24.1" + babel-helper-optimise-call-expression "^6.24.1" + babel-helper-replace-supers "^6.24.1" + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-computed-properties@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz#6fe2a8d16895d5634f4cd999b6d3480a308159b3" + dependencies: + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-destructuring@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz#997bb1f1ab967f682d2b0876fe358d60e765c56d" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-duplicate-keys@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz#73eb3d310ca969e3ef9ec91c53741a6f1576423e" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-for-of@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz#f47c95b2b613df1d3ecc2fdb7573623c75248691" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-function-name@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz#834c89853bc36b1af0f3a4c5dbaa94fd8eacaa8b" + dependencies: + babel-helper-function-name "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-literals@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz#4f54a02d6cd66cf915280019a31d31925377ca2e" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-modules-amd@^6.22.0, babel-plugin-transform-es2015-modules-amd@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz#3b3e54017239842d6d19c3011c4bd2f00a00d154" + dependencies: + babel-plugin-transform-es2015-modules-commonjs "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-modules-commonjs@^6.23.0, babel-plugin-transform-es2015-modules-commonjs@^6.24.1: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.0.tgz#0d8394029b7dc6abe1a97ef181e00758dd2e5d8a" + dependencies: + babel-plugin-transform-strict-mode "^6.24.1" + babel-runtime "^6.26.0" + babel-template "^6.26.0" + babel-types "^6.26.0" + +babel-plugin-transform-es2015-modules-systemjs@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz#ff89a142b9119a906195f5f106ecf305d9407d23" + dependencies: + babel-helper-hoist-variables "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-modules-umd@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz#ac997e6285cd18ed6176adb607d602344ad38468" + dependencies: + babel-plugin-transform-es2015-modules-amd "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-object-super@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz#24cef69ae21cb83a7f8603dad021f572eb278f8d" + dependencies: + babel-helper-replace-supers "^6.24.1" + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-parameters@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz#57ac351ab49caf14a97cd13b09f66fdf0a625f2b" + dependencies: + babel-helper-call-delegate "^6.24.1" + babel-helper-get-function-arity "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-shorthand-properties@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz#24f875d6721c87661bbd99a4622e51f14de38aa0" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-spread@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz#d6d68a99f89aedc4536c81a542e8dd9f1746f8d1" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-sticky-regex@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz#00c1cdb1aca71112cdf0cf6126c2ed6b457ccdbc" + dependencies: + babel-helper-regex "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-template-literals@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz#a84b3450f7e9f8f1f6839d6d687da84bb1236d8d" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-typeof-symbol@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz#dec09f1cddff94b52ac73d505c84df59dcceb372" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-unicode-regex@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz#d38b12f42ea7323f729387f18a7c5ae1faeb35e9" + dependencies: + babel-helper-regex "^6.24.1" + babel-runtime "^6.22.0" + regexpu-core "^2.0.0" + +babel-plugin-transform-exponentiation-operator@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz#2ab0c9c7f3098fa48907772bb813fe41e8de3a0e" + dependencies: + babel-helper-builder-binary-assignment-operator-visitor "^6.24.1" + babel-plugin-syntax-exponentiation-operator "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-regenerator@^6.22.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz#e0703696fbde27f0a3efcacf8b4dca2f7b3a8f2f" + dependencies: + regenerator-transform "^0.10.0" + +babel-plugin-transform-strict-mode@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz#d5faf7aa578a65bbe591cf5edae04a0c67020758" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-preset-env@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.6.0.tgz#2de1c782a780a0a5d605d199c957596da43c44e4" + dependencies: + babel-plugin-check-es2015-constants "^6.22.0" + babel-plugin-syntax-trailing-function-commas "^6.22.0" + babel-plugin-transform-async-to-generator "^6.22.0" + babel-plugin-transform-es2015-arrow-functions "^6.22.0" + babel-plugin-transform-es2015-block-scoped-functions "^6.22.0" + babel-plugin-transform-es2015-block-scoping "^6.23.0" + babel-plugin-transform-es2015-classes "^6.23.0" + babel-plugin-transform-es2015-computed-properties "^6.22.0" + babel-plugin-transform-es2015-destructuring "^6.23.0" + babel-plugin-transform-es2015-duplicate-keys "^6.22.0" + babel-plugin-transform-es2015-for-of "^6.23.0" + babel-plugin-transform-es2015-function-name "^6.22.0" + babel-plugin-transform-es2015-literals "^6.22.0" + babel-plugin-transform-es2015-modules-amd "^6.22.0" + babel-plugin-transform-es2015-modules-commonjs "^6.23.0" + babel-plugin-transform-es2015-modules-systemjs "^6.23.0" + babel-plugin-transform-es2015-modules-umd "^6.23.0" + babel-plugin-transform-es2015-object-super "^6.22.0" + babel-plugin-transform-es2015-parameters "^6.23.0" + babel-plugin-transform-es2015-shorthand-properties "^6.22.0" + babel-plugin-transform-es2015-spread "^6.22.0" + babel-plugin-transform-es2015-sticky-regex "^6.22.0" + babel-plugin-transform-es2015-template-literals "^6.22.0" + babel-plugin-transform-es2015-typeof-symbol "^6.23.0" + babel-plugin-transform-es2015-unicode-regex "^6.22.0" + babel-plugin-transform-exponentiation-operator "^6.22.0" + babel-plugin-transform-regenerator "^6.22.0" + browserslist "^2.1.2" + invariant "^2.2.2" + semver "^5.3.0" + +babel-register@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.26.0.tgz#6ed021173e2fcb486d7acb45c6009a856f647071" + dependencies: + babel-core "^6.26.0" + babel-runtime "^6.26.0" + core-js "^2.5.0" + home-or-tmp "^2.0.0" + lodash "^4.17.4" + mkdirp "^0.5.1" + source-map-support "^0.4.15" + +babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.11.0" + +babel-template@^6.24.1, babel-template@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02" + dependencies: + babel-runtime "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + lodash "^4.17.4" + +babel-traverse@^6.24.1, babel-traverse@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee" + dependencies: + babel-code-frame "^6.26.0" + babel-messages "^6.23.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + debug "^2.6.8" + globals "^9.18.0" + invariant "^2.2.2" + lodash "^4.17.4" + +babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" + dependencies: + babel-runtime "^6.26.0" + esutils "^2.0.2" + lodash "^4.17.4" + to-fast-properties "^1.0.3" + +babel@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel/-/babel-6.23.0.tgz#d0d1e7d803e974765beea3232d4e153c0efb90f4" + +babylon@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + base64url@2.0.0, base64url@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/base64url/-/base64url-2.0.0.tgz#eac16e03ea1438eff9423d69baa36262ed1f70bb" @@ -109,10 +544,24 @@ boom@2.x.x: dependencies: hoek "2.x.x" +brace-expansion@^1.1.7: + version "1.1.8" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + browser-fingerprint@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/browser-fingerprint/-/browser-fingerprint-0.0.1.tgz#8df3cdca25bf7d5b3542d61545d730053fce604a" +browserslist@^2.1.2: + version "2.4.0" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-2.4.0.tgz#693ee93d01e66468a6348da5498e011f578f87f8" + dependencies: + caniuse-lite "^1.0.30000718" + electron-to-chromium "^1.3.18" + buffer-equal-constant-time@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" @@ -121,40 +570,100 @@ buffer-writer@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/buffer-writer/-/buffer-writer-1.0.1.tgz#22a936901e3029afcd7547eb4487ceb697a3bf08" +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + +caniuse-lite@^1.0.30000718: + version "1.0.30000733" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000733.tgz#ebfc48254117cc0c66197a4536cb4397a6cfbccd" + caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" +chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chalk@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.1.0.tgz#ac5becf14fa21b99c6c92ca7a7d7cfd5b17e743e" + dependencies: + ansi-styles "^3.1.0" + escape-string-regexp "^1.0.5" + supports-color "^4.0.0" + +co-body@^5.1.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/co-body/-/co-body-5.1.1.tgz#d97781d1e3344ba4a820fd1806bddf8341505236" + dependencies: + inflation "^2.0.0" + qs "^6.4.0" + raw-body "^2.2.0" + type-is "^1.6.14" + co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" +color-convert@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.0.tgz#1accf97dd739b983bf994d56fec8f95853641b7a" + dependencies: + color-name "^1.1.1" + +color-name@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + combined-stream@^1.0.5, combined-stream@~1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009" dependencies: delayed-stream "~1.0.0" -content-disposition@0.5.2: +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + +content-disposition@~0.5.0: version "0.5.2" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" -content-type@~1.0.2: +content-type@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" -cookie-signature@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" +convert-source-map@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.0.tgz#9acd70851c6d5dfdd93d9282e5edf94a03ff46b5" -cookie@0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" +cookies@~0.7.0: + version "0.7.1" + resolved "https://registry.yarnpkg.com/cookies/-/cookies-0.7.1.tgz#7c8a615f5481c61ab9f16c833731bcb8f663b99b" + dependencies: + depd "~1.1.1" + keygrip "~1.0.2" + +copy-to@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/copy-to/-/copy-to-2.0.1.tgz#2680fbb8068a48d08656b6098092bdafc906f4a5" core-js@^1.1.1: version "1.2.7" resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" +core-js@^2.4.0, core-js@^2.5.0: + version "2.5.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.1.tgz#ae6874dc66937789b80754ff5428df66819ca50b" + core-util-is@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -179,24 +688,44 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" -debug@2.6.8, debug@^2.2.0: +debug@*: + version "3.0.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.0.1.tgz#0564c612b521dc92d9f2988f0549e34f9c98db64" + dependencies: + ms "2.0.0" + +debug@2.6.8, debug@^2.2.0, debug@^2.6.3, debug@^2.6.8: version "2.6.8" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc" dependencies: ms "2.0.0" +deep-equal@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" + delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" -depd@1.1.1, depd@~1.1.0, depd@~1.1.1: +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + +depd@1.1.1, depd@^1.1.0, depd@~1.1.0, depd@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359" -destroy@~1.0.4: +destroy@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" +detect-indent@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" + dependencies: + repeating "^2.0.0" + ecc-jsbn@~0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" @@ -214,71 +743,25 @@ ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" -encodeurl@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.1.tgz#79e3d58655346909fe6f0f45a5de68103b294d20" +electron-to-chromium@^1.3.18: + version "1.3.21" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.21.tgz#a967ebdcfe8ed0083fc244d1894022a8e8113ea2" -escape-html@~1.0.3: +error-inject@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/error-inject/-/error-inject-1.0.0.tgz#e2b3d91b54aed672f309d950d154850fa11d4f37" + +escape-html@~1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" -etag@~1.8.0: - version "1.8.1" - resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" -express-jwt@^5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/express-jwt/-/express-jwt-5.3.0.tgz#3d90cd65802e6336252f19e6a3df3e149e0c5ea0" - dependencies: - async "^1.5.0" - express-unless "^0.3.0" - jsonwebtoken "^7.3.0" - lodash.set "^4.0.0" - -express-promise-router@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/express-promise-router/-/express-promise-router-2.0.0.tgz#dd8c5894b40c061c8dcc6591d2145690e99e9551" - dependencies: - is-promise "^2.1.0" - lodash "^4.13.1" - methods "^1.0.0" - -express-unless@^0.3.0: - version "0.3.1" - resolved "https://registry.yarnpkg.com/express-unless/-/express-unless-0.3.1.tgz#2557c146e75beb903e2d247f9b5ba01452696e20" - -express@^4.15.4: - version "4.15.4" - resolved "https://registry.yarnpkg.com/express/-/express-4.15.4.tgz#032e2253489cf8fce02666beca3d11ed7a2daed1" - dependencies: - accepts "~1.3.3" - array-flatten "1.1.1" - content-disposition "0.5.2" - content-type "~1.0.2" - cookie "0.3.1" - cookie-signature "1.0.6" - debug "2.6.8" - depd "~1.1.1" - encodeurl "~1.0.1" - escape-html "~1.0.3" - etag "~1.8.0" - finalhandler "~1.0.4" - fresh "0.5.0" - merge-descriptors "1.0.1" - methods "~1.1.2" - on-finished "~2.3.0" - parseurl "~1.3.1" - path-to-regexp "0.1.7" - proxy-addr "~1.1.5" - qs "6.5.0" - range-parser "~1.2.0" - send "0.15.4" - serve-static "1.12.4" - setprototypeof "1.0.3" - statuses "~1.3.1" - type-is "~1.6.15" - utils-merge "1.0.0" - vary "~1.1.1" +esutils@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" extend@~3.0.0: version "3.0.1" @@ -288,18 +771,6 @@ extsprintf@1.3.0, extsprintf@^1.2.0: version "1.3.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" -finalhandler@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.0.4.tgz#18574f2e7c4b98b8ae3b230c21f201f31bdb3fb7" - dependencies: - debug "2.6.8" - encodeurl "~1.0.1" - escape-html "~1.0.3" - on-finished "~2.3.0" - parseurl "~1.3.1" - statuses "~1.3.1" - unpipe "~1.0.0" - forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -312,13 +783,9 @@ form-data@~2.1.1: combined-stream "^1.0.5" mime-types "^2.1.12" -forwarded@~0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" - -fresh@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.0.tgz#f474ca5e6a9246d6fd8e0953cfa9b9c805afa78e" +fresh@^0.5.0: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" getpass@^0.1.1: version "0.1.7" @@ -326,6 +793,10 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" +globals@^9.18.0: + version "9.18.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" + har-schema@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e" @@ -337,6 +808,16 @@ har-validator@~4.2.1: ajv "^4.9.1" har-schema "^1.0.5" +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + dependencies: + ansi-regex "^2.0.0" + +has-flag@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" + hawk@~3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" @@ -350,7 +831,21 @@ hoek@2.x.x: version "2.16.3" resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" -http-errors@~1.6.2: +home-or-tmp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.1" + +http-assert@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/http-assert/-/http-assert-1.3.0.tgz#a31a5cf88c873ecbb5796907d4d6f132e8c01e4a" + dependencies: + deep-equal "~1.0.1" + http-errors "~1.6.1" + +http-errors@1.6.2, http-errors@^1.2.8, http-errors@^1.3.1, http-errors@^1.6.1, http-errors@~1.6.1: version "1.6.2" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736" dependencies: @@ -359,6 +854,14 @@ http-errors@~1.6.2: setprototypeof "1.0.3" statuses ">= 1.3.1 < 2" +http-errors@~1.5.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.5.1.tgz#788c0d2c1de2c81b9e6e8c01843b6b97eb920750" + dependencies: + inherits "2.0.3" + setprototypeof "1.0.2" + statuses ">= 1.3.1 < 2" + http-signature@~1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" @@ -367,22 +870,42 @@ http-signature@~1.1.0: jsprim "^1.2.2" sshpk "^1.7.0" +iconv-lite@0.4.19: + version "0.4.19" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" + +inflation@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/inflation/-/inflation-2.0.0.tgz#8b417e47c28f925a45133d914ca1fd389107f30f" + inherits@2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" -ipaddr.js@1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.4.0.tgz#296aca878a821816e5b85d0a285a99bcff4582f0" +invariant@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" + dependencies: + loose-envify "^1.0.0" -is-promise@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" +is-finite@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" + dependencies: + number-is-nan "^1.0.0" + +is-generator-function@^1.0.3: + version "1.0.6" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.6.tgz#9e71653cd15fff341c79c4151460a131d31e9fc4" is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + isemail@1.x.x: version "1.2.0" resolved "https://registry.yarnpkg.com/isemail/-/isemail-1.2.0.tgz#be03df8cc3e29de4d2c5df6501263f1fa4595e9a" @@ -404,10 +927,22 @@ js-string-escape@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1068c587ae4137ef" +js-tokens@^3.0.0, js-tokens@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" + jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" +jsesc@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + json-schema@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" @@ -422,13 +957,17 @@ json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" +json5@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" + jsonify@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" -jsonwebtoken@^7.3.0: - version "7.4.3" - resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-7.4.3.tgz#77f5021de058b605a1783fa1283e99812e645638" +jsonwebtoken@7.4.1: + version "7.4.1" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-7.4.1.tgz#7ca324f5215f8be039cd35a6c45bb8cb74a448fb" dependencies: joi "^6.10.1" jws "^3.1.4" @@ -454,15 +993,14 @@ jwa@^1.1.4: ecdsa-sig-formatter "1.0.9" safe-buffer "^5.0.1" -jwks-rsa@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/jwks-rsa/-/jwks-rsa-1.2.0.tgz#b7e925b9eac0722ad8634ad6a7e97c7daed3d4c8" +jwks-rsa-koa@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/jwks-rsa-koa/-/jwks-rsa-koa-1.1.3.tgz#5f5c5ebacb248b42d985f40155cf77e29532e11c" dependencies: - "@types/express-jwt" "0.0.34" debug "^2.2.0" limiter "^1.1.0" lru-memoizer "^1.6.0" - ms "^2.0.0" + ms "^0.7.1" request "^2.73.0" jws@^3.1.4: @@ -473,6 +1011,110 @@ jws@^3.1.4: jwa "^1.1.4" safe-buffer "^5.0.1" +keygrip@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.0.2.tgz#ad3297c557069dea8bcfe7a4fa491b75c5ddeb91" + +koa-bodyparser@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/koa-bodyparser/-/koa-bodyparser-4.2.0.tgz#bce6e08bc65f8709b6d1faa9411c7f0d8938aa54" + dependencies: + co-body "^5.1.0" + copy-to "^2.0.1" + +koa-compose@^3.0.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-3.2.1.tgz#a85ccb40b7d986d8e5a345b3a1ace8eabcf54de7" + dependencies: + any-promise "^1.1.0" + +koa-compose@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-4.0.0.tgz#2800a513d9c361ef0d63852b038e4f6f2d5a773c" + +koa-convert@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/koa-convert/-/koa-convert-1.2.0.tgz#da40875df49de0539098d1700b50820cebcd21d0" + dependencies: + co "^4.6.0" + koa-compose "^3.0.0" + +koa-is-json@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/koa-is-json/-/koa-is-json-1.0.0.tgz#273c07edcdcb8df6a2c1ab7d59ee76491451ec14" + +koa-jwt@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/koa-jwt/-/koa-jwt-3.2.2.tgz#680de615869678a795838270190d7a44581e334e" + dependencies: + jsonwebtoken "7.4.1" + koa-unless "1.0.0" + +koa-morgan@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/koa-morgan/-/koa-morgan-1.0.1.tgz#08052e0ce0d839d3c43178b90a5bb3424bef1f99" + dependencies: + morgan "^1.6.1" + +koa-router@^7.2.1: + version "7.2.1" + resolved "https://registry.yarnpkg.com/koa-router/-/koa-router-7.2.1.tgz#b40a4ab3c6adb4b40895debd00a9c640304e3039" + dependencies: + debug "^2.2.0" + http-errors "^1.3.1" + koa-compose "^3.0.0" + methods "^1.0.1" + path-to-regexp "^1.1.1" + +koa-send@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/koa-send/-/koa-send-4.1.0.tgz#07d5a4eaab212679fe99916aae6b1109c08c2361" + dependencies: + debug "^2.6.3" + http-errors "^1.6.1" + mz "^2.6.0" + resolve-path "^1.3.3" + +koa-static@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/koa-static/-/koa-static-4.0.1.tgz#b99521ed848d7adb79acae9c824d8d8277a8c4d5" + dependencies: + debug "^2.6.8" + koa-send "^4.1.0" + +koa-unless@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/koa-unless/-/koa-unless-1.0.0.tgz#5aa57384bc882568afc90ac04852a3d58658aeeb" + +koa@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/koa/-/koa-2.3.0.tgz#9e1e8e4da401839c57b8527eadc57f76127555a7" + dependencies: + accepts "^1.2.2" + content-disposition "~0.5.0" + content-type "^1.0.0" + cookies "~0.7.0" + debug "*" + delegates "^1.0.0" + depd "^1.1.0" + destroy "^1.0.3" + error-inject "~1.0.0" + escape-html "~1.0.1" + fresh "^0.5.0" + http-assert "^1.1.0" + http-errors "^1.2.8" + is-generator-function "^1.0.3" + koa-compose "^4.0.0" + koa-convert "^1.2.0" + koa-is-json "^1.0.0" + mime-types "^2.0.7" + on-finished "^2.1.0" + only "0.0.2" + parseurl "^1.3.0" + statuses "^1.2.0" + type-is "^1.5.5" + vary "^1.0.0" + limiter@^1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/limiter/-/limiter-1.1.2.tgz#229d8055891c8b11af9e0ee5200e8e09bb3dcbeb" @@ -485,11 +1127,7 @@ lodash.once@^4.0.0: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" -lodash.set@^4.0.0: - version "4.3.2" - resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23" - -lodash@^4.13.1: +lodash@^4.17.4: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" @@ -497,6 +1135,12 @@ lodash@~4.5.1: version "4.5.1" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.5.1.tgz#80e8a074ca5f3893a6b1c10b2a636492d710c316" +loose-envify@^1.0.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848" + dependencies: + js-tokens "^3.0.0" + lru-cache@~4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.0.2.tgz#1d17679c069cda5d040991a09dbc2c0db377e55e" @@ -517,11 +1161,7 @@ media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" -merge-descriptors@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" - -methods@^1.0.0, methods@~1.1.2: +methods@^1.0.1: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" @@ -529,21 +1169,33 @@ mime-db@~1.30.0: version "1.30.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01" -mime-types@^2.1.12, mime-types@~2.1.15, mime-types@~2.1.16, mime-types@~2.1.7: +mime-types@^2.0.7, mime-types@^2.1.12, mime-types@~2.1.15, mime-types@~2.1.16, mime-types@~2.1.7: version "2.1.17" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.17.tgz#09d7a393f03e995a79f8af857b70a9e0ab16557a" dependencies: mime-db "~1.30.0" -mime@1.3.4: - version "1.3.4" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53" +minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + dependencies: + brace-expansion "^1.1.7" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + +mkdirp@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + dependencies: + minimist "0.0.8" moment@2.x.x: version "2.18.1" resolved "https://registry.yarnpkg.com/moment/-/moment-2.18.1.tgz#c36193dd3ce1c2eed2adb7c802dbbc77a81b1c0f" -morgan@^1.8.2: +morgan@^1.6.1: version "1.8.2" resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.8.2.tgz#784ac7734e4a453a9c6e6e8680a9329275c8b687" dependencies: @@ -557,6 +1209,18 @@ ms@2.0.0, ms@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" +ms@^0.7.1: + version "0.7.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.3.tgz#708155a5e44e33f5fd0fc53e81d0d40a91be1fff" + +mz@^2.6.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" + dependencies: + any-promise "^1.0.0" + object-assign "^4.0.1" + thenify-all "^1.0.0" + negotiator@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" @@ -565,11 +1229,19 @@ node-fingerprint@0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/node-fingerprint/-/node-fingerprint-0.0.2.tgz#31cbabeb71a67ae7dd5a7dc042e51c3c75868501" +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + oauth-sign@~0.8.1: version "0.8.2" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" -on-finished@~2.3.0: +object-assign@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + +on-finished@^2.1.0, on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" dependencies: @@ -579,17 +1251,35 @@ on-headers@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.1.tgz#928f5d0f470d49342651ea6794b0857c100693f7" +only@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/only/-/only-0.0.2.tgz#2afde84d03e50b9a8edc444e30610a70295edfb4" + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + +os-tmpdir@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + packet-reader@0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/packet-reader/-/packet-reader-0.3.1.tgz#cd62e60af8d7fea8a705ec4ff990871c46871f27" -parseurl@~1.3.1: +parseurl@^1.3.0: version "1.3.2" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" -path-to-regexp@0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" +path-is-absolute@1.0.1, path-is-absolute@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + +path-to-regexp@^1.1.1: + version "1.7.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.7.0.tgz#59fde0f435badacba103a84e9d3bc64e96b9937d" + dependencies: + isarray "0.0.1" performance-now@^0.2.0: version "0.2.0" @@ -649,12 +1339,9 @@ postgres-interval@^1.1.0: dependencies: xtend "^4.0.0" -proxy-addr@~1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-1.1.5.tgz#71c0ee3b102de3f202f3b64f608d173fcba1a918" - dependencies: - forwarded "~0.1.0" - ipaddr.js "1.4.0" +private@^0.1.6, private@^0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/private/-/private-0.1.7.tgz#68ce5e8a1ef0a23bb570cc28537b5332aba63ef1" pseudomap@^1.0.1: version "1.0.2" @@ -664,17 +1351,62 @@ punycode@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" -qs@6.5.0: - version "6.5.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.0.tgz#8d04954d364def3efc55b5a0793e1e2c8b1e6e49" +qs@^6.4.0: + version "6.5.1" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" qs@~6.4.0: version "6.4.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" -range-parser@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" +raw-body@^2.2.0: + version "2.3.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.2.tgz#bcd60c77d3eb93cde0050295c3f379389bc88f89" + dependencies: + bytes "3.0.0" + http-errors "1.6.2" + iconv-lite "0.4.19" + unpipe "1.0.0" + +regenerate@^1.2.1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.3.tgz#0c336d3980553d755c39b586ae3b20aa49c82b7f" + +regenerator-runtime@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz#7e54fe5b5ccd5d6624ea6255c3473be090b802e1" + +regenerator-transform@^0.10.0: + version "0.10.1" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.10.1.tgz#1e4996837231da8b7f3cf4114d71b5691a0680dd" + dependencies: + babel-runtime "^6.18.0" + babel-types "^6.19.0" + private "^0.1.6" + +regexpu-core@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-2.0.0.tgz#49d038837b8dcf8bfa5b9a42139938e6ea2ae240" + dependencies: + regenerate "^1.2.1" + regjsgen "^0.2.0" + regjsparser "^0.1.4" + +regjsgen@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7" + +regjsparser@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c" + dependencies: + jsesc "~0.5.0" + +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + dependencies: + is-finite "^1.0.0" request@^2.73.0: version "2.81.0" @@ -703,6 +1435,13 @@ request@^2.73.0: tunnel-agent "^0.6.0" uuid "^3.0.0" +resolve-path@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/resolve-path/-/resolve-path-1.3.3.tgz#4d83aba6468c2b8e632a575e3f52b0fa0dbe1a5c" + dependencies: + http-errors "~1.5.0" + path-is-absolute "1.0.1" + safe-buffer@^5.0.1: version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" @@ -711,43 +1450,38 @@ semver@4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.2.tgz#c7a07158a80bedd052355b770d82d6640f803be7" -send@0.15.4: - version "0.15.4" - resolved "https://registry.yarnpkg.com/send/-/send-0.15.4.tgz#985faa3e284b0273c793364a35c6737bd93905b9" - dependencies: - debug "2.6.8" - depd "~1.1.1" - destroy "~1.0.4" - encodeurl "~1.0.1" - escape-html "~1.0.3" - etag "~1.8.0" - fresh "0.5.0" - http-errors "~1.6.2" - mime "1.3.4" - ms "2.0.0" - on-finished "~2.3.0" - range-parser "~1.2.0" - statuses "~1.3.1" +semver@^5.3.0: + version "5.4.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" -serve-static@1.12.4: - version "1.12.4" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.12.4.tgz#9b6aa98eeb7253c4eedc4c1f6fdbca609901a961" - dependencies: - encodeurl "~1.0.1" - escape-html "~1.0.3" - parseurl "~1.3.1" - send "0.15.4" +setprototypeof@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.2.tgz#81a552141ec104b88e89ce383103ad5c66564d08" setprototypeof@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04" +slash@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" + sntp@1.x.x: version "1.0.9" resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" dependencies: hoek "2.x.x" +source-map-support@^0.4.15: + version "0.4.18" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f" + dependencies: + source-map "^0.5.6" + +source-map@^0.5.6: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + split@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9" @@ -768,7 +1502,7 @@ sshpk@^1.7.0: jsbn "~0.1.0" tweetnacl "~0.14.0" -"statuses@>= 1.3.1 < 2", statuses@~1.3.1: +"statuses@>= 1.3.1 < 2", statuses@^1.2.0: version "1.3.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e" @@ -776,10 +1510,42 @@ stringstream@~0.0.4: version "0.0.5" resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" +strip-ansi@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + dependencies: + ansi-regex "^2.0.0" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + +supports-color@^4.0.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.4.0.tgz#883f7ddabc165142b2a61427f3352ded195d1a3e" + dependencies: + has-flag "^2.0.0" + +thenify-all@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" + dependencies: + thenify ">= 3.1.0 < 4" + +"thenify@>= 3.1.0 < 4": + version "3.3.0" + resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.0.tgz#e69e38a1babe969b0108207978b9f62b88604839" + dependencies: + any-promise "^1.0.0" + through@2: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" +to-fast-properties@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" + topo@1.x.x: version "1.1.0" resolved "https://registry.yarnpkg.com/topo/-/topo-1.1.0.tgz#e9d751615d1bb87dc865db182fa1ca0a5ef536d5" @@ -792,6 +1558,10 @@ tough-cookie@~2.3.0: dependencies: punycode "^1.4.1" +trim-right@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" + tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" @@ -802,26 +1572,22 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" -type-is@~1.6.15: +type-is@^1.5.5, type-is@^1.6.14: version "1.6.15" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.15.tgz#cab10fb4909e441c82842eafe1ad646c81804410" dependencies: media-typer "0.3.0" mime-types "~2.1.15" -unpipe@~1.0.0: +unpipe@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" -utils-merge@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.0.tgz#0294fb922bb9375153541c4f7096231f287c8af8" - uuid@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04" -vary@~1.1.1: +vary@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.1.tgz#67535ebb694c1d52257457984665323f587e8d37" diff --git a/src/app/src/components/Dashboard.vue b/src/app/src/components/Dashboard.vue index fd61dab..b86c3a7 100644 --- a/src/app/src/components/Dashboard.vue +++ b/src/app/src/components/Dashboard.vue @@ -1,14 +1,16 @@ \ No newline at end of file -- 2.45.1 From 015645aa86e32e9a6883ffbead649f2361e47c95 Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Thu, 21 Sep 2017 18:45:06 -0500 Subject: [PATCH 4/4] Adding request works (mostly) Everything works except adding the new request back to the list; this proof of concept is a go, and will be merged to master --- src/api/app.js | 33 ++++++++---- src/api/db/index.js | 2 +- src/api/db/request.js | 51 +++++++++++++++---- src/api/index.js | 3 ++ src/api/json.mjs | 12 +++++ src/api/routes/request.js | 3 +- src/app/src/api/index.js | 8 ++- src/app/src/auth/AuthService.js | 12 +++-- src/app/src/components/Dashboard.vue | 10 +++- src/app/src/components/request/NewRequest.vue | 12 ++++- .../components/request/RequestListItem.vue | 15 ++++++ src/app/src/store/action-types.js | 9 +++- src/app/src/store/index.js | 46 ++++++++++------- src/app/src/store/mutation-types.js | 18 +++++-- 14 files changed, 179 insertions(+), 55 deletions(-) create mode 100644 src/api/json.mjs create mode 100644 src/app/src/components/request/RequestListItem.vue diff --git a/src/api/app.js b/src/api/app.js index 9ba2d29..4e18b89 100644 --- a/src/api/app.js +++ b/src/api/app.js @@ -6,17 +6,30 @@ const env = process.env.NODE_ENV || 'dev' if ('dev' === env) require('babel-register') +const app = require('./index').default +const db = require('./db').default +const json = require('./json.mjs').default + const fullEnv = ('dev' === env) ? 'Development' : 'Production' -/** Configuration for the application */ -const appConfig = require('./appsettings.json') +const { port } = json('./appsettings.json') -/** Express app */ -const app = require('./index').default +/** + * Log a start-up message for the app + * @param {string} status The status to display + */ +const startupMsg = (status) => { + console.log(chalk`{reset myPrayerJournal ${status} | Port: {bold ${port}} | Mode: {bold ${fullEnv}}}`) +} -// Ensure the database exists... -require('./db').default.verify().then(() => - // ...and start it up! - app.listen(appConfig.port, () => { - console.log(chalk`{reset myPrayerJournal | Port: {bold ${appConfig.port}} | Mode: {bold ${fullEnv}}}`) - })) +// Ensure the database exists before starting up +db.verify() +.then(() => app.listen(port, () => startupMsg('ready'))) +.catch(err => { + console.log(chalk`\n{reset {bgRed.white.bold || Error connecting to PostgreSQL }}`) + for (let key of Object.keys(err)) { + console.log(chalk`${key}: {reset {bold ${err[key]}}}`) + } + console.log('') + startupMsg('failed') +}) diff --git a/src/api/db/index.js b/src/api/db/index.js index 0b5b349..5e20290 100644 --- a/src/api/db/index.js +++ b/src/api/db/index.js @@ -18,6 +18,6 @@ const query = (text, params) => pool.query(text, params) export default { query: query, - request: request(query), + request: request(pool), verify: ddl(query).ensureDatabase } diff --git a/src/api/db/request.js b/src/api/db/request.js index c25f2f0..4a96a85 100644 --- a/src/api/db/request.js +++ b/src/api/db/request.js @@ -3,7 +3,7 @@ import { Pool } from 'pg' import cuid from 'cuid' -export default function (query) { +export default function (pool) { return { /** * Get the current requests for a user (i.e., their complete current journal) @@ -11,24 +11,55 @@ export default function (query) { * @return The requests that make up the current journal */ journal: async userId => - (await query('SELECT "requestId" FROM request WHERE "userId" = $1', [userId])).rows, + (await pool.query({ + name: 'journal', + text: ` + SELECT + request."requestId", + (SELECT "text" + FROM mpj.history + WHERE history."requestId" = request."requestId" + AND "text" IS NOT NULL + ORDER BY "asOf" DESC + LIMIT 1) AS "text", + (SELECT "asOf" + FROM mpj.history + WHERE history."requestId" = request."requestId" + ORDER BY "asOf" DESC + LIMIT 1) AS "asOf" + FROM mpj.request + WHERE "userId" = $1 + GROUP BY request."requestId"` + }, [userId])).rows, /** * Add a new prayer request * @param {string} userId The Id of the user * @param {string} requestText The text of the request - * @return {string} The Id of the created request + * @return The created request */ addNew: async (userId, requestText) => { const id = cuid() const enteredOn = Date.now() - await query(` - BEGIN; - INSERT INTO request ("requestId", "enteredOn", "userId") VALUES ($1, $2, $3); - INSERT INTO history ("requestId", "asOf", "status", "text") VALUES ($1, $2, 'Created', $4); - COMMIT;`, - [ id, enteredOn, userId, requestText ]) - return id + ;(async () => { + const client = await pool.connect() + try { + await client.query('BEGIN') + await client.query( + 'INSERT INTO mpj.request ("requestId", "enteredOn", "userId") VALUES ($1, $2, $3)', + [ id, enteredOn, userId ]) + await client.query( + `INSERT INTO mpj.history ("requestId", "asOf", "status", "text") VALUES ($1, $2, 'Created', $3)`, + [ id, enteredOn, requestText ]) + await client.query('COMMIT') + } catch (e) { + await client.query('ROLLBACK') + throw e + } finally { + client.release() + } + return { requestId: id, text: requestText, asOf: enteredOn } + })().catch(e => console.error(e.stack)) } } } diff --git a/src/api/index.js b/src/api/index.js index a467651..f516249 100644 --- a/src/api/index.js +++ b/src/api/index.js @@ -1,6 +1,7 @@ 'use strict' import Koa from 'koa' +import bodyParser from 'koa-bodyparser' import morgan from 'koa-morgan' import send from 'koa-send' import serveFrom from 'koa-static' @@ -16,6 +17,8 @@ export default app .use(morgan('dev')) // Serve the Vue files from /public .use(serveFrom('public')) + // Parse the body into ctx.request.body, if present + .use(bodyParser()) // Tie in all the routes .use(router.routes()) .use(router.allowedMethods()) diff --git a/src/api/json.mjs b/src/api/json.mjs new file mode 100644 index 0000000..c5eb575 --- /dev/null +++ b/src/api/json.mjs @@ -0,0 +1,12 @@ +'use strict' + +import fs from 'fs' + +/** + * Read and parse a JSON file + * @param {string} path The path to the file + * @param {string} encoding The encoding of the file (defaults to UTF-8) + * @return {*} The parsed contents of the file + */ +export default (path, encoding = 'utf-8') => + JSON.parse(fs.readFileSync(path, encoding)) diff --git a/src/api/routes/request.js b/src/api/routes/request.js index b08edf4..06f3191 100644 --- a/src/api/routes/request.js +++ b/src/api/routes/request.js @@ -8,8 +8,7 @@ const router = new Router() export default function (checkJwt) { router.post('/', checkJwt, async (ctx, next) => { - const newId = await db.request.addNew(ctx.state.user.sub, ctx.body.requestText) - ctx.body = { id: newId } + ctx.body = await db.request.addNew(ctx.state.user.sub, ctx.request.body.requestText) await next() }) diff --git a/src/app/src/api/index.js b/src/app/src/api/index.js index 91b7732..c0a1a83 100644 --- a/src/app/src/api/index.js +++ b/src/app/src/api/index.js @@ -23,6 +23,12 @@ export default { /** * Get all prayer requests and their most recent updates */ - journal: () => http.get('journal/') + journal: () => http.get('journal/'), + + /** + * Add a new prayer request + * @param {string} requestText The text of the request to be added + */ + addRequest: requestText => http.post('request/', { requestText }) } diff --git a/src/app/src/auth/AuthService.js b/src/app/src/auth/AuthService.js index c71717f..e840abf 100644 --- a/src/app/src/auth/AuthService.js +++ b/src/app/src/auth/AuthService.js @@ -1,7 +1,9 @@ -import auth0 from 'auth0-js' -import { AUTH_CONFIG } from './auth0-variables' +'use strict' -import * as types from '@/store/mutation-types' +import auth0 from 'auth0-js' + +import AUTH_CONFIG from './auth0-variables' +import mutations from '@/store/mutation-types' export default class AuthService { @@ -64,7 +66,7 @@ export default class AuthService { this.setSession(authResult) this.userInfo(authResult.accessToken) .then(user => { - store.commit(types.USER_LOGGED_ON, user) + store.commit(mutations.USER_LOGGED_ON, user) router.replace('/dashboard') }) } @@ -93,7 +95,7 @@ export default class AuthService { localStorage.removeItem('expires_at') localStorage.setItem('user_profile', JSON.stringify({})) // navigate to the home route - store.commit(types.USER_LOGGED_OFF) + store.commit(mutations.USER_LOGGED_OFF) router.replace('/') } diff --git a/src/app/src/components/Dashboard.vue b/src/app/src/components/Dashboard.vue index b86c3a7..71c273d 100644 --- a/src/app/src/components/Dashboard.vue +++ b/src/app/src/components/Dashboard.vue @@ -5,14 +5,19 @@ template(v-if="!isLoadingJournal") new-request p journal has {{ journal.length }} entries + request-list-item(v-for="request in journal" v-bind:request="request" v-bind:key="request.requestId") \ No newline at end of file diff --git a/src/app/src/components/request/RequestListItem.vue b/src/app/src/components/request/RequestListItem.vue new file mode 100644 index 0000000..d2b2445 --- /dev/null +++ b/src/app/src/components/request/RequestListItem.vue @@ -0,0 +1,15 @@ + + + diff --git a/src/app/src/store/action-types.js b/src/app/src/store/action-types.js index dc4fcf0..6d24c31 100644 --- a/src/app/src/store/action-types.js +++ b/src/app/src/store/action-types.js @@ -1 +1,8 @@ -export const LOAD_JOURNAL = 'load-journal' +'use strict' + +export default { + /** Action to add a prayer request (pass request text) */ + ADD_REQUEST: 'add-request', + /** Action to load the user's prayer journal */ + LOAD_JOURNAL: 'load-journal' +} diff --git a/src/app/src/store/index.js b/src/app/src/store/index.js index a9cb43c..556f9ce 100644 --- a/src/app/src/store/index.js +++ b/src/app/src/store/index.js @@ -4,8 +4,8 @@ import Vuex from 'vuex' import api from '@/api' import AuthService from '@/auth/AuthService' -import * as types from './mutation-types' -import * as actions from './action-types' +import mutations from './mutation-types' +import actions from './action-types' Vue.use(Vuex) @@ -38,38 +38,48 @@ export default new Vuex.Store({ isLoadingJournal: false }, mutations: { - [types.USER_LOGGED_ON] (state, user) { + [mutations.USER_LOGGED_ON] (state, user) { localStorage.setItem('user_profile', JSON.stringify(user)) state.user = user api.setBearer(localStorage.getItem('id_token')) state.isAuthenticated = true }, - [types.USER_LOGGED_OFF] (state) { + [mutations.USER_LOGGED_OFF] (state) { state.user = {} api.removeBearer() state.isAuthenticated = false }, - [types.LOADING_JOURNAL] (state, flag) { + [mutations.LOADING_JOURNAL] (state, flag) { state.isLoadingJournal = flag }, - [types.LOADED_JOURNAL] (state, journal) { + [mutations.LOADED_JOURNAL] (state, journal) { state.journal = journal + }, + [mutations.REQUEST_ADDED] (state, newRequest) { + state.journal.unshift(newRequest) } }, actions: { - [actions.LOAD_JOURNAL] ({ commit }) { - commit(types.LOADED_JOURNAL, {}) - commit(types.LOADING_JOURNAL, true) + async [actions.LOAD_JOURNAL] ({ commit }) { + commit(mutations.LOADED_JOURNAL, {}) + commit(mutations.LOADING_JOURNAL, true) api.setBearer(localStorage.getItem('id_token')) - api.journal() - .then(jrnl => { - commit(types.LOADING_JOURNAL, false) - commit(types.LOADED_JOURNAL, jrnl.data) - }) - .catch(err => { - commit(types.LOADING_JOURNAL, false) - logError(err) - }) + try { + const jrnl = await api.journal() + commit(mutations.LOADED_JOURNAL, jrnl.data) + } catch (err) { + logError(err) + } finally { + commit(mutations.LOADING_JOURNAL, false) + } + }, + async [actions.ADD_REQUEST] ({ commit }, requestText) { + try { + const newRequest = await api.addRequest(requestText) + commit(mutations.REQUEST_ADDED, newRequest) + } catch (err) { + logError(err) + } } }, getters: {}, diff --git a/src/app/src/store/mutation-types.js b/src/app/src/store/mutation-types.js index d306356..e396b63 100644 --- a/src/app/src/store/mutation-types.js +++ b/src/app/src/store/mutation-types.js @@ -1,4 +1,14 @@ -export const USER_LOGGED_OFF = 'user-logged-out' -export const USER_LOGGED_ON = 'user-logged-on' -export const LOADING_JOURNAL = 'loading-journal' -export const LOADED_JOURNAL = 'journal-loaded' +'use strict' + +export default { + /** Mutation for when the user's prayer journal is being loaded */ + LOADING_JOURNAL: 'loading-journal', + /** Mutation for when the user's prayer journal has been loaded */ + LOADED_JOURNAL: 'journal-loaded', + /** Mutation for adding a new prayer request (pass text) */ + REQUEST_ADDED: 'request-added', + /** Mutation for logging a user off */ + USER_LOGGED_OFF: 'user-logged-off', + /** Mutation for logging a user on (pass user) */ + USER_LOGGED_ON: 'user-logged-on' +} -- 2.45.1