2013-02-18 20:45:31 +00:00
express = require ' express '
router = new express . Router ( )
2013-02-18 20:56:05 +00:00
scoring = require ' ../app/scoring '
_ = require ' underscore '
2013-03-06 13:45:51 +00:00
{ tnl } = require ' ../app/algos '
2013-02-23 08:20:16 +00:00
validator = require ' derby-auth/node_modules/validator '
2013-02-23 03:11:46 +00:00
check = validator . check
2013-02-26 21:44:26 +00:00
sanitize = validator . sanitize
2013-02-18 20:45:31 +00:00
2013-02-21 01:04:09 +00:00
NO_TOKEN_OR_UID = err: " You must include a token and uid (user id) in your request "
NO_USER_FOUND = err: " No user found. "
2013-02-25 19:32:46 +00:00
# ---------- /api/v1 API ------------
# Every url added beneath router is prefaced by /api/v1
2013-02-18 20:45:31 +00:00
# ##
2013-02-26 07:36:51 +00:00
v1 API . Requires api - v1 - user ( user id ) and api - v1 - key ( api key ) headers , Test with:
$ cd node_modules / racer && npm install && cd . . / . .
$ mocha test / api . mocha . coffee
2013-02-18 20:45:31 +00:00
# ##
2013-03-01 20:11:35 +00:00
router . get ' /status ' , (req, res) ->
res . json status: ' up '
2013-02-25 04:00:22 +00:00
auth = (req, res, next) ->
2013-02-25 07:43:50 +00:00
uid = req . headers [ ' x-api-user ' ]
token = req . headers [ ' x-api-key ' ]
2013-02-26 21:44:26 +00:00
return res . json 401 , NO_TOKEN_OR_UID unless uid || token
2013-02-20 18:35:21 +00:00
2013-02-25 07:43:50 +00:00
model = req . getModel ( )
query = model . query ( ' users ' ) . withIdAndToken ( uid , token )
2013-02-20 18:35:21 +00:00
2013-02-25 07:43:50 +00:00
query . fetch (err, user) ->
return res . json err: err if err
2013-02-26 07:36:51 +00:00
req.user = user
req.userObj = user . get ( )
2013-02-26 21:44:26 +00:00
return res . json 401 , NO_USER_FOUND if ! req . userObj || _ . isEmpty ( req . userObj )
2013-03-01 01:00:08 +00:00
req._isServer = true
2013-02-25 07:43:50 +00:00
next ( )
2013-02-18 20:45:31 +00:00
2013-02-25 04:00:22 +00:00
router . get ' /user ' , auth , (req, res) ->
2013-03-01 10:16:55 +00:00
user = req . userObj
2013-02-26 07:36:51 +00:00
2013-03-07 01:39:48 +00:00
user.stats.toNextLevel = tnl user . stats . lvl
2013-03-06 14:11:14 +00:00
user.stats.maxHealth = 50
2013-03-06 13:45:51 +00:00
2013-03-01 10:16:55 +00:00
delete user . apiToken
2013-02-22 01:17:23 +00:00
2013-03-01 10:16:55 +00:00
res . json user
2013-02-25 04:00:22 +00:00
2013-03-01 20:11:35 +00:00
router . get ' /user/task/:id ' , auth , (req, res) ->
2013-02-26 08:48:10 +00:00
task = req . userObj . tasks [ req . params . id ]
2013-02-26 21:44:26 +00:00
return res . json 400 , err: " No task found. " if ! task || _ . isEmpty ( task )
2013-02-26 07:36:51 +00:00
2013-02-26 20:21:17 +00:00
res . json 200 , task
2013-03-01 10:16:55 +00:00
validateTask = (req, res, next) ->
task = { }
2013-03-01 10:48:49 +00:00
newTask = { type , text , notes , value , up , down , completed } = req . body
2013-03-01 10:16:55 +00:00
# If we're updating, get the task from the user
2013-03-09 07:08:20 +00:00
if req . method is ' PUT ' or req . method is ' DELETE '
2013-03-01 10:16:55 +00:00
task = req . userObj ? . tasks [ req . params . id ]
return res . json 400 , err: " No task found. " if ! task || _ . isEmpty ( task )
2013-03-01 10:48:49 +00:00
# Strip for now
type = undefined
delete newTask . type
else if req . method is ' POST '
2013-03-01 11:11:32 +00:00
unless /^(habit|todo|daily|reward)$/ . test type
2013-03-01 10:48:49 +00:00
return res . json 400 , err: ' type must be habit, todo, daily, or reward '
2013-02-26 21:44:26 +00:00
2013-03-01 10:16:55 +00:00
text = sanitize ( text ) . xss ( )
notes = sanitize ( notes ) . xss ( )
value = sanitize ( value ) . toInt ( )
2013-02-26 20:21:17 +00:00
2013-03-01 10:16:55 +00:00
switch type
when ' habit '
newTask.up = true unless typeof up is ' boolean '
newTask.down = true unless typeof down is ' boolean '
when ' daily ' , ' todo '
newTask.completed = false unless typeof completed is ' boolean '
2013-02-26 20:21:17 +00:00
2013-03-01 10:16:55 +00:00
_ . extend task , newTask
req.task = task
next ( )
2013-03-01 20:11:35 +00:00
router . put ' /user/task/:id ' , auth , validateTask , (req, res) ->
2013-03-01 10:16:55 +00:00
req . user . set " tasks. #{ req . task . id } " , req . task
2013-02-26 07:36:51 +00:00
2013-03-01 10:16:55 +00:00
res . json 200 , req . task
2013-02-25 04:00:22 +00:00
2013-03-09 07:08:20 +00:00
router . delete ' /user/task/:id ' , auth , validateTask , (req, res) ->
2013-03-09 14:14:07 +00:00
taskIds = req . user . get " #{ req . task . type } Ids "
2013-03-09 07:08:20 +00:00
req . user . del " tasks. #{ req . task . id } "
2013-03-09 14:14:07 +00:00
# Remove one id from array of typeIds
req . user . remove " #{ req . task . type } Ids " , taskIds . indexOf ( req . task . id ) , 1
2013-03-09 07:08:20 +00:00
res . send 204
2013-03-01 10:16:55 +00:00
router . post ' /user/task ' , auth , validateTask , (req, res) ->
task = req . task
type = task . type
2013-02-25 04:00:22 +00:00
model = req . getModel ( )
model . ref ' _user ' , req . user
model . refList " _ #{ type } List " , " _user.tasks " , " _user. #{ type } Ids "
model . at ( " _ #{ type } List " ) . push task
2013-02-26 20:21:17 +00:00
res . json 201 , task
2013-02-25 04:00:22 +00:00
router . get ' /user/tasks ' , auth , (req, res) ->
2013-03-01 10:16:55 +00:00
user = req . userObj
return res . json 400 , NO_USER_FOUND if ! user || _ . isEmpty ( user )
2013-02-22 01:38:12 +00:00
model = req . getModel ( )
2013-02-25 04:00:22 +00:00
model . ref ' _user ' , req . user
tasks = [ ]
2013-02-26 21:44:26 +00:00
types = [ ' habit ' , ' todo ' , ' daily ' , ' reward ' ]
2013-03-01 11:11:32 +00:00
if /^(habit|todo|daily|reward)$/ . test req . query . type
2013-02-26 21:44:26 +00:00
types = [ req . query . type ]
for type in types
2013-02-23 03:11:46 +00:00
model . refList " _ #{ type } List " , " _user.tasks " , " _user. #{ type } Ids "
2013-02-25 04:00:22 +00:00
tasks = tasks . concat model . get ( " _ #{ type } List " )
2013-02-22 01:38:12 +00:00
2013-02-26 20:21:17 +00:00
res . json 200 , tasks
2013-02-18 20:45:31 +00:00
2013-03-04 00:37:54 +00:00
# ##
This is called form deprecated . coffee ' s score function, and the req.headers are setup properly to handle the login
# ##
scoreTask = (req, res, next) ->
{ taskId , direction } = req . params
{ title , service , icon } = req . body
# Send error responses for improper API call
return res . send ( 500 , ' :taskId required ' ) unless taskId
return res . send ( 500 , " :direction must be ' up ' or ' down ' " ) unless direction in [ ' up ' , ' down ' ]
model = req . getModel ( )
{ user , userObj } = req
model . ref ( ' _user ' , user )
# Create task if doesn't exist
# TODO add service & icon to task
unless model . get ( " _user.tasks. #{ taskId } " )
model . refList " _habitList " , " _user.tasks " , " _user.habitIds "
model . at ( ' _habitList ' ) . push
id: taskId
type: ' habit '
text: ( title || taskId )
value: 0
up: true
down: true
notes: " This task was created by a third-party service. Feel free to edit, it won ' t harm the connection to that service. Additionally, multiple services may piggy-back off this task. "
delta = scoring . score ( model , taskId , direction )
result = model . get ( ' _user.stats ' )
result.delta = delta
res . send ( result )
router . post ' /user/tasks/:taskId/:direction ' , auth , scoreTask
2013-02-18 20:45:31 +00:00
module.exports = router
2013-03-04 00:37:54 +00:00
module.exports.auth = auth
module.exports.scoreTask = scoreTask # export so deprecated can call it