diff --git a/api/app.js b/api/app.js index 584feed..e7d2f50 100644 --- a/api/app.js +++ b/api/app.js @@ -1,63 +1,29 @@ -import koa from 'koa'; -import route from 'koa-route'; +import Router from 'koa-router'; import stats from 'koa-statsd'; -import websockify from 'koa-websocket'; -import logger from 'koa-logger'; -import compress from 'koa-compress'; -import bodyparser from 'koa-bodyparser'; import cors from 'kcors'; -import co from 'co'; -import raven from 'raven'; +import StatsD from 'statsy'; import auth from './lib/auth'; -import mongo from '../lib/mongo'; -import redis from '../lib/redis'; import * as user from './routes/user'; import * as file from './routes/file'; import debugname from 'debug'; const debug = debugname('hostr-api'); -import StatsD from 'statsy'; -if (process.env.SENTRY_DSN) { - const ravenClient = new raven.Client(process.env.SENTRY_DSN); - ravenClient.patchGlobal(); -} - -const app = websockify(koa()); - -app.use(function* (next){ - this.set('Server', 'Nintendo 64'); - if(this.req.headers['x-forwarded-proto'] === 'http'){ - return this.redirect('https://' + this.req.headers.host + this.req.url); - } - yield next; -}); +const router = new Router(); let statsdOpts = {prefix: 'hostr-api', host: process.env.STATSD_HOST || 'localhost'}; let statsd = new StatsD(statsdOpts); -app.use(function*(next) { +router.use(function*(next) { this.statsd = statsd; yield next; }); -app.use(stats(statsdOpts)); +router.use(stats(statsdOpts)); -app.use(logger()); - -app.use(cors({ +router.use(cors({ origin: '*', credentials: true })); -app.use(mongo()); -app.use(redis()); -app.ws.use(mongo()); -app.ws.use(redis()); - -app.use(route.get('/', function* (){ - this.status = 200; - this.body = ''; -})); - -app.use(function* (next){ +router.use('/*',function* (next) { try { yield next; if (this.response.status === 404 && !this.response.body) { @@ -90,34 +56,21 @@ app.use(function* (next){ this.type = 'application/json'; }); -app.use(compress()); -app.use(bodyparser()); +router.get('/user', auth, user.get); +router.get('/user/token', auth, user.token); +router.get('/token', auth, user.token); +router.get('/user/transaction', auth, user.transaction); +router.post('/user/settings', auth, user.settings); +router.get('/file', auth, file.list); +router.post('/file', auth, file.post); +router.get('/file/:id', file.get); +router.put('/file/:id', auth, file.put); +router.delete('/file/:id', auth, file.del); +router.delete('/file/:id', auth, file.del); -app.ws.use(route.all('/file/:id', file.events)); -app.ws.use(route.all('/user', user.events)); +// Hack, if no route matches here, router does not dispatch at all +router.get('/(.*)', function* () { + this.throw(404); +}); -app.use(route.get('/file/:id', file.get)); - -// Run auth middleware before all other endpoints -app.use(auth); - -app.use(route.get('/user', user.get)); -app.use(route.get('/user/token', user.token)); -app.use(route.get('/token', user.token)); -app.use(route.get('/user/transaction', user.transaction)); -app.use(route.post('/user/settings', user.settings)); -app.use(route.get('/file', file.list)); -app.use(route.post('/file', file.post)); -app.use(route.put('/file/:id', file.put)); -app.use(route.delete('/file/:id', file.del)); - -if (!module.parent) { - app.listen(process.env.PORT || 4042, function() { - debug('Koa HTTP server listening on port ' + (process.env.PORT || 4042)); - }); - setInterval(function() { - debug('%sMB', process.memoryUsage().rss / 1024 / 1024); - }, 10000); -} - -export default app; +export default router; diff --git a/api/routes/file.js b/api/routes/file.js index f14a0dd..e3ef326 100644 --- a/api/routes/file.js +++ b/api/routes/file.js @@ -224,10 +224,10 @@ export function* list() { } -export function* get(id) { +export function* get() { const Files = this.db.Files; const Users = this.db.Users; - const file = yield Files.findOne({_id: id, status: {'$in': ['active', 'uploading']}}); + const file = yield Files.findOne({_id: this.params.id, status: {'$in': ['active', 'uploading']}}); this.assert(file, 404, '{"error": {"message": "File not found", "code": 604}}'); const user = yield Users.findOne({_id: file.owner}); this.assert(user && !user.banned, 404, '{"error": {"message": "File not found", "code": 604}}'); @@ -236,22 +236,23 @@ export function* get(id) { } -export function* put(id) { +export function* put() { if (this.request.body.trashed) { const Files = this.db.Files; const status = this.request.body.trashed ? 'trashed' : 'active'; - yield Files.updateOne({'_id': id, owner: this.user.id}, {$set: {status: status}}, {w: 1}); + yield Files.updateOne({'_id': this.params.id, owner: this.user.id}, {$set: {status: status}}, {w: 1}); } } -export function* del(id) { +export function* del() { const Files = this.db.Files; - yield Files.updateOne({'_id': id, owner: this.user.id}, {$set: {status: 'deleted'}}, {w: 1}); - const event = {type: 'file-deleted', data: {'id': id}}; + yield Files.updateOne({'_id': this.params.id, owner: this.db.ObjectId(this.user.id)}, {$set: {status: 'deleted'}}, {w: 1}); + const event = {type: 'file-deleted', data: {'id': this.params.id}}; yield this.redis.publish('/user/' + this.user.id, JSON.stringify(event)); - yield this.redis.publish('/file/' + id, JSON.stringify(event)); + yield this.redis.publish('/file/' + this.params.id, JSON.stringify(event)); this.statsd.incr('file.delete', 1); + this.status = 204; this.body = ''; } diff --git a/app.js b/app.js index 55d27ac..0926002 100644 --- a/app.js +++ b/app.js @@ -1,31 +1,53 @@ +import path from 'path'; import koa from 'koa'; import mount from 'koa-mount'; import route from 'koa-route'; +import logger from 'koa-logger'; +import Router from 'koa-router'; +import serve from 'koa-static'; +import favicon from 'koa-favicon'; +import compress from 'koa-compress'; +import bodyparser from 'koa-bodyparser'; import websockify from 'koa-websocket'; +import raven from 'raven'; +import mongo from './lib/mongo'; import redis from './lib/redis'; import co from 'co'; import api from './api/app'; -import { events as fileEvents } from './api/routes/file'; -import { events as userEvents } from './api/routes/user'; import web from './web/app'; import { init as storageInit } from './lib/storage'; +storageInit(); import debugname from 'debug'; const debug = debugname('hostr'); -storageInit(); +if (process.env.SENTRY_DSN) { + const ravenClient = new raven.Client(process.env.SENTRY_DSN); + ravenClient.patchGlobal(); +} const app = websockify(koa()); - app.keys = [process.env.KEYS || 'INSECURE']; -app.ws.use(redis()); +app.use(function* (next){ + this.set('Server', 'Nintendo 64'); + if(this.req.headers['x-forwarded-proto'] === 'http'){ + return this.redirect('https://' + this.req.headers.host + this.req.url); + } + yield next; +}); -app.ws.use(route.all('/api/user', userEvents)); -app.ws.use(route.all('/api/file/:id', fileEvents)); +app.use(mongo()); +app.use(redis()); +app.use(logger()); +app.use(compress()); +app.use(bodyparser()); -app.use(mount('/api', api)); -app.use(mount('/', web)); +app.use(favicon(path.join(__dirname, 'web/public/images/favicon.png'))); +app.use(serve(path.join(__dirname, 'web/public/'), {maxage: 31536000000})); + +app.use(api.prefix('/api').routes()); +app.use(web.prefix('').routes()); if (!module.parent) { app.listen(process.env.PORT || 4040, function() { diff --git a/lib/mongo.js b/lib/mongo.js index a34b789..e520b9d 100644 --- a/lib/mongo.js +++ b/lib/mongo.js @@ -17,6 +17,7 @@ let configuredClient = new Promise(function (resolve, reject) { client.Reset = client.collection('reset'); client.Remember.ensureIndex({'created': 1}, {expireAfterSeconds: 2592000}); client.Files.ensureIndex({'owner': 1, 'status': 1, 'time_added': -1}); + client.ObjectId = mongodb().ObjectId; return resolve(client); }).catch((e) => { reject(e) diff --git a/package.json b/package.json index 39f5ddc..cf79b2e 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,8 @@ "koa-mount": "~1.3.0", "koa-redis": "~1.0.0", "koa-route": "~2.4.2", + "koa-router": "^5.1.2", + "koa-static": "^1.4.9", "koa-statsd": "~0.0.2", "koa-views": "~3.1.0", "koa-websocket": "~1.0.0", diff --git a/test/api/auth.spec.js b/test/api/auth.spec.js index 943302c..97134f9 100644 --- a/test/api/auth.spec.js +++ b/test/api/auth.spec.js @@ -1,5 +1,5 @@ import { agent } from 'supertest'; -import app from '../../api/app'; +import app from '../../app'; const request = agent(app.listen()); @@ -8,7 +8,7 @@ describe('hostr-api auth', function(){ describe('with no credentials', function(){ it('should `throw` 401', function(done){ request - .get('/user') + .get('/api/user') .expect(401, done); }); }); @@ -16,18 +16,19 @@ describe('hostr-api auth', function(){ describe('with invalid credentials', function(){ it('should `throw` 401', function(done){ request - .get('/user') + .get('/api/user') .auth('user', 'invalid password') .expect(401, done); }); }); describe('with valid credentials', function(){ - it('should call the next middleware', function(done){ + it('should 404', function(done){ request - .get('/') + .get('/api/') .auth('test@hostr.co', 'test-password') - .expect(200, done); + .expect('Content-type', /application\/json/) + .expect(404, done); }); }); }); diff --git a/test/api/file.spec.js b/test/api/file.spec.js index 5f38802..cef3934 100644 --- a/test/api/file.spec.js +++ b/test/api/file.spec.js @@ -1,6 +1,6 @@ import assert from 'assert'; import { agent } from 'supertest'; -import app from '../../api/app'; +import app from '../../app'; const request = agent(app.listen()); @@ -11,7 +11,7 @@ describe('hostr-api file', function() { describe('when GET /file', function() { it('should receive a list of file objects', function(done) { request - .get('/file') + .get('/api/file') .auth('test@hostr.co', 'test-password') .expect(200) .expect(function(response) { @@ -25,7 +25,7 @@ describe('hostr-api file', function() { it('should receive a new file object', function(done) { this.timeout(30000); request - .post('/file') + .post('/api/file') .attach('file', 'test/fixtures/utah-arches.jpg') .auth('test@hostr.co', 'test-password') .expect(201) @@ -40,7 +40,7 @@ describe('hostr-api file', function() { describe('when GET /file/:id', function() { it('should receive the file object', function(done) { request - .get('/file/' + id) + .get('/api/file/' + id) .expect(200) .expect(function(response) { assert(response.body.name === 'utah-arches.jpg'); @@ -52,16 +52,16 @@ describe('hostr-api file', function() { describe('when DELETE /file/:id', function() { it('should delete the file object', function(done) { request - .delete('/file/' + id) + .delete('/api/file/' + id) .auth('test@hostr.co', 'test-password') - .expect(200, done); + .expect(204, done); }); }); describe('when GET deleted /file/:id', function() { it('should not receive the file object', function(done) { request - .get('/file/' + id) + .get('/api/file/' + id) .expect(404, done); }); }); diff --git a/test/api/user.spec.js b/test/api/user.spec.js index 47c5b3c..e68d14e 100644 --- a/test/api/user.spec.js +++ b/test/api/user.spec.js @@ -1,6 +1,6 @@ import assert from 'assert'; import { agent } from 'supertest'; -import app from '../../api/app'; +import app from '../../app'; const request = agent(app.listen()); @@ -9,7 +9,7 @@ describe('hostr-api user', function() { describe('when GET /user', function() { it('should receive a user object', function(done) { request - .get('/user') + .get('/api/user') .auth('test@hostr.co', 'test-password') .expect(function(response) { assert(response.body.id === '54fd04a37675bcd06213eac8'); @@ -22,7 +22,7 @@ describe('hostr-api user', function() { describe('when GET /user/token', function() { it('should receive a user token object', function(done) { request - .get('/user/token') + .get('/api/user/token') .auth('test@hostr.co', 'test-password') .expect(function(response) { assert(response.body.token); @@ -35,7 +35,7 @@ describe('hostr-api user', function() { describe('when GET /user/transaction', function() { it('should receive a user transactions object', function(done) { request - .get('/user/transaction') + .get('/api/user/transaction') .auth('test@hostr.co', 'test-password') .expect(200) .expect(function(response) { @@ -48,7 +48,7 @@ describe('hostr-api user', function() { describe('when GET /user/settings', function() { it('should update user password', function(done) { request - .post('/user/settings') + .post('/api/user/settings') .send({'current_password': 'test-password', 'new_password': 'test-password' }) .auth('test@hostr.co', 'test-password') .expect(200) diff --git a/test/fixtures/mongo-file.js b/test/fixtures/mongo-file.js index f71214d..6a3618e 100644 --- a/test/fixtures/mongo-file.js +++ b/test/fixtures/mongo-file.js @@ -3,19 +3,3 @@ db.files.createIndex({ "status": 1, "time_added": -1 }); -db.files.save({"_id": "94U1ruo7anyQ", - "owner": ObjectId("54fd04a37675bcd06213eac8"), - "system_name": "94U1ruo7anyQ", - "file_name": "utah-arches.jpg", - "original_name": "utah-arches.jpg", - "file_size": 194544, - "time_added": 1436223854, - "status": "active", - "last_accessed": null, - "s3": true, - "type": "image", - "ip": "::1", - "md5": "1f4185751b4db05494cbc0aad68d7d77", - "width": 1024, - "height": 683 -}); diff --git a/test/web/file.spec.js b/test/web/file.spec.js index 7b727eb..a3e52dc 100644 --- a/test/web/file.spec.js +++ b/test/web/file.spec.js @@ -1,19 +1,16 @@ -import web from '../../web/app'; -import api from '../../api/app'; import assert from 'assert'; import { agent } from 'supertest'; +import app from '../../app'; -const request = agent(web.listen()); - -const apiRequest = agent(api.listen()); +const request = agent(app.listen()); let file = {}; describe('setup hostr-web file', function() { describe('when POSTing a file to /file', function() { it('should receive a new file object', function(done) { this.timeout(30000); - apiRequest - .post('/file') + request + .post('/api/file') .attach('file', 'test/fixtures/utah-arches.jpg') .auth('test@hostr.co', 'test-password') .expect(201) diff --git a/test/web/user.spec.js b/test/web/user.spec.js index 31bc268..f7840bc 100644 --- a/test/web/user.spec.js +++ b/test/web/user.spec.js @@ -1,5 +1,5 @@ -import app from '../../web/app'; import { agent } from 'supertest'; +import app from '../../app'; const request = agent(app.listen()); diff --git a/web/app.js b/web/app.js index 0b3cc39..99daf6d 100644 --- a/web/app.js +++ b/web/app.js @@ -1,23 +1,15 @@ import path from 'path'; -import koa from 'koa'; +import Router from 'koa-router'; import csrf from 'koa-csrf'; -import route from 'koa-route'; import views from 'koa-views'; import stats from 'koa-statsd'; -import logger from 'koa-logger'; -import favicon from 'koa-favicon'; -import redisStore from 'koa-redis'; -import compress from 'koa-compress'; -import bodyparser from 'koa-bodyparser'; +import redis from '../lib/redis'; +import koaRedis from 'koa-redis' import session from 'koa-generic-session'; -import staticHandler from 'koa-file-server'; import co from 'co'; -import raven from 'raven'; import StatsD from 'statsy'; // waiting for PR to be merged, can remove swig dependency when done import errors from '../lib/koa-error'; -import mongo from '../lib/mongo'; -import redis from '../lib/redis'; import * as index from './routes/index'; import * as file from './routes/file'; import * as pro from './routes/pro'; @@ -26,35 +18,24 @@ import * as user from './routes/user'; import debugname from 'debug'; const debug = debugname('hostr-web'); -if (process.env.SENTRY_DSN) { - const ravenClient = new raven.Client(process.env.SENTRY_DSN); - ravenClient.patchGlobal(); -} - -const redisUrl = process.env.REDIS_URL || process.env.REDISTOGO_URL || 'redis://localhost:6379'; - -const app = koa(); +const router = new Router(); +router.use(errors({template: path.join(__dirname, 'public', '404.html')})); let statsdOpts = {prefix: 'hostr-web', host: process.env.STATSD_HOST || 'localhost'}; let statsd = new StatsD(statsdOpts); -app.use(function*(next) { +router.use(function* (next) { this.statsd = statsd; yield next; }); -app.use(stats(statsdOpts)); +router.use(stats(statsdOpts)); -app.use(errors({template: path.join(__dirname, 'public', '404.html')})); +router.use(session({ + store: koaRedis({client: redis().client}) +})); -app.use(function*(next){ - this.set('Server', 'Nintendo 64'); - if(this.req.headers['x-forwarded-proto'] === 'http'){ - return this.redirect('https://' + this.request.headers.host + this.request.url); - } - yield next; -}); - -app.use(function*(next){ +router.use(function* (next){ this.state = { + session: this.session, apiURL: process.env.API_URL, baseURL: process.env.BASE_URL, stripePublic: process.env.STRIPE_PUBLIC_KEY @@ -62,70 +43,49 @@ app.use(function*(next){ yield next; }); -app.use(mongo()); -app.use(redis()); -app.use(compress()); -app.use(bodyparser()); -app.use(favicon(path.join(__dirname, 'public/images/favicon.png'))); -app.use(staticHandler({root: path.join(__dirname, 'public'), maxage: 31536000000})); -app.use(logger()); -app.use(views('views', { +router.use(views('views', { default: 'ejs' })); -app.keys = [process.env.KEYS || 'INSECURE']; -app.use(session({ - store: redisStore({client: redis().client}) -})); +router.get('/', index.main); +router.get('/account', index.main); +router.get('/billing', index.main); +router.get('/pro', index.main); -app.use(route.get('/', index.main)); -app.use(route.get('/account', index.main)); -app.use(route.get('/billing', index.main)); -app.use(route.get('/pro', index.main)); +router.get('/signin', user.signin); +router.post('/signin', user.signin); +router.get('/signup', user.signup); +router.post('/signup', user.signup); +router.get('/logout', user.logout); +router.post('/logout', user.logout); +router.get('/forgot', user.forgot); +router.get('/forgot/:token', user.forgot); +router.post('/forgot/:token', user.forgot); +router.post('/forgot', user.forgot); +router.get('/activate/:code', user.activate); -app.use(route.get('/signin', user.signin)); -app.use(route.post('/signin', user.signin)); -app.use(route.get('/signup', user.signup)); -app.use(route.post('/signup', user.signup)); -app.use(route.get('/logout', user.logout)); -app.use(route.post('/logout', user.logout)); -app.use(route.get('/forgot', user.forgot)); -app.use(route.get('/forgot/:token', user.forgot)); -app.use(route.post('/forgot/:token', user.forgot)); -app.use(route.post('/forgot', user.forgot)); -app.use(route.get('/activate/:code', user.activate)); +router.get('/terms', index.staticPage); +router.get('/privacy', index.staticPage); +router.get('/pricing', index.staticPage); +router.get('/apps', index.staticPage); +router.get('/stats', index.staticPage); -app.use(route.get('/terms', index.staticPage)); -app.use(route.get('/privacy', index.staticPage)); -app.use(route.get('/pricing', index.staticPage)); -app.use(route.get('/apps', index.staticPage)); -app.use(route.get('/stats', index.staticPage)); +router.post('/pro/create', pro.create); +router.post('/pro/cancel', pro.cancel); -app.use(route.post('/pro/create', pro.create)); -app.use(route.post('/pro/cancel', pro.cancel)); - -app.use(route.get('/:id', file.landing)); -app.use(route.get('/download/:id/:name', function* (id) { +router.get('/:id', file.landing); +router.get('/file/:id/:name', file.get); +router.get('/file/:size/:id/:name', file.get); +router.get('/files/:id/:name', file.get); +router.get('/download/:id/:name', function* (id) { this.redirect('/' + id); -})); -app.use(route.get('/file/:id/:name', file.get)); -app.use(route.get('/files/:id/:name', file.get)); -app.use(route.get('/file/:size/:id/:name', file.resized)); +}); -app.use(route.get('/updaters/mac', function* () { +router.get('/updaters/mac', function* () { this.redirect('/updaters/mac.xml'); -})); -app.use(route.get('/updaters/mac/changelog', function* () { +}); +router.get('/updaters/mac/changelog', function* () { yield this.render('mac-update-changelog'); -})); +}); -if (!module.parent) { - app.listen(process.env.PORT || 4041, function() { - debug('Koa HTTP server listening on port ' + (process.env.PORT || 4041)); - }); - setInterval(function() { - debug('%sMB', process.memoryUsage().rss / 1024 / 1024); - }, 10000); -} - -export default app; +export default router; diff --git a/web/lib/auth.js b/web/lib/auth.js index db40708..b5423f3 100644 --- a/web/lib/auth.js +++ b/web/lib/auth.js @@ -7,11 +7,12 @@ import debugname from 'debug'; const debug = debugname('hostr-web:auth'); import { Mandrill } from 'mandrill-api/mandrill'; const mandrill = new Mandrill(process.env.MANDRILL_KEY); +import mongo from '../../lib/mongo'; -export function* authenticate(ctx, email, password) { - const Users = ctx.db.Users; - const Logins = ctx.db.Logins; - const remoteIp = ctx.headers['x-real-ip'] || ctx.ip; +export function* authenticate(email, password) { + const Users = this.db.Users; + const Logins = this.db.Logins; + const remoteIp = this.headers['x-real-ip'] || this.ip; if (!password || password.length < 6){ debug('No password, or password too short'); @@ -45,10 +46,10 @@ export function* authenticate(ctx, email, password) { } -export function* setupSession(ctx, user) { +export function* setupSession(user) { debug('Setting up session'); const token = uuid.v4(); - yield ctx.redis.set(token, user._id, 'EX', 604800); + yield this.redis.set(token, user._id, 'EX', 604800); const sessionUser = { 'id': user._id, @@ -67,19 +68,19 @@ export function* setupSession(ctx, user) { sessionUser.dailyUploadAllowance = 'unlimited'; } - ctx.session.user = sessionUser; - if (ctx.request.body.remember && ctx.request.body.remember === 'on') { - const Remember = ctx.db.Remember; + this.session.user = sessionUser; + if (this.request.body.remember && this.request.body.remember === 'on') { + const Remember = this.db.Remember; var rememberToken = uuid(); Remember.save({_id: rememberToken, 'user_id': user.id, created: new Date().getTime()}); - ctx.cookies.set('r', rememberToken, { maxAge: 1209600000, httpOnly: true}); + this.cookies.set('r', rememberToken, { maxAge: 1209600000, httpOnly: true}); } debug('Session set up'); } -export function* signup(ctx, email, password, ip) { - const Users = ctx.db.Users; +export function* signup(email, password, ip) { + const Users = this.db.Users; const existingUser = yield Users.findOne({email: email, status: {'$ne': 'deleted'}}); if (existingUser) { debug('Email already in use.'); @@ -120,9 +121,9 @@ ${process.env.BASE_URL + '/activate/' + user.activationCode} } -export function* sendResetToken(ctx, email) { - const Users = ctx.db.Users; - const Reset = ctx.db.Reset; +export function* sendResetToken(email) { + const Users = this.db.Users; + const Reset = this.db.Reset; const user = yield Users.findOne({email: email}); if (user) { var token = uuid.v4(); @@ -155,40 +156,40 @@ Visit ${process.env.BASE_URL + '/forgot/' + token} to set a new one. } -export function* fromToken(ctx, token) { - const Users = ctx.db.Users; - const reply = yield ctx.redis.get(token); +export function* fromToken(token) { + const Users = this.db.Users; + const reply = yield this.redis.get(token); return yield Users.findOne({_id: reply}); } -export function* fromCookie(ctx, cookie) { - const Remember = ctx.db.Remember; - const Users = ctx.db.Users; +export function* fromCookie(cookie) { + const Remember = this.db.Remember; + const Users = this.db.Users; const remember = yield Remember.findOne({_id: cookie}); return yield Users.findOne({_id: remember.user_id}); } -export function* validateResetToken(ctx) { - const Reset = ctx.db.Reset; - return yield Reset.findOne({token: ctx.params.id}); +export function* validateResetToken() { + const Reset = this.db.Reset; + return yield Reset.findOne({token: this.params.id}); } -export function* updatePassword(ctx, userId, password) { - const Users = ctx.db.Users; +export function* updatePassword(userId, password) { + const Users = this.db.Users; const cryptedPassword = yield passwords.crypt(password); yield Users.update({_id: userId}, {'$set': {'salted_password': cryptedPassword}}); } -export function* activateUser(ctx, code) { - const Users = ctx.db.Users; +export function* activateUser(code) { + const Users = this.db.Users; const user = yield Users.findOne({activationCode: code}); if (user) { Users.updateOne({_id: user._id}, {'$unset': {activationCode: ''}}); - yield setupSession(ctx, user); + yield setupSession(this, user); } else { return false; } diff --git a/web/public/50x.html b/web/public/50x.html index 738b514..d8dd755 100644 --- a/web/public/50x.html +++ b/web/public/50x.html @@ -1,5 +1,5 @@ - +
@@ -10,29 +10,23 @@ - -