Add apiutils.

This commit is contained in:
Jonathan Bernard 2022-01-24 04:09:23 -06:00
parent 3777e3dbbd
commit 4e0d06bb67
2 changed files with 101 additions and 7 deletions

View File

@ -1,7 +1,5 @@
# This is just an example to get you started. A typical library package
# exports the main API in this file. Note that you cannot rename this file
# but you can remove it if you wish.
proc add*(x, y: int): int =
## Adds two files together.
return x + y
import buffoonery/apierror,
buffoonery/apiutils,
buffoonery/auth,
buffoonery/jsonutils
export apierror, apiutils, auth, jsonutils

View File

@ -0,0 +1,96 @@
import std/json, std/logging, std/strutils, std/sequtils
import jester, namespaced_logging
import ./apierror
const CONTENT_TYPE_JSON* = "application/json"
var logNs {.threadvar.}: LoggingNamespace
template log(): untyped =
if logNs.isNil: logNs = initLoggingNamespace("buffoonery/apiutils", lvlDebug)
logNs
## Response Utilities
template halt*(
code: HttpCode,
headers: RawHeaders,
content: string) =
## Immediately replies with the specified request. This means any further
## code will not be executed after calling this template in the current
## route.
bind TCActionSend, newHttpHeaders
result[0] = CallbackAction.TCActionSend
result[1] = code
result[2] = if isSome(result[2]): some(result[2].get & headers)
else: some(headers)
result[3] = content
result.matched = true
break allRoutes
template sendJsonResp*(
code: HttpCode,
body: string = "",
knownOrigins: seq[string],
headersToSend: RawHeaders) =
## Immediately send a JSON response and stop processing the request.
let reqOrigin =
if headers(request).hasKey("Origin"): $(headers(request)["Origin"])
else: ""
let corsHeaders =
if knownOrigins.contains(reqOrigin):
@{
"Access-Control-Allow-Origin": reqOrigin,
"Access-Control-Allow-Credentials": "true",
"Access-Control-Allow-Methods": $(reqMethod(request)),
"Access-Control-Allow-Headers": "Authorization,X-CSRF-TOKEN"
}
else:
log().warn "Unrecognized Origin '" & reqOrigin & "', excluding CORS headers."
@{:}
halt(
code,
cast[RawHeaders](headersToSend) & corsHeaders & @{
"Content-Type": CONTENT_TYPE_JSON,
"Cache-Control": "no-cache"
},
body
)
proc makeDataBody*(data: JsonNode): string = $(%*{"details":"","data":data })
proc makeStatusBody*(details: string): string = $(%*{"details":details})
template sendErrorResp*(err: ref ApiError, knownOrigins: seq[string]): void =
log().debug err.respMsg & ( if err.msg.len > 0: ": " & err.msg else: "")
if not err.parent.isNil: log().debug " original exception: " & err.parent.msg
sendJsonResp(err.respCode, makeStatusBody(err.respMsg), knownOrigins, @{:})
## CORS support
template sendOptionsResp*(
allowedMethods: seq[HttpMethod],
knownOrigins: seq[string]) =
let reqOrigin =
if headers(request).hasKey("Origin"): $(headers(request)["Origin"])
else: ""
let corsHeaders =
if knownOrigins.contains(reqOrigin):
@{
"Access-Control-Allow-Origin": reqOrigin,
"Access-Control-Allow-Credentials": "true",
"Access-Control-Allow-Methods": allowedMethods.mapIt($it).join(", "),
"Access-Control-Allow-Headers": "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization,X-CSRF-TOKEN"
}
else:
log().warn "Unrecognized Origin '" & reqOrigin & "', excluding CORS headers."
log().debug "Valid origins: " & knownOrigins.join(", ")
@{:}
halt(
Http200,
corsHeaders,
""
)