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/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/20170104023341_InitialDb.fs b/src/api/Migrations/20170104023341_InitialDb.fs deleted file mode 100644 index 575f0a7..0000000 --- a/src/api/Migrations/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/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/app.js b/src/api/app.js new file mode 100644 index 0000000..4e18b89 --- /dev/null +++ b/src/api/app.js @@ -0,0 +1,35 @@ +'use strict' + +const chalk = require('chalk') + +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' + +const { port } = json('./appsettings.json') + +/** + * 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 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/ddl.js b/src/api/db/ddl.js new file mode 100644 index 0000000..46c3455 --- /dev/null +++ b/src/api/db/ddl.js @@ -0,0 +1,70 @@ +'use strict' + +import { Pool } from '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'` + } +] + +export default function (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 new file mode 100644 index 0000000..5e20290 --- /dev/null +++ b/src/api/db/index.js @@ -0,0 +1,23 @@ +'use strict' + +import { Pool } from 'pg' + +import appConfig from '../appsettings.json' +import ddl from './ddl' +import request from './request' + +/** Pooled PostgreSQL instance */ +const pool = new Pool(appConfig.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) + +export default { + query: query, + request: request(pool), + verify: ddl(query).ensureDatabase +} diff --git a/src/api/db/request.js b/src/api/db/request.js new file mode 100644 index 0000000..4a96a85 --- /dev/null +++ b/src/api/db/request.js @@ -0,0 +1,65 @@ +'use strict' + +import { Pool } from 'pg' +import cuid from 'cuid' + +export default function (pool) { + 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 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 The created request + */ + addNew: async (userId, requestText) => { + const id = cuid() + const enteredOn = Date.now() + ;(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 new file mode 100644 index 0000000..f516249 --- /dev/null +++ b/src/api/index.js @@ -0,0 +1,36 @@ +'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' + +import appConfig from './appsettings.json' +import router from './routes' + +/** Koa app */ +const app = new Koa() + +export default app + // Logging FTW! + .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()) + // 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/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/package.json b/src/api/package.json new file mode 100644 index 0000000..a0e0ecc --- /dev/null +++ b/src/api/package.json @@ -0,0 +1,43 @@ +{ + "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": { + "chalk": "^2.1.0", + "cuid": "^1.3.8", + "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 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 new file mode 100644 index 0000000..50fc8da --- /dev/null +++ b/src/api/routes/index.js @@ -0,0 +1,39 @@ +'use strict' + +import jwt from 'koa-jwt' +import jwksRsa from 'jwks-rsa-koa' +import Router from 'koa-router' + +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.koaJwt2Key({ + cache: true, + rateLimit: true, + jwksRequestsPerMinute: 5, + jwksUri: `https://${appConfig.auth0.domain}/.well-known/jwks.json` + }), + + // Validate the audience and the issuer. + audience: appConfig.auth0.clientId, + issuer: `https://${appConfig.auth0.domain}/`, + algorithms: ['RS256'] +}) + +/** /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 new file mode 100644 index 0000000..1cc17be --- /dev/null +++ b/src/api/routes/journal.js @@ -0,0 +1,16 @@ +'use strict' + +import Router from 'koa-router' +import db from '../db' + +const router = new Router() + +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..06f3191 --- /dev/null +++ b/src/api/routes/request.js @@ -0,0 +1,17 @@ +'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) => { + ctx.body = await db.request.addNew(ctx.state.user.sub, ctx.request.body.requestText) + await next() + }) + + return router +} + diff --git a/src/api/yarn.lock b/src/api/yarn.lock new file mode 100644 index 0000000..9c15ed3 --- /dev/null +++ b/src/api/yarn.lock @@ -0,0 +1,1612 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +accepts@^1.2.2: + 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" + +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" + 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" + +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" + +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" + +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" + +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" + +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" + +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.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + +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" + +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" + +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" + +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" + dependencies: + assert-plus "^1.0.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" + +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.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" + 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" + +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" + +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" + +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" + +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" + 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" + +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" + +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" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + 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" + +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" + +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" + 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" + +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: + depd "1.1.1" + inherits "2.0.3" + 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" + dependencies: + assert-plus "^0.2.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" + +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-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" + +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" + +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" + +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" + +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.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" + 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-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: + debug "^2.2.0" + limiter "^1.1.0" + lru-memoizer "^1.6.0" + ms "^0.7.1" + 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" + +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" + +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@^4.17.4: + 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" + +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" + 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" + +methods@^1.0.1: + 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.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" + +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.6.1: + 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" + +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" + +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" + +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: + 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" + +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.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" + +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" + 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" + +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" + 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.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" + +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" + 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" + +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" + +semver@4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.2.tgz#c7a07158a80bedd052355b770d82d6640f803be7" + +semver@^5.3.0: + version "5.4.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" + +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" + 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.2.0: + 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" + +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" + 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" + +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" + 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.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: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + +uuid@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04" + +vary@^1.0.0: + 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..c0a1a83 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,22 @@ 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/'), + + /** + * 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 fd61dab..71c273d 100644 --- a/src/app/src/components/Dashboard.vue +++ b/src/app/src/components/Dashboard.vue @@ -1,16 +1,23 @@ \ 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 b608063..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,35 +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) - api.journal() - .then(jrnl => { - commit(types.LOADING_JOURNAL, false) - commit(types.LOADED_JOURNAL, jrnl) - }) - .catch(err => { - commit(types.LOADING_JOURNAL, false) - logError(err) - }) + async [actions.LOAD_JOURNAL] ({ commit }) { + commit(mutations.LOADED_JOURNAL, {}) + commit(mutations.LOADING_JOURNAL, true) + api.setBearer(localStorage.getItem('id_token')) + 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' +}