F# API #18
6
.gitignore
vendored
6
.gitignore
vendored
@ -254,8 +254,8 @@ paket-files/
|
|||||||
|
|
||||||
# Compiled files / application
|
# Compiled files / application
|
||||||
src/api/build
|
src/api/build
|
||||||
src/public/index.html
|
src/api/MyPrayerJournal.Api/wwwroot/index.html
|
||||||
src/public/static
|
src/api/MyPrayerJournal.Api/wwwroot/static
|
||||||
src/config.json
|
src/api/MyPrayerJournal.Api/appsettings.development.json
|
||||||
/build
|
/build
|
||||||
src/*.exe
|
src/*.exe
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
/// HTTP handlers for the myPrayerJournal API
|
/// HTTP handlers for the myPrayerJournal API
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
module MyPrayerJournal.Handlers
|
module MyPrayerJournal.Api.Handlers
|
||||||
|
|
||||||
open Giraffe
|
open Giraffe
|
||||||
|
open MyPrayerJournal
|
||||||
open System
|
open System
|
||||||
|
|
||||||
module Error =
|
module Error =
|
||||||
@ -17,7 +18,7 @@ module Error =
|
|||||||
/// Handle 404s from the API, sending known URL paths to the Vue app so that they can be handled there
|
/// Handle 404s from the API, sending known URL paths to the Vue app so that they can be handled there
|
||||||
let notFound : HttpHandler =
|
let notFound : HttpHandler =
|
||||||
fun next ctx ->
|
fun next ctx ->
|
||||||
let vueApp () = htmlFile "/index.html" next ctx
|
let vueApp () = htmlFile "wwwroot/index.html" next ctx
|
||||||
match true with
|
match true with
|
||||||
| _ when ctx.Request.Path.Value.StartsWith "/answered" -> vueApp ()
|
| _ when ctx.Request.Path.Value.StartsWith "/answered" -> vueApp ()
|
||||||
| _ when ctx.Request.Path.Value.StartsWith "/journal" -> vueApp ()
|
| _ when ctx.Request.Path.Value.StartsWith "/journal" -> vueApp ()
|
||||||
@ -31,6 +32,8 @@ module Error =
|
|||||||
module private Helpers =
|
module private Helpers =
|
||||||
|
|
||||||
open Microsoft.AspNetCore.Http
|
open Microsoft.AspNetCore.Http
|
||||||
|
open Microsoft.AspNetCore.Authorization
|
||||||
|
open System.Threading.Tasks
|
||||||
|
|
||||||
/// Get the database context from DI
|
/// Get the database context from DI
|
||||||
let db (ctx : HttpContext) =
|
let db (ctx : HttpContext) =
|
||||||
@ -47,6 +50,21 @@ module private Helpers =
|
|||||||
/// The "now" time in JavaScript
|
/// The "now" time in JavaScript
|
||||||
let jsNow () =
|
let jsNow () =
|
||||||
DateTime.Now.Subtract(DateTime (1970, 1, 1)).TotalSeconds |> int64 |> (*) 1000L
|
DateTime.Now.Subtract(DateTime (1970, 1, 1)).TotalSeconds |> int64 |> (*) 1000L
|
||||||
|
|
||||||
|
let notAuthorized : HttpHandler =
|
||||||
|
setStatusCode 403 >=> fun _ _ -> Task.FromResult<HttpContext option> None
|
||||||
|
|
||||||
|
/// Handler to require authorization
|
||||||
|
let authorize : HttpHandler =
|
||||||
|
fun next ctx ->
|
||||||
|
task {
|
||||||
|
let auth = ctx.GetService<IAuthorizationService>()
|
||||||
|
let! result = auth.AuthorizeAsync (ctx.User, "LoggedOn")
|
||||||
|
Console.WriteLine (sprintf "*** Auth succeeded = %b" result.Succeeded)
|
||||||
|
match result.Succeeded with
|
||||||
|
| true -> return! next ctx
|
||||||
|
| false -> return! notAuthorized next ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Strongly-typed models for post requests
|
/// Strongly-typed models for post requests
|
||||||
@ -87,7 +105,8 @@ module Journal =
|
|||||||
|
|
||||||
/// GET /api/journal
|
/// GET /api/journal
|
||||||
let journal : HttpHandler =
|
let journal : HttpHandler =
|
||||||
fun next ctx ->
|
authorize
|
||||||
|
>=> fun next ctx ->
|
||||||
match user ctx with
|
match user ctx with
|
||||||
| Some u -> json ((db ctx).JournalByUserId u.Value) next ctx
|
| Some u -> json ((db ctx).JournalByUserId u.Value) next ctx
|
||||||
| None -> Error.notFound next ctx
|
| None -> Error.notFound next ctx
|
||||||
|
@ -22,4 +22,8 @@
|
|||||||
<PackageReference Update="FSharp.Core" Version="4.5.2" />
|
<PackageReference Update="FSharp.Core" Version="4.5.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="wwwroot\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,36 +1,50 @@
|
|||||||
namespace MyPrayerJournal.Api
|
namespace MyPrayerJournal.Api
|
||||||
|
|
||||||
open System
|
|
||||||
open Microsoft.AspNetCore
|
|
||||||
open Microsoft.AspNetCore.Builder
|
open Microsoft.AspNetCore.Builder
|
||||||
open Microsoft.AspNetCore.Hosting
|
open Microsoft.AspNetCore.Hosting
|
||||||
open Microsoft.Extensions.Configuration
|
|
||||||
open Microsoft.Extensions.Logging
|
|
||||||
|
|
||||||
/// Configuration functions for the application
|
/// Configuration functions for the application
|
||||||
module Configure =
|
module Configure =
|
||||||
|
|
||||||
|
open Microsoft.AspNetCore.Authentication.JwtBearer
|
||||||
|
open Microsoft.AspNetCore.Server.Kestrel.Core
|
||||||
|
open Microsoft.Extensions.Configuration
|
||||||
open Microsoft.Extensions.DependencyInjection
|
open Microsoft.Extensions.DependencyInjection
|
||||||
|
open Microsoft.Extensions.Logging
|
||||||
open Giraffe
|
open Giraffe
|
||||||
open Giraffe.TokenRouter
|
open Giraffe.TokenRouter
|
||||||
open MyPrayerJournal
|
|
||||||
|
|
||||||
/// Set up the configuration for the app
|
/// Set up the configuration for the app
|
||||||
let configuration (ctx : WebHostBuilderContext) (cfg : IConfigurationBuilder) =
|
let configuration (ctx : WebHostBuilderContext) (cfg : IConfigurationBuilder) =
|
||||||
cfg.SetBasePath(ctx.HostingEnvironment.ContentRootPath)
|
cfg.SetBasePath(ctx.HostingEnvironment.ContentRootPath)
|
||||||
.AddJsonFile("appsettings.json", optional = true, reloadOnChange = true)
|
.AddJsonFile("appsettings.json", optional = true, reloadOnChange = true)
|
||||||
.AddJsonFile(sprintf "appsettings.%s.json" ctx.HostingEnvironment.EnvironmentName, optional = true)
|
.AddJsonFile(sprintf "appsettings.%s.json" ctx.HostingEnvironment.EnvironmentName)
|
||||||
.AddEnvironmentVariables()
|
.AddEnvironmentVariables()
|
||||||
|> ignore
|
|> ignore
|
||||||
|
|
||||||
|
/// Configure Kestrel from appsettings.json
|
||||||
|
let kestrel (ctx : WebHostBuilderContext) (opts : KestrelServerOptions) =
|
||||||
|
(ctx.Configuration.GetSection >> opts.Configure >> ignore) "Kestrel"
|
||||||
|
|
||||||
/// Configure dependency injection
|
/// Configure dependency injection
|
||||||
let services (sc : IServiceCollection) =
|
let services (sc : IServiceCollection) =
|
||||||
sc.AddAuthentication()
|
sc.AddGiraffe () |> ignore
|
||||||
.AddJwtBearer("Auth0",
|
// mad props to Andrea Chiarelli @ https://auth0.com/blog/securing-asp-dot-net-core-2-applications-with-jwts/
|
||||||
fun opt ->
|
use sp = sc.BuildServiceProvider()
|
||||||
opt.Audience <- "")
|
let cfg = sp.GetRequiredService<IConfiguration>().GetSection "Auth0"
|
||||||
|
sc.AddAuthentication(
|
||||||
|
fun opts ->
|
||||||
|
opts.DefaultAuthenticateScheme <- JwtBearerDefaults.AuthenticationScheme
|
||||||
|
opts.DefaultChallengeScheme <- JwtBearerDefaults.AuthenticationScheme)
|
||||||
|
.AddJwtBearer (
|
||||||
|
fun opts ->
|
||||||
|
opts.Authority <- sprintf "https://%s/" cfg.["Domain"]
|
||||||
|
opts.Audience <- cfg.["Audience"]
|
||||||
|
opts.TokenValidationParameters.ValidateAudience <- false)
|
||||||
|
|> ignore
|
||||||
|
sc.AddAuthorization (fun opts -> opts.AddPolicy ("LoggedOn", fun p -> p.RequireClaim "sub" |> ignore))
|
||||||
|> ignore
|
|> ignore
|
||||||
()
|
|
||||||
|
|
||||||
/// Routes for the available URLs within myPrayerJournal
|
/// Routes for the available URLs within myPrayerJournal
|
||||||
let webApp =
|
let webApp =
|
||||||
@ -82,6 +96,7 @@ module Configure =
|
|||||||
|
|
||||||
module Program =
|
module Program =
|
||||||
|
|
||||||
|
open System
|
||||||
open System.IO
|
open System.IO
|
||||||
|
|
||||||
let exitCode = 0
|
let exitCode = 0
|
||||||
@ -89,10 +104,10 @@ module Program =
|
|||||||
let CreateWebHostBuilder args =
|
let CreateWebHostBuilder args =
|
||||||
let contentRoot = Directory.GetCurrentDirectory ()
|
let contentRoot = Directory.GetCurrentDirectory ()
|
||||||
WebHostBuilder()
|
WebHostBuilder()
|
||||||
.UseKestrel()
|
|
||||||
.UseContentRoot(contentRoot)
|
.UseContentRoot(contentRoot)
|
||||||
|
.ConfigureAppConfiguration(Configure.configuration)
|
||||||
|
.UseKestrel(Configure.kestrel)
|
||||||
.UseWebRoot(Path.Combine (contentRoot, "wwwroot"))
|
.UseWebRoot(Path.Combine (contentRoot, "wwwroot"))
|
||||||
.ConfigureAppConfiguration(Action<WebHostBuilderContext, IConfigurationBuilder> Configure.configuration)
|
|
||||||
.ConfigureServices(Configure.services)
|
.ConfigureServices(Configure.services)
|
||||||
.ConfigureLogging(Configure.logging)
|
.ConfigureLogging(Configure.logging)
|
||||||
.Configure(Action<IApplicationBuilder> Configure.application)
|
.Configure(Action<IApplicationBuilder> Configure.application)
|
||||||
|
9
src/api/MyPrayerJournal.Api/appsettings.json
Normal file
9
src/api/MyPrayerJournal.Api/appsettings.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"Kestrel": {
|
||||||
|
"EndPoints": {
|
||||||
|
"Http": {
|
||||||
|
"Url": "http://localhost:3000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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, '../../public/index.html'),
|
index: path.resolve(__dirname, '../../api/MyPrayerJournal.Api/wwwroot/index.html'),
|
||||||
assetsRoot: path.resolve(__dirname, '../../public'),
|
assetsRoot: path.resolve(__dirname, '../../api/MyPrayerJournal.Api/wwwroot'),
|
||||||
assetsSubDirectory: 'static',
|
assetsSubDirectory: 'static',
|
||||||
assetsPublicPath: '/',
|
assetsPublicPath: '/',
|
||||||
productionSourceMap: true,
|
productionSourceMap: true,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "my-prayer-journal",
|
"name": "my-prayer-journal",
|
||||||
"version": "0.9.6",
|
"version": "0.9.7",
|
||||||
"description": "myPrayerJournal - Front End",
|
"description": "myPrayerJournal - Front End",
|
||||||
"author": "Daniel J. Summers <daniel@bitbadger.solutions>",
|
"author": "Daniel J. Summers <daniel@bitbadger.solutions>",
|
||||||
"private": true,
|
"private": true,
|
||||||
@ -12,8 +12,8 @@
|
|||||||
"e2e": "node test/e2e/runner.js",
|
"e2e": "node test/e2e/runner.js",
|
||||||
"test": "npm run unit && npm run e2e",
|
"test": "npm run unit && npm run e2e",
|
||||||
"lint": "eslint --ext .js,.vue src test/unit/specs test/e2e/specs",
|
"lint": "eslint --ext .js,.vue src test/unit/specs test/e2e/specs",
|
||||||
"apistart": "cd .. && go build -o mpj-api.exe && mpj-api.exe",
|
"apistart": "cd ../api/MyPrayerJournal.Api && dotnet run",
|
||||||
"vue": "node build/build.js prod && cd .. && go build -o mpj-api.exe && mpj-api.exe"
|
"vue": "node build/build.js prod && cd ../api/MyPrayerJournal.Api && dotnet run"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"auth0-js": "^9.3.3",
|
"auth0-js": "^9.3.3",
|
||||||
|
Loading…
Reference in New Issue
Block a user