api: Refactor so all endpoints are CORS-aware.

This commit is contained in:
Jonathan Bernard 2019-09-25 08:15:08 -05:00
parent cf60793395
commit 5c81d756df
4 changed files with 119 additions and 104 deletions

View File

@ -3,5 +3,6 @@
"dbConnString":"host=localhost port=5500 dbname=personal_measure user=postgres password=password", "dbConnString":"host=localhost port=5500 dbname=personal_measure user=postgres password=password",
"debug":true, "debug":true,
"port":8081, "port":8081,
"pwdCost":11 "pwdCost":11,
"knownOrigins": [ "https://curl.localhost" ]
} }

View File

@ -1,5 +1,6 @@
{ {
"debug":false, "debug":false,
"port":80, "port":80,
"pwdCost":11 "pwdCost":11,
"knownOrigins": [ "https://pm.jdb-labs.com" ]
} }

View File

@ -32,15 +32,18 @@ template halt(code: HttpCode,
result.matched = true result.matched = true
break allRoutes break allRoutes
template jsonResp(code: HttpCode, details: string = "", headersToSend: RawHeaders = @{:} ) = template jsonResp(code: HttpCode, body: string = "", headersToSend: RawHeaders = @{:} ) =
let reqOrigin =
if request.headers.hasKey("Origin"): $(request.headers["Origin"])
else: ""
let reqOrigin = $(request.headers["Origin"])
let corsHeaders = let corsHeaders =
if ctx.cfg.knownOrigins.contains(reqOrigin): if ctx.cfg.knownOrigins.contains(reqOrigin):
@{ @{
"Access-Control-Allow-Origin": reqOrigin, "Access-Control-Allow-Origin": reqOrigin,
"Access-Control-Allow-Credentials": "true", "Access-Control-Allow-Credentials": "true",
"Access-Control-Allow-Methods": $(request.reqMeth) "Access-Control-Allow-Methods": $(request.reqMethod)
} }
else: @{:} else: @{:}
@ -50,17 +53,25 @@ template jsonResp(code: HttpCode, details: string = "", headersToSend: RawHeader
"Content-Type": JSON, "Content-Type": JSON,
"Cache-Control": "no-cache" "Cache-Control": "no-cache"
}, },
body
)
template jsonResp(body: string) = jsonResp(Http200, body)
template statusResp(code: HttpCode, details: string = "", headersToSend: RawHeaders = @{:} ) =
jsonResp(
code,
$(%* { $(%* {
"statusCode": code.int, "statusCode": code.int,
"status": $code, "status": $code,
"details": details "details": details
}) }),
) headersToSend)
template json500Resp(ex: ref Exception, details: string = ""): void = template execptionResp(ex: ref Exception, details: string = ""): void =
when not defined(release): debug ex.getStackTrace() when not defined(release): debug ex.getStackTrace()
error details & ":\n" & ex.msg error details & ":\n" & ex.msg
jsonResp(Http500) statusResp(Http500)
# internal JSON parsing utils # internal JSON parsing utils
proc getIfExists(n: JsonNode, key: string): JsonNode = proc getIfExists(n: JsonNode, key: string): JsonNode =
@ -187,10 +198,10 @@ template checkAuth(requiresAdmin = false) =
try: session = extractSession(ctx, request) try: session = extractSession(ctx, request)
except: except:
debug "Auth failed: " & getCurrentExceptionMsg() debug "Auth failed: " & getCurrentExceptionMsg()
jsonResp(Http401, "Unauthorized", @{"WWW-Authenticate": "Bearer"}) statusResp(Http401, "Unauthorized", @{"WWW-Authenticate": "Bearer"})
if requiresAdmin and not session.user.isAdmin: if requiresAdmin and not session.user.isAdmin:
jsonResp(Http401, "Unauthorized", @{"WWW-Authenticate": "Bearer"}) statusResp(Http401, "Unauthorized", @{"WWW-Authenticate": "Bearer"})
proc start*(ctx: PMApiContext): void = proc start*(ctx: PMApiContext): void =
@ -205,7 +216,7 @@ proc start*(ctx: PMApiContext): void =
routes: routes:
get "/version": get "/version":
resp($(%("personal_measure_api v" & PM_API_VERSION)), JSON) jsonResp($(%("personal_measure_api v" & PM_API_VERSION)))
post "/auth-token": post "/auth-token":
@ -214,9 +225,9 @@ proc start*(ctx: PMApiContext): void =
let email = jsonBody.getOrFail("email").getStr let email = jsonBody.getOrFail("email").getStr
let pwd = jsonBody.getOrFail("password").getStr let pwd = jsonBody.getOrFail("password").getStr
let authToken = makeAuthToken(ctx, email, pwd) let authToken = makeAuthToken(ctx, email, pwd)
resp($(%authToken), JSON) jsonResp($(%authToken))
except JsonParsingError: jsonResp(Http400, getCurrentExceptionMsg()) except JsonParsingError: statusResp(Http400, getCurrentExceptionMsg())
except: jsonResp(Http401, getCurrentExceptionMsg()) except: statusResp(Http401, getCurrentExceptionMsg())
post "/change-pwd": post "/change-pwd":
checkAuth() checkAuth()
@ -229,15 +240,15 @@ proc start*(ctx: PMApiContext): void =
let newHash = hashWithSalt(jsonBody.getOrFail("newPassword").getStr, session.user.salt) let newHash = hashWithSalt(jsonBody.getOrFail("newPassword").getStr, session.user.salt)
session.user.hashedPwd = newHash.hash session.user.hashedPwd = newHash.hash
if ctx.db.updateUser(session.user): jsonResp(Http200) if ctx.db.updateUser(session.user): statusResp(Http200)
else: jsonResp(Http500, "unable to change pwd") else: statusResp(Http500, "unable to change pwd")
except JsonParsingError: jsonResp(Http400, getCurrentExceptionMsg()) except JsonParsingError: statusResp(Http400, getCurrentExceptionMsg())
except BadRequestError: jsonResp(Http400, getCurrentExceptionMsg()) except BadRequestError: statusResp(Http400, getCurrentExceptionMsg())
except AuthError: jsonResp(Http401, getCurrentExceptionMsg()) except AuthError: statusResp(Http401, getCurrentExceptionMsg())
except: except:
error "internal error changing password: " & getCurrentExceptionMsg() error "internal error changing password: " & getCurrentExceptionMsg()
jsonResp(Http500) statusResp(Http500)
post "/change-pwd/@userId": post "/change-pwd/@userId":
checkAuth(true) checkAuth(true)
@ -248,22 +259,22 @@ proc start*(ctx: PMApiContext): void =
var user = ctx.db.getUser(parseUUID(@"userId")) var user = ctx.db.getUser(parseUUID(@"userId"))
let newHash = hashWithSalt(jsonBody.getOrFail("newPassword").getStr, user.salt) let newHash = hashWithSalt(jsonBody.getOrFail("newPassword").getStr, user.salt)
user.hashedPwd = newHash.hash user.hashedPwd = newHash.hash
if ctx.db.updateUser(user): jsonResp(Http200) if ctx.db.updateUser(user): statusResp(Http200)
else: jsonResp(Http500, "unable to change pwd") else: statusResp(Http500, "unable to change pwd")
except ValueError: jsonResp(Http400, "invalid UUID") except ValueError: statusResp(Http400, "invalid UUID")
except JsonParsingError: jsonResp(Http400, getCurrentExceptionMsg()) except JsonParsingError: statusResp(Http400, getCurrentExceptionMsg())
except BadRequestError: jsonResp(Http400, getCurrentExceptionMsg()) except BadRequestError: statusResp(Http400, getCurrentExceptionMsg())
except AuthError: jsonResp(Http401, getCurrentExceptionMsg()) except AuthError: statusResp(Http401, getCurrentExceptionMsg())
except NotFoundError: jsonResp(Http404, "no such user") except NotFoundError: statusResp(Http404, "no such user")
except: except:
error "internal error changing password: " & getCurrentExceptionMsg() error "internal error changing password: " & getCurrentExceptionMsg()
jsonResp(Http500) statusResp(Http500)
get "/user": get "/user":
checkAuth() checkAuth()
resp(Http200, $(%session.user), JSON) jsonResp($(%session.user))
put "/user": put "/user":
checkAuth() checkAuth()
@ -276,18 +287,18 @@ proc start*(ctx: PMApiContext): void =
if jsonBody.hasKey("displayName"): if jsonBody.hasKey("displayName"):
updatedUser.displayName = jsonBody["displayName"].getStr() updatedUser.displayName = jsonBody["displayName"].getStr()
jsonResp(Http200, $(%ctx.db.updateUser(updatedUser))) statusResp(Http200, $(%ctx.db.updateUser(updatedUser)))
except JsonParsingError: jsonResp(Http400, getCurrentExceptionMsg()) except JsonParsingError: statusResp(Http400, getCurrentExceptionMsg())
except BadRequestError: jsonResp(Http400, getCurrentExceptionMsg()) except BadRequestError: statusResp(Http400, getCurrentExceptionMsg())
except: except:
error "Could not update user information:\n\t" & getCurrentExceptionMsg() error "Could not update user information:\n\t" & getCurrentExceptionMsg()
jsonResp(Http500) statusResp(Http500)
get "/users": get "/users":
checkAuth(true) checkAuth(true)
resp(Http200, $(%ctx.db.getAllUsers())) jsonResp($(%ctx.db.getAllUsers()))
post "/users": post "/users":
checkAuth(true) checkAuth(true)
@ -304,18 +315,18 @@ proc start*(ctx: PMApiContext): void =
salt: pwdAndSalt.salt, salt: pwdAndSalt.salt,
isAdmin: false) isAdmin: false)
resp($(%ctx.db.createUser(newUser)), JSON) jsonResp($(%ctx.db.createUser(newUser)))
except JsonParsingError: jsonResp(Http400, getCurrentExceptionMsg()) except JsonParsingError: statusResp(Http400, getCurrentExceptionMsg())
except BadRequestError: jsonResp(Http400, getCurrentExceptionMsg()) except BadRequestError: statusResp(Http400, getCurrentExceptionMsg())
except: except:
error "Could not create new user:\n\t" & getCurrentExceptionMsg() error "Could not create new user:\n\t" & getCurrentExceptionMsg()
jsonResp(Http500) statusResp(Http500)
get "/users/@userId": get "/users/@userId":
checkAuth(true) checkAuth(true)
resp(Http200, $(%ctx.db.getUser(parseUUID(@"userId")))) jsonResp($(%ctx.db.getUser(parseUUID(@"userId"))))
delete "/users/@userId": delete "/users/@userId":
checkAuth(true) checkAuth(true)
@ -324,18 +335,18 @@ proc start*(ctx: PMApiContext): void =
try: try:
let userId = parseUUID(@"userId") let userId = parseUUID(@"userId")
user = ctx.db.getUser(userId) user = ctx.db.getUser(userId)
except: jsonResp(Http404) except: statusResp(Http404)
try: try:
if not ctx.db.deleteUser(user): raiseEx "unable to delete user" if not ctx.db.deleteUser(user): raiseEx "unable to delete user"
jsonResp(Http200, "user " & user.email & " deleted") statusResp(Http200, "user " & user.email & " deleted")
except: jsonResp(Http500, getCurrentExceptionMsg()) except: statusResp(Http500, getCurrentExceptionMsg())
get "/api-tokens": get "/api-tokens":
checkAuth() checkAuth()
resp(Http200, $(%ctx.db.findApiTokensByUserId($session.user.id))) jsonResp($(%ctx.db.findApiTokensByUserId($session.user.id)))
post "/api-tokens": post "/api-tokens":
checkAuth() checkAuth()
@ -357,40 +368,40 @@ proc start*(ctx: PMApiContext): void =
let respToken = %newToken let respToken = %newToken
respToken["value"] = %tokenValue respToken["value"] = %tokenValue
resp($respToken, JSON) jsonResp($respToken)
except JsonParsingError: jsonResp(Http400, getCurrentExceptionMsg()) except JsonParsingError: statusResp(Http400, getCurrentExceptionMsg())
except BadRequestError: jsonResp(Http400, getCurrentExceptionMsg()) except BadRequestError: statusResp(Http400, getCurrentExceptionMsg())
except AuthError: jsonResp(Http401, getCurrentExceptionMsg()) except AuthError: statusResp(Http401, getCurrentExceptionMsg())
except: except:
debug getCurrentExceptionMsg() debug getCurrentExceptionMsg()
jsonResp(Http500) statusResp(Http500)
get "/api-tokens/@tokenId": get "/api-tokens/@tokenId":
checkAuth() checkAuth()
try: try:
resp(Http200, $(%ctx.db.getApiToken(parseUUID(@"tokenId")))) jsonResp($(%ctx.db.getApiToken(parseUUID(@"tokenId"))))
except NotFoundError: jsonResp(Http404, getCurrentExceptionMsg()) except NotFoundError: statusResp(Http404, getCurrentExceptionMsg())
except: jsonResp(Http500) except: statusResp(Http500)
delete "/api-tokens/@tokenId": delete "/api-tokens/@tokenId":
checkAuth() checkAuth()
try: try:
let token = ctx.db.getApiToken(parseUUID(@"tokenId")) let token = ctx.db.getApiToken(parseUUID(@"tokenId"))
if ctx.db.deleteApiToken(token): jsonResp(Http200) if ctx.db.deleteApiToken(token): statusResp(Http200)
else: jsonResp(Http500) else: statusResp(Http500)
except NotFoundError: jsonResp(Http404, getCurrentExceptionMsg()) except NotFoundError: statusResp(Http404, getCurrentExceptionMsg())
except: jsonResp(Http500) except: statusResp(Http500)
get "/measures": get "/measures":
checkAuth() checkAuth()
try: resp($(%ctx.db.findMeasuresByUserId($session.user.id)), JSON) try: jsonResp($(%ctx.db.findMeasuresByUserId($session.user.id)))
except: except:
error "unable to retrieve measures for user:\n\t" & getCurrentExceptionMsg() error "unable to retrieve measures for user:\n\t" & getCurrentExceptionMsg()
jsonResp(Http500) statusResp(Http500)
post "/measures": post "/measures":
checkAuth() checkAuth()
@ -420,45 +431,45 @@ proc start*(ctx: PMApiContext): void =
description: jsonBody.getIfExists("description").getStr(""), description: jsonBody.getIfExists("description").getStr(""),
config: config) config: config)
resp($(%ctx.db.createMeasure(newMeasure)), JSON) jsonResp($(%ctx.db.createMeasure(newMeasure)))
except JsonParsingError: jsonResp(Http400, getCurrentExceptionMsg()) except JsonParsingError: statusResp(Http400, getCurrentExceptionMsg())
except BadRequestError: jsonResp(Http400, getCurrentExceptionMsg()) except BadRequestError: statusResp(Http400, getCurrentExceptionMsg())
except: except:
error "unable to create new measure:\n\t" & getCurrentExceptionMsg() error "unable to create new measure:\n\t" & getCurrentExceptionMsg()
jsonResp(Http500) statusResp(Http500)
get "/measures/@slug": get "/measures/@slug":
checkAuth() checkAuth()
try: resp($(%ctx.getMeasureForSlug(session.user.id, @"slug")), JSON) try: jsonResp($(%ctx.getMeasureForSlug(session.user.id, @"slug")))
except NotFoundError: jsonResp(Http404, getCurrentExceptionMsg()) except NotFoundError: statusResp(Http404, getCurrentExceptionMsg())
except: except:
error "unable to look up a measure by id:\n\t" & getCurrentExceptionMsg() error "unable to look up a measure by id:\n\t" & getCurrentExceptionMsg()
jsonResp(Http500) statusResp(Http500)
delete "/measures/@slug": delete "/measures/@slug":
checkAuth() checkAuth()
try: try:
let measure = ctx.getMeasureForSlug(session.user.id, @"slug") let measure = ctx.getMeasureForSlug(session.user.id, @"slug")
if ctx.db.deleteMeasure(measure): jsonResp(Http200) if ctx.db.deleteMeasure(measure): statusResp(Http200)
else: raiseEx "" else: raiseEx ""
except NotFoundError: jsonResp(Http404, getCurrentExceptionMsg()) except NotFoundError: statusResp(Http404, getCurrentExceptionMsg())
except: except:
error "unable to delete a measure:\n\t" & getCurrentExceptionMsg() error "unable to delete a measure:\n\t" & getCurrentExceptionMsg()
jsonResp(Http500) statusResp(Http500)
get "/measure/@slug": get "/measure/@slug":
checkAuth() checkAuth()
try: try:
let measure = ctx.getMeasureForSlug(session.user.id, @"slug") let measure = ctx.getMeasureForSlug(session.user.id, @"slug")
resp($(%ctx.db.findMeasurementsByMeasureId($measure.id)), JSON) jsonResp($(%ctx.db.findMeasurementsByMeasureId($measure.id)))
except NotFoundError: jsonResp(Http404, getCurrentExceptionMsg()) except NotFoundError: statusResp(Http404, getCurrentExceptionMsg())
except: except:
error "unable to list measurements:\n\t" & getCurrentExceptionMsg() error "unable to list measurements:\n\t" & getCurrentExceptionMsg()
jsonResp(Http500) statusResp(Http500)
post "/measure/@slug": post "/measure/@slug":
checkAuth() checkAuth()
@ -477,29 +488,29 @@ proc start*(ctx: PMApiContext): void =
if jsonBody.hasKey("extData"): jsonBody["extData"] if jsonBody.hasKey("extData"): jsonBody["extData"]
else: newJObject()) else: newJObject())
resp($(%ctx.db.createMeasurement(newMeasurement)), JSON) jsonResp($(%ctx.db.createMeasurement(newMeasurement)))
except JsonParsingError: jsonResp(Http400, getCurrentExceptionMsg()) except JsonParsingError: statusResp(Http400, getCurrentExceptionMsg())
except BadRequestError: jsonResp(Http400, getCurrentExceptionMsg()) except BadRequestError: statusResp(Http400, getCurrentExceptionMsg())
except NotFoundError: jsonResp(Http404, getCurrentExceptionMsg()) except NotFoundError: statusResp(Http404, getCurrentExceptionMsg())
except: except:
error "unable to add measurement:\n\t" & getCurrentExceptionMsg() error "unable to add measurement:\n\t" & getCurrentExceptionMsg()
jsonResp(Http500) statusResp(Http500)
get "/measure/@slug/@id": get "/measure/@slug/@id":
checkAuth() checkAuth()
try: try:
let measure = ctx.getMeasureForSlug(session.user.id, @"slug") let measure = ctx.getMeasureForSlug(session.user.id, @"slug")
resp($(%ctx.getMeasurementForMeasure(measure.id, parseUUID(@"id"))), JSON) jsonResp($(%ctx.getMeasurementForMeasure(measure.id, parseUUID(@"id"))))
except ValueError: jsonResp(Http400, getCurrentExceptionMsg()) except ValueError: statusResp(Http400, getCurrentExceptionMsg())
except JsonParsingError: jsonResp(Http400, getCurrentExceptionMsg()) except JsonParsingError: statusResp(Http400, getCurrentExceptionMsg())
except BadRequestError: jsonResp(Http400, getCurrentExceptionMsg()) except BadRequestError: statusResp(Http400, getCurrentExceptionMsg())
except NotFoundError: jsonResp(Http404, getCurrentExceptionMsg()) except NotFoundError: statusResp(Http404, getCurrentExceptionMsg())
except: except:
error "unable to retrieve measurement:\n\t" & getCurrentExceptionMsg() error "unable to retrieve measurement:\n\t" & getCurrentExceptionMsg()
jsonResp(Http500) statusResp(Http500)
put "/measure/@slug/@id": put "/measure/@slug/@id":
checkAuth() checkAuth()
@ -511,15 +522,15 @@ proc start*(ctx: PMApiContext): void =
if jsonBody.hasKey("value"): measurement.value = jsonBody["value"].getInt if jsonBody.hasKey("value"): measurement.value = jsonBody["value"].getInt
if jsonBody.hasKey("timestamp"): measurement.timestamp = jsonBody["timestamp"].getStr.parseIso8601 if jsonBody.hasKey("timestamp"): measurement.timestamp = jsonBody["timestamp"].getStr.parseIso8601
if jsonBody.hasKey("extData"): measurement.extData = jsonBody["extData"] if jsonBody.hasKey("extData"): measurement.extData = jsonBody["extData"]
resp($(%ctx.db.updateMeasurement(measurement)), JSON) jsonResp($(%ctx.db.updateMeasurement(measurement)))
except ValueError: jsonResp(Http400, getCurrentExceptionMsg()) except ValueError: statusResp(Http400, getCurrentExceptionMsg())
except JsonParsingError: jsonResp(Http400, getCurrentExceptionMsg()) except JsonParsingError: statusResp(Http400, getCurrentExceptionMsg())
except BadRequestError: jsonResp(Http400, getCurrentExceptionMsg()) except BadRequestError: statusResp(Http400, getCurrentExceptionMsg())
except NotFoundError: jsonResp(Http404, getCurrentExceptionMsg()) except NotFoundError: statusResp(Http404, getCurrentExceptionMsg())
except: except:
error "unable to retrieve measurement:\n\t" & getCurrentExceptionMsg() error "unable to retrieve measurement:\n\t" & getCurrentExceptionMsg()
jsonResp(Http500) statusResp(Http500)
delete "/measure/@slug/@id": delete "/measure/@slug/@id":
checkAuth() checkAuth()
@ -527,16 +538,16 @@ proc start*(ctx: PMApiContext): void =
try: try:
let measure = ctx.getMeasureForSlug(session.user.id, @"slug") let measure = ctx.getMeasureForSlug(session.user.id, @"slug")
let measurement = ctx.getMeasurementForMeasure(measure.id, parseUUID(@"id")) let measurement = ctx.getMeasurementForMeasure(measure.id, parseUUID(@"id"))
if ctx.db.deleteMeasurement(measurement): jsonResp(Http200) if ctx.db.deleteMeasurement(measurement): statusResp(Http200)
else: raiseEx "" else: raiseEx ""
except ValueError: jsonResp(Http400, getCurrentExceptionMsg()) except ValueError: statusResp(Http400, getCurrentExceptionMsg())
except JsonParsingError: jsonResp(Http400, getCurrentExceptionMsg()) except JsonParsingError: statusResp(Http400, getCurrentExceptionMsg())
except BadRequestError: jsonResp(Http400, getCurrentExceptionMsg()) except BadRequestError: statusResp(Http400, getCurrentExceptionMsg())
except NotFoundError: jsonResp(Http404, getCurrentExceptionMsg()) except NotFoundError: statusResp(Http404, getCurrentExceptionMsg())
except: except:
error "unable to delete measurement:\n\t" & getCurrentExceptionMsg() error "unable to delete measurement:\n\t" & getCurrentExceptionMsg()
jsonResp(Http500) statusResp(Http500)
post "/log": post "/log":
checkAuth() checkAuth()
@ -551,9 +562,9 @@ proc start*(ctx: PMApiContext): void =
stacktrace: jsonBody.getIfExists("stacktrace").getStr(""), stacktrace: jsonBody.getIfExists("stacktrace").getStr(""),
timestamp: jsonBody.getOrFail("timestamp").getStr.parseIso8601 timestamp: jsonBody.getOrFail("timestamp").getStr.parseIso8601
) )
resp(Http200, $(%ctx.db.createClientLogEntry(logEntry)), JSON) jsonResp($(%ctx.db.createClientLogEntry(logEntry)))
except BadRequestError: jsonResp(Http400, getCurrentExceptionMsg()) except BadRequestError: statusResp(Http400, getCurrentExceptionMsg())
except: jsonResp(Http500, getCurrentExceptionMsg()) except: statusResp(Http500, getCurrentExceptionMsg())
post "/log/batch": post "/log/batch":
checkAuth() checkAuth()
@ -569,15 +580,15 @@ proc start*(ctx: PMApiContext): void =
stacktrace: it.getIfExists("stacktrace").getStr(""), stacktrace: it.getIfExists("stacktrace").getStr(""),
timestamp: it.getOrFail("timestamp").getStr.parseIso8601 timestamp: it.getOrFail("timestamp").getStr.parseIso8601
)) ))
resp(Http200, $(%respMsgs), JSON) jsonResp($(%respMsgs))
except BadRequestError: jsonResp(Http400, getCurrentExceptionMsg()) except BadRequestError: statusResp(Http400, getCurrentExceptionMsg())
except: jsonResp(Http500, getCurrentExceptionMsg()) except: statusResp(Http500, getCurrentExceptionMsg())
post "/service/debug/stop": post "/service/debug/stop":
if not ctx.cfg.debug: jsonResp(Http404) if not ctx.cfg.debug: statusResp(Http404)
else: else:
let shutdownFut = sleepAsync(100) let shutdownFut = sleepAsync(100)
shutdownFut.callback = proc(): void = complete(stopFuture) shutdownFut.callback = proc(): void = complete(stopFuture)
resp($(%"shutting down"), JSON) jsonResp($(%"shutting down"))
waitFor(stopFuture) waitFor(stopFuture)

View File

@ -17,6 +17,8 @@ fi
curl -s -X "$method" \ curl -s -X "$method" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-H "Authorization: $(cat credential)"\ -H "Authorization: $(cat credential)" \
-H "Origin: https://curl.localhost" \
"http://${host}/api/$url" \ "http://${host}/api/$url" \
-d "$data" -d "$data" \
-v