diff --git a/api/app.js b/api/app.js index 0927185..859e3a3 100644 --- a/api/app.js +++ b/api/app.js @@ -129,6 +129,7 @@ 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)); diff --git a/api/routes/file.js b/api/routes/file.js index 10bc559..9b19baf 100644 --- a/api/routes/file.js +++ b/api/routes/file.js @@ -15,7 +15,7 @@ const debug = debugname('hostr-api:file'); const redisUrl = process.env.REDIS_URL || process.env.REDISTOGO_URL || 'redis://localhost:6379'; -const fileHost = process.env.FILE_HOST || 'https://localhost:4040'; +const fileHost = process.env.FILE_HOST || 'http://localhost:4040'; const storePath = process.env.STORE_PATH || path.join(process.env.HOME, '.hostr', 'uploads'); @@ -105,7 +105,7 @@ export function* post(next) { percentComplete = Math.floor(receivedSize * 100 / expectedSize); if (percentComplete > lastPercent && lastTick < Date.now() - 1000) { - const progressEvent = `{type: 'file-progress', data: {id: ${fileId}, complete: ${percentComplete}}}`; + const progressEvent = `{"type": "file-progress", "data": {"id": "${fileId}", "complete": ${percentComplete}}}`; this.redis.publish('/file/' + fileId, progressEvent); this.redis.publish('/user/' + this.user.id, progressEvent); lastTick = Date.now(); @@ -116,10 +116,10 @@ export function* post(next) { }); // Fire an event to let the frontend map the GUID it sent to the real ID. Allows immediate linking to the file - let acceptedEvent = `{type: 'file-accepted', data: {id: ${fileId}, guid: ${tempGuid}, href: ${fileHost}/${fileId}}}`; + let acceptedEvent = `{"type": "file-accepted", "data": {"id": "${fileId}", "guid": "${tempGuid}", "href": "${fileHost}/${fileId}"}}`; this.redis.publish('/user/' + this.user.id, acceptedEvent); // Fire final upload progress event so users know it's now processing - const completeEvent = `{type: 'file-progress', data: {id: ${fileId}, complete: 100}}`; + const completeEvent = `{"type": "file-progress", "data": {"id": "${fileId}", "complete": 100}}`; this.redis.publish('/file/' + fileId, completeEvent); this.redis.publish('/user/' + this.user.id, completeEvent); @@ -160,7 +160,7 @@ export function* post(next) { yield Files.updateOne({_id: fileId}, {$set: dbFile}); // Fire upload complete event - const addedEvent = `{type: 'file-added', data: ${formattedFile}}`; + const addedEvent = `{"type": "file-added", "data": ${JSON.stringify(formattedFile)}}`; this.redis.publish('/file/' + fileId, addedEvent); this.redis.publish('/user/' + this.user.id, addedEvent); this.status = 201; @@ -246,14 +246,14 @@ export function* del(id) { export function* events() { const pubsub = redis.connect(redisUrl); - pubsub.on('ready', function() { + pubsub.on('ready', () => { pubsub.subscribe(this.path); - }.bind(this)); + }); - pubsub.on('message', function(channel, message) { + pubsub.on('message', (channel, message) => { this.websocket.send(message); - }.bind(this)); - this.on('close', function() { + }); + this.websocket.on('close', function() { pubsub.quit(); }); } diff --git a/api/routes/user.js b/api/routes/user.js index 36ac916..06eea43 100644 --- a/api/routes/user.js +++ b/api/routes/user.js @@ -4,7 +4,7 @@ import co from 'co'; import passwords from 'passwords'; import debugname from 'debug'; -const debug = debugname('hostr-api:file'); +const debug = debugname('hostr-api:user'); const redisUrl = process.env.REDIS_URL || process.env.REDISTOGO_URL || 'redis://localhost:6379'; @@ -57,10 +57,10 @@ export function* settings() { export function* events() { const pubsub = redis.connect(redisUrl); - pubsub.on('message', function(channel, message) { + pubsub.on('message', (channel, message) => { this.websocket.send(message); - }.bind(this)); - pubsub.on('ready', function () { + }); + pubsub.on('ready', () => { this.websocket.on('message', co.wrap(function* (message) { let json; try{ @@ -76,9 +76,9 @@ export function* events() { } else { this.websocket.send('Invalid authentication token.'); } - })); - }.bind(this)); - this.on('close', function() { + }.bind(this))); + }); + this.websocket.on('close', () => { debug('Socket closed'); pubsub.quit(); }); diff --git a/app.js b/app.js index 9923a5c..f15619b 100644 --- a/app.js +++ b/app.js @@ -1,7 +1,14 @@ import koa from 'koa'; import mount from 'koa-mount'; +import route from 'koa-route'; +import websockify from 'koa-websocket'; +import redis from 'redis-url'; +import coRedis from 'co-redis'; +import co from 'co'; import spdy from 'spdy'; 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'; @@ -10,26 +17,37 @@ const debug = debugname('hostr'); storageInit(); -const app = koa(); +const app = websockify(koa()); app.keys = [process.env.KEYS || 'INSECURE']; +const redisUrl = process.env.REDIS_URL || process.env.REDISTOGO_URL || 'redis://localhost:6379'; + +let coRedisConn = {}; + +co(function*() { + coRedisConn = coRedis(redis.connect(redisUrl)); + coRedisConn.on('error', function (err) { + debug('Redis error ' + err); + }); +}).catch(function(err) { + console.error(err); +}); +app.ws.use(function*(next) { + this.redis = coRedisConn; + yield next; +}); + +app.ws.use(route.all('/api/user', userEvents)); +app.ws.use(route.all('/api/file/:id', fileEvents)); + app.use(mount('/api', api)); app.use(mount('/', web)); if (!module.parent) { - if (process.env.LOCALHOST_KEY) { - spdy.createServer({ - key: process.env.LOCALHOST_KEY, - cert: process.env.LOCALHOST_CRT - }, app.callback()).listen(4040, function() { - debug('Koa SPDY server listening on port ' + (process.env.PORT || 4040)); - }); - } else { - app.listen(process.env.PORT || 4040, function() { - debug('Koa HTTP server listening on port ' + (process.env.PORT || 4040)); - }); - } + app.listen(process.env.PORT || 4040, function() { + debug('Koa HTTP server listening on port ' + (process.env.PORT || 4040)); + }); } module.exports = app; diff --git a/package.json b/package.json index 34ede98..9516584 100644 --- a/package.json +++ b/package.json @@ -17,13 +17,13 @@ "jspm": "jspm install", "start": "npm run build && node -r 'babel/register' app.js", "test": "mongo hostr test/fixtures/mongo-user.js test/fixtures/mongo-file.js && mocha -r babel/register test/api test/web", - "watch": "nodemon -x \"node -r 'babel/register'\" -i web/public/ app.js", + "watch": "nodemon -x \"node -r 'babel/register'\" app.js", "watch-js": "babel -D -w -m system -d web/public/build web/public/src", "watch-sass": "node-sass -w -r -o web/public/styles/ web/public/styles/" }, "dependencies": { - "aws-sdk": "~2.1.42", - "babel": "~5.8.20", + "aws-sdk": "~2.1.44", + "babel": "~5.8.21", "basic-auth": "~1.0.3", "co": "~4.6.0", "co-busboy": "~1.3.0", @@ -61,13 +61,13 @@ "redis-url": "~1.2.1", "s3-upload-stream": "^1.0.7", "spdy": "~1.32.4", - "stripe": "~3.6.0", + "stripe": "^3.7.0", "supertest": "~1.0.1", "swig": "^1.4.2", "virustotal.js": "~0.3.1" }, "devDependencies": { - "eslint": "~1.0.0", + "eslint": "~1.1.0", "istanbul": "^0.3.17", "mocha": "~2.2.5", "nodemon": "~1.4.0", diff --git a/web/app.js b/web/app.js index ff63caa..6b62bb8 100644 --- a/web/app.js +++ b/web/app.js @@ -1,5 +1,4 @@ import path from 'path'; -import spdy from 'spdy'; import koa from 'koa'; import route from 'koa-route'; import views from 'koa-views'; @@ -153,18 +152,9 @@ app.use(route.get('/updaters/mac/changelog', function() { })); if (!module.parent) { - if (process.env.LOCALHOST_KEY) { - spdy.createServer({ - key: process.env.LOCALHOST_KEY, - cert: process.env.LOCALHOST_CRT - }, app.callback()).listen(4041, function() { - debug('Koa SPDY server listening on port ' + (process.env.PORT || 4041)); - }); - } else { - app.listen(process.env.PORT || 4041, function() { - debug('Koa HTTP server listening on port ' + (process.env.PORT || 4041)); - }); - } + app.listen(process.env.PORT || 4041, function() { + debug('Koa HTTP server listening on port ' + (process.env.PORT || 4041)); + }); } export default app; diff --git a/web/public/src/app.js b/web/public/src/app.js index a548ada..a165e69 100644 --- a/web/public/src/app.js +++ b/web/public/src/app.js @@ -22,7 +22,7 @@ var app = angular.module('hostr', [ app.factory('FileService', ['$resource', '$cacheFactory', FileService.factory]); app.factory('UserService', ['$resource', UserService.factory]); -app.factory('EventService', ['$rootScope', ReconnectingWebSocket, EventService.factory]); +app.factory('EventService', ['$rootScope', 'WebSocket', EventService.factory]); app.factory('TransactionService', ['$resource', '$cacheFactory', TransactionService.factory]); app.factory('SettingService', ['$http', SettingService.factory]); @@ -38,7 +38,6 @@ app.directive('searchShortcut', ['$document', searchShortcut]); app.directive('stripeSubscribe', ['$http', stripeSubscribe]); app.config(['$routeProvider', '$locationProvider', '$httpProvider', '$tooltipProvider', function($routeProvider, $locationProvider, $httpProvider, $tooltipProvider) { - $tooltipProvider.defaults.template = '/jspm_packages/npm/angular-strap@2.1.2/src/tooltip/tooltip.tpl.html'; if (typeof window.user !== 'undefined') { diff --git a/web/public/src/app/controllers.js b/web/public/src/app/controllers.js index 547752d..229da5e 100644 --- a/web/public/src/app/controllers.js +++ b/web/public/src/app/controllers.js @@ -1,5 +1,5 @@ export class FilesController { - constructor($scope, UserService, files) { + constructor($scope, UserService, EventService, files) { $scope.$root.user = UserService.get(); files.$promise.then(function() { $scope.$root.loadingView = false; @@ -22,7 +22,7 @@ export class FilesController { }; } } -FilesController.$inject = ['$scope', 'UserService', 'files']; +FilesController.$inject = ['$scope', 'UserService', 'EventService', 'files']; export class FileController { constructor ($scope, $rootScope, $routeParams, ReconnectingWebSocket, file) {