From 79bdeedeb8032aac60ed0d062226ec9b6d154122 Mon Sep 17 00:00:00 2001 From: kingcody Date: Sat, 11 Feb 2017 01:20:29 -0500 Subject: [PATCH 1/3] fix(events): mongoose event registration As of `mongoose@4.8.0` schema events should be registered before the model is compiled fixes #2479 --- .../app/server/api/user(auth)/user.events.js | 17 ++++++++++------- .../user(auth)/user.model(mongooseModels).js | 4 +++- templates/endpoint/basename.events(models).js | 17 ++++++++++------- .../endpoint/basename.model(mongooseModels).js | 2 ++ 4 files changed, 25 insertions(+), 15 deletions(-) diff --git a/templates/app/server/api/user(auth)/user.events.js b/templates/app/server/api/user(auth)/user.events.js index f8ebebe90..4e3ec953a 100644 --- a/templates/app/server/api/user(auth)/user.events.js +++ b/templates/app/server/api/user(auth)/user.events.js @@ -4,8 +4,7 @@ 'use strict'; -import {EventEmitter} from 'events';<% if (filters.mongooseModels) { %> -import User from './user.model';<% } if (filters.sequelizeModels) { %> +import {EventEmitter} from 'events';<% if (filters.sequelizeModels) { %> import {User} from '../../sqldb';<% } %> var UserEvents = new EventEmitter(); @@ -24,10 +23,12 @@ var events = { };<% } %> // Register the event emitter to the model events -for(var e in events) { - let event = events[e];<% if (filters.mongooseModels) { %> - User.schema.post(e, emitEvent(event));<% } if (filters.sequelizeModels) { %> - User.hook(e, emitEvent(event));<% } %> +function registerEvents(User) { + for(var e in events) { + let event = events[e];<% if (filters.mongooseModels) { %> + User.post(e, emitEvent(event));<% } if (filters.sequelizeModels) { %> + User.hook(e, emitEvent(event));<% } %> + } } function emitEvent(event) { @@ -37,5 +38,7 @@ function emitEvent(event) { done(null);<% } %> } } - +<% if (filters.sequelizeModels) { %> +registerEvents(User);<% } if (filters.mongooseModels) { %> +export {registerEvents};<% } %> export default UserEvents; diff --git a/templates/app/server/api/user(auth)/user.model(mongooseModels).js b/templates/app/server/api/user(auth)/user.model(mongooseModels).js index f23208f12..b19e3ff36 100644 --- a/templates/app/server/api/user(auth)/user.model(mongooseModels).js +++ b/templates/app/server/api/user(auth)/user.model(mongooseModels).js @@ -2,7 +2,8 @@ /*eslint no-invalid-this:0*/ import crypto from 'crypto'; mongoose.Promise = require('bluebird'); -import mongoose, {Schema} from 'mongoose';<% if(filters.oauth) { %> +import mongoose, {Schema} from 'mongoose'; +import {registerEvents} from './user.events';<% if(filters.oauth) { %> const authTypes = ['github', 'twitter', 'facebook', 'google'];<% } %> @@ -257,4 +258,5 @@ UserSchema.methods = { } }; +registerEvents(UserSchema); export default mongoose.model('User', UserSchema); diff --git a/templates/endpoint/basename.events(models).js b/templates/endpoint/basename.events(models).js index 374c8108f..6040e60dd 100644 --- a/templates/endpoint/basename.events(models).js +++ b/templates/endpoint/basename.events(models).js @@ -4,8 +4,7 @@ 'use strict'; -import {EventEmitter} from 'events';<% if(filters.mongooseModels) { %> -import <%= classedName %> from './<%= basename %>.model';<% } if(filters.sequelizeModels) { %> +import {EventEmitter} from 'events';<% if(filters.sequelizeModels) { %> var <%= classedName %> = require('<%= relativeRequire(config.get('registerModelsFile')) %>').<%= classedName %>;<% } %> var <%= classedName %>Events = new EventEmitter(); @@ -27,10 +26,12 @@ var events = { <%_ } -%> // Register the event emitter to the model events -for(var e in events) { - let event = events[e];<% if(filters.mongooseModels) { %> - <%= classedName %>.schema.post(e, emitEvent(event));<% } if(filters.sequelizeModels) { %> - <%= classedName %>.hook(e, emitEvent(event));<% } %> +function registerEvents(<%= classedName %>) { + for(var e in events) { + let event = events[e];<% if(filters.mongooseModels) { %> + <%= classedName %>.post(e, emitEvent(event));<% } if(filters.sequelizeModels) { %> + <%= classedName %>.hook(e, emitEvent(event));<% } %> + } } function emitEvent(event) { @@ -40,5 +41,7 @@ function emitEvent(event) { done(null);<% } %> }; } - +<% if (filters.sequelizeModels) { %> +registerEvents(<%= classedName %>);<% } if (filters.mongooseModels) { %> +export {registerEvents};<% } %> export default <%= classedName %>Events; diff --git a/templates/endpoint/basename.model(mongooseModels).js b/templates/endpoint/basename.model(mongooseModels).js index 58391e89a..201a56502 100644 --- a/templates/endpoint/basename.model(mongooseModels).js +++ b/templates/endpoint/basename.model(mongooseModels).js @@ -1,6 +1,7 @@ 'use strict'; import mongoose from 'mongoose'; +import {registerEvents} from './<%= basename %>.events'; var <%= classedName %>Schema = new mongoose.Schema({ name: String, @@ -8,4 +9,5 @@ var <%= classedName %>Schema = new mongoose.Schema({ active: Boolean }); +registerEvents(<%= classedName %>Schema); export default mongoose.model('<%= classedName %>', <%= classedName %>Schema); From ccea1f3a807c9f264a4f0d0048bec4ee3279a52e Mon Sep 17 00:00:00 2001 From: Andrew Koroluk Date: Sat, 13 May 2017 01:53:46 -0400 Subject: [PATCH 2/3] feat(gen): replace socket.io w/ primus + uws #2565 --- Gruntfile.js | 2 +- .../01_Getting_Started/04_Project_Overview.md | 2 +- src/generators/app/index.js | 10 ++-- src/generators/endpoint/index.js | 11 ++-- src/test/endpoint.test.js | 2 +- src/test/fixtures/.yo-rc.json | 4 +- src/test/get-expected-files.js | 8 +-- src/test/main.test.js | 8 +-- templates/app/_package.json | 8 +-- .../app/client/app/main/main.component.js | 20 +++---- .../client/app/main/main.component.spec.js | 6 +- templates/app/client/app/main/main.html | 2 +- templates/app/client/app/main/main.module.js | 4 +- .../socket.mock.js | 0 .../socket.service.js | 44 +++++++++----- templates/app/server/app.js | 30 +++++++--- .../app/server/config/socketio(socketio).js | 57 ------------------ templates/app/server/config/websockets(ws).js | 59 +++++++++++++++++++ templates/app/webpack.server.js | 6 +- ...et(socketio).js => basename.socket(ws).js} | 13 ++-- test/fixtures/.yo-rc.json | 4 +- 21 files changed, 164 insertions(+), 136 deletions(-) rename templates/app/client/components/{socket(socketio) => socket(ws)}/socket.mock.js (100%) rename templates/app/client/components/{socket(socketio) => socket(ws)}/socket.service.js (55%) delete mode 100644 templates/app/server/config/socketio(socketio).js create mode 100644 templates/app/server/config/websockets(ws).js rename templates/endpoint/{basename.socket(socketio).js => basename.socket(ws).js} (65%) diff --git a/Gruntfile.js b/Gruntfile.js index e0c11dd49..1a71c410a 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -112,7 +112,7 @@ module.exports = function (grunt) { testing: 'jasmine', auth: true, oauth: ['googleAuth', 'twitterAuth'], - socketio: true + ws: true }; var deps = [ diff --git a/docs/01_Getting_Started/04_Project_Overview.md b/docs/01_Getting_Started/04_Project_Overview.md index 6b8ac7a78..abfedb0c8 100644 --- a/docs/01_Getting_Started/04_Project_Overview.md +++ b/docs/01_Getting_Started/04_Project_Overview.md @@ -111,7 +111,7 @@ webpack.make.js // main file for Webpack configuration │ local.env.js // ignored by Git │ local.env.sample.js // sensitive environment variables are stored here, and added at server start. Copy to `local.env.js`. │ seed.js // re-seeds database with fresh data - │ socketio.js // Socket IO configuration / imports + │ websockets.js // WebSocket configuration / imports │ └───environment development.js diff --git a/src/generators/app/index.js b/src/generators/app/index.js index dd6bfc4d8..51da70f27 100644 --- a/src/generators/app/index.js +++ b/src/generators/app/index.js @@ -270,14 +270,14 @@ export class Generator extends Base { }] }, { type: 'confirm', - name: 'socketio', - message: 'Would you like to use socket.io?', + name: 'ws', + message: 'Would you like to use WebSockets?', // to-do: should not be dependent on ODMs when: answers => answers.odms && answers.odms.length !== 0, default: true }]).then(answers => { - if(answers.socketio) this.filters.socketio = true; - insight.track('socketio', !!answers.socketio); + if(answers.ws) this.filters.ws = true; + insight.track('ws', !!answers.ws); if(answers.auth) this.filters.auth = true; insight.track('auth', !!answers.auth); @@ -374,7 +374,7 @@ export class Generator extends Base { this.config.set('pluralizeRoutes', true); this.config.set('insertSockets', true); - this.config.set('registerSocketsFile', 'server/config/socketio.js'); + this.config.set('registerSocketsFile', 'server/config/websockets.js'); this.config.set('socketsNeedle', '// Insert sockets below'); this.config.set('insertModels', true); diff --git a/src/generators/endpoint/index.js b/src/generators/endpoint/index.js index 16a7f5764..f31a3bdcb 100644 --- a/src/generators/endpoint/index.js +++ b/src/generators/endpoint/index.js @@ -97,7 +97,7 @@ export class Generator extends NamedBase { end() { if(this.config.get('insertRoutes')) { var routesFile = this.config.get('registerRoutesFile'); - var reqPath = this.relativeRequire(this.routeDest, routesFile); + let reqPath = this.relativeRequire(this.routeDest, routesFile); var routeConfig = { file: routesFile, needle: this.config.get('routesNeedle'), @@ -108,15 +108,14 @@ export class Generator extends NamedBase { this.rewriteFile(routeConfig); } - if(this.filters.socketio && this.config.get('insertSockets')) { + if(this.filters.ws && this.config.get('insertSockets')) { var socketsFile = this.config.get('registerSocketsFile'); - var reqPath = this.relativeRequire(this.routeDest + '/' + this.basename + - '.socket', socketsFile); + let reqPath = this.relativeRequire(this.routeDest + '/' + this.basename + '.socket', socketsFile); var socketConfig = { file: socketsFile, needle: this.config.get('socketsNeedle'), splicable: [ - `require('${reqPath}').register(socket);` + `require('${reqPath}').register,` ] }; this.rewriteFile(socketConfig); @@ -124,7 +123,7 @@ export class Generator extends NamedBase { if(this.filters.sequelize && this.config.get('insertModels')) { var modelsFile = this.config.get('registerModelsFile'); - var reqPath = this.relativeRequire(`${this.routeDest}/${this.basename}.model`, modelsFile); + let reqPath = this.relativeRequire(`${this.routeDest}/${this.basename}.model`, modelsFile); var modelConfig = { file: modelsFile, needle: this.config.get('modelsNeedle'), diff --git a/src/test/endpoint.test.js b/src/test/endpoint.test.js index 5101bfd73..145d680f3 100644 --- a/src/test/endpoint.test.js +++ b/src/test/endpoint.test.js @@ -30,7 +30,7 @@ const defaultOptions = { odms: ['mongoose'], auth: true, oauth: [], - socketio: true + ws: true }; function runEndpointGen(name, opt={}) { diff --git a/src/test/fixtures/.yo-rc.json b/src/test/fixtures/.yo-rc.json index ca971c119..bace79b9f 100644 --- a/src/test/fixtures/.yo-rc.json +++ b/src/test/fixtures/.yo-rc.json @@ -7,7 +7,7 @@ "routesBase": "/api/", "pluralizeRoutes": true, "insertSockets": true, - "registerSocketsFile": "server/config/socketio.js", + "registerSocketsFile": "server/config/websockets.js", "socketsNeedle": "// Insert sockets below", "insertModels": true, "registerModelsFile": "server/sqldb/index.js", @@ -21,7 +21,7 @@ "uirouter": true, "bootstrap": true, "uibootstrap": true, - "socketio": true, + "ws": true, "auth": true, "models": true, "mongooseModels": true, diff --git a/src/test/get-expected-files.js b/src/test/get-expected-files.js index 3b82811a9..235c42caf 100644 --- a/src/test/get-expected-files.js +++ b/src/test/get-expected-files.js @@ -203,18 +203,18 @@ export function app(options) { 'client/components/oauth-buttons/oauth-buttons.' + stylesheet, 'client/components/oauth-buttons/oauth-buttons.' + markup, 'client/components/oauth-buttons/oauth-buttons.component.' + script, - 'client/components/oauth-buttons/oauth-buttons.component.spec.' + script, + 'client/components/oauth-buttons/oauth-buttons.component.spec.' + script, 'e2e/components/oauth-buttons/oauth-buttons.po.js' ]); } - /* Socket.IO */ - if (options.socketio) { + /* WebSockets */ + if (options.ws) { files = files.concat([ 'client/components/socket/socket.service.' + script, 'client/components/socket/socket.mock.' + script, 'server/api/thing/thing.socket.js', - 'server/config/socketio.js' + 'server/config/websockets.js' ]); } diff --git a/src/test/main.test.js b/src/test/main.test.js index 79dbfff5d..43e337a98 100644 --- a/src/test/main.test.js +++ b/src/test/main.test.js @@ -27,7 +27,7 @@ const defaultOptions = { odms: ['mongoose'], auth: true, oauth: [], - socketio: true + ws: true }; const TEST_DIR = __dirname; @@ -198,7 +198,7 @@ describe('angular-fullstack:app', function() { odms: ['mongoose'], auth: true, oauth: ['twitterAuth', 'facebookAuth', 'googleAuth'], - socketio: true, + ws: true, bootstrap: true, uibootstrap: true }; @@ -270,7 +270,7 @@ describe('angular-fullstack:app', function() { odms: ['sequelize'], auth: true, oauth: ['twitterAuth', 'facebookAuth', 'googleAuth'], - socketio: true, + ws: true, bootstrap: true, uibootstrap: true }; @@ -343,7 +343,7 @@ describe('angular-fullstack:app', function() { odms: [], auth: false, oauth: [], - socketio: false, + ws: false, bootstrap: false, uibootstrap: false }; diff --git a/templates/app/_package.json b/templates/app/_package.json index 91d6b3c4f..3ca824ff0 100644 --- a/templates/app/_package.json +++ b/templates/app/_package.json @@ -41,10 +41,10 @@ "passport-local": "^1.0.0",<% } %><% if(filters.facebookAuth) { %> "passport-facebook": "^2.0.0",<% } %><% if(filters.twitterAuth) { %> "passport-twitter": "^1.0.3",<% } %><% if(filters.googleAuth) { %> - "passport-google-oauth20": "^1.0.0",<% } %><% if(filters.socketio) { %> - "socket.io": "^1.3.5", - "socket.io-client": "^1.3.5", - "socketio-jwt": "^4.2.0",<% } %> + "passport-google-oauth20": "^1.0.0",<% } %><% if(filters.ws) { %> + "primus": "^7.0.1", + "primus-emit": "^1.0.0", + "uws": "^0.14.5",<% } %> "serve-favicon": "^2.3.0", "shrink-ray": "^0.1.3" }, diff --git a/templates/app/client/app/main/main.component.js b/templates/app/client/app/main/main.component.js index d8736e080..79d0bdc79 100644 --- a/templates/app/client/app/main/main.component.js +++ b/templates/app/client/app/main/main.component.js @@ -1,4 +1,4 @@ -import { Component, OnInit<% if(filters.socketio) { %>, OnDestroy<% } %> } from '@angular/core'; +import { Component, OnInit<% if(filters.ws) { %>, OnDestroy<% } %> } from '@angular/core'; import { Http } from '@angular/http'; import { Observable } from 'rxjs/Observable'; import { SocketService } from '../../components/socket/socket.service'; @@ -8,8 +8,8 @@ import { SocketService } from '../../components/socket/socket.service'; template: require('./main.<%=templateExt%>'), styles: [require('./main.<%=styleExt%>')], }) -export class MainComponent implements OnInit<% if(filters.socketio) { %>, OnDestroy<% } %> { - <%_ if(filters.socketio) { -%> +export class MainComponent implements OnInit<% if(filters.ws) { %>, OnDestroy<% } %> { + <%_ if(filters.ws) { -%> SocketService;<% } %> awesomeThings = []; <%_ if(filters.models) { -%> @@ -17,9 +17,9 @@ export class MainComponent implements OnInit<% if(filters.socketio) { %>, OnDest <%_ if(filters.babel) { -%> static parameters = [Http, SocketService];<% } %> - constructor(<%= private() %>http: Http<% if(filters.socketio) { %>, <%= private() %>socketService: SocketService<% } %>) { + constructor(<%= private() %>http: Http<% if(filters.ws) { %>, <%= private() %>socketService: SocketService<% } %>) { this.Http = http; - <%_ if(filters.socketio) { -%> + <%_ if(filters.ws) { -%> this.SocketService = socketService;<% } %> } @@ -27,15 +27,15 @@ export class MainComponent implements OnInit<% if(filters.socketio) { %>, OnDest this.Http.get('/api/things') .map(res => { return res.json(); - <%_ if(filters.socketio) { -%> - // this.SocketService.syncUpdates('thing', this.awesomeThings);<% } %> }) .catch(err => Observable.throw(err.json().error || 'Server error')) .subscribe(things => { this.awesomeThings = things; + <%_ if(filters.ws) { -%> + this.SocketService.syncUpdates('thing', this.awesomeThings);<% } %> }); }<% if (filters.models) { %> - <%_ if(filters.socketio) { %> + <%_ if(filters.ws) { %> ngOnDestroy() { this.SocketService.unsyncUpdates('thing'); @@ -50,7 +50,7 @@ export class MainComponent implements OnInit<% if(filters.socketio) { %>, OnDest .map(res => res.json()) .catch(err => Observable.throw(err.json().error || 'Server error')) .subscribe(thing => { - this.awesomeThings.push(thing); + console.log('Added Thing:', thing); }); } } @@ -60,7 +60,7 @@ export class MainComponent implements OnInit<% if(filters.socketio) { %>, OnDest .map(res => res.json()) .catch(err => Observable.throw(err.json().error || 'Server error')) .subscribe(() => { - this.awesomeThings.splice(this.awesomeThings.findIndex(el => el._id === thing._id), 1); + console.log('Deleted Thing'); }); }<% } %> } diff --git a/templates/app/client/app/main/main.component.spec.js b/templates/app/client/app/main/main.component.spec.js index ac10b1c54..4c3f7ea33 100644 --- a/templates/app/client/app/main/main.component.spec.js +++ b/templates/app/client/app/main/main.component.spec.js @@ -8,7 +8,7 @@ describe('Component: MainComponent', function() { beforeEach(angular.mock.module(main)); <%_ if (filters.uirouter) { _%> beforeEach(angular.mock.module('stateMock'));<% } _%> - <%_ if (filters.socketio) { _%> + <%_ if (filters.ws) { _%> beforeEach(angular.mock.module('socketMock'));<% } %> var scope; @@ -22,7 +22,7 @@ describe('Component: MainComponent', function() { $http, $componentController, $rootScope<% if (filters.uirouter) {%>, - $state<% } %><% if (filters.socketio) {%>, + $state<% } %><% if (filters.ws) {%>, socket<% } %>) { $httpBackend = _$httpBackend_; $httpBackend.expectGET('/api/things') @@ -32,7 +32,7 @@ describe('Component: MainComponent', function() { state = $state;<% } %> mainComponent = $componentController('main', { $http: $http, - $scope: scope<% if (filters.socketio) {%>, + $scope: scope<% if (filters.ws) {%>, socket: socket<% } %> }); })); diff --git a/templates/app/client/app/main/main.html b/templates/app/client/app/main/main.html index df7fe9fee..ee86e6381 100644 --- a/templates/app/client/app/main/main.html +++ b/templates/app/client/app/main/main.html @@ -17,7 +17,7 @@

Features:

- <% if (filters.socketio) { %> + <% if (filters.ws) { %>
diff --git a/templates/app/client/app/main/main.module.js b/templates/app/client/app/main/main.module.js index cda298e89..c158a9f29 100644 --- a/templates/app/client/app/main/main.module.js +++ b/templates/app/client/app/main/main.module.js @@ -9,7 +9,7 @@ import { RouterModule, Routes } from '@angular/router';<% } %> import { TooltipModule } from 'ng2-bootstrap';<% } %> import { MainComponent } from './main.component'; -<%_ if(filters.socketio) { -%> +<%_ if(filters.ws) { -%> import { SocketService } from '../../components/socket/socket.service';<% } %> <%_ if(filters.ngroute) { _%> @@ -37,7 +37,7 @@ export const STATES = [ declarations: [ MainComponent, ], - <%_ if(filters.socketio) { -%> + <%_ if(filters.ws) { -%> providers: [ SocketService, ],<% } %> diff --git a/templates/app/client/components/socket(socketio)/socket.mock.js b/templates/app/client/components/socket(ws)/socket.mock.js similarity index 100% rename from templates/app/client/components/socket(socketio)/socket.mock.js rename to templates/app/client/components/socket(ws)/socket.mock.js diff --git a/templates/app/client/components/socket(socketio)/socket.service.js b/templates/app/client/components/socket(ws)/socket.service.js similarity index 55% rename from templates/app/client/components/socket(socketio)/socket.service.js rename to templates/app/client/components/socket(ws)/socket.service.js index 978221287..ccd8a7ee9 100644 --- a/templates/app/client/components/socket(socketio)/socket.service.js +++ b/templates/app/client/components/socket(ws)/socket.service.js @@ -1,18 +1,32 @@ 'use strict'; +import Primus from './primus'; +import primusEmit from 'primus-emit'; import { Injectable } from '@angular/core'; import { noop, find, remove } from 'lodash'; -import io from 'socket.io-client'; -import constants from '../../app/app.constants'; @Injectable() export class SocketService { - socket; + primus; constructor() { - this.socket = io(constants.env === 'development' ? `localhost:${constants.port}` : '', { - // Send auth token on connection, you will need to DI the Auth service above - // 'query': 'token=' + Auth.getToken() + const primus = Primus.connect(); + primus.plugin('emit', primusEmit); + + primus.on('open', function open() { + console.log('Connection opened'); + }); + + if(process.env.NODE_ENV === 'development') { + primus.on('data', function message(data) { + console.log('Socket:', data); + }); + } + + primus.on('info', data => { + console.log('info:', data); }); + + this.primus = primus; } /** @@ -29,10 +43,11 @@ export class SocketService { /** * Syncs item creation/updates on 'model:save' */ - this.socket.on(`${modelName}:save`, function(item) { - var oldItem = find(array, {_id: item._id}); - var index = array.indexOf(oldItem); - var event = 'created'; + this.primus.on(`${modelName}:save`, item => { + console.log(item); + let oldItem = find(array, {_id: item._id}); + let index = array.indexOf(oldItem); + let event = 'created'; // replace oldItem if it exists // otherwise just add item to the collection @@ -49,10 +64,9 @@ export class SocketService { /** * Syncs removed items on 'model:remove' */ - this.socket.on(`${modelName}:remove`, function(item) { - var event = 'deleted'; + this.primus.on(`${modelName}:remove`, item => { remove(array, {_id: item._id}); - cb(event, item, array); + cb('deleted', item, array); }); } @@ -62,7 +76,7 @@ export class SocketService { * @param modelName */ unsyncUpdates(modelName) { - this.socket.removeAllListeners(`${modelName}:save`); - this.socket.removeAllListeners(`${modelName}:remove`); + this.primus.removeAllListeners(`${modelName}:save`); + this.primus.removeAllListeners(`${modelName}:remove`); } } diff --git a/templates/app/server/app.js b/templates/app/server/app.js index 6f3a9dcf6..265c1da59 100644 --- a/templates/app/server/app.js +++ b/templates/app/server/app.js @@ -10,6 +10,11 @@ mongoose.Promise = require('bluebird');<% } %><% if (filters.sequelize) { %> import sqldb from './sqldb';<% } %> import config from './config/environment'; import http from 'http'; +<%_ if (filters.ws) { -%> +import initWebSocketServer from './config/websockets';<% } %> +import expressConfig from './config/express'; +import registerRoutes from './routes'; + <% if (filters.mongoose) { %> // Connect to MongoDB mongoose.connect(config.mongo.uri, config.mongo.options); @@ -25,13 +30,11 @@ if(config.seedDB) { <% } %> // Setup server var app = express(); -var server = http.createServer(app);<% if (filters.socketio) { %> -var socketio = require('socket.io')(server, { - serveClient: false -}); -require('./config/socketio').default(socketio);<% } %> -require('./config/express').default(app); -require('./routes').default(app); +var server = http.createServer(app); +<%_ if(filters.ws) { -%> +const wsInitPromise = initWebSocketServer(server);<% } %> +expressConfig(app); +registerRoutes(app); // Start server function startServer() { @@ -41,12 +44,21 @@ function startServer() { } <% if(filters.sequelize) { %> sqldb.sequelize.sync() + <%_ if(filters.ws) { -%> + .then(wsInitPromise)<% } %> .then(startServer) - .catch(function(err) { + .catch(err => { console.log('Server failed to start due to error: %s', err); }); <% } else { %> -setImmediate(startServer); +<%_ if(filters.ws) { -%> +wsInitPromise + .then(startServer) + .catch(err => { + console.log('Server failed to start due to error: %s', err); + });<% } %> +<%_ if(!filters.ws) { -%> +setImmediate(startServer);<% } %> <% } %> // Expose app exports = module.exports = app; diff --git a/templates/app/server/config/socketio(socketio).js b/templates/app/server/config/socketio(socketio).js deleted file mode 100644 index 367677f82..000000000 --- a/templates/app/server/config/socketio(socketio).js +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Socket.io configuration - */ -'use strict'; - -// import config from './environment'; - -// When the user disconnects.. perform this -function onDisconnect(/*socket*/) {} - -// When the user connects.. perform this -function onConnect(socket) { - // When the client emits 'info', this listens and executes - socket.on('info', data => { - socket.log(JSON.stringify(data, null, 2)); - }); - - // Insert sockets below - -} - -export default function(socketio) { - // socket.io (v1.x.x) is powered by debug. - // In order to see all the debug output, set DEBUG (in server/config/local.env.js) to including the desired scope. - // - // ex: DEBUG: "http*,socket.io:socket" - - // We can authenticate socket.io users and access their token through socket.decoded_token - // - // 1. You will need to send the token in `client/components/socket/socket.service.js` - // - // 2. Require authentication here: - // socketio.use(require('socketio-jwt').authorize({ - // secret: config.secrets.session, - // handshake: true - // })); - - socketio.on('connection', function(socket) { - socket.address = `${socket.request.connection.remoteAddress}:${socket.request.connection.remotePort}`; - - socket.connectedAt = new Date(); - - socket.log = function(...data) { - console.log(`SocketIO ${socket.nsp.name} [${socket.address}]`, ...data); - }; - - // Call onDisconnect. - socket.on('disconnect', () => { - onDisconnect(socket); - socket.log('DISCONNECTED'); - }); - - // Call onConnect. - onConnect(socket); - socket.log('CONNECTED'); - }); -} diff --git a/templates/app/server/config/websockets(ws).js b/templates/app/server/config/websockets(ws).js new file mode 100644 index 000000000..cef152f2d --- /dev/null +++ b/templates/app/server/config/websockets(ws).js @@ -0,0 +1,59 @@ +/** + * Socket.io configuration + */ +'use strict'; +import path from 'path'; +import Primus from 'primus'; +import primusEmit from 'primus-emit'; + +const registerFunctions = [ + // Insert sockets below + require('../api/thing/thing.socket').register, +]; + +// When the user disconnects.. perform this +function onDisconnect(spark) { + console.info(`WebSocket from ${spark.address.ip}:${spark.address.port} disconnected`); +} + +// When the user connects.. perform this +function onConnect(spark) { + console.info(`WebSocket from ${spark.address.ip}:${spark.address.port} connected`); + + // When the client emits 'info', this listens and executes + spark.on('info', data => { + spark.log(JSON.stringify(data, null, 2)); + }); + + // Register the spark with each WebSocket event handler + for(let register of registerFunctions) { + register(spark); + } +} + +let primus; + +export function broadcast(message) { + primus.forEach(spark => { + spark.emit('broadcast', message); + }); +} + +export default function initWebSocketServer(server) { + primus = new Primus(server, { + transformer: 'uws', + }); + primus.plugin('emit', primusEmit); + + primus.on('connection', onConnect); + primus.on('disconnection', onDisconnect); + + return new Promise((resolve, reject) => { + // Save the primus client library configured for our server settings + primus.save(path.join(__dirname, '../../client/components/socket/primus.js'), err => { + if(err) return reject(err); + + resolve(); + }); + }); +} diff --git a/templates/app/webpack.server.js b/templates/app/webpack.server.js index 06ea583ac..3abc5a508 100644 --- a/templates/app/webpack.server.js +++ b/templates/app/webpack.server.js @@ -33,10 +33,12 @@ export const server = new WebpackDevServer(compiler, { target: 'http://localhost:9000', secure: false, }, - '/socket.io': { + <%_ if(filters.ws) { -%> + '/primus': { target: 'http://localhost:9000', secure: false, - }, + ws: true, + },<% } %> }, }); diff --git a/templates/endpoint/basename.socket(socketio).js b/templates/endpoint/basename.socket(ws).js similarity index 65% rename from templates/endpoint/basename.socket(socketio).js rename to templates/endpoint/basename.socket(ws).js index 0662cb886..6010c130b 100644 --- a/templates/endpoint/basename.socket(socketio).js +++ b/templates/endpoint/basename.socket(ws).js @@ -9,21 +9,20 @@ import <%= classedName %>Events from './<%= basename %>.events'; // Model events to emit var events = ['save', 'remove']; -export function register(socket) { +export function register(spark) { // Bind model events to socket events - for(var i = 0, eventsLength = events.length; i < eventsLength; i++) { - var event = events[i]; - var listener = createListener(`<%= cameledName %>:${event}`, socket); + for(let event of events) { + var listener = createListener(`<%= cameledName %>:${event}`, spark); <%= classedName %>Events.on(event, listener); - socket.on('disconnect', removeListener(event, listener)); + spark.on('disconnect', removeListener(event, listener)); } } -function createListener(event, socket) { +function createListener(event, spark) { return function(doc) { - socket.emit(event, doc); + spark.emit(event, doc); }; } diff --git a/test/fixtures/.yo-rc.json b/test/fixtures/.yo-rc.json index ca971c119..bace79b9f 100644 --- a/test/fixtures/.yo-rc.json +++ b/test/fixtures/.yo-rc.json @@ -7,7 +7,7 @@ "routesBase": "/api/", "pluralizeRoutes": true, "insertSockets": true, - "registerSocketsFile": "server/config/socketio.js", + "registerSocketsFile": "server/config/websockets.js", "socketsNeedle": "// Insert sockets below", "insertModels": true, "registerModelsFile": "server/sqldb/index.js", @@ -21,7 +21,7 @@ "uirouter": true, "bootstrap": true, "uibootstrap": true, - "socketio": true, + "ws": true, "auth": true, "models": true, "mongooseModels": true, From 2ba3dd8d0677b03a23a6166da76908f6c1df68df Mon Sep 17 00:00:00 2001 From: Andrew Koroluk Date: Sun, 14 May 2017 04:27:36 -0400 Subject: [PATCH 3/3] fix(mocha): fix server not closing --- templates/app/mocha.global.js | 19 +++++++++++++++---- templates/app/server/app.js | 8 +++++++- templates/app/server/config/websockets(ws).js | 2 +- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/templates/app/mocha.global.js b/templates/app/mocha.global.js index c34b2da8c..949372363 100644 --- a/templates/app/mocha.global.js +++ b/templates/app/mocha.global.js @@ -1,8 +1,19 @@ import app from './';<% if (filters.mongoose) { %> import mongoose from 'mongoose';<% } %> -after(function(done) { - app.angularFullstack.on('close', () => done());<% if (filters.mongoose) { %> - mongoose.connection.close();<% } %> - app.angularFullstack.close(); +after(function() { + return Promise.all([ + // Add any promises here for processes that need to be closed before the tests can finish + <% if (filters.mongoose) { %> + new Promise(resolve => { + mongoose.connection.close(resolve); + }),<% } %> + new Promise(resolve => { + app.angularFullstack.close(resolve); + }), + <%_ if(filters.ws) { -%> + new Promise(resolve => { + app.primus.end(resolve); + }),<% } %> + ]); }); diff --git a/templates/app/server/app.js b/templates/app/server/app.js index 265c1da59..02d6f88af 100644 --- a/templates/app/server/app.js +++ b/templates/app/server/app.js @@ -45,7 +45,10 @@ function startServer() { <% if(filters.sequelize) { %> sqldb.sequelize.sync() <%_ if(filters.ws) { -%> - .then(wsInitPromise)<% } %> + .then(wsInitPromise) + .then(primus => { + app.primus = primus; + })<% } %> .then(startServer) .catch(err => { console.log('Server failed to start due to error: %s', err); @@ -53,6 +56,9 @@ sqldb.sequelize.sync() <% } else { %> <%_ if(filters.ws) { -%> wsInitPromise + .then(primus => { + app.primus = primus; + }) .then(startServer) .catch(err => { console.log('Server failed to start due to error: %s', err); diff --git a/templates/app/server/config/websockets(ws).js b/templates/app/server/config/websockets(ws).js index cef152f2d..ed6efcf57 100644 --- a/templates/app/server/config/websockets(ws).js +++ b/templates/app/server/config/websockets(ws).js @@ -53,7 +53,7 @@ export default function initWebSocketServer(server) { primus.save(path.join(__dirname, '../../client/components/socket/primus.js'), err => { if(err) return reject(err); - resolve(); + resolve(primus); }); }); }