Reorganize files under /src, separate express app in two apps, one for api v1 and v2 and the other \

for the upcoming api v3

Reorganize files under /src, separate express app in two apps, one for api v1 and v2 and the other for the upcoming api v3

move api v2 routes in a separate folder, rename apiv1 file for better readability, remove auth routes for api v1

move api-v2 controllers in subdirectory

move unorganized files to /libs

fix gulp requires and separate server in old (api v1 and v2) and new (api v3) app

fix require paths

fix require paths

fix require paths

put api v1 back

Reorganize files under /src and separate express app in one for api v1 and v2 and the other for v3
This commit is contained in:
Matteo Pagliazzi
2015-10-30 15:05:50 +01:00
parent 9227dd89be
commit ed0e4c6d20
41 changed files with 923 additions and 1043 deletions

View File

@@ -140,7 +140,7 @@ module.exports = function(grunt) {
grunt.registerTask('test:prepare:translations', function() { grunt.registerTask('test:prepare:translations', function() {
require('coffee-script'); require('coffee-script');
var i18n = require('./website/src/i18n'), var i18n = require('./website/src/libs/i18n'),
fs = require('fs'); fs = require('fs');
fs.writeFileSync('test/spec/mocks/translations.js', fs.writeFileSync('test/spec/mocks/translations.js',
"if(!window.env) window.env = {};\n" + "if(!window.env) window.env = {};\n" +

View File

@@ -2,9 +2,9 @@ import 'coffee-script';
import mongoose from 'mongoose'; import mongoose from 'mongoose';
import autoinc from 'mongoose-id-autoinc'; import autoinc from 'mongoose-id-autoinc';
import logging from '../website/src/logging'; import logging from '../website/src/libs/logging';
import nconf from 'nconf'; import nconf from 'nconf';
import utils from '../website/src/utils'; import utils from '../website/src/libs/utils';
import repl from 'repl'; import repl from 'repl';
import gulp from 'gulp'; import gulp from 'gulp';

View File

@@ -19,7 +19,7 @@ describe "Push-Notifications", ->
done() done()
context "Challenges", -> context "Challenges", ->
challenges = rewire("../../website/src/controllers/challenges") challenges = rewire("../../website/src/controllers/api-v2/challenges")
challenges.__set__('pushNotify', pushSpy) challenges.__set__('pushNotify', pushSpy)
challengeMock = { challengeMock = {
findById: (arg, cb) -> findById: (arg, cb) ->
@@ -65,7 +65,7 @@ describe "Push-Notifications", ->
recipient = null recipient = null
groups = rewire("../../website/src/controllers/groups") groups = rewire("../../website/src/controllers/api-v2/groups")
groups.__set__('pushNotify', pushSpy) groups.__set__('pushNotify', pushSpy)
before (done) -> before (done) ->
@@ -226,7 +226,7 @@ describe "Push-Notifications", ->
, false , false
context "sending gems from balance", -> context "sending gems from balance", ->
members = rewire("../../website/src/controllers/members") members = rewire("../../website/src/controllers/api-v2/members")
members.sendMessage = -> true members.sendMessage = -> true
members.__set__('pushNotify', pushSpy) members.__set__('pushNotify', pushSpy)

View File

@@ -3,7 +3,7 @@ expect = require 'expect.js'
sinon = require 'sinon' sinon = require 'sinon'
moment = require 'moment' moment = require 'moment'
shared = require '../../common/script/index.coffee' shared = require '../../common/script/index.coffee'
shared.i18n.translations = require('../../website/src/i18n.js').translations shared.i18n.translations = require('../../website/src/libs/i18n.js').translations
test_helper = require './test_helper' test_helper = require './test_helper'
test_helper.addCustomMatchers() test_helper.addCustomMatchers()
$w = (s)->s.split(' ') $w = (s)->s.split(' ')

View File

@@ -3,7 +3,7 @@ expect = require 'expect.js'
sinon = require 'sinon' sinon = require 'sinon'
moment = require 'moment' moment = require 'moment'
shared = require '../../common/script/index.coffee' shared = require '../../common/script/index.coffee'
shared.i18n.translations = require('../../website/src/i18n.js').translations shared.i18n.translations = require('../../website/src/libs/i18n.js').translations
repeatWithoutLastWeekday = ()-> repeatWithoutLastWeekday = ()->
repeat = {su:true,m:true,t:true,w:true,th:true,f:true,s:true} repeat = {su:true,m:true,t:true,w:true,th:true,f:true,s:true}

View File

@@ -1,7 +1,7 @@
'use strict'; 'use strict';
var shared = require('../../common/script/index.coffee'); var shared = require('../../common/script/index.coffee');
shared.i18n.translations = require('../../website/src/i18n.js').translations shared.i18n.translations = require('../../website/src/libs/i18n.js').translations
require('./test_helper'); require('./test_helper');

View File

@@ -9,7 +9,7 @@ import {v4 as generateUUID} from 'uuid';
import superagent from 'superagent'; import superagent from 'superagent';
import i18n from '../../common/script/src/i18n'; import i18n from '../../common/script/src/i18n';
require('coffee-script'); require('coffee-script');
i18n.translations = require('../../website/src/i18n.js').translations; i18n.translations = require('../../website/src/libs/i18n.js').translations;
const API_TEST_SERVER_PORT = 3003; const API_TEST_SERVER_PORT = 3003;

View File

@@ -3,7 +3,7 @@ import {each} from 'lodash';
import i18n from '../../common/script/src/i18n'; import i18n from '../../common/script/src/i18n';
require('coffee-script'); require('coffee-script');
i18n.translations = require('../../website/src/i18n.js').translations; i18n.translations = require('../../website/src/libs/i18n.js').translations;
export const STRING_ERROR_MSG = 'Error processing the string. Please see Help > Report a Bug.'; export const STRING_ERROR_MSG = 'Error processing the string. Please see Help > Report a Bug.';
export const STRING_DOES_NOT_EXIST_MSG = /^String '.*' not found.$/; export const STRING_DOES_NOT_EXIST_MSG = /^String '.*' not found.$/;

View File

@@ -30,7 +30,7 @@ describe('analytics', function() {
}); });
describe('init', function() { describe('init', function() {
var analytics = rewire('../../website/src/analytics'); var analytics = rewire('../../website/src/libs/analytics');
it('throws an error if no options are passed in', function() { it('throws an error if no options are passed in', function() {
expect(analytics).to.throw('No options provided'); expect(analytics).to.throw('No options provided');
@@ -62,7 +62,7 @@ describe('analytics', function() {
describe('track', function() { describe('track', function() {
var analyticsData, event_type; var analyticsData, event_type;
var analytics = rewire('../../website/src/analytics'); var analytics = rewire('../../website/src/libs/analytics');
var initializedAnalytics; var initializedAnalytics;
beforeEach(function() { beforeEach(function() {
@@ -370,7 +370,7 @@ describe('analytics', function() {
var purchaseData; var purchaseData;
var analytics = rewire('../../website/src/analytics'); var analytics = rewire('../../website/src/libs/analytics');
var initializedAnalytics; var initializedAnalytics;
beforeEach(function() { beforeEach(function() {

View File

@@ -5,10 +5,10 @@ var expect = chai.expect;
var Q = require('q'); var Q = require('q');
var Group = require('../../../website/src/models/group').model; var Group = require('../../../website/src/models/group').model;
var groupsController = require('../../../website/src/controllers/groups'); var groupsController = require('../../../website/src/controllers/api-v2/groups');
describe('Groups Controller', function() { describe('Groups Controller', function() {
var utils = require('../../../website/src/utils'); var utils = require('../../../website/src/libs/utils');
describe('#invite', function() { describe('#invite', function() {
var res, req, user, group; var res, req, user, group;

View File

@@ -4,7 +4,7 @@ chai.use(require("sinon-chai"))
var expect = chai.expect var expect = chai.expect
var rewire = require('rewire'); var rewire = require('rewire');
var userController = rewire('../../../website/src/controllers/user'); var userController = rewire('../../../website/src/controllers/api-v2/user');
describe('User Controller', function() { describe('User Controller', function() {
@@ -359,7 +359,7 @@ describe('User Controller', function() {
}); });
it('sends webhooks', function() { it('sends webhooks', function() {
var webhook = require('../../../website/src/webhook'); var webhook = require('../../../website/src/libs/webhook');
sinon.spy(webhook, 'sendTaskWebhook'); sinon.spy(webhook, 'sendTaskWebhook');
userController.score(req, res); userController.score(req, res);

View File

@@ -4,7 +4,7 @@ chai.use(require("sinon-chai"))
var expect = chai.expect var expect = chai.expect
var rewire = require('rewire'); var rewire = require('rewire');
var webhook = rewire('../../website/src/webhook'); var webhook = rewire('../../website/src/libs/webhook');
describe('webhooks', function() { describe('webhooks', function() {
var postSpy; var postSpy;

View File

@@ -1,16 +1,16 @@
var _ = require('lodash'); var _ = require('lodash');
var validator = require('validator'); var validator = require('validator');
var passport = require('passport'); var passport = require('passport');
var shared = require('../../../common'); var shared = require('../../../../common');
var async = require('async'); var async = require('async');
var utils = require('../utils'); var utils = require('../../libs/utils');
var nconf = require('nconf'); var nconf = require('nconf');
var request = require('request'); var request = require('request');
var FirebaseTokenGenerator = require('firebase-token-generator'); var FirebaseTokenGenerator = require('firebase-token-generator');
var User = require('../models/user').model; var User = require('../../models/user').model;
var EmailUnsubscription = require('../models/emailUnsubscription').model; var EmailUnsubscription = require('../../models/emailUnsubscription').model;
var analytics = utils.analytics; var analytics = utils.analytics;
var i18n = require('./../i18n'); var i18n = require('./../../libs/i18n');
var isProd = nconf.get('NODE_ENV') === 'production'; var isProd = nconf.get('NODE_ENV') === 'production';

View File

@@ -3,15 +3,15 @@
var _ = require('lodash'); var _ = require('lodash');
var nconf = require('nconf'); var nconf = require('nconf');
var async = require('async'); var async = require('async');
var shared = require('../../../common'); var shared = require('../../../../common');
var User = require('./../models/user').model; var User = require('./../../models/user').model;
var Group = require('./../models/group').model; var Group = require('./../../models/group').model;
var Challenge = require('./../models/challenge').model; var Challenge = require('./../../models/challenge').model;
var logging = require('./../logging'); var logging = require('./../../libs/logging');
var csv = require('express-csv'); var csv = require('express-csv');
var utils = require('../utils'); var utils = require('../../libs/utils');
var api = module.exports; var api = module.exports;
var pushNotify = require('./pushNotifications'); var pushNotify = require('./../pushNotifications');
/* /*
------------------------------------------------------------------------ ------------------------------------------------------------------------

View File

@@ -1,5 +1,5 @@
var _ = require('lodash'); var _ = require('lodash');
var Coupon = require('./../models/coupon').model; var Coupon = require('./../../models/coupon').model;
var api = module.exports; var api = module.exports;
var csv = require('express-csv'); var csv = require('express-csv');
var async = require('async'); var async = require('async');

View File

@@ -9,17 +9,17 @@ var _ = require('lodash');
var nconf = require('nconf'); var nconf = require('nconf');
var async = require('async'); var async = require('async');
var Q = require('q'); var Q = require('q');
var utils = require('./../utils'); var utils = require('./../../libs/utils');
var shared = require('../../../common'); var shared = require('../../../../common');
var User = require('./../models/user').model; var User = require('./../../models/user').model;
var Group = require('./../models/group').model; var Group = require('./../../models/group').model;
var Challenge = require('./../models/challenge').model; var Challenge = require('./../../models/challenge').model;
var EmailUnsubscription = require('./../models/emailUnsubscription').model; var EmailUnsubscription = require('./../../models/emailUnsubscription').model;
var isProd = nconf.get('NODE_ENV') === 'production'; var isProd = nconf.get('NODE_ENV') === 'production';
var api = module.exports; var api = module.exports;
var pushNotify = require('./pushNotifications'); var pushNotify = require('./../pushNotifications');
var analytics = utils.analytics; var analytics = utils.analytics;
var firebase = require('../libs/firebase'); var firebase = require('../../libs/firebase');
/* /*
------------------------------------------------------------------------ ------------------------------------------------------------------------

View File

@@ -1,9 +1,9 @@
var _ = require('lodash'); var _ = require('lodash');
var nconf = require('nconf'); var nconf = require('nconf');
var async = require('async'); var async = require('async');
var shared = require('../../../common'); var shared = require('../../../../common');
var User = require('./../models/user').model; var User = require('./../../models/user').model;
var Group = require('./../models/group').model; var Group = require('./../../models/group').model;
var api = module.exports; var api = module.exports;
api.ensureAdmin = function(req, res, next) { api.ensureAdmin = function(req, res, next) {

View File

@@ -1,13 +1,13 @@
var User = require('mongoose').model('User'); var User = require('mongoose').model('User');
var groups = require('../models/group'); var groups = require('../../models/group');
var partyFields = require('./groups').partyFields var partyFields = require('./groups').partyFields
var api = module.exports; var api = module.exports;
var async = require('async'); var async = require('async');
var _ = require('lodash'); var _ = require('lodash');
var shared = require('../../../common'); var shared = require('../../../../common');
var utils = require('../utils'); var utils = require('../../libs/utils');
var nconf = require('nconf'); var nconf = require('nconf');
var pushNotify = require('./pushNotifications'); var pushNotify = require('./../pushNotifications');
var fetchMember = function(uuid, restrict){ var fetchMember = function(uuid, restrict){
return function(cb){ return function(cb){

View File

@@ -1,7 +1,7 @@
var User = require('../models/user').model; var User = require('../../models/user').model;
var EmailUnsubscription = require('../models/emailUnsubscription').model; var EmailUnsubscription = require('../../models/emailUnsubscription').model;
var utils = require('../utils'); var utils = require('../../libs/utils');
var i18n = require('../../../common').i18n; var i18n = require('../../../../common').i18n;
var api = module.exports = {}; var api = module.exports = {};

View File

@@ -5,19 +5,19 @@ var ipn = require('paypal-ipn');
var _ = require('lodash'); var _ = require('lodash');
var nconf = require('nconf'); var nconf = require('nconf');
var async = require('async'); var async = require('async');
var shared = require('../../../common'); var shared = require('../../../../common');
var User = require('./../models/user').model; var User = require('./../../models/user').model;
var utils = require('./../utils'); var utils = require('./../../libs/utils');
var analytics = utils.analytics; var analytics = utils.analytics;
var Group = require('./../models/group').model; var Group = require('./../../models/group').model;
var Challenge = require('./../models/challenge').model; var Challenge = require('./../../models/challenge').model;
var moment = require('moment'); var moment = require('moment');
var logging = require('./../logging'); var logging = require('./../../libs/logging');
var acceptablePUTPaths; var acceptablePUTPaths;
var api = module.exports; var api = module.exports;
var qs = require('qs'); var qs = require('qs');
var firebase = require('../libs/firebase'); var firebase = require('../../libs/firebase');
var webhook = require('../webhook'); var webhook = require('../../libs/webhook');
// api.purchase // Shared.ops // api.purchase // Shared.ops
@@ -290,7 +290,7 @@ api.getUserAnonymized = function(req, res, next) {
* The trick here is to only accept leaf paths, not root/intermediate paths (see http://goo.gl/OEzkAs) * The trick here is to only accept leaf paths, not root/intermediate paths (see http://goo.gl/OEzkAs)
* FIXME - one-by-one we want to widdle down this list, instead replacing each needed set path with API operations * FIXME - one-by-one we want to widdle down this list, instead replacing each needed set path with API operations
*/ */
acceptablePUTPaths = _.reduce(require('./../models/user').schema.paths, function(m,v,leaf){ acceptablePUTPaths = _.reduce(require('./../../models/user').schema.paths, function(m,v,leaf){
var found= _.find('achievements filters flags invitations lastCron party preferences profile stats inbox'.split(' '), function(root){ var found= _.find('achievements filters flags invitations lastCron party preferences profile stats inbox'.split(' '), function(root){
return leaf.indexOf(root) == 0; return leaf.indexOf(root) == 0;
}); });

View File

@@ -2,13 +2,13 @@
var _ = require('lodash'); var _ = require('lodash');
var shared = require('../../../../common'); var shared = require('../../../../common');
var nconf = require('nconf'); var nconf = require('nconf');
var utils = require('./../../utils'); var utils = require('./../../libs/utils');
var moment = require('moment'); var moment = require('moment');
var isProduction = nconf.get("NODE_ENV") === "production"; var isProduction = nconf.get("NODE_ENV") === "production";
var stripe = require('./stripe'); var stripe = require('./stripe');
var paypal = require('./paypal'); var paypal = require('./paypal');
var amazon = require('./amazon'); var amazon = require('./amazon');
var members = require('../members') var members = require('../api-v2/members')
var async = require('async'); var async = require('async');
var iap = require('./iap'); var iap = require('./iap');
var mongoose= require('mongoose'); var mongoose= require('mongoose');

View File

@@ -5,7 +5,7 @@ var _ = require('lodash');
var url = require('url'); var url = require('url');
var User = require('mongoose').model('User'); var User = require('mongoose').model('User');
var payments = require('./index'); var payments = require('./index');
var logger = require('../../logging'); var logger = require('../../libs/logging');
var ipn = require('paypal-ipn'); var ipn = require('paypal-ipn');
var paypal = require('paypal-rest-sdk'); var paypal = require('paypal-rest-sdk');
var shared = require('../../../../common'); var shared = require('../../../../common');

View File

@@ -2,7 +2,7 @@ require('coffee-script');
require('./i18n'); require('./i18n');
var _ = require('lodash'); var _ = require('lodash');
var Content = require('../../common').content; var Content = require('../../../common').content;
var Amplitude = require('amplitude'); var Amplitude = require('amplitude');
var googleAnalytics = require('universal-analytics'); var googleAnalytics = require('universal-analytics');

View File

@@ -1,11 +1,11 @@
var fs = require('fs'), var fs = require('fs'),
path = require('path'), path = require('path'),
_ = require('lodash'), _ = require('lodash'),
User = require('./models/user').model, User = require('../models/user').model,
shared = require('../../common'), shared = require('../../../common'),
translations = {}; translations = {};
var localePath = path.join(__dirname, "/../../common/locales/") var localePath = path.join(__dirname, "/../../../common/locales/")
var loadTranslations = function(locale){ var loadTranslations = function(locale){
var files = fs.readdirSync(path.join(localePath, locale)); var files = fs.readdirSync(path.join(localePath, locale));
@@ -54,7 +54,7 @@ _.each(langCodes, function(code){
lang.momentLangCode = (momentLangsMapping[code] || code); lang.momentLangCode = (momentLangsMapping[code] || code);
try{ try{
// MomentJS lang files are JS files that has to be executed in the browser so we load them as plain text files // MomentJS lang files are JS files that has to be executed in the browser so we load them as plain text files
var f = fs.readFileSync(path.join(__dirname, '/../../node_modules/moment/locale/' + lang.momentLangCode + '.js'), 'utf8'); var f = fs.readFileSync(path.join(__dirname, '/../../../node_modules/moment/locale/' + lang.momentLangCode + '.js'), 'utf8');
momentLangs[code] = f; momentLangs[code] = f;
}catch (e){} }catch (e){}
}); });

View File

@@ -170,7 +170,7 @@ module.exports.setupConfig = function(){
nconf.argv() nconf.argv()
.env() .env()
//.file('defaults', path.join(path.resolve(__dirname, '../config.json.example'))) //.file('defaults', path.join(path.resolve(__dirname, '../config.json.example')))
.file('user', path.join(path.resolve(__dirname, './../../config.json'))); .file('user', path.join(path.resolve(__dirname, './../../../config.json')));
if (nconf.get('NODE_ENV') === "development") if (nconf.get('NODE_ENV') === "development")
Error.stackTraceLimit = Infinity; Error.stackTraceLimit = Infinity;

View File

@@ -1,4 +1,4 @@
var logging = require('../logging'); var logging = require('../libs/logging');
module.exports = function(err, req, res, next) { module.exports = function(err, req, res, next) {
//res.locals.domain.emit('error', err); //res.locals.domain.emit('error', err);

View File

@@ -1,8 +1,8 @@
var nconf = require('nconf'); var nconf = require('nconf');
var _ = require('lodash'); var _ = require('lodash');
var utils = require('../utils'); var utils = require('../libs/utils');
var shared = require('../../../common'); var shared = require('../../../common');
var i18n = require('../i18n.js'); var i18n = require('../libs/i18n');
var buildManifest = require('../libs/buildManifest'); var buildManifest = require('../libs/buildManifest');
var shared = require('../../../common'); var shared = require('../../../common');
var forceRefresh = require('./forceRefresh'); var forceRefresh = require('./forceRefresh');

View File

@@ -4,7 +4,7 @@ var User = require('./user').model;
var shared = require('../../../common'); var shared = require('../../../common');
var _ = require('lodash'); var _ = require('lodash');
var async = require('async'); var async = require('async');
var logging = require('../logging'); var logging = require('../libs/logging');
var Challenge = require('./../models/challenge').model; var Challenge = require('./../models/challenge').model;
var firebase = require('../libs/firebase'); var firebase = require('../libs/firebase');

View File

@@ -3,10 +3,10 @@ var router = new express.Router();
var _ = require('lodash'); var _ = require('lodash');
var async = require('async'); var async = require('async');
var icalendar = require('icalendar'); var icalendar = require('icalendar');
var api = require('./../controllers/user'); var api = require('./../controllers/api-v2/user');
var auth = require('./../controllers/auth'); var auth = require('./../controllers/api-v2/auth');
var logging = require('./../logging'); var logging = require('./../libs/logging');
var i18n = require('./../i18n'); var i18n = require('./../libs/i18n');
var forceRefresh = require('../middlewares/forceRefresh').middleware; var forceRefresh = require('../middlewares/forceRefresh').middleware;
/* ---------- Deprecated API ------------*/ /* ---------- Deprecated API ------------*/
@@ -170,4 +170,4 @@ router.get('*', i18n.getUserLanguage, deprecated);
router.post('*', i18n.getUserLanguage, deprecated); router.post('*', i18n.getUserLanguage, deprecated);
router.put('*', i18n.getUserLanguage, deprecated); router.put('*', i18n.getUserLanguage, deprecated);
module.exports = router; module.exports = router;

View File

@@ -1,6 +1,6 @@
var auth = require('../controllers/auth'); var auth = require('../../controllers/api-v2/auth');
var express = require('express'); var express = require('express');
var i18n = require('../i18n'); var i18n = require('../../libs/i18n');
var router = new express.Router(); var router = new express.Router();
/* auth.auth*/ /* auth.auth*/
@@ -15,8 +15,7 @@ router.post('/api/v2/user/change-username', i18n.getUserLanguage, auth.auth, aut
router.post('/api/v2/user/change-email', i18n.getUserLanguage, auth.auth, auth.changeEmail); router.post('/api/v2/user/change-email', i18n.getUserLanguage, auth.auth, auth.changeEmail);
router.post('/api/v2/user/auth/firebase', i18n.getUserLanguage, auth.auth, auth.getFirebaseToken); router.post('/api/v2/user/auth/firebase', i18n.getUserLanguage, auth.auth, auth.getFirebaseToken);
router.post('/api/v1/register', i18n.getUserLanguage, auth.registerUser); router.post('/api/v1/register', i18n.getUserLanguage, auth.registerUser);
router.post('/api/v1/user/auth/local', i18n.getUserLanguage, auth.loginLocal); router.post('/api/v1/user/auth/local', i18n.getUserLanguage, auth.loginLocal);
router.post('/api/v1/user/auth/social', i18n.getUserLanguage, auth.loginSocial); router.post('/api/v1/user/auth/social', i18n.getUserLanguage, auth.loginSocial);
module.exports = router; module.exports = router;

View File

@@ -1,9 +1,9 @@
var nconf = require('nconf'); var nconf = require('nconf');
var express = require('express'); var express = require('express');
var router = new express.Router(); var router = new express.Router();
var auth = require('../controllers/auth'); var auth = require('../../controllers/api-v2/auth');
var coupon = require('../controllers/coupon'); var coupon = require('../../controllers/api-v2/coupon');
var i18n = require('../i18n'); var i18n = require('../../libs/i18n');
router.get('/api/v2/coupons', auth.authWithUrl, i18n.getUserLanguage, coupon.ensureAdmin, coupon.getCoupons); router.get('/api/v2/coupons', auth.authWithUrl, i18n.getUserLanguage, coupon.ensureAdmin, coupon.getCoupons);
router.post('/api/v2/coupons/generate/:event', auth.auth, i18n.getUserLanguage, coupon.ensureAdmin, coupon.generateCoupons); router.post('/api/v2/coupons/generate/:event', auth.auth, i18n.getUserLanguage, coupon.ensureAdmin, coupon.generateCoupons);

View File

@@ -0,0 +1,777 @@
/*
---------- /api/v2 API ------------
see https://github.com/wordnik/swagger-node-express
Every url added to router is prefaced by /api/v2
Note: Many user-route ops exist in ../../common/script/index.coffee#user.ops, so that they can (1) be called both
client and server.
v1 user. Requires x-api-user (user id) and x-api-key (api key) headers, Test with:
$ mocha test/user.mocha.coffee
*/
require('coffee-script');
var user = require("../../controllers/api-v2/user");
var groups = require("../../controllers/api-v2/groups");
var members = require("../../controllers/api-v2/members");
var auth = require("../../controllers/api-v2/auth");
var hall = require("../../controllers/api-v2/hall");
var challenges = require("../../controllers/api-v2/challenges");
var dataexport = require("../../controllers/dataexport");
var nconf = require("nconf");
var cron = user.cron;
var _ = require('lodash');
var content = require('../../../../common').content;
var i18n = require('../../libs/i18n');
var forceRefresh = require('../../middlewares/forceRefresh').middleware;
module.exports = function(swagger, v2) {
var path = swagger.pathParam;
var body = swagger.bodyParam;
var query = swagger.queryParam;
swagger.setAppHandler(v2);
swagger.setErrorHandler("next");
swagger.setHeaders = function() {};
swagger.configureSwaggerPaths("", "/api-docs", "");
var api = {
'/status': {
spec: {
description: "Returns the status of the server (up or down). Does not require authentication."
},
action: function(req, res) {
return res.json({
status: "up"
});
}
},
'/content': {
spec: {
description: "Get all available content objects. This is essential, since Habit often depends on item keys (eg, when purchasing a weapon). Does not require authentication.",
parameters: [query("language", "Optional language to use for content's strings. Default is english.", "string")]
},
action: user.getContent
},
'/content/paths': {
spec: {
description: "Show user model tree. Does not require authentication."
},
action: user.getModelPaths
},
"/export/history": {
spec: {
description: "Export user history",
method: 'GET'
},
middleware: [auth.auth, i18n.getUserLanguage],
action: dataexport.history
},
"/user/tasks/{id}/{direction}": {
spec: {
description: "Simple scoring of a task (Habit, Daily, To-Do, or Reward). This is most-likely the only API route you'll be using as a 3rd-party developer. The most common operation is for the user to gain or lose points based on some action (browsing Reddit, running a mile, 1 Pomodor, etc). Call this route, if the task you're trying to score doesn't exist, it will be created for you. When random events occur, the <b>user._tmp</b> variable will be filled. Critical hits can be accessed through <b>user._tmp.crit</b>. The Streakbonus can be accessed through <b>user._tmp.streakBonus</b>. Both will contain the multiplier value. When random drops occur, the following values are available: <b>user._tmp.drop = {text,type,dialog,value,key,notes}</b>",
parameters: [path("id", "ID of the task to score. If this task doesn't exist, a task will be created automatically", "string"), path("direction", "Either 'up' or 'down'", "string"), body('', "If you're creating a 3rd-party task, pass up any task attributes in the body (see TaskSchema).", 'object')],
method: 'POST'
},
action: user.score
},
"/user/tasks:GET": {
spec: {
path: '/user/tasks',
description: "Get all user's tasks"
},
action: user.getTasks
},
"/user/tasks:POST": {
spec: {
path: '/user/tasks',
description: "Create a task",
method: 'POST',
parameters: [body("", "Send up the whole task (see TaskSchema)", "object")]
},
action: user.addTask
},
"/user/tasks/{id}:GET": {
spec: {
path: '/user/tasks/{id}',
description: "Get an individual task",
parameters: [path("id", "Task ID", "string")]
},
action: user.getTask
},
"/user/tasks/{id}:PUT": {
spec: {
path: '/user/tasks/{id}',
description: "Update a user's task",
method: 'PUT',
parameters: [path("id", "Task ID", "string"), body("", "Send up the whole task (see TaskSchema)", "object")]
},
action: user.updateTask
},
"/user/tasks/{id}:DELETE": {
spec: {
path: '/user/tasks/{id}',
description: "Delete a task",
method: 'DELETE',
parameters: [path("id", "Task ID", "string")]
},
action: user.deleteTask
},
"/user/tasks/{id}/sort": {
spec: {
method: 'POST',
description: 'Sort tasks',
parameters: [path("id", "Task ID", "string"), query("from", "Index where you're sorting from (0-based)", "integer"), query("to", "Index where you're sorting to (0-based)", "integer")]
},
action: user.sortTask
},
"/user/tasks/clear-completed": {
spec: {
method: 'POST',
description: "Clears competed To-Dos (needed periodically for performance)."
},
action: user.clearCompleted
},
"/user/tasks/{id}/unlink": {
spec: {
method: 'POST',
description: 'Unlink a task from its challenge',
parameters: [path("id", "Task ID", "string"), query('keep', "When unlinking a challenge task, how to handle the orphans?", 'string', ['keep', 'keep-all', 'remove', 'remove-all'])]
},
middleware: [auth.auth, i18n.getUserLanguage],
action: challenges.unlink
},
"/user/inventory/buy": {
spec: {
description: "Get a list of buyable gear"
},
action: user.getBuyList
},
"/user/inventory/buy/{key}": {
spec: {
method: 'POST',
description: "Buy a gear piece and equip it automatically",
parameters: [path('key', "The key of the item to buy (call /content route for available keys)", 'string', _.keys(content.gear.flat))]
},
action: user.buy
},
"/user/inventory/sell/{type}/{key}": {
spec: {
method: 'POST',
description: "Sell inventory items back to Alexander",
parameters: [path('type', "The type of object you're selling back.", 'string', ['eggs', 'hatchingPotions', 'food']), path('key', "The object key you're selling back (call /content route for available keys)", 'string')]
},
action: user.sell
},
"/user/inventory/purchase/{type}/{key}": {
spec: {
method: 'POST',
description: "Purchase a Gem-purchasable item from Alexander",
parameters: [path('type', "The type of object you're purchasing.", 'string', ['eggs', 'hatchingPotions', 'food', 'quests', 'special']), path('key', "The object key you're purchasing (call /content route for available keys)", 'string')]
},
action: user.purchase
},
"/user/inventory/hourglass/{type}/{key}": {
spec: {
method: 'POST',
description: "Purchase a pet or mount using a Mystic Hourglass",
parameters: [path('type', "The type of object you're purchasing.", 'string', ['pets', 'mounts']), path('key', "The object key you're purchasing (call /content route for available keys)", 'string')]
},
action: user.hourglassPurchase
},
"/user/inventory/mystery/{key}": {
spec: {
method: 'POST',
description: "Purchase a Mystery Item Set using a Mystic Hourglass",
parameters: [path('key', "The key for the Mystery Set you're purchasing (call /content route for available keys)", 'string')]
},
action: user.buyMysterySet
},
"/user/inventory/feed/{pet}/{food}": {
spec: {
method: 'POST',
description: "Feed your pet some food",
parameters: [path('pet', "The key of the pet you're feeding", 'string', _.keys(content.pets)), path('food', "The key of the food to feed your pet", 'string', _.keys(content.food))]
},
action: user.feed
},
"/user/inventory/equip/{type}/{key}": {
spec: {
method: 'POST',
description: "Equip an item (either pet, mount, equipped or costume)",
parameters: [path('type', "Type to equip", 'string', ['pet', 'mount', 'equipped', 'costume']), path('key', "The object key you're equipping (call /content route for available keys)", 'string')]
},
action: user.equip
},
"/user/inventory/hatch/{egg}/{hatchingPotion}": {
spec: {
method: 'POST',
description: "Pour a hatching potion on an egg",
parameters: [path('egg', "The egg key to hatch", 'string', _.keys(content.eggs)), path('hatchingPotion', "The hatching potion to pour", 'string', _.keys(content.hatchingPotions))]
},
action: user.hatch
},
"/user:GET": {
spec: {
path: '/user',
description: "Get the full user object"
},
action: user.getUser
},
"/user/anonymized": {
spec: {
description: "Get the user object without any personal data"
},
action: user.getUserAnonymized
},
"/user:PUT": {
spec: {
path: '/user',
method: 'PUT',
description: "Update the user object (only certain attributes are supported)",
parameters: [body('', 'The user object (see UserSchema)', 'object')]
},
action: user.update
},
"/user:DELETE": {
spec: {
path: '/user',
method: 'DELETE',
description: "Delete a user object entirely, USE WITH CAUTION!"
},
middleware: [auth.auth, i18n.getUserLanguage],
action: user["delete"]
},
"/user/revive": {
spec: {
method: 'POST',
description: "Revive your dead user"
},
action: user.revive
},
"/user/reroll": {
spec: {
method: 'POST',
description: 'Drink the Fortify Potion (Note, it used to be called re-roll)'
},
action: user.reroll
},
"/user/reset": {
spec: {
method: 'POST',
description: "Completely reset your account"
},
action: user.reset
},
"/user/sleep": {
spec: {
method: 'POST',
description: "Toggle whether you're resting in the inn"
},
action: user.sleep
},
"/user/rebirth": {
spec: {
method: 'POST',
description: "Rebirth your avatar"
},
action: user.rebirth
},
"/user/class/change": {
spec: {
method: 'POST',
description: "Either remove your avatar's class, or change it to something new",
parameters: [query('class', "The key of the class to change to. If not provided, user's class is removed.", 'string', ['warrior', 'healer', 'rogue', 'wizard', ''])]
},
action: user.changeClass
},
"/user/class/allocate": {
spec: {
method: 'POST',
description: "Allocate one point towards an attribute",
parameters: [query('stat', 'The stat to allocate towards', 'string', ['str', 'per', 'int', 'con'])]
},
action: user.allocate
},
"/user/class/cast/{spell}": {
spec: {
method: 'POST',
description: "Casts a spell on a target.",
parameters: [path('spell', "The key of the spell to cast (see ../../common#content/index.coffee)", 'string'), query('targetType', "The type of object you're targeting", 'string', ['party', 'self', 'user', 'task']), query('targetId', "The ID of the object you're targeting", 'string')]
},
action: user.cast
},
"/user/unlock": {
spec: {
method: 'POST',
description: "Unlock a certain gem-purchaseable path (or multiple paths)",
parameters: [query('path', "The path to unlock, such as hair.green or shirts.red,shirts.blue", 'string')]
},
action: user.unlock
},
"/user/batch-update": {
spec: {
method: 'POST',
description: "This is an advanced route which is useful for apps which might for example need offline support. You can send a whole batch of user-based operations, which allows you to queue them up offline and send them all at once. The format is {op:'nameOfOperation',parameters:{},body:{},query:{}}",
parameters: [body('', 'The array of batch-operations to perform', 'object')]
},
middleware: [forceRefresh, auth.auth, i18n.getUserLanguage, cron, user.sessionPartyInvite],
action: user.batchUpdate
},
"/user/tags/{id}:GET": {
spec: {
path: '/user/tags/{id}',
method: 'GET',
description: "Get a tag",
parameters: [path('id', 'The id of the tag to get', 'string')]
},
action: user.getTag
},
"/user/tags:POST": {
spec: {
path: "/user/tags",
method: 'POST',
description: 'Create a new tag',
parameters: [body('', 'New tag (see UserSchema.tags)', 'object')]
},
action: user.addTag
},
"/user/tags:GET": {
spec: {
path: "/user/tags",
method: 'GET',
description: 'List all of a user\'s tags'
},
action: user.getTags
},
"/user/tags/sort": {
spec: {
method: 'POST',
description: 'Sort tags',
parameters: [query("from", "Index where you're sorting from (0-based)", "integer"), query("to", "Index where you're sorting to (0-based)", "integer")]
},
action: user.sortTag
},
"/user/tags/{id}:PUT": {
spec: {
path: '/user/tags/{id}',
method: 'PUT',
description: "Edit a tag",
parameters: [path('id', 'The id of the tag to edit', 'string'), body('', 'Tag edits (see UserSchema.tags)', 'object')]
},
action: user.updateTag
},
"/user/tags/{id}:DELETE": {
spec: {
path: '/user/tags/{id}',
method: 'DELETE',
description: 'Delete a tag',
parameters: [path('id', 'Id of tag to delete', 'string')]
},
action: user.deleteTag
},
"/user/webhooks": {
spec: {
method: 'POST',
description: 'Create a new webhook',
parameters: [body('', 'New Webhook {url:"webhook endpoint (required)", id:"id of webhook (shared.uuid(), optional)", enabled:"whether webhook is enabled (true by default, optional)"}', 'object')]
},
action: user.addWebhook
},
"/user/webhooks/{id}:PUT": {
spec: {
path: '/user/webhooks/{id}',
method: 'PUT',
description: "Edit a webhook",
parameters: [path('id', 'The id of the webhook to edit', 'string'), body('', 'New Webhook {url:"webhook endpoint (required)", id:"id of webhook (shared.uuid(), optional)", enabled:"whether webhook is enabled (true by default, optional)"}', 'object')]
},
action: user.updateWebhook
},
"/user/webhooks/{id}:DELETE": {
spec: {
path: '/user/webhooks/{id}',
method: 'DELETE',
description: 'Delete a webhook',
parameters: [path('id', 'Id of webhook to delete', 'string')]
},
action: user.deleteWebhook
},
"/user/pushDevice": {
spec: {
method: 'POST',
description: 'Add a new push devices registration ID',
parameters: [body('', 'New push registration { regId: "123123", type: "android"}', 'object')]
},
action: user.addPushDevice
},
"/groups:GET": {
spec: {
path: '/groups',
description: "Get a list of groups",
parameters: [query('type', "Comma-separated types of groups to return, eg 'party,guilds,public,tavern'", 'string')]
},
middleware: [auth.auth, i18n.getUserLanguage],
action: groups.list
},
"/groups:POST": {
spec: {
path: '/groups',
method: 'POST',
description: 'Create a group',
parameters: [body('', 'Group object (see GroupSchema)', 'object')]
},
middleware: [auth.auth, i18n.getUserLanguage],
action: groups.create
},
"/groups/{gid}:GET": {
spec: {
path: '/groups/{gid}',
description: "Get a group. The party the user currently is in can be accessed with the gid 'party'.",
parameters: [path('gid', 'Group ID', 'string')]
},
middleware: [auth.auth, i18n.getUserLanguage],
action: groups.get
},
"/groups/{gid}:POST": {
spec: {
path: '/groups/{gid}',
method: 'POST',
description: "Edit a group",
parameters: [body('', 'Group object (see GroupSchema)', 'object')]
},
middleware: [auth.auth, i18n.getUserLanguage, groups.attachGroup],
action: groups.update
},
"/groups/{gid}/join": {
spec: {
method: 'POST',
description: 'Join a group',
parameters: [path('gid', 'Id of the group to join', 'string')]
},
middleware: [auth.auth, i18n.getUserLanguage, groups.attachGroup],
action: groups.join
},
"/groups/{gid}/leave": {
spec: {
method: 'POST',
description: 'Leave a group',
parameters: [path('gid', 'ID of the group to leave', 'string')]
},
middleware: [auth.auth, i18n.getUserLanguage, groups.attachGroup],
action: groups.leave
},
"/groups/{gid}/invite": {
spec: {
method: 'POST',
description: "Invite a user to a group",
parameters: [path('gid', 'Group id', 'string'), body('', 'a payload of invites either under body.uuids or body.emails, only one of them!', 'object')]
},
middleware: [auth.auth, i18n.getUserLanguage, groups.attachGroup],
action: groups.invite
},
"/groups/{gid}/removeMember": {
spec: {
method: 'POST',
description: "Remove / boot a member from a group",
parameters: [path('gid', 'Group id', 'string'), query('uuid', 'User id to boot', 'string')]
},
middleware: [auth.auth, i18n.getUserLanguage, groups.attachGroup],
action: groups.removeMember
},
"/groups/{gid}/questAccept": {
spec: {
method: 'POST',
description: "Accept a quest invitation",
parameters: [path('gid', "Group id", 'string'), query('key', "optional. if provided, trigger new invite, if not, accept existing invite", 'string')]
},
middleware: [auth.auth, i18n.getUserLanguage, groups.attachGroup],
action: groups.questAccept
},
"/groups/{gid}/questReject": {
spec: {
method: 'POST',
description: 'Reject quest invitation',
parameters: [path('gid', 'Group id', 'string')]
},
middleware: [auth.auth, i18n.getUserLanguage, groups.attachGroup],
action: groups.questReject
},
"/groups/{gid}/questCancel": {
spec: {
method: 'POST',
description: 'Cancel quest before it starts (in invitation stage)',
parameters: [path('gid', 'Group to cancel quest in', 'string')]
},
middleware: [auth.auth, i18n.getUserLanguage, groups.attachGroup],
action: groups.questCancel
},
"/groups/{gid}/questAbort": {
spec: {
method: 'POST',
description: 'Abort quest after it has started (all progress will be lost)',
parameters: [path('gid', 'Group to abort quest in', 'string')]
},
middleware: [auth.auth, i18n.getUserLanguage, groups.attachGroup],
action: groups.questAbort
},
"/groups/{gid}/questLeave": {
spec: {
method: 'POST',
description: 'Leave an active quest (Quest leaders cannot leave active quests. They must abort the quest to leave)',
parameters: [path('gid', 'Group to leave quest in', 'string')]
},
middleware: [auth.auth, i18n.getUserLanguage, groups.attachGroup],
action: groups.questLeave
},
"/groups/{gid}/chat:GET": {
spec: {
path: "/groups/{gid}/chat",
description: "Get all chat messages",
parameters: [path('gid', 'Group to return the chat from ', 'string')]
},
middleware: [auth.auth, i18n.getUserLanguage, groups.attachGroup],
action: groups.getChat
},
"/groups/{gid}/chat:POST": {
spec: {
method: 'POST',
path: "/groups/{gid}/chat",
description: "Send a chat message",
parameters: [query('message', 'Chat message', 'string'), path('gid', 'Group id', 'string')]
},
middleware: [auth.auth, i18n.getUserLanguage, groups.attachGroup],
action: groups.postChat
},
"/groups/{gid}/chat/seen": {
spec: {
method: 'POST',
description: "Flag chat messages for a particular group as seen",
parameters: [path('gid', 'Group id', 'string')]
},
action: groups.seenMessage
},
"/groups/{gid}/chat/{messageId}": {
spec: {
method: 'DELETE',
description: 'Delete a chat message in a given group',
parameters: [path('gid', 'ID of the group containing the message to be deleted', 'string'), path('messageId', 'ID of message to be deleted', 'string')]
},
middleware: [auth.auth, i18n.getUserLanguage, groups.attachGroup],
action: groups.deleteChatMessage
},
"/groups/{gid}/chat/{mid}/like": {
spec: {
method: 'POST',
description: "Like a chat message",
parameters: [path('gid', 'Group id', 'string'), path('mid', 'Message id', 'string')]
},
middleware: [auth.auth, i18n.getUserLanguage, groups.attachGroup],
action: groups.likeChatMessage
},
"/groups/{gid}/chat/{mid}/flag": {
spec: {
method: 'POST',
description: "Flag a chat message",
parameters: [path('gid', 'Group id', 'string'), path('mid', 'Message id', 'string')]
},
middleware: [auth.auth, i18n.getUserLanguage, groups.attachGroup],
action: groups.flagChatMessage
},
"/groups/{gid}/chat/{mid}/clearflags": {
spec: {
method: 'POST',
description: "Clear flag count from message and unhide it",
parameters: [path('gid', 'Group id', 'string'), path('mid', 'Message id', 'string')]
},
middleware: [auth.auth, i18n.getUserLanguage, groups.attachGroup],
action: groups.clearFlagCount
},
"/members/{uuid}:GET": {
spec: {
path: '/members/{uuid}',
description: "Get a member.",
parameters: [path('uuid', 'Member ID', 'string')]
},
middleware: [i18n.getUserLanguage],
action: members.getMember
},
"/members/{uuid}/message": {
spec: {
method: 'POST',
description: 'Send a private message to a member',
parameters: [path('uuid', 'The UUID of the member to message', 'string'), body('', '{"message": "The private message to send"}', 'object')]
},
middleware: [auth.auth],
action: members.sendPrivateMessage
},
"/members/{uuid}/block": {
spec: {
method: 'POST',
description: 'Block a member from sending private messages',
parameters: [path('uuid', 'The UUID of the member to message', 'string')]
},
middleware: [auth.auth],
action: user.blockUser
},
"/members/{uuid}/gift": {
spec: {
method: 'POST',
description: 'Send a gift to a member',
parameters: [path('uuid', 'The UUID of the member', 'string'), body('', '{"type": "gems or subscription", "gems":{"amount":Number, "fromBalance":Boolean}, "subscription":{"months":Number}}', 'object')]
},
middleware: [auth.auth],
action: members.sendGift
},
"/hall/heroes": {
spec: {},
middleware: [auth.auth, i18n.getUserLanguage],
action: hall.getHeroes
},
"/hall/heroes/{uid}:GET": {
spec: {
path: "/hall/heroes/{uid}"
},
middleware: [auth.auth, i18n.getUserLanguage, hall.ensureAdmin],
action: hall.getHero
},
"/hall/heroes/{uid}:POST": {
spec: {
method: 'POST',
path: "/hall/heroes/{uid}"
},
middleware: [auth.auth, i18n.getUserLanguage, hall.ensureAdmin],
action: hall.updateHero
},
"/hall/patrons": {
spec: {
parameters: [query('page', 'Page number to fetch (this list is long)', 'string')]
},
middleware: [auth.auth, i18n.getUserLanguage],
action: hall.getPatrons
},
"/challenges:GET": {
spec: {
path: '/challenges',
description: "Get a list of challenges"
},
middleware: [auth.auth, i18n.getUserLanguage],
action: challenges.list
},
"/challenges:POST": {
spec: {
path: '/challenges',
method: 'POST',
description: "Create a challenge",
parameters: [body('', 'Challenge object (see ChallengeSchema)', 'object')]
},
middleware: [auth.auth, i18n.getUserLanguage],
action: challenges.create
},
"/challenges/{cid}:GET": {
spec: {
path: '/challenges/{cid}',
description: 'Get a challenge',
parameters: [path('cid', 'Challenge id', 'string')]
},
middleware: [auth.auth, i18n.getUserLanguage],
action: challenges.get
},
"/challenges/{cid}/csv": {
spec: {
description: 'Get a challenge (csv format)',
parameters: [path('cid', 'Challenge id', 'string')]
},
action: challenges.csv
},
"/challenges/{cid}:POST": {
spec: {
path: '/challenges/{cid}',
method: 'POST',
description: "Update a challenge",
parameters: [path('cid', 'Challenge id', 'string'), body('', 'Challenge object (see ChallengeSchema)', 'object')]
},
middleware: [auth.auth, i18n.getUserLanguage],
action: challenges.update
},
"/challenges/{cid}:DELETE": {
spec: {
path: '/challenges/{cid}',
method: 'DELETE',
description: "Delete a challenge",
parameters: [path('cid', 'Challenge id', 'string')]
},
middleware: [auth.auth, i18n.getUserLanguage],
action: challenges["delete"]
},
"/challenges/{cid}/close": {
spec: {
method: 'POST',
description: 'Close a challenge',
parameters: [path('cid', 'Challenge id', 'string'), query('uid', 'User ID of the winner', 'string', true)]
},
middleware: [auth.auth, i18n.getUserLanguage],
action: challenges.selectWinner
},
"/challenges/{cid}/join": {
spec: {
method: 'POST',
description: "Join a challenge",
parameters: [path('cid', 'Challenge id', 'string')]
},
middleware: [auth.auth, i18n.getUserLanguage],
action: challenges.join
},
"/challenges/{cid}/leave": {
spec: {
method: 'POST',
description: 'Leave a challenge',
parameters: [path('cid', 'Challenge id', 'string')]
},
middleware: [auth.auth, i18n.getUserLanguage],
action: challenges.leave
},
"/challenges/{cid}/member/{uid}": {
spec: {
description: "Get a member's progress in a particular challenge",
parameters: [path('cid', 'Challenge id', 'string'), path('uid', 'User id', 'string')]
},
middleware: [auth.auth, i18n.getUserLanguage],
action: challenges.getMember
}
};
if (nconf.get("NODE_ENV") === "development") {
api["/user/addTenGems"] = {
spec: {
method: 'POST'
},
action: user.addTenGems
};
api["/user/addHourglass"] = {
spec: {
method: 'POST'
},
action: user.addHourglass
};
};
_.each(api, function(route, path) {
var base;
if ((base = route.spec).description == null) {
base.description = '';
}
_.defaults(route.spec, {
path: path,
nickname: path,
notes: route.spec.description,
summary: route.spec.description,
parameters: [],
errorResponses: [],
method: 'GET'
});
if (route.middleware == null) {
route.middleware = path.indexOf('/user') === 0 ? [auth.auth, i18n.getUserLanguage, cron] : [i18n.getUserLanguage];
}
swagger["add" + route.spec.method](route);
return true;
});
return swagger.configure((nconf.get('BASE_URL')) + "/api/v2", "2");
};

View File

@@ -1,7 +1,7 @@
var express = require('express'); var express = require('express');
var router = new express.Router(); var router = new express.Router();
var i18n = require('../i18n'); var i18n = require('../../libs/i18n');
var unsubscription = require('../controllers/unsubscription'); var unsubscription = require('../../controllers/api-v2/unsubscription');
router.get('/unsubscribe', i18n.getUserLanguage, unsubscription.unsubscribe); router.get('/unsubscribe', i18n.getUserLanguage, unsubscription.unsubscribe);

View File

@@ -1,853 +0,0 @@
###
---------- /api/v2 API ------------
see https://github.com/wordnik/swagger-node-express
Every url added to router is prefaced by /api/v2
Note: Many user-route ops exist in ../../common/script/index.coffee#user.ops, so that they can (1) be called both
client and server.
v1 user. Requires x-api-user (user id) and x-api-key (api key) headers, Test with:
$ mocha test/user.mocha.coffee
###
user = require("../controllers/user")
groups = require("../controllers/groups")
members = require("../controllers/members")
auth = require("../controllers/auth")
hall = require("../controllers/hall")
challenges = require("../controllers/challenges")
dataexport = require("../controllers/dataexport")
nconf = require("nconf")
cron = user.cron
_ = require('lodash')
content = require('../../../common').content
i18n = require('../i18n')
forceRefresh = require('../middlewares/forceRefresh').middleware
module.exports = (swagger, v2) ->
[path,body,query] = [swagger.pathParam, swagger.bodyParam, swagger.queryParam]
swagger.setAppHandler(v2)
swagger.setErrorHandler("next")
swagger.setHeaders = -> #disable setHeaders, since we have our own thing going on in middleware.js (and which requires `req`, which swagger doesn't pass in)
swagger.configureSwaggerPaths("", "/api-docs", "")
api =
'/status':
spec:
description: "Returns the status of the server (up or down). Does not require authentication."
action: (req, res) ->
res.json status: "up"
'/content':
spec:
description: "Get all available content objects. This is essential, since Habit often depends on item keys (eg, when purchasing a weapon). Does not require authentication."
parameters: [
query("language","Optional language to use for content's strings. Default is english.","string")
]
action: user.getContent
'/content/paths':
spec:
description: "Show user model tree. Does not require authentication."
action: user.getModelPaths
"/export/history":
spec:
description: "Export user history"
method: 'GET'
middleware: [auth.auth, i18n.getUserLanguage]
action: dataexport.history #[todo] encode data output options in the data controller and use these to build routes
# ---------------------------------
# User
# ---------------------------------
# Scoring
"/user/tasks/{id}/{direction}":
spec:
#notes: "Simple scoring of a task."
description: "Simple scoring of a task (Habit, Daily, To-Do, or Reward). This is most-likely the only API route you'll be using as a 3rd-party developer. The most common operation is for the user to gain or lose points based on some action (browsing Reddit, running a mile, 1 Pomodor, etc). Call this route, if the task you're trying to score doesn't exist, it will be created for you. When random events occur, the <b>user._tmp</b> variable will be filled. Critical hits can be accessed through <b>user._tmp.crit</b>. The Streakbonus can be accessed through <b>user._tmp.streakBonus</b>. Both will contain the multiplier value. When random drops occur, the following values are available: <b>user._tmp.drop = {text,type,dialog,value,key,notes}</b>"
parameters: [
path("id", "ID of the task to score. If this task doesn't exist, a task will be created automatically", "string")
path("direction", "Either 'up' or 'down'", "string")
body '',"If you're creating a 3rd-party task, pass up any task attributes in the body (see TaskSchema).",'object'
]
method: 'POST'
action: user.score
# Tasks
"/user/tasks:GET":
spec:
path: '/user/tasks'
description: "Get all user's tasks"
action: user.getTasks
"/user/tasks:POST":
spec:
path: '/user/tasks'
description: "Create a task"
method: 'POST'
parameters: [ body "","Send up the whole task (see TaskSchema)","object" ]
action: user.addTask
"/user/tasks/{id}:GET":
spec:
path: '/user/tasks/{id}'
description: "Get an individual task"
parameters: [
path("id", "Task ID", "string")
]
action: user.getTask
"/user/tasks/{id}:PUT":
spec:
path: '/user/tasks/{id}'
description: "Update a user's task"
method: 'PUT'
parameters: [
path "id", "Task ID", "string"
body "","Send up the whole task (see TaskSchema)","object"
]
action: user.updateTask
"/user/tasks/{id}:DELETE":
spec:
path: '/user/tasks/{id}'
description: "Delete a task"
method: 'DELETE'
parameters: [ path("id", "Task ID", "string") ]
action: user.deleteTask
"/user/tasks/{id}/sort":
spec:
method: 'POST'
description: 'Sort tasks'
parameters: [
path("id", "Task ID", "string")
query("from","Index where you're sorting from (0-based)","integer")
query("to","Index where you're sorting to (0-based)","integer")
]
action: user.sortTask
"/user/tasks/clear-completed":
spec:
method: 'POST'
description: "Clears competed To-Dos (needed periodically for performance)."
action: user.clearCompleted
"/user/tasks/{id}/unlink":
spec:
method: 'POST'
description: 'Unlink a task from its challenge'
parameters: [
path("id", "Task ID", "string")
query 'keep',"When unlinking a challenge task, how to handle the orphans?",'string',['keep','keep-all','remove','remove-all']
]
middleware: [auth.auth, i18n.getUserLanguage] ## removing cron since they may want to remove task first
action: challenges.unlink
# Inventory
"/user/inventory/buy":
spec:
description: "Get a list of buyable gear"
action: user.getBuyList
"/user/inventory/buy/{key}":
spec:
method: 'POST'
description: "Buy a gear piece and equip it automatically"
parameters:[
path 'key',"The key of the item to buy (call /content route for available keys)",'string', _.keys(content.gear.flat)
]
action: user.buy
"/user/inventory/sell/{type}/{key}":
spec:
method: 'POST'
description: "Sell inventory items back to Alexander"
parameters: [
#TODO verify these are the correct types
path('type',"The type of object you're selling back.",'string',['eggs','hatchingPotions','food'])
path('key',"The object key you're selling back (call /content route for available keys)",'string')
]
action: user.sell
"/user/inventory/purchase/{type}/{key}":
spec:
method: 'POST'
description: "Purchase a Gem-purchasable item from Alexander"
parameters:[
path('type',"The type of object you're purchasing.",'string',['eggs','hatchingPotions','food','quests','special'])
path('key',"The object key you're purchasing (call /content route for available keys)",'string')
]
action: user.purchase
"/user/inventory/hourglass/{type}/{key}":
spec:
method: 'POST'
description: "Purchase a pet or mount using a Mystic Hourglass"
parameters:[
path('type',"The type of object you're purchasing.",'string',['pets','mounts'])
path('key',"The object key you're purchasing (call /content route for available keys)",'string')
]
action: user.hourglassPurchase
"/user/inventory/mystery/{key}":
spec:
method: 'POST'
description: "Purchase a Mystery Item Set using a Mystic Hourglass"
parameters:[
path('key',"The key for the Mystery Set you're purchasing (call /content route for available keys)",'string')
]
action: user.buyMysterySet
"/user/inventory/feed/{pet}/{food}":
spec:
method: 'POST'
description: "Feed your pet some food"
parameters: [
path 'pet',"The key of the pet you're feeding",'string',_.keys(content.pets)
path 'food',"The key of the food to feed your pet",'string',_.keys(content.food)
]
action: user.feed
"/user/inventory/equip/{type}/{key}":
spec:
method: 'POST'
description: "Equip an item (either pet, mount, equipped or costume)"
parameters: [
path 'type',"Type to equip",'string',['pet','mount','equipped', 'costume']
path 'key',"The object key you're equipping (call /content route for available keys)",'string'
]
action: user.equip
"/user/inventory/hatch/{egg}/{hatchingPotion}":
spec:
method: 'POST'
description: "Pour a hatching potion on an egg"
parameters: [
path 'egg',"The egg key to hatch",'string',_.keys(content.eggs)
path 'hatchingPotion',"The hatching potion to pour",'string',_.keys(content.hatchingPotions)
]
action: user.hatch
# User
"/user:GET":
spec:
path: '/user'
description: "Get the full user object"
action: user.getUser
"/user/anonymized":
spec:
description: "Get the user object without any personal data"
action: user.getUserAnonymized
"/user:PUT":
spec:
path: '/user'
method: 'PUT'
description: "Update the user object (only certain attributes are supported)"
parameters: [
body '','The user object (see UserSchema)','object'
]
action: user.update
"/user:DELETE":
spec:
path: '/user'
method: 'DELETE'
description: "Delete a user object entirely, USE WITH CAUTION!"
middleware: [auth.auth, i18n.getUserLanguage]
action: user.delete
"/user/revive":
spec:
method: 'POST'
description: "Revive your dead user"
action: user.revive
"/user/reroll":
spec:
method: 'POST'
description: 'Drink the Fortify Potion (Note, it used to be called re-roll)'
action: user.reroll
"/user/reset":
spec:
method: 'POST'
description: "Completely reset your account"
action: user.reset
"/user/sleep":
spec:
method: 'POST'
description: "Toggle whether you're resting in the inn"
action: user.sleep
"/user/rebirth":
spec:
method: 'POST'
description: "Rebirth your avatar"
action: user.rebirth
"/user/class/change":
spec:
method: 'POST'
description: "Either remove your avatar's class, or change it to something new"
parameters: [
query 'class',"The key of the class to change to. If not provided, user's class is removed.",'string',['warrior','healer','rogue','wizard','']
]
action: user.changeClass
"/user/class/allocate":
spec:
method: 'POST'
description: "Allocate one point towards an attribute"
parameters: [
query 'stat','The stat to allocate towards','string',['str','per','int','con']
]
action:user.allocate
"/user/class/cast/{spell}":
spec:
method: 'POST'
description: "Casts a spell on a target."
parameters: [
path 'spell',"The key of the spell to cast (see ../../common#content/index.coffee)",'string'
query 'targetType',"The type of object you're targeting",'string',['party','self','user','task']
query 'targetId',"The ID of the object you're targeting",'string'
]
action: user.cast
"/user/unlock":
spec:
method: 'POST'
description: "Unlock a certain gem-purchaseable path (or multiple paths)"
parameters: [
query 'path',"The path to unlock, such as hair.green or shirts.red,shirts.blue",'string'
]
action: user.unlock
"/user/batch-update":
spec:
method: 'POST'
description: "This is an advanced route which is useful for apps which might for example need offline support. You can send a whole batch of user-based operations, which allows you to queue them up offline and send them all at once. The format is {op:'nameOfOperation',parameters:{},body:{},query:{}}"
parameters:[
body '','The array of batch-operations to perform','object'
]
middleware: [forceRefresh, auth.auth, i18n.getUserLanguage, cron, user.sessionPartyInvite]
action: user.batchUpdate
# Tags
"/user/tags/{id}:GET":
spec:
path: '/user/tags/{id}'
method: 'GET'
description: "Get a tag"
parameters: [
path 'id','The id of the tag to get','string'
]
action: user.getTag
"/user/tags:POST":
spec:
path: "/user/tags"
method: 'POST'
description: 'Create a new tag'
parameters: [
body '','New tag (see UserSchema.tags)','object'
]
action: user.addTag
"/user/tags:GET":
spec:
path: "/user/tags"
method: 'GET'
description: 'List all of a user\'s tags'
action: user.getTags
"/user/tags/sort":
spec:
method: 'POST'
description: 'Sort tags'
parameters: [
query("from","Index where you're sorting from (0-based)","integer")
query("to","Index where you're sorting to (0-based)","integer")
]
action: user.sortTag
"/user/tags/{id}:PUT":
spec:
path: '/user/tags/{id}'
method: 'PUT'
description: "Edit a tag"
parameters: [
path 'id','The id of the tag to edit','string'
body '','Tag edits (see UserSchema.tags)','object'
]
action: user.updateTag
"/user/tags/{id}:DELETE":
spec:
path: '/user/tags/{id}'
method: 'DELETE'
description: 'Delete a tag'
parameters: [
path 'id','Id of tag to delete','string'
]
action: user.deleteTag
# Webhooks
"/user/webhooks":
spec:
method: 'POST'
description: 'Create a new webhook'
parameters: [
body '','New Webhook {url:"webhook endpoint (required)", id:"id of webhook (shared.uuid(), optional)", enabled:"whether webhook is enabled (true by default, optional)"}','object'
]
action: user.addWebhook
"/user/webhooks/{id}:PUT":
spec:
path: '/user/webhooks/{id}'
method: 'PUT'
description: "Edit a webhook"
parameters: [
path 'id','The id of the webhook to edit','string'
body '','New Webhook {url:"webhook endpoint (required)", id:"id of webhook (shared.uuid(), optional)", enabled:"whether webhook is enabled (true by default, optional)"}','object'
]
action: user.updateWebhook
"/user/webhooks/{id}:DELETE":
spec:
path: '/user/webhooks/{id}'
method: 'DELETE'
description: 'Delete a webhook'
parameters: [
path 'id','Id of webhook to delete','string'
]
action: user.deleteWebhook
# Push Notifications
"/user/pushDevice":
spec:
method: 'POST'
description: 'Add a new push devices registration ID'
parameters: [
body '','New push registration { regId: "123123", type: "android"}','object'
]
action: user.addPushDevice
# ---------------------------------
# Groups
# ---------------------------------
"/groups:GET":
spec:
path: '/groups'
description: "Get a list of groups"
parameters: [
query 'type',"Comma-separated types of groups to return, eg 'party,guilds,public,tavern'",'string'
]
middleware: [auth.auth, i18n.getUserLanguage]
action: groups.list
"/groups:POST":
spec:
path: '/groups'
method: 'POST'
description: 'Create a group'
parameters: [
body '','Group object (see GroupSchema)','object'
]
middleware: [auth.auth, i18n.getUserLanguage]
action: groups.create
"/groups/{gid}:GET":
spec:
path: '/groups/{gid}'
description: "Get a group. The party the user currently is in can be accessed with the gid 'party'."
parameters: [path('gid','Group ID','string')]
middleware: [auth.auth, i18n.getUserLanguage]
action: groups.get
"/groups/{gid}:POST":
spec:
path: '/groups/{gid}'
method: 'POST'
description: "Edit a group"
parameters: [body('','Group object (see GroupSchema)','object')]
middleware: [auth.auth, i18n.getUserLanguage, groups.attachGroup]
action: groups.update
"/groups/{gid}/join":
spec:
method: 'POST'
description: 'Join a group'
parameters: [path('gid','Id of the group to join','string')]
middleware: [auth.auth, i18n.getUserLanguage, groups.attachGroup]
action: groups.join
"/groups/{gid}/leave":
spec:
method: 'POST'
description: 'Leave a group'
parameters: [path('gid','ID of the group to leave','string')]
middleware: [auth.auth, i18n.getUserLanguage, groups.attachGroup]
action: groups.leave
"/groups/{gid}/invite":
spec:
method: 'POST'
description: "Invite a user to a group"
parameters: [
path 'gid','Group id','string'
body '','a payload of invites either under body.uuids or body.emails, only one of them!','object'
]
middleware: [auth.auth, i18n.getUserLanguage, groups.attachGroup]
action:groups.invite
"/groups/{gid}/removeMember":
spec:
method: 'POST'
description: "Remove / boot a member from a group"
parameters: [
path 'gid','Group id','string'
query 'uuid','User id to boot','string'
]
middleware: [auth.auth, i18n.getUserLanguage, groups.attachGroup]
action:groups.removeMember
"/groups/{gid}/questAccept":
spec:
method: 'POST'
description: "Accept a quest invitation"
parameters: [
path 'gid',"Group id",'string'
query 'key',"optional. if provided, trigger new invite, if not, accept existing invite",'string'
]
middleware: [auth.auth, i18n.getUserLanguage, groups.attachGroup]
action:groups.questAccept
"/groups/{gid}/questReject":
spec:
method: 'POST'
description: 'Reject quest invitation'
parameters: [
path 'gid','Group id','string'
]
middleware: [auth.auth, i18n.getUserLanguage, groups.attachGroup]
action: groups.questReject
"/groups/{gid}/questCancel":
spec:
method: 'POST'
description: 'Cancel quest before it starts (in invitation stage)'
parameters: [path('gid','Group to cancel quest in','string')]
middleware: [auth.auth, i18n.getUserLanguage, groups.attachGroup]
action: groups.questCancel
"/groups/{gid}/questAbort":
spec:
method: 'POST'
description: 'Abort quest after it has started (all progress will be lost)'
parameters: [path('gid','Group to abort quest in','string')]
middleware: [auth.auth, i18n.getUserLanguage, groups.attachGroup]
action: groups.questAbort
"/groups/{gid}/questLeave":
spec:
method: 'POST'
description: 'Leave an active quest (Quest leaders cannot leave active quests. They must abort the quest to leave)'
parameters: [path('gid','Group to leave quest in','string')]
middleware: [auth.auth, i18n.getUserLanguage, groups.attachGroup]
action: groups.questLeave
#TODO PUT /groups/:gid/chat/:messageId
"/groups/{gid}/chat:GET":
spec:
path: "/groups/{gid}/chat"
description: "Get all chat messages"
parameters: [path('gid','Group to return the chat from ','string')]
middleware: [auth.auth, i18n.getUserLanguage, groups.attachGroup]
action: groups.getChat
"/groups/{gid}/chat:POST":
spec:
method: 'POST'
path: "/groups/{gid}/chat"
description: "Send a chat message"
parameters: [
query 'message', 'Chat message','string'
path 'gid','Group id','string'
]
middleware: [auth.auth, i18n.getUserLanguage, groups.attachGroup]
action: groups.postChat
# placing before route below, so that if !=='seen' it goes to next()
"/groups/{gid}/chat/seen":
spec:
method: 'POST'
description: "Flag chat messages for a particular group as seen"
parameters: [
path 'gid','Group id','string'
]
action: groups.seenMessage
"/groups/{gid}/chat/{messageId}":
spec:
method: 'DELETE'
description: 'Delete a chat message in a given group'
parameters: [
path 'gid', 'ID of the group containing the message to be deleted', 'string'
path 'messageId', 'ID of message to be deleted', 'string'
]
middleware: [auth.auth, i18n.getUserLanguage, groups.attachGroup]
action: groups.deleteChatMessage
"/groups/{gid}/chat/{mid}/like":
spec:
method: 'POST'
description: "Like a chat message"
parameters: [
path 'gid','Group id','string'
path 'mid','Message id','string'
]
middleware: [auth.auth, i18n.getUserLanguage, groups.attachGroup]
action: groups.likeChatMessage
"/groups/{gid}/chat/{mid}/flag":
spec:
method: 'POST'
description: "Flag a chat message"
parameters: [
path 'gid','Group id','string'
path 'mid','Message id','string'
]
middleware: [auth.auth, i18n.getUserLanguage, groups.attachGroup]
action: groups.flagChatMessage
"/groups/{gid}/chat/{mid}/clearflags":
spec:
method: 'POST'
description: "Clear flag count from message and unhide it"
parameters: [
path 'gid','Group id','string'
path 'mid','Message id','string'
]
middleware: [auth.auth, i18n.getUserLanguage, groups.attachGroup]
action: groups.clearFlagCount
# ---------------------------------
# Members
# ---------------------------------
"/members/{uuid}:GET":
spec:
path: '/members/{uuid}'
description: "Get a member."
parameters: [path('uuid','Member ID','string')]
middleware: [i18n.getUserLanguage] # removed auth.auth, so anon users can view shared avatars
action: members.getMember
"/members/{uuid}/message":
spec:
method: 'POST'
description: 'Send a private message to a member'
parameters: [
path 'uuid', 'The UUID of the member to message', 'string'
body '', '{"message": "The private message to send"}', 'object'
]
middleware: [auth.auth]
action: members.sendPrivateMessage
"/members/{uuid}/block":
spec:
method: 'POST'
description: 'Block a member from sending private messages'
parameters: [
path 'uuid', 'The UUID of the member to message', 'string'
]
middleware: [auth.auth]
action: user.blockUser
"/members/{uuid}/gift":
spec:
method: 'POST'
description: 'Send a gift to a member'
parameters: [
path 'uuid', 'The UUID of the member', 'string'
body '', '{"type": "gems or subscription", "gems":{"amount":Number, "fromBalance":Boolean}, "subscription":{"months":Number}}', 'object'
]
middleware: [auth.auth]
action: members.sendGift
# ---------------------------------
# Hall of Heroes / Patrons
# ---------------------------------
"/hall/heroes":
spec: {}
middleware:[auth.auth, i18n.getUserLanguage]
action: hall.getHeroes
"/hall/heroes/{uid}:GET":
spec: path: "/hall/heroes/{uid}"
middleware:[auth.auth, i18n.getUserLanguage, hall.ensureAdmin]
action: hall.getHero
"/hall/heroes/{uid}:POST":
spec:
method: 'POST'
path: "/hall/heroes/{uid}"
middleware: [auth.auth, i18n.getUserLanguage, hall.ensureAdmin]
action: hall.updateHero
"/hall/patrons":
spec:
parameters: [
query 'page','Page number to fetch (this list is long)','string'
]
middleware:[auth.auth, i18n.getUserLanguage]
action: hall.getPatrons
# ---------------------------------
# Challenges
# ---------------------------------
# Note: while challenges belong to groups, and would therefore make sense as a nested resource
# (eg /groups/:gid/challenges/:cid), they will also be referenced by users from the "challenges" tab
# without knowing which group they belong to. So to prevent unecessary lookups, we have them as a top-level resource
"/challenges:GET":
spec:
path: '/challenges'
description: "Get a list of challenges"
middleware: [auth.auth, i18n.getUserLanguage]
action: challenges.list
"/challenges:POST":
spec:
path: '/challenges'
method: 'POST'
description: "Create a challenge"
parameters: [body('','Challenge object (see ChallengeSchema)','object')]
middleware: [auth.auth, i18n.getUserLanguage]
action: challenges.create
"/challenges/{cid}:GET":
spec:
path: '/challenges/{cid}'
description: 'Get a challenge'
parameters: [path('cid','Challenge id','string')]
middleware: [auth.auth, i18n.getUserLanguage]
action: challenges.get
"/challenges/{cid}/csv":
spec:
description: 'Get a challenge (csv format)'
parameters: [path('cid','Challenge id','string')]
action: challenges.csv
"/challenges/{cid}:POST":
spec:
path: '/challenges/{cid}'
method: 'POST'
description: "Update a challenge"
parameters: [
path 'cid','Challenge id','string'
body('','Challenge object (see ChallengeSchema)','object')
]
middleware: [auth.auth, i18n.getUserLanguage]
action: challenges.update
"/challenges/{cid}:DELETE":
spec:
path: '/challenges/{cid}'
method: 'DELETE'
description: "Delete a challenge"
parameters: [path('cid','Challenge id','string')]
middleware: [auth.auth, i18n.getUserLanguage]
action: challenges.delete
"/challenges/{cid}/close":
spec:
method: 'POST'
description: 'Close a challenge'
parameters: [
path 'cid','Challenge id','string'
query 'uid','User ID of the winner','string',true
]
middleware: [auth.auth, i18n.getUserLanguage]
action: challenges.selectWinner
"/challenges/{cid}/join":
spec:
method: 'POST'
description: "Join a challenge"
parameters: [path('cid','Challenge id','string')]
middleware: [auth.auth, i18n.getUserLanguage]
action: challenges.join
"/challenges/{cid}/leave":
spec:
method: 'POST'
description: 'Leave a challenge'
parameters: [path('cid','Challenge id','string')]
middleware: [auth.auth, i18n.getUserLanguage]
action: challenges.leave
"/challenges/{cid}/member/{uid}":
spec:
description: "Get a member's progress in a particular challenge"
parameters: [
path 'cid','Challenge id','string'
path 'uid','User id','string'
]
middleware: [auth.auth, i18n.getUserLanguage]
action: challenges.getMember
if nconf.get("NODE_ENV") is "development"
api["/user/addTenGems"] =
spec: method:'POST'
action: user.addTenGems
api["/user/addHourglass"] =
spec: method:'POST'
action: user.addHourglass
_.each api, (route, path) ->
## Spec format is:
# spec:
# path: "/pet/{petId}"
# description: "Operations about pets"
# notes: "Returns a pet based on ID"
# summary: "Find pet by ID"
# method: "GET"
# parameters: [path("petId", "ID of pet that needs to be fetched", "string")]
# type: "Pet"
# errorResponses: [swagger.errors.invalid("id"), swagger.errors.notFound("pet")]
# nickname: "getPetById"
route.spec.description ?= ''
_.defaults route.spec,
path: path
nickname: path
notes: route.spec.description
summary: route.spec.description
parameters: []
#type: 'Pet'
errorResponses: []
method: 'GET'
route.middleware ?= if path.indexOf('/user') is 0 then [auth.auth, i18n.getUserLanguage, cron] else [i18n.getUserLanguage]
swagger["add#{route.spec.method}"](route);true
swagger.configure("#{nconf.get('BASE_URL')}/api/v2", "2")

View File

@@ -1,9 +1,9 @@
var express = require('express'); var express = require('express');
var router = new express.Router(); var router = new express.Router();
var dataexport = require('../controllers/dataexport'); var dataexport = require('../controllers/dataexport');
var auth = require('../controllers/auth'); var auth = require('../controllers/api-v2/auth');
var nconf = require('nconf'); var nconf = require('nconf');
var i18n = require('../i18n'); var i18n = require('../libs/i18n');
var locals = require('../middlewares/locals'); var locals = require('../middlewares/locals');
/* Data export */ /* Data export */

View File

@@ -3,9 +3,7 @@ var express = require('express');
var router = new express.Router(); var router = new express.Router();
var _ = require('lodash'); var _ = require('lodash');
var locals = require('../middlewares/locals'); var locals = require('../middlewares/locals');
var user = require('../controllers/user'); var i18n = require('../libs/i18n');
var auth = require('../controllers/auth');
var i18n = require('../i18n');
// -------- App -------- // -------- App --------
router.get('/', i18n.getUserLanguage, locals, function(req, res) { router.get('/', i18n.getUserLanguage, locals, function(req, res) {

View File

@@ -1,9 +1,9 @@
var nconf = require('nconf'); var nconf = require('nconf');
var express = require('express'); var express = require('express');
var router = new express.Router(); var router = new express.Router();
var auth = require('../controllers/auth'); var auth = require('../controllers/api-v2/auth');
var payments = require('../controllers/payments'); var payments = require('../controllers/payments');
var i18n = require('../i18n'); var i18n = require('../libs/i18n');
router.get('/paypal/checkout', auth.authWithUrl, i18n.getUserLanguage, payments.paypalCheckout); router.get('/paypal/checkout', auth.authWithUrl, i18n.getUserLanguage, payments.paypalCheckout);
router.get('/paypal/checkout/success', i18n.getUserLanguage, payments.paypalCheckoutSuccess); router.get('/paypal/checkout/success', i18n.getUserLanguage, payments.paypalCheckoutSuccess);

View File

@@ -1,55 +0,0 @@
/*
* This script is no longer required due to this code in src/models/group.js:
* // initialize tavern if !exists (fresh installs)
* Group.count({_id:'habitrpg'},function(err,ct){
* ...
* })
*
* However we're keeping this script in case future seed updates are needed.
*
* Reference: https://github.com/HabitRPG/habitrpg/issues/3852#issuecomment-55334572
*/
/*
require('coffee-script') // for habitrpg-shared
var nconf = require('nconf');
var utils = require('./utils');
var logging = require('./logging');
utils.setupConfig();
var async = require('async');
var mongoose = require('mongoose');
User = require('./models/user').model;
Group = require('./models/group').model;
async.waterfall([
function(cb){
mongoose.connect(nconf.get('NODE_DB_URI'), cb);
},
function(cb){
Group.findById('habitrpg', cb);
},
function(tavern, cb){
logging.info({tavern:tavern,cb:cb});
if (!tavern) {
tavern = new Group({
_id: 'habitrpg',
chat: [],
leader: '9',
name: 'HabitRPG',
type: 'guild',
privacy:'public'
});
tavern.save(cb)
} else {
cb();
}
}
],function(err){
if (err) throw err;
logging.info("Done initializing database");
mongoose.disconnect();
})
*/

View File

@@ -2,9 +2,9 @@
var cluster = require("cluster"); var cluster = require("cluster");
var _ = require('lodash'); var _ = require('lodash');
var nconf = require('nconf'); var nconf = require('nconf');
var utils = require('./utils'); var utils = require('./libs/utils');
utils.setupConfig(); utils.setupConfig();
var logging = require('./logging'); var logging = require('./libs/logging');
var isProd = nconf.get('NODE_ENV') === 'production'; var isProd = nconf.get('NODE_ENV') === 'production';
var isDev = nconf.get('NODE_ENV') === 'development'; var isDev = nconf.get('NODE_ENV') === 'development';
var DISABLE_LOGGING = nconf.get('DISABLE_REQUEST_LOGGING'); var DISABLE_LOGGING = nconf.get('DISABLE_REQUEST_LOGGING');
@@ -29,7 +29,7 @@ if (cores!==0 && cluster.isMaster && (isDev || isProd)) {
var shared = require('../../common'); var shared = require('../../common');
// Setup translations // Setup translations
var i18n = require('./i18n'); var i18n = require('./libs/i18n');
var TWO_WEEKS = 1000 * 60 * 60 * 24 * 14; var TWO_WEEKS = 1000 * 60 * 60 * 24 * 14;
var app = express(); var app = express();
@@ -90,54 +90,68 @@ if (cores!==0 && cluster.isMaster && (isDev || isProd)) {
var publicDir = path.join(__dirname, "/../public"); var publicDir = path.join(__dirname, "/../public");
app.set("port", nconf.get('PORT')); app.set("port", nconf.get('PORT'));
require('./middlewares/apiThrottle')(app);
app.use(require('./middlewares/domain')(server,mongoose)); // Setup two different Express apps, one that matches everything except '/api/v3'
if (!isProd && !DISABLE_LOGGING) app.use(express.logger("dev")); // and the other for /api/v3 routes, so we can keep the old an new api versions completely separate
app.use(express.compress()); // not sharing a single middleware if we don't want to
app.set("views", __dirname + "/../views"); var oldApp = express(); // api v1 and v2, and not scoped routes
app.set("view engine", "jade"); var newApp = express(); // api v3
app.use(express.favicon(publicDir + '/favicon.ico'));
app.use(require('./middlewares/cors')); // Route requests to the right app
app.use(app.router);
// Matches all request except the ones going to /api/v3/**
app.all(/^(?!\/api\/v3).+/i, oldApp);
// Matches all requests going to /api/v3
app.all('/api/v3', newApp);
require('./middlewares/apiThrottle')(oldApp);
oldApp.use(require('./middlewares/domain')(server,mongoose));
if (!isProd && !DISABLE_LOGGING) oldApp.use(express.logger("dev"));
oldApp.use(express.compress());
oldApp.set("views", __dirname + "/../views");
oldApp.set("view engine", "jade");
oldApp.use(express.favicon(publicDir + '/favicon.ico'));
oldApp.use(require('./middlewares/cors'));
var redirects = require('./middlewares/redirects'); var redirects = require('./middlewares/redirects');
app.use(redirects.forceHabitica); oldApp.use(redirects.forceHabitica);
app.use(redirects.forceSSL); oldApp.use(redirects.forceSSL);
app.use(express.urlencoded()); oldApp.use(express.urlencoded());
app.use(express.json()); oldApp.use(express.json());
app.use(require('method-override')()); oldApp.use(require('method-override')());
//app.use(express.cookieParser(nconf.get('SESSION_SECRET'))); //oldApp.use(express.cookieParser(nconf.get('SESSION_SECRET')));
app.use(express.cookieParser()); oldApp.use(express.cookieParser());
app.use(express.cookieSession({ secret: nconf.get('SESSION_SECRET'), httpOnly: false, cookie: { maxAge: TWO_WEEKS }})); oldApp.use(express.cookieSession({ secret: nconf.get('SESSION_SECRET'), httpOnly: false, cookie: { maxAge: TWO_WEEKS }}));
//app.use(express.session()); //oldApp.use(express.session());
// Initialize Passport! Also use passport.session() middleware, to support // Initialize Passport! Also use passport.session() middleware, to support
// persistent login sessions (recommended). // persistent login sessions (recommended).
app.use(passport.initialize()); oldApp.use(passport.initialize());
app.use(passport.session()); oldApp.use(passport.session());
app.use(app.router); oldApp.use(oldApp.router);
var maxAge = isProd ? 31536000000 : 0; var maxAge = isProd ? 31536000000 : 0;
// Cache emojis without copying them to build, they are too many // Cache emojis without copying them to build, they are too many
app.use(express['static'](path.join(__dirname, "/../build"), { maxAge: maxAge })); oldApp.use(express['static'](path.join(__dirname, "/../build"), { maxAge: maxAge }));
app.use('/common/dist', express['static'](publicDir + "/../../common/dist", { maxAge: maxAge })); oldApp.use('/common/dist', express['static'](publicDir + "/../../common/dist", { maxAge: maxAge }));
app.use('/common/audio', express['static'](publicDir + "/../../common/audio", { maxAge: maxAge })); oldApp.use('/common/audio', express['static'](publicDir + "/../../common/audio", { maxAge: maxAge }));
app.use('/common/script/public', express['static'](publicDir + "/../../common/script/public", { maxAge: maxAge })); oldApp.use('/common/script/public', express['static'](publicDir + "/../../common/script/public", { maxAge: maxAge }));
app.use('/common/img', express['static'](publicDir + "/../../common/img", { maxAge: maxAge })); oldApp.use('/common/img', express['static'](publicDir + "/../../common/img", { maxAge: maxAge }));
app.use(express['static'](publicDir)); oldApp.use(express['static'](publicDir));
// Custom Directives // Custom Directives
app.use(require('./routes/pages').middleware); oldApp.use(require('./routes/pages').middleware);
app.use(require('./routes/payments').middleware); oldApp.use(require('./routes/payments').middleware);
app.use(require('./routes/auth').middleware); oldApp.use(require('./routes/api-v2/auth').middleware);
app.use(require('./routes/coupon').middleware); oldApp.use(require('./routes/api-v2/coupon').middleware);
app.use(require('./routes/unsubscription').middleware); oldApp.use(require('./routes/api-v2/unsubscription').middleware);
var v2 = express(); var v2 = express();
app.use('/api/v2', v2); oldApp.use('/api/v2', v2);
app.use('/api/v1', require('./routes/apiv1').middleware); oldApp.use('/api/v1', require('./routes/api-v1').middleware);
app.use('/export', require('./routes/dataexport').middleware); oldApp.use('/export', require('./routes/dataexport').middleware);
require('./routes/apiv2.coffee')(swagger, v2); require('./routes/api-v2/swagger')(swagger, v2);
app.use(require('./middlewares/errorHandler')); oldApp.use(require('./middlewares/errorHandler'));
server.on('request', app); server.on('request', app);
server.listen(app.get("port"), function() { server.listen(app.get("port"), function() {