mostly housekeeping
- App - some package clean-up - API - removed commented code that is not needed, postured for JSON outputs - Build - modified the scripts to build Vue into API's wwwroot folder, which will get copied on publish; adjusted FAKE task dependencies to be more granular
This commit is contained in:
		
							parent
							
								
									196db30cc5
								
							
						
					
					
						commit
						5a7a74c167
					
				
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -253,8 +253,7 @@ paket-files/
 | 
				
			|||||||
*.sln.iml
 | 
					*.sln.iml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Compiled files / application
 | 
					# Compiled files / application
 | 
				
			||||||
**/vendor-bundle.js
 | 
					 | 
				
			||||||
**/app-bundle.js*
 | 
					 | 
				
			||||||
src/api/wwwroot/index.html
 | 
					src/api/wwwroot/index.html
 | 
				
			||||||
 | 
					src/api/wwwroot/static
 | 
				
			||||||
src/api/appsettings.json
 | 
					src/api/appsettings.json
 | 
				
			||||||
build/
 | 
					build/
 | 
				
			||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
@echo off
 | 
					@echo off
 | 
				
			||||||
cd .\src\app
 | 
					cd .\src\app
 | 
				
			||||||
au build
 | 
					npm run build prod
 | 
				
			||||||
cd ..\..
 | 
					cd ..\..
 | 
				
			||||||
exit %errorlevel%
 | 
					exit %errorlevel%
 | 
				
			||||||
							
								
								
									
										55
									
								
								build.fsx
									
									
									
									
									
								
							
							
						
						
									
										55
									
								
								build.fsx
									
									
									
									
									
								
							@ -4,7 +4,7 @@ open System
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
let buildDir = "./build/"
 | 
					let buildDir = "./build/"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Path to the Aurelia app
 | 
					/// Path to the Vue app
 | 
				
			||||||
let appPath = "src" @@ "app"
 | 
					let appPath = "src" @@ "app"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Path to the Suave API
 | 
					/// Path to the Suave API
 | 
				
			||||||
@ -14,27 +14,15 @@ let apiPath = "src" @@ "api"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Target "Clean" (fun _ ->
 | 
					Target "Clean" (fun _ ->
 | 
				
			||||||
  CleanDir buildDir
 | 
					  CleanDir buildDir
 | 
				
			||||||
 | 
					  CleanDir (apiPath @@ "wwwroot")
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Target "BuildApp" (fun _ ->
 | 
					Target "BuildApp" (fun _ ->
 | 
				
			||||||
  let result =
 | 
					  let result =
 | 
				
			||||||
    ExecProcessAndReturnMessages (fun info ->
 | 
					    ExecProcessAndReturnMessages (fun info ->
 | 
				
			||||||
      info.UseShellExecute <- false
 | 
					      info.UseShellExecute <- false
 | 
				
			||||||
      info.FileName <- "." @@ "build-au.bat") (TimeSpan.FromMinutes 2.)
 | 
					      info.FileName <- "build-vue.bat") (TimeSpan.FromMinutes 2.)
 | 
				
			||||||
  match result.ExitCode with
 | 
					  match result.ExitCode with 0 -> Log "AppBuild-Output: " result.Messages | _ -> failwith "Vue build failed"
 | 
				
			||||||
  | 0 -> Log "AppBuild-Output: " result.Messages
 | 
					 | 
				
			||||||
  | _ -> failwith "Aurelia build failed"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Target "CopyApp" (fun _ ->
 | 
					 | 
				
			||||||
  let apiWebPath = apiPath @@ "wwwroot"
 | 
					 | 
				
			||||||
  [ "scripts" @@ "app-bundle.js"
 | 
					 | 
				
			||||||
    "scripts" @@ "vendor-bundle.js"
 | 
					 | 
				
			||||||
    "index.html"
 | 
					 | 
				
			||||||
    ]
 | 
					 | 
				
			||||||
  |> List.iter (fun file ->
 | 
					 | 
				
			||||||
      IO.File.Copy (appPath @@ file,  apiWebPath @@ file, true)
 | 
					 | 
				
			||||||
      Log "CopyApp--Output: " (Seq.singleton file))
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Target "BuildApi" (fun _ ->
 | 
					Target "BuildApi" (fun _ ->
 | 
				
			||||||
@ -43,26 +31,24 @@ Target "BuildApi" (fun _ ->
 | 
				
			|||||||
      info.UseShellExecute <- false
 | 
					      info.UseShellExecute <- false
 | 
				
			||||||
      info.FileName <- "dotnet"
 | 
					      info.FileName <- "dotnet"
 | 
				
			||||||
      info.Arguments <- "build"
 | 
					      info.Arguments <- "build"
 | 
				
			||||||
      info.WorkingDirectory <- "src" @@ "api") (TimeSpan.FromMinutes 2.)
 | 
					      info.WorkingDirectory <- apiPath) (TimeSpan.FromMinutes 2.)
 | 
				
			||||||
  Log "AppBuild-Output: " result.Messages
 | 
					  Log "AppBuild-Output: " result.Messages
 | 
				
			||||||
  match result.ExitCode with
 | 
					  match result.ExitCode with 0 -> () | _ -> failwith "API build failed"
 | 
				
			||||||
  | 0 -> ()
 | 
					)
 | 
				
			||||||
  | _ -> failwith "API build failed"
 | 
					
 | 
				
			||||||
  (*!! "src/api/*.fsproj"
 | 
					Target "Publish" (fun _ ->
 | 
				
			||||||
  |> MSBuildRelease buildDir "Build"
 | 
					  ExecProcess (fun info ->
 | 
				
			||||||
  |> Log "ApiBuild-Output: " *)
 | 
					    info.FileName <- "dotnet"
 | 
				
			||||||
 | 
					    info.Arguments <- """publish -o ..\..\build"""
 | 
				
			||||||
 | 
					    info.WorkingDirectory <- apiPath) TimeSpan.MaxValue
 | 
				
			||||||
 | 
					  |> ignore
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Target "Run" (fun _ ->
 | 
					Target "Run" (fun _ ->
 | 
				
			||||||
  ExecProcess (fun info ->
 | 
					 | 
				
			||||||
    info.FileName <- "dotnet"
 | 
					 | 
				
			||||||
    info.Arguments <- """publish -o ..\..\build"""
 | 
					 | 
				
			||||||
    info.WorkingDirectory <- "src" @@ "api") TimeSpan.MaxValue
 | 
					 | 
				
			||||||
  |> ignore
 | 
					 | 
				
			||||||
  ExecProcess (fun info ->
 | 
					  ExecProcess (fun info ->
 | 
				
			||||||
    info.FileName <- "dotnet"
 | 
					    info.FileName <- "dotnet"
 | 
				
			||||||
    info.Arguments <- "myPrayerJournal.dll"
 | 
					    info.Arguments <- "myPrayerJournal.dll"
 | 
				
			||||||
    info.WorkingDirectory <- "build") TimeSpan.MaxValue
 | 
					    info.WorkingDirectory <- buildDir) TimeSpan.MaxValue
 | 
				
			||||||
  |> ignore
 | 
					  |> ignore
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -74,11 +60,18 @@ Target "Default" (fun _ ->
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
"Clean"
 | 
					"Clean"
 | 
				
			||||||
  ==> "BuildApp"
 | 
					  ==> "BuildApp"
 | 
				
			||||||
  ==> "CopyApp"
 | 
					
 | 
				
			||||||
 | 
					"BuildApp"
 | 
				
			||||||
  ==> "BuildApi"
 | 
					  ==> "BuildApi"
 | 
				
			||||||
  ==> "Default"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
"BuildApi"
 | 
					"BuildApi"
 | 
				
			||||||
 | 
					  ==> "Publish"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"Publish"
 | 
				
			||||||
  ==> "Run"
 | 
					  ==> "Run"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"BuildApi" 
 | 
				
			||||||
 | 
					  ==> "Default"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RunTargetOrDefault "Default"
 | 
					RunTargetOrDefault "Default"
 | 
				
			||||||
							
								
								
									
										231
									
								
								src/api/App.fs
									
									
									
									
									
								
							
							
						
						
									
										231
									
								
								src/api/App.fs
									
									
									
									
									
								
							@ -1,76 +1,81 @@
 | 
				
			|||||||
/// Main server module for myPrayerJournal
 | 
					/// Main server module for myPrayerJournal
 | 
				
			||||||
module MyPrayerJournal.App
 | 
					module MyPrayerJournal.App
 | 
				
			||||||
 | 
					
 | 
				
			||||||
open Auth0.AuthenticationApi
 | 
					 | 
				
			||||||
open Auth0.AuthenticationApi.Models
 | 
					 | 
				
			||||||
open Microsoft.EntityFrameworkCore
 | 
					open Microsoft.EntityFrameworkCore
 | 
				
			||||||
open Newtonsoft.Json
 | 
					open Newtonsoft.Json
 | 
				
			||||||
open Newtonsoft.Json.Linq
 | 
					open Newtonsoft.Json.Linq
 | 
				
			||||||
open Reader
 | 
					 | 
				
			||||||
open System
 | 
					open System
 | 
				
			||||||
open System.IO
 | 
					open System.IO
 | 
				
			||||||
open Suave
 | 
					open Suave
 | 
				
			||||||
open Suave.Filters
 | 
					open Suave.Filters
 | 
				
			||||||
open Suave.Operators
 | 
					open Suave.Operators
 | 
				
			||||||
open Suave.Redirection
 | 
					 | 
				
			||||||
open Suave.RequestErrors
 | 
					 | 
				
			||||||
open Suave.State.CookieStateStore
 | 
					 | 
				
			||||||
open Suave.Successful
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
let utf8 = System.Text.Encoding.UTF8
 | 
					// --- Types ---
 | 
				
			||||||
 | 
					 | 
				
			||||||
type JsonNetCookieSerializer () =
 | 
					 | 
				
			||||||
  interface CookieSerialiser with
 | 
					 | 
				
			||||||
    member x.serialise m =
 | 
					 | 
				
			||||||
      utf8.GetBytes (JsonConvert.SerializeObject m)
 | 
					 | 
				
			||||||
    member x.deserialise m =
 | 
					 | 
				
			||||||
      JsonConvert.DeserializeObject<Map<string, obj>> (utf8.GetString m)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Auth0 settings
 | 
				
			||||||
type Auth0Config = {
 | 
					type Auth0Config = {
 | 
				
			||||||
 | 
					  /// The domain used with Auth0
 | 
				
			||||||
  Domain : string
 | 
					  Domain : string
 | 
				
			||||||
 | 
					  /// The client Id
 | 
				
			||||||
  ClientId : string
 | 
					  ClientId : string
 | 
				
			||||||
 | 
					  /// The base64-encoded client secret
 | 
				
			||||||
  ClientSecret : string
 | 
					  ClientSecret : string
 | 
				
			||||||
}
 | 
					  /// The URL-safe base64-encoded client secret
 | 
				
			||||||
 | 
					  ClientSecretJwt : string
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
with
 | 
					with
 | 
				
			||||||
 | 
					  /// An empty set of Auth0 settings
 | 
				
			||||||
  static member empty =
 | 
					  static member empty =
 | 
				
			||||||
    { Domain = ""
 | 
					    { Domain = ""
 | 
				
			||||||
      ClientId = ""
 | 
					      ClientId = ""
 | 
				
			||||||
      ClientSecret = ""
 | 
					      ClientSecret = ""
 | 
				
			||||||
 | 
					      ClientSecretJwt = ""
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Application configuration
 | 
				
			||||||
type Config = {
 | 
					type Config = {
 | 
				
			||||||
 | 
					  /// PostgreSQL connection string
 | 
				
			||||||
  Conn : string
 | 
					  Conn : string
 | 
				
			||||||
 | 
					  /// Auth0 settings
 | 
				
			||||||
  Auth0 : Auth0Config
 | 
					  Auth0 : Auth0Config
 | 
				
			||||||
}
 | 
					  }
 | 
				
			||||||
with
 | 
					with
 | 
				
			||||||
  static member empty =
 | 
					  static member empty =
 | 
				
			||||||
    { Conn = ""
 | 
					    { Conn = ""
 | 
				
			||||||
      Auth0 = Auth0Config.empty
 | 
					      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
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// --- Support ---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Configuration instance
 | 
				
			||||||
let cfg =
 | 
					let cfg =
 | 
				
			||||||
  try
 | 
					  try
 | 
				
			||||||
    use sr = File.OpenText "appsettings.json"
 | 
					    use sr = File.OpenText "appsettings.json"
 | 
				
			||||||
    let settings = JToken.ReadFrom(new JsonTextReader(sr)) :?> JObject
 | 
					    use tr = new JsonTextReader (sr)
 | 
				
			||||||
 | 
					    let settings = JToken.ReadFrom tr
 | 
				
			||||||
 | 
					    let secret = settings.["auth0"].["client-secret"].ToObject<string>()
 | 
				
			||||||
    { Conn = settings.["conn"].ToObject<string>()
 | 
					    { Conn = settings.["conn"].ToObject<string>()
 | 
				
			||||||
      Auth0 =
 | 
					      Auth0 =
 | 
				
			||||||
        { Domain = settings.["auth0"].["domain"].ToObject<string>()
 | 
					        { Domain = settings.["auth0"].["domain"].ToObject<string>()
 | 
				
			||||||
          ClientId = settings.["auth0"].["client-id"].ToObject<string>()
 | 
					          ClientId = settings.["auth0"].["client-id"].ToObject<string>()
 | 
				
			||||||
          ClientSecret = settings.["auth0"].["client-secret"].ToObject<string>()
 | 
					          ClientSecret = secret
 | 
				
			||||||
 | 
					          ClientSecretJwt = secret.TrimEnd('=').Replace("-", "+").Replace("_", "/")
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
  with _ -> Config.empty
 | 
					  with _ -> Config.empty
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Data Configuration singleton 
 | 
					 | 
				
			||||||
//let lazyCfg = lazy (DataConfig.FromJson <| try File.ReadAllText "data-config.json" with _ -> "{}")
 | 
					 | 
				
			||||||
/// RethinkDB connection singleton
 | 
					 | 
				
			||||||
//let lazyConn = lazy lazyCfg.Force().CreateConnection ()
 | 
					 | 
				
			||||||
/// Application dependencies
 | 
					 | 
				
			||||||
//let deps = {
 | 
					 | 
				
			||||||
//  new IDependencies with
 | 
					 | 
				
			||||||
//    member __.Conn with get () = lazyConn.Force ()
 | 
					 | 
				
			||||||
//  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Get the scheme, host, and port of the URL
 | 
					/// Get the scheme, host, and port of the URL
 | 
				
			||||||
let schemeHostPort (req : HttpRequest) =
 | 
					let schemeHostPort (req : HttpRequest) =
 | 
				
			||||||
  sprintf "%s://%s" req.url.Scheme (req.headers |> List.filter (fun x -> fst x = "host") |> List.head |> snd)
 | 
					  sprintf "%s://%s" req.url.Scheme (req.headers |> List.filter (fun x -> fst x = "host") |> List.head |> snd)
 | 
				
			||||||
@ -78,80 +83,37 @@ let schemeHostPort (req : HttpRequest) =
 | 
				
			|||||||
/// Authorization functions
 | 
					/// Authorization functions
 | 
				
			||||||
module Auth =
 | 
					module Auth =
 | 
				
			||||||
 | 
					
 | 
				
			||||||
(*
 | 
					  /// Shorthand for Console.WriteLine
 | 
				
			||||||
  let exchangeCodeForToken code = context (fun ctx ->
 | 
					 | 
				
			||||||
      async {
 | 
					 | 
				
			||||||
        let client = AuthenticationApiClient (Uri (sprintf "https://%s" cfg.Auth0.Domain))
 | 
					 | 
				
			||||||
        let! req =
 | 
					 | 
				
			||||||
          client.ExchangeCodeForAccessTokenAsync
 | 
					 | 
				
			||||||
            (ExchangeCodeRequest
 | 
					 | 
				
			||||||
              (AuthorizationCode = code,
 | 
					 | 
				
			||||||
              ClientId = cfg.Auth0.ClientId,
 | 
					 | 
				
			||||||
              ClientSecret = cfg.Auth0.ClientSecret,
 | 
					 | 
				
			||||||
              RedirectUri = sprintf "%s/user/log-on" (schemeHostPort ctx.request)))
 | 
					 | 
				
			||||||
        let! user = client.GetUserInfoAsync ((req : AccessToken).AccessToken)
 | 
					 | 
				
			||||||
        return
 | 
					 | 
				
			||||||
          ctx
 | 
					 | 
				
			||||||
          |> HttpContext.state
 | 
					 | 
				
			||||||
          |> function
 | 
					 | 
				
			||||||
          | None -> FORBIDDEN "Cannot sign in without state"
 | 
					 | 
				
			||||||
          | Some state ->
 | 
					 | 
				
			||||||
              state.set "auth-token" req.IdToken
 | 
					 | 
				
			||||||
              >=> Writers.setUserData "user" user
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      |> Async.RunSynchronously
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /// Handle the sign-in callback from Auth0
 | 
					 | 
				
			||||||
  let handleSignIn =
 | 
					 | 
				
			||||||
    context (fun ctx ->
 | 
					 | 
				
			||||||
      GET
 | 
					 | 
				
			||||||
      >=> match ctx.request.queryParam "code" with
 | 
					 | 
				
			||||||
          | Choice1Of2 authCode ->
 | 
					 | 
				
			||||||
              exchangeCodeForToken authCode
 | 
					 | 
				
			||||||
              >=> FOUND (sprintf "%s/journal" (schemeHostPort ctx.request))
 | 
					 | 
				
			||||||
          | Choice2Of2 msg -> BAD_REQUEST msg
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
  /// Handle signing out a user
 | 
					 | 
				
			||||||
  let handleSignOut =
 | 
					 | 
				
			||||||
    context (fun ctx ->
 | 
					 | 
				
			||||||
      match ctx |> HttpContext.state with
 | 
					 | 
				
			||||||
      | Some state -> state.set "auth-key" null
 | 
					 | 
				
			||||||
      | _ -> succeed
 | 
					 | 
				
			||||||
      >=> FOUND (sprintf "%s/" (schemeHostPort ctx.request))) *)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  let cw (x : string) = Console.WriteLine x
 | 
					  let cw (x : string) = Console.WriteLine x
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Convert microtime to ticks, add difference from 1/1/1 to 1/1/1970
 | 
					  /// Convert microtime to ticks, add difference from 1/1/1 to 1/1/1970
 | 
				
			||||||
  let jsDate jsTicks =
 | 
					  let jsDate jsTicks =
 | 
				
			||||||
    DateTime(jsTicks * 10000000L).AddTicks(DateTime(1970, 1, 1).Ticks)
 | 
					    DateTime(jsTicks * 10000000L).AddTicks(DateTime(1970, 1, 1).Ticks)
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  let getIdFromToken token =
 | 
					  /// Get the user Id (sub) from a JSON Web Token
 | 
				
			||||||
    match token with
 | 
					  let getIdFromToken jwt =
 | 
				
			||||||
    | Some jwt ->
 | 
					    try
 | 
				
			||||||
        try
 | 
					      let payload = Jose.JWT.Decode<JObject>(jwt, cfg.Auth0.ClientSecretJwt)
 | 
				
			||||||
          let key = Convert.FromBase64String(cfg.Auth0.ClientSecret.Replace("-", "+").Replace("_", "/"))
 | 
					      let tokenExpires = jsDate (payload.["exp"].ToObject<int64>())
 | 
				
			||||||
          let payload = Jose.JWT.Decode<JObject>(jwt, key)
 | 
					      match tokenExpires > DateTime.UtcNow with
 | 
				
			||||||
          let tokenExpires = jsDate (payload.["exp"].ToObject<int64>())
 | 
					      | true -> Some (payload.["sub"].ToObject<string>())
 | 
				
			||||||
          match tokenExpires > DateTime.UtcNow with
 | 
					      | _ -> None
 | 
				
			||||||
          | true -> Some (payload.["sub"].ToObject<string>())
 | 
					    with ex ->
 | 
				
			||||||
          | _ -> None
 | 
					      sprintf "Token Deserialization Exception - %s" (ex.GetType().FullName) |> cw
 | 
				
			||||||
        with ex ->
 | 
					      sprintf "Message - %s" ex.Message |> cw
 | 
				
			||||||
          sprintf "Token Deserialization Exception - %s" (ex.GetType().FullName) |> cw
 | 
					      ex.StackTrace |> cw
 | 
				
			||||||
          sprintf "Message - %s" ex.Message |> cw
 | 
					      None
 | 
				
			||||||
          ex.StackTrace |> cw
 | 
					 | 
				
			||||||
          None
 | 
					 | 
				
			||||||
    | _ -> None
 | 
					 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  /// Add the logged on user Id to the context if it exists
 | 
					  /// Add the logged on user Id to the context if it exists
 | 
				
			||||||
  let loggedOn = warbler (fun ctx ->
 | 
					  let loggedOn =
 | 
				
			||||||
      match HttpContext.state ctx with
 | 
					    warbler (fun ctx ->
 | 
				
			||||||
      | Some state -> Writers.setUserData "user" (state.get "auth-token" |> getIdFromToken)
 | 
					      match ctx.request.header "Authorization" with
 | 
				
			||||||
 | 
					      | Choice1Of2 bearer -> Writers.setUserData "user" ((bearer.Split(' ').[1]) |> getIdFromToken)
 | 
				
			||||||
      | _ -> Writers.setUserData "user" None)
 | 
					      | _ -> Writers.setUserData "user" None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Create a user context for the currently assigned user
 | 
					
 | 
				
			||||||
  //let userCtx ctx = { Id = ctx.userState.["user"] :?> string option }
 | 
					/// Serialize an object to JSON
 | 
				
			||||||
 | 
					let toJson = JsonConvert.SerializeObject
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Read an item from the user state, downcast to the expected type
 | 
					/// Read an item from the user state, downcast to the expected type
 | 
				
			||||||
let read ctx key : 'value =
 | 
					let read ctx key : 'value =
 | 
				
			||||||
@ -161,37 +123,6 @@ let read ctx key : 'value =
 | 
				
			|||||||
let dataCtx () =
 | 
					let dataCtx () =
 | 
				
			||||||
  new DataContext (((DbContextOptionsBuilder<DataContext>()).UseNpgsql cfg.Conn).Options)
 | 
					  new DataContext (((DbContextOptionsBuilder<DataContext>()).UseNpgsql cfg.Conn).Options)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Return an HTML page
 | 
					 | 
				
			||||||
let html ctx content =
 | 
					 | 
				
			||||||
  ""//Views.page (Auth.userCtx ctx) content
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Home page
 | 
					 | 
				
			||||||
let viewHome = warbler (fun ctx -> OK ("" (*Views.home*) |> html ctx))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Journal page
 | 
					 | 
				
			||||||
let viewJournal =
 | 
					 | 
				
			||||||
  context (fun ctx ->
 | 
					 | 
				
			||||||
    use dataCtx = dataCtx ()
 | 
					 | 
				
			||||||
    let reqs = Data.Requests.allForUser (defaultArg (read ctx "user") "") dataCtx
 | 
					 | 
				
			||||||
    OK ("" (*Views.journal reqs*) |> html ctx))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
let idx =
 | 
					 | 
				
			||||||
  context (fun ctx ->
 | 
					 | 
				
			||||||
    Console.WriteLine "serving index"
 | 
					 | 
				
			||||||
    succeed)
 | 
					 | 
				
			||||||
/// Suave application
 | 
					 | 
				
			||||||
let app =
 | 
					 | 
				
			||||||
  statefulForSession
 | 
					 | 
				
			||||||
  >=> Auth.loggedOn
 | 
					 | 
				
			||||||
  >=> choose [
 | 
					 | 
				
			||||||
        path Route.home >=> Files.browseFileHome "index.html"
 | 
					 | 
				
			||||||
        path Route.journal >=> viewJournal
 | 
					 | 
				
			||||||
        //path Route.User.logOn >=> Auth.handleSignIn
 | 
					 | 
				
			||||||
        //path Route.User.logOff >=> Auth.handleSignOut
 | 
					 | 
				
			||||||
        Writers.setHeader "Cache-Control" "no-cache" >=> Files.browseHome
 | 
					 | 
				
			||||||
        NOT_FOUND "Page not found." 
 | 
					 | 
				
			||||||
        ]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Ensure the EF context is created in the right format
 | 
					/// Ensure the EF context is created in the right format
 | 
				
			||||||
let ensureDatabase () =
 | 
					let ensureDatabase () =
 | 
				
			||||||
  async {
 | 
					  async {
 | 
				
			||||||
@ -204,16 +135,54 @@ let suaveCfg =
 | 
				
			|||||||
  { defaultConfig with
 | 
					  { defaultConfig with
 | 
				
			||||||
      homeFolder = Some (Path.GetFullPath "./wwwroot/")
 | 
					      homeFolder = Some (Path.GetFullPath "./wwwroot/")
 | 
				
			||||||
      serverKey = Text.Encoding.UTF8.GetBytes("12345678901234567890123456789012")
 | 
					      serverKey = Text.Encoding.UTF8.GetBytes("12345678901234567890123456789012")
 | 
				
			||||||
      cookieSerialiser = JsonNetCookieSerializer ()
 | 
					      bindings = [ HttpBinding.createSimple HTTP "127.0.0.1" 8084 ]
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
open Suave.Utils
 | 
					
 | 
				
			||||||
 | 
					// --- Routes ---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// URL routes for myPrayerJournal
 | 
				
			||||||
 | 
					module Route =
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// /api/journal ~ All active prayer requests for a user
 | 
				
			||||||
 | 
					  let journal = "/api/journal"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// --- WebParts ---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// 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
 | 
				
			||||||
 | 
					    >=> Writers.setStatus code
 | 
				
			||||||
 | 
					    >=> Response.response code ((toJson >> UTF8.bytes) { error = error })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Journal page
 | 
				
			||||||
 | 
					  let viewJournal =
 | 
				
			||||||
 | 
					    context (fun ctx ->
 | 
				
			||||||
 | 
					      use dataCtx = dataCtx ()
 | 
				
			||||||
 | 
					      let reqs = Data.Requests.allForUser (defaultArg (read ctx "user") "") dataCtx
 | 
				
			||||||
 | 
					      JSON reqs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Suave application
 | 
				
			||||||
 | 
					  let app =
 | 
				
			||||||
 | 
					    Auth.loggedOn
 | 
				
			||||||
 | 
					    >=> choose [
 | 
				
			||||||
 | 
					          path Route.journal >=> viewJournal
 | 
				
			||||||
 | 
					          errorJSON HttpCode.HTTP_404 "Page not found"
 | 
				
			||||||
 | 
					          ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[<EntryPoint>]
 | 
					[<EntryPoint>]
 | 
				
			||||||
let main argv = 
 | 
					let main argv = 
 | 
				
			||||||
  // Establish the data environment
 | 
					 | 
				
			||||||
  //liftDep getConn (Data.establishEnvironment >> Async.RunSynchronously)
 | 
					 | 
				
			||||||
  //|> run deps
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
  ensureDatabase ()
 | 
					  ensureDatabase ()
 | 
				
			||||||
  startWebServer suaveCfg app
 | 
					  startWebServer suaveCfg WebParts.app
 | 
				
			||||||
  0 
 | 
					  0 
 | 
				
			||||||
 | 
				
			|||||||
@ -16,7 +16,6 @@
 | 
				
			|||||||
    <Compile Include="Data.fs" />
 | 
					    <Compile Include="Data.fs" />
 | 
				
			||||||
    <Compile Include="Migrations/20170104023341_InitialDb.fs" />
 | 
					    <Compile Include="Migrations/20170104023341_InitialDb.fs" />
 | 
				
			||||||
    <Compile Include="Migrations/DataContextModelSnapshot.fs" />
 | 
					    <Compile Include="Migrations/DataContextModelSnapshot.fs" />
 | 
				
			||||||
    <Compile Include="Route.fs" />
 | 
					 | 
				
			||||||
    <Compile Include="App.fs" />
 | 
					    <Compile Include="App.fs" />
 | 
				
			||||||
    <None Update="appsettings.json">
 | 
					    <None Update="appsettings.json">
 | 
				
			||||||
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 | 
					      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 | 
				
			||||||
@ -28,9 +27,6 @@
 | 
				
			|||||||
  
 | 
					  
 | 
				
			||||||
  <!-- Import Project="..\..\.paket\Paket.Restore.targets" / -->
 | 
					  <!-- Import Project="..\..\.paket\Paket.Restore.targets" / -->
 | 
				
			||||||
  <ItemGroup>
 | 
					  <ItemGroup>
 | 
				
			||||||
    <PackageReference Include="Auth0.AuthenticationApi">
 | 
					 | 
				
			||||||
      <Version>4.1.0</Version>
 | 
					 | 
				
			||||||
    </PackageReference>
 | 
					 | 
				
			||||||
    <PackageReference Include="FSharp.Core">
 | 
					    <PackageReference Include="FSharp.Core">
 | 
				
			||||||
      <Version>4.1.17</Version>
 | 
					      <Version>4.1.17</Version>
 | 
				
			||||||
    </PackageReference>
 | 
					    </PackageReference>
 | 
				
			||||||
@ -38,16 +34,16 @@
 | 
				
			|||||||
      <Version>1.0.5</Version>
 | 
					      <Version>1.0.5</Version>
 | 
				
			||||||
    </PackageReference>
 | 
					    </PackageReference>
 | 
				
			||||||
    <PackageReference Include="jose-jwt">
 | 
					    <PackageReference Include="jose-jwt">
 | 
				
			||||||
      <Version>2.3.0</Version>
 | 
					      <Version>2.*</Version>
 | 
				
			||||||
    </PackageReference>
 | 
					    </PackageReference>
 | 
				
			||||||
    <PackageReference Include="Newtonsoft.Json">
 | 
					    <PackageReference Include="Newtonsoft.Json">
 | 
				
			||||||
      <Version>10.0.2</Version>
 | 
					      <Version>10.*</Version>
 | 
				
			||||||
    </PackageReference>
 | 
					    </PackageReference>
 | 
				
			||||||
    <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL">
 | 
					    <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL">
 | 
				
			||||||
      <Version>1.1.0</Version>
 | 
					      <Version>1.*</Version>
 | 
				
			||||||
    </PackageReference>
 | 
					    </PackageReference>
 | 
				
			||||||
    <PackageReference Include="Suave">
 | 
					    <PackageReference Include="Suave">
 | 
				
			||||||
      <Version>2.1.0</Version>
 | 
					      <Version>2.*</Version>
 | 
				
			||||||
    </PackageReference>
 | 
					    </PackageReference>
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
  </ItemGroup>
 | 
					  </ItemGroup>
 | 
				
			||||||
 | 
				
			|||||||
@ -1,15 +0,0 @@
 | 
				
			|||||||
/// URL routes for myPrayerJournal
 | 
					 | 
				
			||||||
module MyPrayerJournal.Route
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// The home page
 | 
					 | 
				
			||||||
let home = "/"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// The main journal page
 | 
					 | 
				
			||||||
let journal = "/journal"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Routes dealing with users
 | 
					 | 
				
			||||||
module User =
 | 
					 | 
				
			||||||
  /// The route for user log on response from Auth0
 | 
					 | 
				
			||||||
  let logOn = "/user/log-on"
 | 
					 | 
				
			||||||
  let logOff = "/user/log-off"
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
@ -1,35 +0,0 @@
 | 
				
			|||||||
body {
 | 
					 | 
				
			||||||
  padding-top: 50px;
 | 
					 | 
				
			||||||
  padding-bottom: 20px;
 | 
					 | 
				
			||||||
  font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Wrapping element */
 | 
					 | 
				
			||||||
/* Set some basic padding to keep content from hitting the edges */
 | 
					 | 
				
			||||||
.body-content {
 | 
					 | 
				
			||||||
  padding-left: 15px;
 | 
					 | 
				
			||||||
  padding-right: 15px;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
.material-icons.md-18 {
 | 
					 | 
				
			||||||
  font-size: 18px;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
.material-icons.md-24 {
 | 
					 | 
				
			||||||
  font-size: 24px;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
.material-icons.md-36 {
 | 
					 | 
				
			||||||
  font-size: 36px;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
.material-icons.md-48 {
 | 
					 | 
				
			||||||
  font-size: 48px;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
.material-icons {
 | 
					 | 
				
			||||||
  vertical-align: middle;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
.mpj-page-title {
 | 
					 | 
				
			||||||
  border-bottom: solid 1px lightgray;
 | 
					 | 
				
			||||||
  margin-bottom: 20px;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
.mpj-footer {
 | 
					 | 
				
			||||||
  border-top: solid 1px lightgray;
 | 
					 | 
				
			||||||
  margin-top: 20px;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,15 +0,0 @@
 | 
				
			|||||||
/**
 | 
					 | 
				
			||||||
 * myPrayerJournal script file
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
var mpj = {
 | 
					 | 
				
			||||||
  lock: new Auth0Lock('Of2s0RQCQ3mt3dwIkOBY5h85J9sXbF2n', 'djs-consulting.auth0.com', {
 | 
					 | 
				
			||||||
    auth: {
 | 
					 | 
				
			||||||
      redirectUrl: 'http://localhost:8080/user/log-on',
 | 
					 | 
				
			||||||
      allowSignUp: false
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }),
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  signIn: function() {
 | 
					 | 
				
			||||||
    this.lock.show()
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -4,8 +4,8 @@ var path = require('path')
 | 
				
			|||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
  build: {
 | 
					  build: {
 | 
				
			||||||
    env: require('./prod.env'),
 | 
					    env: require('./prod.env'),
 | 
				
			||||||
    index: path.resolve(__dirname, '../dist/index.html'),
 | 
					    index: path.resolve(__dirname, '../../api/wwwroot/index.html'),
 | 
				
			||||||
    assetsRoot: path.resolve(__dirname, '../dist'),
 | 
					    assetsRoot: path.resolve(__dirname, '../../api/wwwroot'),
 | 
				
			||||||
    assetsSubDirectory: 'static',
 | 
					    assetsSubDirectory: 'static',
 | 
				
			||||||
    assetsPublicPath: '/',
 | 
					    assetsPublicPath: '/',
 | 
				
			||||||
    productionSourceMap: true,
 | 
					    productionSourceMap: true,
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										1027
									
								
								src/app/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1027
									
								
								src/app/package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -15,6 +15,7 @@
 | 
				
			|||||||
  },
 | 
					  },
 | 
				
			||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
    "auth0-js": "^8.8.0",
 | 
					    "auth0-js": "^8.8.0",
 | 
				
			||||||
 | 
					    "axios": "^0.16.2",
 | 
				
			||||||
    "element-ui": "^1.4.1",
 | 
					    "element-ui": "^1.4.1",
 | 
				
			||||||
    "pug": "^2.0.0-rc.2",
 | 
					    "pug": "^2.0.0-rc.2",
 | 
				
			||||||
    "vue": "^2.3.3",
 | 
					    "vue": "^2.3.3",
 | 
				
			||||||
 | 
				
			|||||||
@ -19,9 +19,6 @@ export default {
 | 
				
			|||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style>
 | 
					<style>
 | 
				
			||||||
@import url('../node_modules/bootstrap/dist/css/bootstrap.css');
 | 
					 | 
				
			||||||
@import url('../node_modules/bootstrap-vue/dist/bootstrap-vue.css');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
body {
 | 
					body {
 | 
				
			||||||
  padding-top: 60px;
 | 
					  padding-top: 60px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					import axios from 'axios'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const http = axios.create({
 | 
				
			||||||
 | 
					  baseURL: 'http://localhost:8084'
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default {
 | 
				
			||||||
 | 
					  something: http.get('/blah')
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user