117 lines
4.4 KiB
Forth
117 lines
4.4 KiB
Forth
module CurrencyAPI.App
|
|
|
|
open System
|
|
open System.IO
|
|
open Microsoft.AspNetCore.Builder
|
|
open Microsoft.AspNetCore.Hosting
|
|
open Microsoft.AspNetCore.Http
|
|
open Microsoft.Extensions.Hosting
|
|
open Microsoft.Extensions.Logging
|
|
open Microsoft.Extensions.DependencyInjection
|
|
open Giraffe
|
|
open dotenv.net
|
|
open Npgsql.FSharp
|
|
|
|
let prodEnv = DotEnv.Read(DotEnvOptions(envFilePaths = [ "../.prod.env"], overwriteExistingVars = false))
|
|
|
|
let ( _ , connStr ) = prodEnv.TryGetValue("DATABASE_URL")
|
|
let ( _ , apiKey ) = prodEnv.TryGetValue("API_KEY")
|
|
|
|
let validateApiKey (ctx : HttpContext) =
|
|
match ctx.TryGetRequestHeader "X-API-Key" with
|
|
| Some key -> apiKey.Equals key
|
|
| None -> false
|
|
|
|
let accessDenied = setStatusCode 401 >=> text "Access Denied"
|
|
let requiresApiKey = authorizeRequest validateApiKey accessDenied
|
|
|
|
let getCurrentBalance (discordId : string) =
|
|
task {
|
|
let! amounts =
|
|
connStr
|
|
|> Sql.connect
|
|
|> Sql.parameters [ "did" , Sql.string discordId ]
|
|
|> Sql.query """SELECT gbt FROM "user" WHERE discord_id = @did"""
|
|
|> Sql.executeAsync (fun r -> r.int "gbt")
|
|
match amounts with
|
|
| [] -> return Error "User not found"
|
|
| a::_ -> return Ok a
|
|
}
|
|
|
|
let get (discordId : string) : HttpHandler =
|
|
fun (next : HttpFunc) (ctx : HttpContext) ->
|
|
task {
|
|
try
|
|
match! getCurrentBalance discordId with
|
|
| Ok amount -> return! json {| Amount = amount |} next ctx
|
|
| Error e -> return! RequestErrors.notFound (json {| Error = e |}) next ctx
|
|
with ex ->
|
|
return! ServerErrors.internalError (json {| Error = ex.Message |}) next ctx
|
|
}
|
|
|
|
let modify sign (discordId : string) : HttpHandler =
|
|
fun (next : HttpFunc) (ctx : HttpContext) ->
|
|
task {
|
|
let! body = ctx.BindJsonAsync<{|Amount:int|}>()
|
|
match! getCurrentBalance discordId with
|
|
| Ok current ->
|
|
let amount = body.Amount * sign
|
|
if current + amount < 0 then
|
|
return! RequestErrors.badRequest (json {| Error = "Insufficient funds" |}) next ctx
|
|
else
|
|
try
|
|
let! _ =
|
|
connStr
|
|
|> Sql.connect
|
|
|> Sql.parameters [ "did" , Sql.string discordId ; "amount" , Sql.int amount ]
|
|
|> Sql.query """UPDATE "user" SET gbt = GREATEST(gbt + @amount, 0) WHERE discord_id = @did"""
|
|
|> Sql.executeNonQueryAsync
|
|
return! json {| NewBalance = current + amount |} next ctx
|
|
with ex -> return! RequestErrors.notFound (json {| Error = ex.Message |}) next ctx
|
|
| Error e -> return! RequestErrors.notFound (json {| Error = e |}) next ctx
|
|
}
|
|
|
|
let webApp =
|
|
choose [
|
|
GET >=> requiresApiKey >=> routef "/user/%s/balance" get
|
|
PATCH >=> requiresApiKey >=> routef "/user/%s/balance/withdraw" (modify -1)
|
|
PATCH >=> requiresApiKey >=> routef "/user/%s/balance/deposit" (modify +1)
|
|
RequestErrors.NOT_FOUND "Not Found" ]
|
|
|
|
let errorHandler (ex : Exception) (logger : ILogger) =
|
|
logger.LogError(ex, "An unhandled exception has occurred while executing the request.")
|
|
clearResponse >=> setStatusCode 500 >=> text ex.Message
|
|
|
|
let configureApp (app : IApplicationBuilder) =
|
|
let env = app.ApplicationServices.GetService<IWebHostEnvironment>()
|
|
if env.IsDevelopment() then
|
|
app.UseDeveloperExceptionPage()
|
|
else
|
|
app.UseGiraffeErrorHandler(errorHandler)
|
|
|> ignore
|
|
app.UseGiraffe(webApp)
|
|
|
|
let configureServices (services : IServiceCollection) =
|
|
services.AddGiraffe() |> ignore
|
|
|
|
let configureLogging (builder : ILoggingBuilder) =
|
|
builder.AddConsole()
|
|
.AddDebug() |> ignore
|
|
|
|
[<EntryPoint>]
|
|
let main args =
|
|
Host.CreateDefaultBuilder(args)
|
|
.ConfigureWebHostDefaults(
|
|
fun webHostBuilder ->
|
|
webHostBuilder
|
|
.ConfigureKestrel(fun opt ->
|
|
opt.AddServerHeader <- false
|
|
opt.ListenLocalhost(3333, (fun o -> o.UseHttps() |> ignore)))
|
|
.Configure(Action<IApplicationBuilder> configureApp)
|
|
.ConfigureServices(configureServices)
|
|
.ConfigureLogging(configureLogging)
|
|
|> ignore)
|
|
.Build()
|
|
.Run()
|
|
0
|