Router: account

This commit is contained in:
jysperm
2015-04-12 16:31:23 +08:00
parent 4e10c2df1d
commit 9d45feb188
7 changed files with 187 additions and 105 deletions

View File

@@ -38,10 +38,6 @@
"level": "error",
"limitComments": true
},
"missing_fat_arrows": {
"name": "missing_fat_arrows",
"level": "warn"
},
"newlines_after_classes": {
"name": "newlines_after_classes",
"value": 1,

View File

@@ -64,7 +64,7 @@ class BillingPlan
setupDefaultComponents: (account) ->
Q.all _.values(@components).map ({type, defaults}) ->
Q.all defaults.map (defaultOptions) ->
rp.components.byName(type).createComponent account, defaultOptions(account)
root.components.byName(type).createComponent account, defaultOptions(account)
triggerTimeBilling: (account) ->
unless @billing.time

View File

@@ -79,8 +79,8 @@ module.exports = class I18nManager
Return {Object}.
###
packClientLocale: (language) ->
return @packLocale @alternativeLanguages language
packTranslations: (language) ->
return @packTranslationsByLanguages @alternativeLanguages language
###
Public: Get hash of packaged translations by language or request.
@@ -89,8 +89,8 @@ module.exports = class I18nManager
Return {Object}.
###
localeHash: (language) ->
utils.sha256 jsonStableStringify @pickClientLocale language
translationsHash: (language) ->
utils.sha256 jsonStableStringify @packTranslations language
###
Public: Translate name by languages.
@@ -135,9 +135,11 @@ module.exports = class I18nManager
* `languages` {Array} of {String}
TODO: Cache.
Return {Object}.
###
packLocale: (languages) ->
packTranslationsByLanguages: (languages) ->
result = {}
for language in languages

View File

@@ -1,11 +1,14 @@
expressBunyanLogger = require 'express-bunyan-logger'
expressSession = require 'express-session'
redisStore = require 'connect-redis'
moment = require 'moment-timezone'
crypto = require 'crypto'
csrf = require 'csrf'
path = require 'path'
fs = require 'fs'
_ = require 'lodash'
{config} = app
{_, path, fs, moment, crypto} = app.libs
{Account, SecurityLog} = app.models
{Account, SecurityLog, config} = root
exports.reqHelpers = (req, res, next) ->
req.getCsrfToken = ->
@@ -36,10 +39,10 @@ exports.reqHelpers = (req, res, next) ->
return req.cookies?.timezone ? config.i18n.default_timezone
req.getTranslator = ->
return rp.translatorByReq req
return root.i18n.translator req
req.getMoment = ->
return moment.apply(@, arguments).locale(req.getLanguage()).tz(req.getTimezone())
return moment.apply(arguments...).locale(req.getLanguage()).tz(req.getTimezone())
req.createSecurityLog = (type, options, {account, token} = {}) ->
SecurityLog.createLog (account ? req.account),
@@ -87,6 +90,7 @@ exports.renderHelpers = (req, res, next) ->
next()
exports.logger = ->
# TODO: refactor
return expressBunyanLogger
genReqId: (req) -> req.sessionID
parseUA: false
@@ -122,7 +126,7 @@ exports.csrf = ->
if req.path in app.getHooks('app.ignore_csrf', null, pluck: 'path')
return next()
if req.method in ['GET', 'HEAD', 'OPTIONS']
if req.method in ['HEAD', 'OPTIONS']
return next()
unless provider.verify req.session.csrf_secret, req.getCsrfToken()
@@ -145,17 +149,15 @@ exports.authenticate = (req, res, next) ->
_.extend req,
token: token
account: account
.finally next
exports.requireAuthenticate = (req, res, next) ->
if req.account
next()
else if req.method == 'GET'
res.redirect '/account/login/'
else
if req.method == 'GET'
res.redirect '/account/login/'
else
res.error 403, 'auth_failed'
res.error 403, 'auth_failed'
exports.requireAdminAuthenticate = (req, res, next) ->
if req.account?.isAdmin()

View File

@@ -53,6 +53,14 @@ CouponCode = mabolo.model 'CouponCode',
# Public: Apply log of coupon
apply_log: [ApplyLog]
CouponCode.ensureIndex
code: 1
,
unique: true
CouponCode.findByCode = (code, options...) ->
@findOne code: code, options...
###
Public: Create coupons.
@@ -76,6 +84,9 @@ CouponCode.createCoupons = ({type, options, expired_at, available_times}, count)
expired_at: expired_at
available_times: available_times
CouponCode::pick = ->
return _.omit @, 'apply_log'
###
Public: Check availability for specified account.
@@ -84,10 +95,13 @@ CouponCode.createCoupons = ({type, options, expired_at, available_times}, count)
Return {Promise} resolve with {Boolean}.
###
CouponCode::validate = (account) ->
if @available_times <= 0
return Q false
@populate().then ->
if @available_times != undefined and @available_times <= 0
return false
if @expired and new Date() > @expired
return false
@provider.validate account, @
###
@@ -98,18 +112,19 @@ CouponCode::validate = (account) ->
Return {Promise}.
###
CouponCode::apply = (account) ->
if @available_times <= 0
throw new Error 'coupon_unavailable'
@populate().then ->
@provider.apply(account, @).then =>
@update
$inc:
available_times: -1
$push:
apply_log:
account_id: account._id
created_at: new Date()
@validate(account).then (available) ->
unless available
throw new Error 'coupon_unavailable'
.then =>
@provider.apply account, @
.then =>
@update
$inc:
available_times: -1
$push:
apply_log:
account_id: account._id
created_at: new Date()
###
Public: Populate.

View File

@@ -58,9 +58,9 @@ module.exports = class ViewRegistry
async.detect([
view
rp.resolve view
rp.resolve 'core', view
rp.resolve 'core/view', view
root.resolve view
root.resolve 'core', view
root.resolve 'core/view', view
], fs.exists).then (filename) ->
fs.read(filename).then (source) ->
return jade.compile extendSource(source),

View File

@@ -1,38 +1,69 @@
{_, express} = app.libs
{requireAuthenticate} = app.middleware
{Account, SecurityLog, CouponCode} = app.models
{config, utils, logger, i18n} = app
express = require 'express'
_ = require 'lodash'
module.exports = exports = express.Router()
utils = require '../utils'
exports.get '/register', (req, res) ->
{i18n, Account, CouponCode} = root
{requireAuthenticate} = root.middleware
module.router = router = express.Router()
###
Router: GET /account/register
Response HTML.
###
router.get '/register', (req, res) ->
res.render 'account/register'
exports.get '/login', (req, res) ->
###
Router: GET /account/login
Response HTML.
###
router.get '/login', (req, res) ->
res.render 'account/login'
exports.get '/locale/:language?', (req, res) ->
if req.params['language']
req.cookies['language'] = req.params['language']
###
Router: GET /account/translations/:language?
res.json i18n.pickClientLocale i18n.getLanguagesByReq req
Response {Object}.
###
router.get '/translations/:language?', (req, res) ->
if req.params.language
res.json i18n.packTranslations req.params.language
else
res.json i18n.packTranslations req
exports.get '/preferences', requireAuthenticate, (req, res) ->
###
Router: GET /account/preferences/edit
Response HTML.
###
router.get '/preferences/edit', requireAuthenticate, (req, res) ->
res.render 'account/preferences'
exports.get '/session_info/', (req, res) ->
response =
csrf_token: req.session.csrf_token
###
Router: GET /account/self
if req.account
_.extend response,
account_id: req.account._id
username: req.account.username
preferences: req.account.preferences
Response {Account} from {Account::pick}
###
router.get '/self', requireAuthenticate, (req, res) ->
res.json req.account.pick 'self'
res.json response
###
Router: POST /account/register
exports.post '/register', (req, res) ->
Request {Object}
* `username` {String}
* `email` {String}
* `password` {String}
Response {Token}.
Set-Cookie: token.
###
router.post '/register', (req, res) ->
Account.register(req.body).then (account) ->
res.createToken account
.catch (err) ->
@@ -43,7 +74,18 @@ exports.post '/register', (req, res) ->
else
res.error err
exports.post '/login', (req, res) ->
###
Router: POST /account/login
Request {Object}
* `username` {String} Username, email or account_id.
* `password` {String}
Response {Token}.
Set-Cookie: token, language.
###
router.post '/login', (req, res) ->
Account.search(req.body.username).then (account) ->
unless account?.matchPassword req.body.password
throw new Error 'wrong_password'
@@ -61,14 +103,28 @@ exports.post '/login', (req, res) ->
else
res.error err
exports.post '/logout', requireAuthenticate, (req, res) ->
req.token.revoke().then ->
###
Router: POST /account/logout
Set-Cookie: token.
###
router.post '/logout', requireAuthenticate, (req, res) ->
req.token.revoke().done ->
req.createSecurityLog('revoke_token').then ->
res.clearCookie('token').sendStatus 204
.catch res.error
, res.error
exports.post '/update_password', requireAuthenticate, (req, res) ->
Q().then ->
###
Router: PUT /account/password
Request {Object}
* `original_password` {String}
* `password` {String}
###
router.put '/password', requireAuthenticate, (req, res) ->
Q().done ->
unless req.account.matchPassword req.body.original_password
throw new Error 'wrong_password'
@@ -77,10 +133,20 @@ exports.post '/update_password', requireAuthenticate, (req, res) ->
req.account.setPassword(req.body.password).then ->
req.createSecurityLog 'update_password'
res.sendStatus 204
.catch res.error
, res.error
exports.post '/update_email', requireAuthenticate, (req, res) ->
###
Router: PUT /email
Request {Object}
* `email` {String}
* `password` {String}
###
router.post '/update_email', requireAuthenticate, (req, res) ->
Q().done ->
unless req.account.matchPassword req.body.password
throw new Error 'wrong_password'
@@ -95,46 +161,47 @@ exports.post '/update_email', requireAuthenticate, (req, res) ->
original_email: original_email
current_email: req.account.email
res.sendStatus 204
, req.error
exports.post '/update_preferences', requireAuthenticate, (req, res) ->
###
Router: PATCH /account/preferences
exports.use do ->
router = new express.Router()
Request {Preferences}.
router.use requireAuthenticate
Response {Preferences}.
###
router.patch '/preferences', requireAuthenticate, (req, res) ->
req.account.updatePreferences(req.body).done (preferences) ->
res.json preferences
, res.error
router.get '/info', (req, res) ->
CouponCode.findOne
code: req.query.code
, (err, coupon) ->
unless coupon
return res.error 'code_not_exist'
router.use '/coupons', do (router = express.Router()) ->
###
Router: GET /account/coupons/:code
coupon.validateCode req.account, (is_available) ->
unless is_available
return res.error 'code_not_available'
Response {CouponCode}.
###
router.get '/:code', (req, res) ->
CouponCode.findByCode(req.params.code).done (coupon) ->
if coupon
coupon.populate(req: req).then ->
coupon.validate(req.account).then (available) ->
res.json _.extend coupon.pick(),
available: available
else
throw new Error 'coupon_not_found'
, res.error
coupon.getMessage req, (message) ->
res.json
message: message
router.post '/apply', (req, res) ->
CouponCode.findOne
code: req.body.code
, (err, coupon) ->
unless coupon
return res.error 'code_not_exist'
if coupon.expired and Date.now() > coupon.expired.getTime()
return res.error 'code_expired'
if coupon.available_times and coupon.available_times < 0
return res.error 'code_not_available'
coupon.validateCode req.account, (is_available) ->
unless is_available
return res.error 'code_not_available'
coupon.applyCode req.account, ->
res.json {}
###
Router: POST /account/coupons/:code/apply
###
router.post '/:code/apply', requireAuthenticate, (req, res) ->
CouponCode.findByCode(req.params.code).done (coupon) ->
if coupon
coupon.apply(account).then ->
res.sendStatus 204
else
throw new Error 'coupon_not_found'
, res.error