Beginning implementation of the planned API endpoints.
This commit is contained in:
parent
a16ef81077
commit
e3450d5f8f
16
api/api.txt
16
api/api.txt
@ -3,10 +3,10 @@ Personal Measure API
|
|||||||
|
|
||||||
### Measure
|
### Measure
|
||||||
|
|
||||||
☐ GET /measure Get a list of all defined measures for this user.
|
☐ GET /measures Get a list of all defined measures for this user.
|
||||||
☐ POST /measure Create a new measure (post the definition).
|
☐ POST /measures Create a new measure (post the definition).
|
||||||
☐ GET /measure/<measure-slug> Get the definition for a specific measure.
|
☐ GET /measures/<measure-slug> Get the definition for a specific measure.
|
||||||
☐ DELETE /measure/<measure-slug> Delete a measure (and all values associated with it).
|
☐ DELETE /measures/<measure-slug> Delete a measure (and all values associated with it).
|
||||||
|
|
||||||
### Values
|
### Values
|
||||||
|
|
||||||
@ -18,9 +18,11 @@ Personal Measure API
|
|||||||
|
|
||||||
### Auth
|
### Auth
|
||||||
|
|
||||||
☐ GET /auth-token Given a valid username/password combo, get an auth token.
|
☑ GET /auth-token Given a valid username/password combo, get an auth token.
|
||||||
☐ GET /user Given a valid auth token, return the user details.
|
☑ GET /user Given a valid auth token, return the user details.
|
||||||
☐ PSOT /app-token With a valid session, create a new app token.
|
☐ GET /api-tokens List api tokens.
|
||||||
|
☐ DELETE /api-tokens/<id> Delete a specific api token.
|
||||||
|
☐ POST /api-tokens With a valid session, create a new api token.
|
||||||
|
|
||||||
Legend
|
Legend
|
||||||
------
|
------
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
import cliutils, docopt, logging, jester, json, os, strutils, tables
|
import cliutils, docopt, logging, jester, json, os, strutils, tables
|
||||||
|
|
||||||
import personal_measure_apipkg/configuration
|
|
||||||
import personal_measure_apipkg/version
|
|
||||||
import personal_measure_apipkg/api
|
import personal_measure_apipkg/api
|
||||||
|
import personal_measure_apipkg/configuration
|
||||||
|
import personal_measure_apipkg/service
|
||||||
|
import personal_measure_apipkg/version
|
||||||
|
|
||||||
|
import personal_measure_apipkg/db
|
||||||
|
import personal_measure_apipkg/models
|
||||||
|
|
||||||
const DEFAULT_CONFIG = PMApiConfig(
|
const DEFAULT_CONFIG = PMApiConfig(
|
||||||
authSecret: "change me",
|
authSecret: "change me",
|
||||||
@ -15,7 +19,7 @@ proc loadConfig*(args: Table[string, docopt.Value] = initTable[string, docopt.Va
|
|||||||
|
|
||||||
let filePath =
|
let filePath =
|
||||||
if args["--config"]: $args["--config"]
|
if args["--config"]: $args["--config"]
|
||||||
else: "personal_measure.config.json"
|
else: "personal_measure_api.config.json"
|
||||||
|
|
||||||
var json: JsonNode
|
var json: JsonNode
|
||||||
try: json = parseFile(filePath)
|
try: json = parseFile(filePath)
|
||||||
@ -57,6 +61,7 @@ when isMainModule:
|
|||||||
Usage:
|
Usage:
|
||||||
personal_measure_api test [options]
|
personal_measure_api test [options]
|
||||||
personal_measure_api serve [options]
|
personal_measure_api serve [options]
|
||||||
|
personal_measure_api hashpwd <password> [<cost>] [options]
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
|
|
||||||
@ -68,15 +73,19 @@ Options:
|
|||||||
let args = docopt(doc, version = PM_API_VERSION)
|
let args = docopt(doc, version = PM_API_VERSION)
|
||||||
let ctx = initContext(args)
|
let ctx = initContext(args)
|
||||||
|
|
||||||
if args["test"]:
|
if args["hashpwd"]:
|
||||||
echo "Test"
|
let cost =
|
||||||
|
if args["<cost>"]: parseInt($args["<cost>"])
|
||||||
|
else: 11
|
||||||
|
|
||||||
|
echo hashPwd($args["<password>"], cast[int8](cost))
|
||||||
|
|
||||||
|
if args["test"]:
|
||||||
|
echo "test"
|
||||||
|
echo ctx.db.getUserByEmail("jonathan@jdbernard.com")
|
||||||
|
|
||||||
|
if args["serve"]: start(ctx)
|
||||||
|
|
||||||
if args["serve"]:
|
|
||||||
start(PMApiConfig(
|
|
||||||
debug: true,
|
|
||||||
port: 8090,
|
|
||||||
pwdCost: 11,
|
|
||||||
dbConnString: "host=localhost port=5500 username=postgres password=password dbname=personal_measure"))
|
|
||||||
except:
|
except:
|
||||||
fatal "pit: " & getCurrentExceptionMsg()
|
fatal "pit: " & getCurrentExceptionMsg()
|
||||||
#raise getCurrentException()
|
#raise getCurrentException()
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
import asyncdispatch, jester, json, jwt, strutils, times, timeutils, uuids
|
import asyncdispatch, jester, json, jwt, logging, options, sequtils, strutils,
|
||||||
|
times, timeutils, uuids
|
||||||
import ./db
|
import ./db, ./configuration, ./models, ./service, ./version
|
||||||
import ./configuration
|
|
||||||
import ./models
|
|
||||||
import ./service
|
|
||||||
|
|
||||||
const JSON = "application/json"
|
const JSON = "application/json"
|
||||||
|
|
||||||
@ -101,7 +98,10 @@ proc makeAuthToken*(ctx: PMApiContext, email, pwd: string): string =
|
|||||||
|
|
||||||
# find the user record
|
# find the user record
|
||||||
var user: User
|
var user: User
|
||||||
try: user = ctx.db.getUserByEmail(email)
|
try:
|
||||||
|
let users = ctx.db.getUserByEmail(email)
|
||||||
|
if users.len != 1: raiseEx ""
|
||||||
|
user = users[0]
|
||||||
except: raiseEx "invalid username or password"
|
except: raiseEx "invalid username or password"
|
||||||
|
|
||||||
if not validatePwd(user, pwd): raiseEx "invalid username or password"
|
if not validatePwd(user, pwd): raiseEx "invalid username or password"
|
||||||
@ -117,17 +117,19 @@ template checkAuth() =
|
|||||||
|
|
||||||
var session {.inject.}: Session
|
var session {.inject.}: Session
|
||||||
|
|
||||||
try: session = extractSession(cfg, request)
|
try: session = extractSession(ctx, request)
|
||||||
except:
|
except:
|
||||||
debug "Auth failed: " & getCurrentExceptionMsg()
|
debug "Auth failed: " & getCurrentExceptionMsg()
|
||||||
jsonResp(Http401, "Unauthorized", @{"WWW-Authenticate": "Bearer"})
|
jsonResp(Http401, "Unauthorized", @{"WWW-Authenticate": "Bearer"})
|
||||||
|
|
||||||
proc start*(cfg: PMApiConfig): void =
|
proc start*(ctx: PMApiContext): void =
|
||||||
|
|
||||||
|
if ctx.cfg.debug: setLogFilter(lvlDebug)
|
||||||
|
|
||||||
var stopFuture = newFuture[void]()
|
var stopFuture = newFuture[void]()
|
||||||
|
|
||||||
settings:
|
settings:
|
||||||
port = Port(cfg.port)
|
port = Port(ctx.cfg.port)
|
||||||
appName = "/api"
|
appName = "/api"
|
||||||
|
|
||||||
routes:
|
routes:
|
||||||
@ -135,12 +137,57 @@ proc start*(cfg: PMApiConfig): void =
|
|||||||
get "/version":
|
get "/version":
|
||||||
resp($(%("personal_measure_api v" & PM_API_VERSION)), JSON)
|
resp($(%("personal_measure_api v" & PM_API_VERSION)), JSON)
|
||||||
|
|
||||||
|
post "/auth-token":
|
||||||
|
var email, pwd: string
|
||||||
|
try:
|
||||||
|
let jsonBody = parseJson(request.body)
|
||||||
|
email = jsonBody["email"].getStr
|
||||||
|
pwd = jsonBody["password"].getStr
|
||||||
|
except: jsonResp(Http400)
|
||||||
|
|
||||||
|
try:
|
||||||
|
let authToken = makeAuthToken(ctx, email, pwd)
|
||||||
|
resp($(%authToken), JSON)
|
||||||
|
except: jsonResp(Http401, getCurrentExceptionMsg())
|
||||||
|
|
||||||
|
get "/user":
|
||||||
|
checkAuth()
|
||||||
|
|
||||||
|
resp(Http200, $(%session.user), JSON)
|
||||||
|
|
||||||
post "/service/debug/stop":
|
post "/service/debug/stop":
|
||||||
if not cfg.debug: jsonResp(Http404)
|
if not ctx.cfg.debug: jsonResp(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)
|
resp($(%"shutting down"), JSON)
|
||||||
|
|
||||||
|
get "/api-tokens":
|
||||||
|
checkAuth()
|
||||||
|
|
||||||
|
resp(Http200, $(%ctx.db.getApiTokenByUserId($session.user.id)))
|
||||||
|
|
||||||
|
post "/api-tokens":
|
||||||
|
checkAuth()
|
||||||
|
|
||||||
|
var newToken: ApiToken
|
||||||
|
try:
|
||||||
|
let jsonBody = parseJson(request.body)
|
||||||
|
newToken = ApiToken(
|
||||||
|
id: genUUID(),
|
||||||
|
userId: session.user.id,
|
||||||
|
name: jsonBody["name"].getStr,
|
||||||
|
expires: none[DateTime](),
|
||||||
|
hashedToken: "")
|
||||||
|
except: jsonResp(Http400)
|
||||||
|
|
||||||
|
try:
|
||||||
|
let tokenValue = "" # TODO
|
||||||
|
newToken.hashedToken = hashPwd(tokenValue)
|
||||||
|
ctx.db.createApiToken(token)
|
||||||
|
let respToken = %newToken
|
||||||
|
newToken["value"] = tokenValue
|
||||||
|
resp($newToken, JSON)
|
||||||
|
|
||||||
waitFor(stopFuture)
|
waitFor(stopFuture)
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import options, times, timeutils, uuids
|
import json, options, times, timeutils, uuids
|
||||||
|
|
||||||
type
|
type
|
||||||
User* = object
|
User* = object
|
||||||
@ -46,3 +46,44 @@ proc `$`*(m: Measure): string =
|
|||||||
|
|
||||||
proc `$`*(v: Value): string =
|
proc `$`*(v: Value): string =
|
||||||
return "Value " & ($v.id)[0..6] & " - " & ($v.measureId)[0..6] & " = " & $v.value
|
return "Value " & ($v.id)[0..6] & " - " & ($v.measureId)[0..6] & " = " & $v.value
|
||||||
|
|
||||||
|
proc `%`*(u: User): JsonNode =
|
||||||
|
result = %*{
|
||||||
|
"id": $u.id,
|
||||||
|
"email": u.email,
|
||||||
|
"displayName": u.displayName
|
||||||
|
}
|
||||||
|
|
||||||
|
proc `%`*(tok: ApiToken): JsonNode =
|
||||||
|
result = %*{
|
||||||
|
"id": $tok.id,
|
||||||
|
"userId": $tok.userId,
|
||||||
|
"name": tok.name
|
||||||
|
}
|
||||||
|
|
||||||
|
if tok.expires.isSome:
|
||||||
|
result["expires"] = %(tok.expires.get.formatIso8601)
|
||||||
|
|
||||||
|
proc `%`*(m: Measure): JsonNode =
|
||||||
|
result = %*{
|
||||||
|
"id": $m.id,
|
||||||
|
"userId": $m.userId,
|
||||||
|
"slug": m.slug,
|
||||||
|
"name": m.name,
|
||||||
|
"description": m.description,
|
||||||
|
"domainUnits": m.domainUnits,
|
||||||
|
"rangeUnits": m.rangeUnits,
|
||||||
|
"analysis": m.analysis
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.domainSource.isSome: result["domainSource"] = %(m.domainSource.get)
|
||||||
|
if m.rangeSource.isSome: result["rangeSource"] = %(m.rangeSource.get)
|
||||||
|
|
||||||
|
proc `%`*(v: Value): JsonNode =
|
||||||
|
result = %*{
|
||||||
|
"id": $v.id,
|
||||||
|
"measureId": $v.measureId,
|
||||||
|
"value": v.value,
|
||||||
|
"timestampe": v.timestamp.formatIso8601,
|
||||||
|
"extData": v.extData
|
||||||
|
}
|
||||||
|
@ -4,7 +4,7 @@ create extension if not exists "uuid-ossp";
|
|||||||
create table "users" (
|
create table "users" (
|
||||||
id uuid default uuid_generate_v4() primary key,
|
id uuid default uuid_generate_v4() primary key,
|
||||||
display_name varchar not null,
|
display_name varchar not null,
|
||||||
email varchar not null,
|
email varchar not null unique,
|
||||||
hashed_pwd varchar not null
|
hashed_pwd varchar not null
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user