mirror of
https://github.com/sudoxnym/habitica-self-host.git
synced 2026-07-31 19:20:25 +00:00
wip
This commit is contained in:
parent
b92145f5a3
commit
ddff29ccf0
11 changed files with 234 additions and 72 deletions
|
|
@ -26,7 +26,9 @@
|
|||
"nodetime": "*",
|
||||
"resolve": "~0.2.3",
|
||||
"request": "~2.14.0",
|
||||
"querystring": "~0.1.0"
|
||||
"querystring": "~0.1.0",
|
||||
"nock": "~0.15.2",
|
||||
"superagent": "~0.12.4"
|
||||
},
|
||||
"private": true,
|
||||
"subdomain": "habitrpg",
|
||||
|
|
|
|||
|
|
@ -67,7 +67,9 @@ process.on('uncaughtException', function (error) {
|
|||
});
|
||||
|
||||
require('coffee-script') // remove intermediate compilation requirement
|
||||
require('./src/server').listen(process.env.PORT || 3000, process.env.IP || '0.0.0.0');
|
||||
module.exports = server = require('./src/server')
|
||||
|
||||
server.listen(process.env.PORT || 3000, process.env.IP || '0.0.0.0');
|
||||
|
||||
// Note: removed "up" module, which is default for development (but interferes with and production + PaaS)
|
||||
// Restore to 5310bb0 if I want it back (see https://github.com/codeparty/derby/issues/165#issuecomment-10405693)
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ module.exports.updateUser = (model) ->
|
|||
batch = new BatchUpdate(model)
|
||||
user = batch.user
|
||||
obj = batch.obj()
|
||||
tasks = obj.tasks
|
||||
tasks = obj?.tasks
|
||||
|
||||
# Remove corrupted tasks
|
||||
_.each tasks, (task, key) ->
|
||||
|
|
@ -172,49 +172,49 @@ module.exports.BatchUpdate = BatchUpdate = (model) ->
|
|||
updates = {}
|
||||
|
||||
{
|
||||
user: user
|
||||
user: user
|
||||
|
||||
obj: ->
|
||||
obj ?= model.get 'users.'+user.get('id')
|
||||
return obj
|
||||
obj: ->
|
||||
obj ?= model.get 'users.'+user.get('id')
|
||||
return obj
|
||||
|
||||
startTransaction: ->
|
||||
# start a batch transaction - nothing between now and @commit() will be set immediately
|
||||
transactionInProgress = true
|
||||
model._dontPersist = true
|
||||
@obj()
|
||||
startTransaction: ->
|
||||
# start a batch transaction - nothing between now and @commit() will be set immediately
|
||||
transactionInProgress = true
|
||||
model._dontPersist = true
|
||||
@obj()
|
||||
|
||||
###
|
||||
Handles updating the user model. If this is an en-mass operation (eg, server cron), changes are queued
|
||||
but not actually set to the model. It also modifies userObj in case you need to access properties manually later.
|
||||
If transaction not in progress, it just runs standard model.set()
|
||||
###
|
||||
set: (path, val) ->
|
||||
updates[path] = val if transactionInProgress
|
||||
user.set(path, val)
|
||||
###
|
||||
Handles updating the user model. If this is an en-mass operation (eg, server cron), changes are queued
|
||||
but not actually set to the model. It also modifies userObj in case you need to access properties manually later.
|
||||
If transaction not in progress, it just runs standard model.set()
|
||||
###
|
||||
set: (path, val) ->
|
||||
updates[path] = val if transactionInProgress
|
||||
user.set(path, val)
|
||||
|
||||
###
|
||||
Hack to get around dom bindings being lost if parent objects are replaced whole-sale
|
||||
eg, user.set('stats', {hp:50, exp:10...}) will break dom bindings, but user.set('stats.hp',50) is ok
|
||||
###
|
||||
setStats: (stats) ->
|
||||
stats ?= obj.stats
|
||||
that = @
|
||||
_.each Object.keys(stats), (key) -> that.set "stats.#{key}", stats[key]
|
||||
###
|
||||
Hack to get around dom bindings being lost if parent objects are replaced whole-sale
|
||||
eg, user.set('stats', {hp:50, exp:10...}) will break dom bindings, but user.set('stats.hp',50) is ok
|
||||
###
|
||||
setStats: (stats) ->
|
||||
stats ?= obj.stats
|
||||
that = @
|
||||
_.each Object.keys(stats), (key) -> that.set "stats.#{key}", stats[key]
|
||||
|
||||
# queue: (path, val) ->
|
||||
# # Special function for setting object properties by string dot-notation. See http://stackoverflow.com/a/6394168/362790
|
||||
# arr = path.split('.')
|
||||
# arr.reduce (curr, next, index) ->
|
||||
# if (arr.length - 1) == index
|
||||
# curr[next] = val
|
||||
# curr[next]
|
||||
# , obj
|
||||
# queue: (path, val) ->
|
||||
# # Special function for setting object properties by string dot-notation. See http://stackoverflow.com/a/6394168/362790
|
||||
# arr = path.split('.')
|
||||
# arr.reduce (curr, next, index) ->
|
||||
# if (arr.length - 1) == index
|
||||
# curr[next] = val
|
||||
# curr[next]
|
||||
# , obj
|
||||
|
||||
commit: ->
|
||||
model._dontPersist = false
|
||||
# some hackery in our own branched racer-db-mongo, see findAndModify of lefnire/racer-db-mongo#habitrpg index.js
|
||||
user.set "update__", updates
|
||||
transactionInProgress = false
|
||||
updates = {}
|
||||
commit: ->
|
||||
model._dontPersist = false
|
||||
# some hackery in our own branched racer-db-mongo, see findAndModify of lefnire/racer-db-mongo#habitrpg index.js
|
||||
user.set "update__", updates
|
||||
transactionInProgress = false
|
||||
updates = {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,17 +21,19 @@ router.get '/status', (req, res) ->
|
|||
status: 'up'
|
||||
|
||||
router.get '/user', (req, res) ->
|
||||
console.log 'hi'
|
||||
{ uid, token } = req.query
|
||||
return res.json 500, NO_TOKEN_OR_UID unless uid || token
|
||||
|
||||
model = req.getModel()
|
||||
query = model.query('users').withIdAndToken(uid, token)
|
||||
|
||||
model.fetch query, (err, user) ->
|
||||
query.fetch (err, user) ->
|
||||
self = user.get()
|
||||
return res.json 500, err: err if err
|
||||
return res.json 500, NO_USER_FOUND if !user || _.isEmpty(user)
|
||||
return res.json 500, NO_USER_FOUND if !self || _.isEmpty(self)
|
||||
|
||||
res.json user
|
||||
res.json self
|
||||
|
||||
router.get '/user/calendar.ics', (req, res) ->
|
||||
#return next() #disable for now
|
||||
|
|
|
|||
|
|
@ -13,6 +13,31 @@ router.get '/:uid/up/:score?', (req, res) -> res.send(500, deprecatedMessage)
|
|||
router.get '/:uid/down/:score?', (req, res) -> res.send(500, deprecatedMessage)
|
||||
router.post '/users/:uid/tasks/:taskId/:direction', (req, res) -> res.send(500, deprecatedMessage)
|
||||
|
||||
router.get '/v1/users/:uid/tasks/:taskId/:direction', (req, res) ->
|
||||
{uid, taskId, direction} = req.params
|
||||
{apiToken} = req.query
|
||||
{title, service, icon} = req.body
|
||||
console.log {params:req.params, body:req.body} if process.env.NODE_ENV == 'development'
|
||||
|
||||
# Send error responses for improper API call
|
||||
return res.send(500, 'request body "apiToken" required') unless apiToken
|
||||
return res.send(500, ':uid required') unless uid
|
||||
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()
|
||||
req._isServer = true
|
||||
model.fetch model.query('users').withIdAndToken(uid, apiToken), (err, result) ->
|
||||
return res.send(500, err) if err
|
||||
user = result.at(0)
|
||||
userObj = user.get()
|
||||
if _.isEmpty(userObj)
|
||||
return res.send(500, "User with uid=#{uid}, token=#{apiToken} not found. Make sure you're not using your username, but your User Id")
|
||||
|
||||
model.ref('_user', user)
|
||||
|
||||
res.send(user)
|
||||
|
||||
router.post '/v1/users/:uid/tasks/:taskId/:direction', (req, res) ->
|
||||
{uid, taskId, direction} = req.params
|
||||
{apiToken, title, service, icon} = req.body
|
||||
|
|
|
|||
|
|
@ -17,9 +17,9 @@ middleware = require './middleware'
|
|||
racer.io.set('transports', ['xhr-polling'])
|
||||
racer.ioClient.set('reconnection limit', 300000) # max reconect timeout to 5 minutes
|
||||
racer.set('bundleTimeout', 40000)
|
||||
#unless process.env.NODE_ENV == 'production'
|
||||
# racer.use(racer.logPlugin)
|
||||
# derby.use(derby.logPlugin)
|
||||
unless process.env.NODE_ENV == 'production'
|
||||
racer.use(racer.logPlugin)
|
||||
derby.use(derby.logPlugin)
|
||||
|
||||
## SERVER CONFIGURATION ##
|
||||
|
||||
|
|
@ -28,7 +28,7 @@ server = http.createServer expressApp
|
|||
module.exports = server
|
||||
|
||||
derby.use require('racer-db-mongo')
|
||||
store = derby.createStore
|
||||
module.exports.habitStore = store = derby.createStore
|
||||
db: {type: 'Mongo', uri: process.env.NODE_DB_URI, safe:true}
|
||||
listen: server
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
module.exports.splash = (req, res, next) ->
|
||||
# This was an API call, not a page load
|
||||
return next() if req.is('json')
|
||||
|
||||
return next() if /^\/api/.test req.path
|
||||
if !req.session.userId? and !req.query?.play?
|
||||
res.redirect('/splash.html')
|
||||
else
|
||||
|
|
|
|||
|
|
@ -54,12 +54,12 @@ userAccess = (store) ->
|
|||
###
|
||||
REST = (store) ->
|
||||
store.query.expose "users", "withIdAndToken", (uid, token) ->
|
||||
@where('id').equals(uid)
|
||||
@byId(uid)
|
||||
.where('apiToken').equals(token)
|
||||
.one
|
||||
|
||||
store.queryAccess "users", "withIdAndToken", (id, token, accept, err) ->
|
||||
return accept(true) if id && token
|
||||
store.queryAccess "users", "withIdAndToken", (uid, token, accept, err) ->
|
||||
return accept(true) if uid && token
|
||||
accept(false) # only user has id & token
|
||||
|
||||
|
||||
|
|
@ -90,4 +90,4 @@ partySystem = (store) ->
|
|||
|
||||
store.writeAccess "*", "parties.*", ->
|
||||
accept = arguments[arguments.length-2]
|
||||
accept(true)
|
||||
accept(true)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,16 @@
|
|||
assert = require 'assert'
|
||||
{BrowserModel: Model} = require 'racer/test/util/model'
|
||||
derby = require 'derby'
|
||||
racer = require 'racer'
|
||||
_ = require 'underscore'
|
||||
moment = require 'moment'
|
||||
request = require 'request'
|
||||
request = require 'superagent'
|
||||
qs = require 'querystring'
|
||||
|
||||
app = require '../server'
|
||||
|
||||
store = app.habitStore
|
||||
|
||||
# Custom modules
|
||||
scoring = require '../src/app/scoring'
|
||||
character = require '../src/app/character'
|
||||
|
|
@ -75,27 +80,55 @@ modificationsLookup = (direction, options = {}) ->
|
|||
|
||||
describe 'API', ->
|
||||
model = null
|
||||
user = null
|
||||
params = null
|
||||
|
||||
describe 'Without token or user id', ->
|
||||
|
||||
it '/api/v1/user', (done) ->
|
||||
request.get { uri: "#{baseURL}/user" }, (err, res, body) ->
|
||||
console.log "#{baseURL}/user", body
|
||||
assert.ok !err
|
||||
assert.equal res.statusCode, 500
|
||||
assert.ok body.err
|
||||
done()
|
||||
request(app)
|
||||
.get("/api/v1/user")
|
||||
.set('Accept', 'application/json')
|
||||
.on('error', (err) ->
|
||||
console.log 'err', err
|
||||
)
|
||||
.end (res) ->
|
||||
assert.equal res.statusCode, 500
|
||||
assert.ok JSON.parse(res.text).err
|
||||
done()
|
||||
|
||||
describe 'With token and user id', ->
|
||||
before ->
|
||||
model = new Model
|
||||
model.set '_user', character.newUserObject()
|
||||
scoring.setModel model
|
||||
before (done) ->
|
||||
model = store.createModel()
|
||||
store.flush()
|
||||
|
||||
uid = model.id()
|
||||
user = character.newUserObject()
|
||||
user.apiToken = derby.uuid()
|
||||
model.set "users.#{uid}", user
|
||||
user = model.get("users.#{uid}")
|
||||
|
||||
params =
|
||||
uid: user.id
|
||||
token: user.apiToken
|
||||
|
||||
done()
|
||||
|
||||
after (done) ->
|
||||
store.flush done
|
||||
|
||||
it '/api/v1/user', (done) ->
|
||||
user = model.get '_user'
|
||||
|
||||
request.get { uri: "#{baseURL}/user?#{qs.stringify(UID_AND_TOKEN)}" }, (err, res, body) ->
|
||||
assert.ok !err
|
||||
assert.equal res.statusCode, 200
|
||||
done()
|
||||
console.log params
|
||||
request(app)
|
||||
.get("/api/v1/user")
|
||||
.set('Accept', 'application/json')
|
||||
.query(params)
|
||||
.on('error', (err) ->
|
||||
console.log 'err', err
|
||||
)
|
||||
.end (res) ->
|
||||
console.log 'test', res.body
|
||||
assert.ok !res.body.err
|
||||
assert.equal res.statusCode, 200
|
||||
assert.ok res.body
|
||||
done()
|
||||
|
|
|
|||
25
test/request/local.coffee
Normal file
25
test/request/local.coffee
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
Request = require './request'
|
||||
qs = require 'querystring'
|
||||
|
||||
makeUrl = (path, params) ->
|
||||
if typeof params is "object"
|
||||
params = qs.stringify(params)
|
||||
else
|
||||
params = ''
|
||||
return "http://localhost:3000/#{path}?#{params}"
|
||||
|
||||
# url, params (optional), callback
|
||||
get = (obj, callback) ->
|
||||
obj.params ||= {}
|
||||
obj.path ||= ''
|
||||
|
||||
requestUrl = makeUrl obj.path, obj.params
|
||||
|
||||
request = new Request requestUrl
|
||||
|
||||
request.done (res) ->
|
||||
callback null, JSON.parse(res)
|
||||
|
||||
request.fire()
|
||||
|
||||
module.exports = { get }
|
||||
74
test/request/request.coffee
Normal file
74
test/request/request.coffee
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
http = require 'http'
|
||||
|
||||
# ### Request
|
||||
#
|
||||
# The Request class is just a wrapper
|
||||
# around the `http.request` function,
|
||||
# providing a cleaner and more reusable
|
||||
# interface.
|
||||
#
|
||||
# `constructor(options)`
|
||||
#
|
||||
# `options` can be anything `http.request` takes.
|
||||
#
|
||||
# Callbacks can be queued up by calling `request.done`
|
||||
# and passing it a callback function to be invoked when the
|
||||
# http request has completed
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# callback = (json) ->
|
||||
# doSomethingWithJSON( json )
|
||||
#
|
||||
# request = new Request('http://api.phish.net/api.js?')
|
||||
# request.done (response) ->
|
||||
# callback JSON.parse(response)[0]
|
||||
#
|
||||
# request.fire(data) # data refers to the post data
|
||||
#
|
||||
class Request
|
||||
constructor: (@options) ->
|
||||
@callbacks = {}
|
||||
|
||||
fire: (data) ->
|
||||
request = http.request(@options, @_handler)
|
||||
request.on 'error', (err) =>
|
||||
for callback in @callbacks['fail']
|
||||
callback(err)
|
||||
|
||||
# Send data with request
|
||||
if data
|
||||
request.write data
|
||||
|
||||
request.end()
|
||||
|
||||
this
|
||||
|
||||
done: (callback) ->
|
||||
push.call(this, 'done', callback)
|
||||
|
||||
this
|
||||
|
||||
fail: (callback) ->
|
||||
push.call(this, 'fail', callback)
|
||||
|
||||
this
|
||||
|
||||
###########
|
||||
# PRIVATE #
|
||||
###########
|
||||
|
||||
_handler: (response) =>
|
||||
data = ''
|
||||
response.on 'data', (chunk) ->
|
||||
data += chunk
|
||||
response.on 'end', =>
|
||||
for callback in @callbacks['done']
|
||||
callback(data)
|
||||
|
||||
|
||||
push = (type, callback) ->
|
||||
@callbacks[type] ?= []
|
||||
@callbacks[type].push callback
|
||||
|
||||
module.exports = Request
|
||||
Loading…
Reference in a new issue