Skip to content
This repository

Activity Streams Boilerplate App Part 3 #4

Merged
merged 60 commits into from over 1 year ago
 +11,822 3,257

1 participant

Monica Wilkinson
Monica Wilkinson
Owner

This pull request contains all of the items discussed on http://blog.cloudfoundry.com/tag/asms/ Part 3 of the series.
Read this blog post for more info

  • Use Mongoose-Auth to store basic user information including information from Facebook, Twitter and Github
  • Fix for bcrypt module with native dependencies on Cloud Foundry
  • Use Mongo GridFS and ImageMagick to store user uploaded photos and profile pictures.
  • Perform powerful stream filtering, thanks to new capabilities exposed in MongoDB 2.0.
  • Update the UX of the app to become a real-time stream client using Bootstrap, Backbone.js and Jade.
added some commits August 03, 2012
Monica Wilkinson Fix to be able to use MongoLab 1497600
Monica Wilkinson Merge branch 'master' of github.com:ciberch/node-activities-boilerpla…
…te into mongolab
4216be3
Monica Wilkinson Changes to support offline mode aff18ec
Monica Wilkinson Modal and Create Activity 86228b1
Monica Wilkinson Before backbone 550c923
Monica Wilkinson Bump version of activity-streams-mongoose efe3881
Monica Wilkinson Defaults for Backbone Models - still not sure if I like having to def…
…ine for a second time my schema client side
78d43c2
Monica Wilkinson Link the new activity-streams-mongoose which supports location. Testi…
…ng out backbone models
28e591c
Monica Wilkinson Use a Backbone View for the activity list d3c479f
Monica Wilkinson Fix for bad glyphicons paths 637de99
Monica Wilkinson Like event not firing yet 573739f
Monica Wilkinson In progress synching via backbone models via socket.io bf9766d
Monica Wilkinson Add user friendly date 79246d7
Monica Wilkinson Remove separator because it messes up with filters b5417fd
Monica Wilkinson Add a photo upload form to test with 0135d8e
Monica Wilkinson Woohoo endpoints to upload and download pics via Mongo Grid. TODO: Ad…
…d user security
041419c
Monica Wilkinson Sample JSON response for stream 0b08a50
Monica Wilkinson Make it work on Cloud Foundry 507c006
Monica Wilkinson Add GUIDs so there are no conflicts with file names 4673fca
Monica Wilkinson ImageMagick never ceases to impress me - This is an initial commit wh…
…ich reads the original photo metadata
f16962a
Monica Wilkinson Cleanup code for photo ingestion e980d64
Monica Wilkinson Added code to resize to sm and xs using ImageMagick 60f6ecc
Monica Wilkinson Only authenticated users can upload photos 7fa299f
Monica Wilkinson fix deprecation warning 0569f6e
Monica Wilkinson Switch to use Mongoose Auth so we persist the users a07f221
Monica Wilkinson Display a user friendly date 4be5e4b
Monica Wilkinson Use Backbone's model events for triggering re render of view. Leverag…
…e user friendly date
003a039
Monica Wilkinson Lock down the everyauth gem to 2.x to it works with Mongoose-Auth 42d27c0
Monica Wilkinson Mongoose-Auth now fully working and saving common attributes to db 9471b92
Monica Wilkinson In case there is no object 6cff8af
Monica Wilkinson Use the new version of activity-streams-mongoose 109e12c
Monica Wilkinson Refactor MongoDB use to a separate module fa5060a
Monica Wilkinson Further cleanup to encapsulate all the asms logic fdc6b4c
Monica Wilkinson Normalized the data from the 3 different providers acc5f61
Monica Wilkinson Reduce the number of start activities 3cc5c59
Monica Wilkinson Make it Cloud Foundry ready 10f7a4e
Monica Wilkinson Get the user location from Twitter and Email from Facebook db37380
Monica Wilkinson Add validator for model 8001e2d
Monica Wilkinson Reduce the number of options so I dont have to create so many jade te…
…mplates :)
d353fc1
Monica Wilkinson Use a more specific message so we can add handling other types of mes…
…sages and REST operations with socket.io(and backbone)
11df1c0
Monica Wilkinson Use the latest activity-stream-mongoose module and shrinkwrap it to l…
…ock in exact versions.
de83bb5
Monica Wilkinson Change the activity create form to render client side via Backbone so…
… we can have more dynamic behavior
2fb0314
Monica Wilkinson Handle trimming strings directly 79af1d0
Monica Wilkinson Bubble up errors reading the stream and add more .js files to get min…
…ified and bundled
5dba60e
Monica Wilkinson Move the check for the user being logged in to index.jade 598a00e
Monica Wilkinson Make it clear to users that they can also sign up with Twitter, Faceb…
…ook or Github
df9703a
Monica Wilkinson Make the name only show on hover 8b8ba0f
Monica Wilkinson New jade templates for object types used in this app 48a0ed6
Monica Wilkinson New file to submit the photos via ajax fbd7263
Monica Wilkinson Update default backbone sync to use socket.io and replace activity cr…
…eate for jquery handlers to a Backbone view
89e5b4b
Monica Wilkinson Compiled jade and how to generate it deb8ef6
Monica Wilkinson ciberch commented on the diff September 10, 2012
README.md
@@ -34,6 +34,13 @@ npm install
34 34
 ```
35 35
 
36 36
 * Edit siteConfig.js if needed
  37
+* If you change the activities templates you need to regenerate the client side version of them, you can do this with `clientjade`
  38
+
  39
+```bash
  40
+npm install -g clientjade
  41
+clientjade views/*.* > public/js/templates.js
1
Monica Wilkinson Owner

Compile all jade templates into one js file for client side rendering

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Monica Wilkinson ciberch commented on the diff September 10, 2012
README.md
@@ -80,6 +87,7 @@ vmc env-add $APP_NAME facebook_app_secret=fb_secret
80 87
 vmc env-add $APP_NAME NODE_ENV=production
81 88
 vmc env-add $APP_NAME twitter_consumer_key=twitter_key
82 89
 vmc env-add $APP_NAME twitter_consumer_secret=twitter_secret
  90
+vmc env-add $APP_NAME TMP=tmp
1
Monica Wilkinson Owner

Used by https://github.com/felixge/node-formidable which is used for processing uploaded photos

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Monica Wilkinson ciberch merged commit 18dccb2 into from September 11, 2012
Monica Wilkinson ciberch closed this September 11, 2012
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 60 unique commits by 1 author.

Aug 03, 2012
Monica Wilkinson Fix to be able to use MongoLab 1497600
Monica Wilkinson Merge branch 'master' of github.com:ciberch/node-activities-boilerpla…
…te into mongolab
4216be3
Aug 19, 2012
Monica Wilkinson Changes to support offline mode aff18ec
Aug 22, 2012
Monica Wilkinson Modal and Create Activity 86228b1
Aug 23, 2012
Monica Wilkinson Before backbone 550c923
Monica Wilkinson Bump version of activity-streams-mongoose efe3881
Monica Wilkinson Defaults for Backbone Models - still not sure if I like having to def…
…ine for a second time my schema client side
78d43c2
Aug 24, 2012
Monica Wilkinson Link the new activity-streams-mongoose which supports location. Testi…
…ng out backbone models
28e591c
Monica Wilkinson Use a Backbone View for the activity list d3c479f
Monica Wilkinson Fix for bad glyphicons paths 637de99
Monica Wilkinson Like event not firing yet 573739f
Aug 28, 2012
Monica Wilkinson In progress synching via backbone models via socket.io bf9766d
Aug 29, 2012
Monica Wilkinson Add user friendly date 79246d7
Aug 30, 2012
Monica Wilkinson Remove separator because it messes up with filters b5417fd
Monica Wilkinson Add a photo upload form to test with 0135d8e
Monica Wilkinson Woohoo endpoints to upload and download pics via Mongo Grid. TODO: Ad…
…d user security
041419c
Monica Wilkinson Sample JSON response for stream 0b08a50
Monica Wilkinson Make it work on Cloud Foundry 507c006
Aug 31, 2012
Monica Wilkinson Add GUIDs so there are no conflicts with file names 4673fca
Monica Wilkinson ImageMagick never ceases to impress me - This is an initial commit wh…
…ich reads the original photo metadata
f16962a
Monica Wilkinson Cleanup code for photo ingestion e980d64
Monica Wilkinson Added code to resize to sm and xs using ImageMagick 60f6ecc
Monica Wilkinson Only authenticated users can upload photos 7fa299f
Monica Wilkinson fix deprecation warning 0569f6e
Sep 01, 2012
Monica Wilkinson Switch to use Mongoose Auth so we persist the users a07f221
Sep 02, 2012
Monica Wilkinson Display a user friendly date 4be5e4b
Monica Wilkinson Use Backbone's model events for triggering re render of view. Leverag…
…e user friendly date
003a039
Monica Wilkinson Lock down the everyauth gem to 2.x to it works with Mongoose-Auth 42d27c0
Monica Wilkinson Mongoose-Auth now fully working and saving common attributes to db 9471b92
Sep 04, 2012
Monica Wilkinson In case there is no object 6cff8af
Monica Wilkinson Use the new version of activity-streams-mongoose 109e12c
Monica Wilkinson Refactor MongoDB use to a separate module fa5060a
Sep 05, 2012
Monica Wilkinson Further cleanup to encapsulate all the asms logic fdc6b4c
Monica Wilkinson Normalized the data from the 3 different providers acc5f61
Monica Wilkinson Reduce the number of start activities 3cc5c59
Monica Wilkinson Make it Cloud Foundry ready 10f7a4e
Sep 07, 2012
Monica Wilkinson Get the user location from Twitter and Email from Facebook db37380
Sep 09, 2012
Monica Wilkinson Add validator for model 8001e2d
Sep 10, 2012
Monica Wilkinson Reduce the number of options so I dont have to create so many jade te…
…mplates :)
d353fc1
Monica Wilkinson Use a more specific message so we can add handling other types of mes…
…sages and REST operations with socket.io(and backbone)
11df1c0
Monica Wilkinson Use the latest activity-stream-mongoose module and shrinkwrap it to l…
…ock in exact versions.
de83bb5
Monica Wilkinson Change the activity create form to render client side via Backbone so…
… we can have more dynamic behavior
2fb0314
Monica Wilkinson Handle trimming strings directly 79af1d0
Monica Wilkinson Bubble up errors reading the stream and add more .js files to get min…
…ified and bundled
5dba60e
Monica Wilkinson Move the check for the user being logged in to index.jade 598a00e
Monica Wilkinson Make it clear to users that they can also sign up with Twitter, Faceb…
…ook or Github
df9703a
Monica Wilkinson Make the name only show on hover 8b8ba0f
Monica Wilkinson New jade templates for object types used in this app 48a0ed6
Monica Wilkinson New file to submit the photos via ajax fbd7263
Monica Wilkinson Update default backbone sync to use socket.io and replace activity cr…
…eate for jquery handlers to a Backbone view
89e5b4b
Monica Wilkinson Compiled jade and how to generate it deb8ef6
Monica Wilkinson No need for that b3b2445
Sep 11, 2012
Monica Wilkinson Switch to using regular MongoDB now in version 2.0 4fb1255
Monica Wilkinson Fixed bug with object Types and actor types 0d5bd62
Monica Wilkinson New grpahics 835dabe
Monica Wilkinson New hero unit look and better photo uploader 29334c8
Monica Wilkinson Better Sizes d91081c
Monica Wilkinson Better instructions f47c6da
Monica Wilkinson Fix to align Like and Post button better as well as Timestamp 2522723
Monica Wilkinson Switch to have filters which query the backend 333378c
This page is out of date. Refresh to see the latest.

Showing 60 changed files with 11,822 additions and 3,257 deletions. Show diff stats Hide diff stats

  1. 6  Makefile
  2. 8  README.md
  3. 1  cloudfoundry.json
  4. 198  lib/asms-client.js
  5. 156  lib/authentication.js
  6. 116  lib/socket-io-server.js
  7. 410  npm-shrinkwrap.json
  8. 22  package.json
  9. 1,166  public/css/bootstrap-responsive.css
  10. 21  public/css/bootstrap-responsive.min.css
  11. 4,150  public/css/bootstrap.css
  12. 698  public/css/bootstrap.min.css
  13. 92  public/css/client.css
  14. BIN  public/facebook.ico
  15. BIN  public/github.ico
  16. BIN  public/img/asms-blog.png
  17. BIN  public/img/generic_photo_icon.gif
  18. BIN  public/img/glyphicons-halflings-white.png
  19. BIN  public/img/glyphicons-halflings.png
  20. BIN  public/img/me.jpg
  21. 100  public/js/backbone/activity_create_view.js
  22. 1,431  public/js/backbone/backbone-0.9.2.js
  23. 77  public/js/backbone/models.js
  24. 77  public/js/backbone/views.js
  25. 1,021  public/js/bootstrap.js
  26. 4  public/js/bootstrap.min.js
  27. 68  public/js/helper.js
  28. 69  public/js/jquery.client.js
  29. 1,089  public/js/jquery.form.js
  30. 4  public/js/jquery.min.js
  31. 60  public/js/routers/dashboard.js
  32. 2,660  public/js/templates.js
  33. 32  public/js/underscore-min.js
  34. 56  public/js/views/activity_create.js
  35. 239  public/js/views/filters.js
  36. BIN  public/twitter.ico
  37. 459  server.js
  38. 9  siteConfig.js
  39. 75  test/fixtures/index.json
  40. 40  test/server-test.js
  41. 0  tmp/.gitkeep
  42. 50  views/actions.jade
  43. 36  views/activity.jade
  44. 95  views/activity_create.jade
  45. 9  views/activity_details.jade
  46. 16  views/activity_object.jade
  47. 7  views/activity_stream_actor.jade
  48. 17  views/application.jade
  49. 11  views/article.jade
  50. 13  views/auth.jade
  51. 15  views/filters.jade
  52. 56  views/index.jade
  53. 11  views/layout.jade
  54. 22  views/nav_bar.jade
  55. 8  views/person.jade
  56. 39  views/photo.jade
  57. 17  views/place.jade
  58. 17  views/service.jade
  59. 22  views/start-modal.jade
  60. 4  views/user-box.jade
6  Makefile
... ...
@@ -0,0 +1,6 @@
  1
+TESTS = $(shell find test/ -name '*.tobi.js' -o -name '*.test.js')
  2
+
  3
+test:
  4
+	@node_modules/.bin/mocha --reporter spec $(TESTS)
  5
+
  6
+.PHONY: test
8  README.md
Source Rendered
@@ -34,6 +34,13 @@ npm install
34 34
 ```
35 35
 
36 36
 * Edit siteConfig.js if needed
  37
+* If you change the activities templates you need to regenerate the client side version of them, you can do this with `clientjade`
  38
+
  39
+```bash
  40
+npm install -g clientjade
  41
+clientjade views/*.* > public/js/templates.js
  42
+```
  43
+
37 44
 * Run locally
38 45
 
39 46
 ``` bash
@@ -80,6 +87,7 @@ vmc env-add $APP_NAME facebook_app_secret=fb_secret
80 87
 vmc env-add $APP_NAME NODE_ENV=production
81 88
 vmc env-add $APP_NAME twitter_consumer_key=twitter_key
82 89
 vmc env-add $APP_NAME twitter_consumer_secret=twitter_secret
  90
+vmc env-add $APP_NAME TMP=tmp
83 91
 ```
84 92
 
85 93
 ## Finally
1  cloudfoundry.json
... ...
@@ -0,0 +1 @@
  1
+{ "ignoreNodeModules" : true }
198  lib/asms-client.js
... ...
@@ -0,0 +1,198 @@
  1
+module.exports = function(app, cf) {
  2
+    var https = require('https');
  3
+
  4
+    var getUri = function(path) {
  5
+        return app.siteConf.uri + path;
  6
+    }
  7
+
  8
+    var defaultStream = "firehose";
  9
+
  10
+    // Setup the Asms DB and User Schema and Auth
  11
+    var defaultAvatar = getUri('/img/codercat-sm.jpg');
  12
+
  13
+    var actorTypes = ['Person', 'Group', 'Application', 'Service'];
  14
+    var objectTypes = ['Photo', 'Application', 'Article', 'Person', 'Place', 'Service'];
  15
+    var verbs = ['Post', 'Favorite', 'Follow', 'Join', 'Like', 'Friend', 'Play', 'Save', 'Share', 'Tag', 'Create', 'Update', 'Read', 'Delete', 'Check In'];
  16
+
  17
+    var githubHash = {objectType: 'Service', displayName: 'GitHub', url: 'http://github.com', icon : {url: 'http://github.com/favicon.ico'}};
  18
+    var facebookHash = {objectType: 'Service', displayName: 'Facebook', url: 'http://facebook.com', icon : {url: 'http://facebook.com/favicon.ico'}};
  19
+    var twitterHash = {objectType: 'Service', displayName: 'Twitter', url: 'http://twitter.com', icon : {url: 'http://twitter.com/favicon.ico'}};
  20
+
  21
+    var streamLib = require('activity-streams-mongoose')({
  22
+        mongoUrl: app.siteConf.mongoUrl,
  23
+        redis: app.siteConf.redisOptions,
  24
+        defaultActor: defaultAvatar
  25
+    });
  26
+
  27
+    var authentication = new require('./authentication.js')(streamLib, app.siteConf);
  28
+
  29
+    // Moved normalization to only be done on pre save
  30
+    streamLib.types.UserSchema.pre('save', function (next) {
  31
+        var user = this;
  32
+        var svcUrl = null;
  33
+        if (user.fb && user.fb.id) {
  34
+            user.displayName = user.fb.name.full;
  35
+            asmsDB.ActivityObject.findOne().where('url', facebookHash.url).exec(function(err, doc){
  36
+                if (err) throw err;
  37
+                user.author = doc._id;
  38
+                // Need to fetch the users image...
  39
+                https.get({
  40
+                    'host': 'graph.facebook.com'
  41
+                    , 'path': '/me/picture?access_token='+ user.fb.accessToken
  42
+                }, function(response) {
  43
+                    user.image = {url: response.headers.location};
  44
+                    next();
  45
+                }).on('error', function(e) {
  46
+                    next();
  47
+                });
  48
+            })
  49
+        } else  {
  50
+            if (user.github && user.github.id) {
  51
+                user.displayName = user.github.name;
  52
+                var avatar = 'http://1.gravatar.com/avatar/'+ user.github.gravatarId + '?s=48'
  53
+                user.image = {url: avatar};
  54
+                svcUrl = githubHash.url;
  55
+            } else if (user.twit && user.twit.id) {
  56
+                if (user.twit.geoEnabled && user.twit.location){
  57
+                    //"ÜT: 34.075755,-118.393416"
  58
+                    var parts = user.twit.location.substr(4).split(',');
  59
+                    user.location = {position: {latitude: parts[0],longitude: parts[1]}};
  60
+                }
  61
+                user.displayName = user.twit.name;
  62
+                user.image = {url: user.twit.profileImageUrl};
  63
+                user.url = user.twit.url;
  64
+                svcUrl = twitterHash.url;
  65
+            }
  66
+
  67
+            if(!user.actor) {
  68
+                asmsDB.ActivityObject.findOne().where('url', svcUrl).exec(function(err, doc){
  69
+                    user.author = doc;
  70
+                    next();
  71
+                });
  72
+            } else {
  73
+                next();
  74
+            }
  75
+        }
  76
+      });
  77
+
  78
+    var asmsDB = new streamLib.DB(streamLib.db, streamLib.types);
  79
+    streamLib.asmsDB = asmsDB;
  80
+
  81
+    // Setup default Objects
  82
+    // TODO: Change to find or create by
  83
+    var thisInstance = {objectType : "AppInstance"};
  84
+    if (cf.app) {
  85
+        thisInstance.image = {url: getUri('/img/cf-process.jpg')};
  86
+        thisInstance.url = "http://" + cf.host + ":" + cf.port;
  87
+        thisInstance.displayName = "App Instance " + cf.app['instance_index'] + " at " + thisInstance.url;
  88
+        thisInstance.content = cf.app['instance_id']
  89
+    } else {
  90
+        thisInstance.displayName =  "Instance 0 -- Local";
  91
+    }
  92
+
  93
+    var appOwner = {displayName: app.siteConf.user_email, image:{url: getUri("/img/me.jpg")}};
  94
+
  95
+    var thisApp = new asmsDB.ActivityObject({
  96
+        objectType: 'Application',
  97
+        displayName: 'Activity Streams App',
  98
+        url: app.siteConf.uri,
  99
+        image:{url: getUri('/img/as-logo-sm.png')}
  100
+    });
  101
+
  102
+    thisApp.save(function (err) {
  103
+        if (err) throw err;
  104
+    });
  105
+
  106
+    var thisProvider = new asmsDB.ActivityObject({
  107
+        objectType: 'Service',
  108
+        'displayName': 'Cloud Foundry',
  109
+        icon:{url: 'http://www.cloudfoundry.com/images/favicon.ico'}
  110
+    });
  111
+    thisProvider.save(function(err){if (err) throw err});
  112
+
  113
+    var github = new asmsDB.ActivityObject(githubHash);
  114
+    github.save(function(err){if (err) throw err});
  115
+    var facebook = new asmsDB.ActivityObject(facebookHash);
  116
+    facebook.save(function(err){if (err) throw err});
  117
+    var twitter = new asmsDB.ActivityObject(twitterHash);
  118
+    twitter.save(function(err){if (err) throw err});
  119
+
  120
+    var getCurrentUserObject = function(session) {
  121
+        if (session.user) {
  122
+            return session.user;
  123
+        } else if(session.uid) {
  124
+            var currentUser = {};
  125
+            currentUser.displayName = session.uid;
  126
+            currentUser.image = { url: defaultAvatar};
  127
+            return currentUser;
  128
+        }
  129
+        return null;
  130
+    }
  131
+
  132
+    var publishActivity = function(desiredStream, currentUser, actHash) {
  133
+
  134
+        if (!currentUser) throw("Cannot publish activity without an actor");
  135
+        if (actHash && actHash.object && actHash.object.objectType && actHash.verb) {
  136
+            actHash.actor = currentUser;
  137
+
  138
+            if (!actHash.title) {
  139
+                actHash.title = actHash.verb  + "ed a new " + actHash.object.objectType;
  140
+            }
  141
+
  142
+            var act = new asmsDB.Activity(actHash);
  143
+            // Send back the message to the users room.
  144
+            act.publish(desiredStream);
  145
+        } else {
  146
+            console.log("Missing required parameters");
  147
+            console.log(actHash);
  148
+            throw(new Error("Missing required parameters"));
  149
+        }
  150
+    };
  151
+
  152
+    var getDistinct = function (req, res, next, term, init){
  153
+        var key = 'used.' + term;
  154
+        req[key] = init ? init : [];
  155
+        asmsDB.Activity.distinct(term, {streams: req.session.desiredStream}, function(err, docs) {
  156
+            if (!err && docs) {
  157
+                _.each(docs, function(result){
  158
+                    req[key].push(result);
  159
+                });
  160
+                next();
  161
+            } else {
  162
+                next(new Error('Failed to fetch distinct ' + term));
  163
+            }
  164
+        });
  165
+    }
  166
+
  167
+    return {
  168
+        helpers: {
  169
+            getUri: getUri,
  170
+            getCurrentUserObject : getCurrentUserObject,
  171
+            publishActivity : publishActivity,
  172
+            getDistinct : getDistinct
  173
+        },
  174
+        default :{
  175
+            owner: appOwner,
  176
+            provider : thisProvider,
  177
+            app: thisApp,
  178
+            instance: thisInstance,
  179
+            avatar: defaultAvatar,
  180
+            stream : defaultStream
  181
+        },
  182
+        users : {
  183
+            providers :{
  184
+                facebook: facebook,
  185
+                twitter: twitter,
  186
+                github : github
  187
+            }
  188
+        },
  189
+        streamLib : streamLib,
  190
+        asmsDB: asmsDB,
  191
+        authentication : authentication,
  192
+        metadata: {
  193
+            actorTypes: actorTypes,
  194
+            objectTypes: objectTypes,
  195
+            verbs: verbs
  196
+        }
  197
+    }
  198
+};
156  lib/authentication.js
... ...
@@ -1,92 +1,98 @@
1  
-var everyauth = require('everyauth');
2  
-var https = require('https');
3  
-module.exports = function Server(expressInstance, siteConf) {
4  
-	everyauth.debug = siteConf.debug;
  1
+module.exports = function Server(streamLib, siteConf) {
  2
+    var everyauth = require('everyauth');
  3
+    var types = streamLib.types;
5 4
 
6  
-	everyauth.everymodule.handleLogout( function (req, res) {
7  
-		delete req.session.user;
8  
-		req.logout();
9  
-		res.writeHead(303, { 'Location': this.logoutRedirectPath() });
10  
-		res.end();
11  
-	});
  5
+    var mongoose = require("mongoose")
  6
+        , Schema = mongoose.Schema
  7
+        , mongooseAuth = require('mongoose-auth')
  8
+      , User;
12 9
 
13  
-	// Facebook
14  
-	if (siteConf.external && siteConf.external.facebook) {
15  
-		everyauth.facebook
16  
-		.appId(siteConf.external.facebook.appId)
17  
-		.appSecret(siteConf.external.facebook.appSecret)
18  
-		.findOrCreateUser(function (session, accessToken, accessTokenExtra, facebookUserMetaData) {return true;})
19  
-		.redirectPath('/');
20  
-	}
21  
-
22  
-	// Twitter
23  
-	if (siteConf.external && siteConf.external.twitter) {
24  
-		everyauth.twitter
25  
-		.myHostname(siteConf.uri)
26  
-		.consumerKey(siteConf.external.twitter.consumerKey)
27  
-		.consumerSecret(siteConf.external.twitter.consumerSecret)
28  
-		.findOrCreateUser(function (session, accessToken, accessSecret, twitterUser) {return true;})
29  
-		.redirectPath('/');
30  
-	}
31  
-
32  
-	// Github
33  
-	if (siteConf.external && siteConf.external.github) {
34  
-		everyauth.github
35  
-		.myHostname(siteConf.uri)
36  
-		.appId(siteConf.external.github.appId)
37  
-		.appSecret(siteConf.external.github.appSecret)
38  
-		.findOrCreateUser(function (session, accessToken, accessTokenExtra, githubUser) {return true;})
39  
-		.redirectPath('/');
40  
-	}
41  
-
42  
-	everyauth.helpExpress(expressInstance);
  10
+    types.UserSchema.plugin(mongooseAuth, {
  11
+            facebook: {
  12
+                everyauth: {
  13
+                    myHostname: siteConf.uri
  14
+                  , appId: siteConf.external.facebook.appId
  15
+                  , appSecret: siteConf.external.facebook.appSecret
  16
+                  , redirectPath: '/'
  17
+                  , scope: 'user_location, email, user_about_me'
  18
+                }
  19
+            },
  20
+            twitter: {
  21
+                everyauth: {
  22
+                    myHostname: siteConf.uri
  23
+                  , consumerKey: siteConf.external.twitter.consumerKey
  24
+                  , consumerSecret: siteConf.external.twitter.consumerSecret
  25
+                  , redirectPath: '/'
  26
+                }
  27
+            },
  28
+            github: {
  29
+                everyauth: {
  30
+                    myHostname: siteConf.uri
  31
+                  , appId: siteConf.external.github.appId
  32
+                  , appSecret: siteConf.external.github.appSecret
  33
+                  , redirectPath: '/'
  34
+                }
  35
+            },
  36
+            // Here, we attach your User model to every module
  37
+            everymodule: {
  38
+              everyauth: {
  39
+                  User: function () {
  40
+                    return streamLib.asmsDB.User;
  41
+                  },
  42
+                  handleLogout: function (req, res) {
  43
+                    delete req.session.user;
  44
+                    req.logout();
  45
+                    res.writeHead(303, { 'Location': this.logoutRedirectPath() });
  46
+                    res.end();
  47
+                  }
  48
+              }
  49
+            }
  50
+        }
  51
+    );
43 52
 
44 53
 	// Fetch and format data so we have an easy object with user data to work with.
45 54
 	function normalizeUserData() {
46 55
 		function handler(req, res, next) {
47 56
 			if (req.session && !req.session.user && req.session.auth && req.session.auth.loggedIn) {
48  
-                console.log("Logged in a user");
49  
-                console.dir(req.session.auth);
50  
-				var user = {};
51  
-				if (req.session.auth.github) {
52  
-					user.image = 'http://1.gravatar.com/avatar/'+req.session.auth.github.user.gravatar_id+'?s=48';
53  
-					user.name = req.session.auth.github.user.name;
54  
-					user.id = 'github-'+req.session.auth.github.user.id;
55  
-				}
56  
-				if (req.session.auth.twitter) {
57  
-					user.image = req.session.auth.twitter.user.profile_image_url;
58  
-					user.name = req.session.auth.twitter.user.name;
59  
-					user.id = 'twitter-'+req.session.auth.twitter.user.id_str;
60  
-				}
61  
-				if (req.session.auth.facebook) {
62  
-					user.image = req.session.auth.facebook.user.picture;
63  
-					user.name = req.session.auth.facebook.user.name;
64  
-					user.id = 'facebook-'+req.session.auth.facebook.user.id;
  57
+				var id = null;
  58
+                var provider = null;
65 59
 
66  
-					// Need to fetch the users image...
67  
-					https.get({
68  
-						'host': 'graph.facebook.com'
69  
-						, 'path': '/me/picture?access_token='+req.session.auth.facebook.accessToken
70  
-					}, function(response) {
71  
-						user.image = response.headers.location;
72  
-						req.session.user = user;
73  
-						next();
74  
-					}).on('error', function(e) {
75  
-						req.session.user = user;
76  
-						next();
77  
-					});
78  
-					return;
79  
-				}
80  
-				req.session.user = user;
81  
-			}
82  
-			next();
  60
+                if (req.session.auth.github && req.session.auth.github.user) {
  61
+                    provider = 'github';
  62
+                    id = req.session.auth.github.user.id;
  63
+                } else if (req.session.auth.twitter && req.session.auth.twitter.user) {
  64
+                    provider = 'twit';
  65
+                    id = req.session.auth.twitter.user.id_str;
  66
+                } else if (req.session.auth.facebook && req.session.auth.facebook.user) {
  67
+                    provider = 'fb';
  68
+                    id = req.session.auth.facebook.user.id;
  69
+                }
  70
+                if (provider && id) {
  71
+                    console.log("Looking for user with id" + id);
  72
+                    streamLib.asmsDB.User.findOne().where(provider + '.id', id).exec(function(err, user){
  73
+                        if (err) {
  74
+                            // Should we create the user here if we can't find it ?
  75
+                            throw(new Error("Did you delete the DB ? I couldn't find the current user in the db"));
  76
+                        }
  77
+
  78
+                        console.log("Found user with id " + id)
  79
+                        req.session.user = user;
  80
+                        next();
  81
+                    })
  82
+                } else {
  83
+                    throw(new Error("Bad session data - Couldn't read the ID for the current user"));
  84
+                }
  85
+            } else {
  86
+                next();
  87
+            }
83 88
 		}
  89
+
84 90
 		return handler;
85 91
 	}
86 92
 
87 93
 	return {
88 94
 		'middleware': {
89  
-			'auth': everyauth.middleware
  95
+			'mongooseAuth': mongooseAuth.middleware
90 96
 			, 'normalizeUserData': normalizeUserData
91 97
 		}
92 98
 	};
116  lib/socket-io-server.js
... ...
@@ -1,9 +1,10 @@
1 1
 module.exports = function Server(expressInstance, sessionStore) {
2 2
 	var parseCookie = require('connect').utils.parseCookie;
3 3
 	var io = require('socket.io').listen(expressInstance);
4  
-    var asmsServer = expressInstance.asmsDB;
5  
-    var thisApp = expressInstance.thisApp;
6  
-    var thisInstance = expressInstance.thisInstance;
  4
+
  5
+    var asmsClient = expressInstance.asmsClient;
  6
+    var asmsDB = asmsClient.asmsDB;
  7
+
7 8
 
8 9
 	io.configure(function () {
9 10
 		io.set('log level', 0);
@@ -19,103 +20,48 @@ module.exports = function Server(expressInstance, sessionStore) {
19 20
 	});
20 21
 
21 22
 	io.sockets.on('connection', function(client) {
22  
-		var user = client.handshake.session.user ? client.handshake.session.user.name : 'UID: '+(client.handshake.session.uid || 'has no UID');
23  
-
24  
-        var desiredStream = "firehose";
25 23
 
26  
-        if (client.handshake.session && client.handshake.session.desiredStream) {
27  
-            desiredStream = client.handshake.session.desiredStream;
28  
-            console.log("User " + user + " has chosen to view this stream " + desiredStream);
29  
-        } else {
30  
-            console.log("No desired stream");
31  
-            console.dir(client.handshake);
32  
-        }
33  
-
34  
-		// Join user specific channel, this is good so content is send across user tabs.
  24
+        // Join user specific channel, this is good so content is send across user tabs.
35 25
 		client.join(client.handshake.sid);
36 26
 
  27
+        var session = client.handshake.session;
37 28
 
38  
-        var avatarUrl = ((client.handshake.session.auth && client.handshake.session.user.image) ? client.handshake.session.user.image : '/img/codercat-sm.jpg');
39  
-        var currentUser = {displayName: user, image: {url: avatarUrl}};
40  
-
41  
-
42  
-        asmsServer.subscribe(desiredStream,  function(channel, json) {
43  
-
44  
-            client.send(json);
  29
+        var desiredStream = session.desiredStream ? session.desiredStream : asmsClient.default.stream;
  30
+        asmsDB.Activity.subscribe(desiredStream,  function(channel, json) {
  31
+          if (channel == desiredStream) {
  32
+              client.send(json);
  33
+          }
45 34
         });
46 35
 
47  
-        var cf_provider = new asmsServer.ActivityObject({'displayName': 'Cloud Foundry', icon:{url: 'http://www.cloudfoundry.com/images/favicon.ico'}});
48  
-        cf_provider.save(function(err) {
49  
-            if (err == null) {
50  
-                if (client.handshake.session.user && client.handshake.session.user.name) {
51  
-                    var act = new asmsServer.Activity({
52  
-                            id: 1,
53  
-                            actor: currentUser,
54  
-                            verb: 'connect',
55  
-                            object: thisInstance,
56  
-                            target: thisApp._id,
57  
-                            title: "connected to",
58  
-                            provider: cf_provider._id
59  
-                        });
60  
-                }
61  
-                console.dir(act);
62  
-            asmsServer.publish(desiredStream, act);
63  
-
64  
-            } else {
65  
-                console.log("Got error publishing welcome message")
66  
-            }
67  
-        });
68  
-
69  
-        var provider = new asmsServer.ActivityObject({'displayName': 'The Internet', icon: {url: 'http://www.w3.org/favicon.ico'}});
70  
-        if (client.handshake.session.auth) {
71  
-            if (client.handshake.session.auth.github) {
72  
-                provider.displayName = 'GitHub';
73  
-                provider.icon = {url: 'http://github.com/favicon.ico'};
74  
-            } else if (client.handshake.session.auth.facebook) {
75  
-                provider.displayName = 'Facebook';
76  
-                provider.icon = {url: 'http://facebook.com/favicon.ico'};
77  
-            } else if (client.handshake.session.auth.twitter) {
78  
-                provider.displayName = 'Twitter';
79  
-                provider.icon = {url: 'http://twitter.com/favicon.ico'};
80  
-            }
81  
-        }
82  
-        provider.save();
  36
+        var currentUser = asmsClient.helpers.getCurrentUserObject(session);
83 37
 
84  
-		client.on('message', function(actHash) {
85  
-            console.log("Got a new message from the client");
86  
-            console.dir(actHash);
  38
+        if (currentUser._id) {
  39
+            asmsClient.helpers.publishActivity(desiredStream, currentUser, {
  40
+                    verb: 'connect',
  41
+                    object: asmsClient.default.instance,
  42
+                    target: asmsClient.default.app,
  43
+                    provider: asmsClient.default.provider,
  44
+                    title: "connected to"
  45
+                });
87 46
 
88  
-            actHash.actor = currentUser;
89  
-            actHash.provider = provider._id;
90  
-
91  
-            if (actHash.verb == "post") {
92  
-                actHash.title = "posted a " + actHash.object.objectType;
93  
-
94  
-            }
95  
-
96  
-            var act = new asmsServer.Activity(actHash);
97  
-
98  
-            // Send back the message to the users room.
99  
-            asmsServer.publish(desiredStream, act);
100  
-
101  
-            console.log("Published act");
102  
-            console.dir(act);
103  
-		});
104  
-
105  
-        if (client.handshake.session.user && client.handshake.session.user.name) {
106 47
             client.on('disconnect', function() {
107 48
                 //console.log('disconnect');
108  
-                asmsServer.publish(desiredStream, new asmsServer.Activity({
109  
-                    actor: currentUser,
  49
+                asmsClient.helpers.publishActivity(desiredStream, currentUser, {
110 50
                     verb: 'disconnect',
111  
-                    object: thisInstance,
112  
-                    target: thisApp._id,
113 51
                     title: "disconnected from",
114  
-                    provider: cf_provider._id
115  
-                }));
  52
+                    provider: asmsClient.default.provider,
  53
+                    object: asmsClient.default.instance,
  54
+                    target: asmsClient.default.app
  55
+                });
116 56
 
117 57
             });
118 58
         }
  59
+
  60
+		client.on('create-activity', function(actHash) {
  61
+            actHash.provider = asmsClient.default.provider;
  62
+            asmsClient.helpers.publishActivity(desiredStream, currentUser, actHash);
  63
+		});
  64
+
119 65
 	});
120 66
 
121 67
 	io.sockets.on('error', function(){ console.log(arguments); });
410  npm-shrinkwrap.json
... ...
@@ -0,0 +1,410 @@
  1
+{
  2
+  "name": "node-activities-boilerplate",
  3
+  "version": "0.0.7",
  4
+  "dependencies": {
  5
+    "connect": {
  6
+      "version": "1.9.2",
  7
+      "dependencies": {
  8
+        "qs": {
  9
+          "version": "0.5.0"
  10
+        },
  11
+        "mime": {
  12
+          "version": "1.2.7"
  13
+        },
  14
+        "formidable": {
  15
+          "version": "1.0.11"
  16
+        }
  17
+      }
  18
+    },
  19
+    "connect-assetmanager": {
  20
+      "version": "0.0.28",
  21
+      "dependencies": {
  22
+        "request": {
  23
+          "version": "2.11.1",
  24
+          "dependencies": {
  25
+            "form-data": {
  26
+              "version": "0.0.3",
  27
+              "dependencies": {
  28
+                "combined-stream": {
  29
+                  "version": "0.0.3",
  30
+                  "dependencies": {
  31
+                    "delayed-stream": {
  32
+                      "version": "0.0.5"
  33
+                    }
  34
+                  }
  35
+                },
  36
+                "async": {
  37
+                  "version": "0.1.9"
  38
+                }
  39
+              }
  40
+            },
  41
+            "mime": {
  42
+              "version": "1.2.7"
  43
+            }
  44
+          }
  45
+        },
  46
+        "step": {
  47
+          "version": "0.0.5"
  48
+        }
  49
+      }
  50
+    },
  51
+    "connect-assetmanager-handlers": {
  52
+      "version": "0.0.18",
  53
+      "dependencies": {
  54
+        "request": {
  55
+          "version": "2.11.1",
  56
+          "dependencies": {
  57
+            "form-data": {
  58
+              "version": "0.0.3",
  59
+              "dependencies": {
  60
+                "combined-stream": {
  61
+                  "version": "0.0.3",
  62
+                  "dependencies": {
  63
+                    "delayed-stream": {
  64
+                      "version": "0.0.5"
  65
+                    }
  66
+                  }
  67
+                },
  68
+                "async": {
  69
+                  "version": "0.1.9"
  70
+                }
  71
+              }
  72
+            },
  73
+            "mime": {
  74
+              "version": "1.2.7"
  75
+            }
  76
+          }
  77
+        },
  78
+        "step": {
  79
+          "version": "0.0.5"
  80
+        },
  81
+        "uglify-js": {
  82
+          "version": "1.3.3"
  83
+        }
  84
+      }
  85
+    },
  86
+    "jade": {
  87
+      "version": "0.27.2",
  88
+      "dependencies": {
  89
+        "commander": {
  90
+          "version": "0.6.1"
  91
+        },
  92
+        "mkdirp": {
  93
+          "version": "0.3.0"
  94
+        }
  95
+      }
  96
+    },
  97
+    "express": {
  98
+      "version": "2.5.11",
  99
+      "dependencies": {
  100
+        "mime": {
  101
+          "version": "1.2.4"
  102
+        },
  103
+        "qs": {
  104
+          "version": "0.4.2"
  105
+        },
  106
+        "mkdirp": {
  107
+          "version": "0.3.0"
  108
+        }
  109
+      }
  110
+    },
  111
+    "socket.io": {
  112
+      "version": "0.9.10",
  113
+      "dependencies": {
  114
+        "socket.io-client": {
  115
+          "version": "0.9.10",
  116
+          "dependencies": {
  117
+            "uglify-js": {
  118
+              "version": "1.2.5"
  119
+            },
  120
+            "ws": {
  121
+              "version": "0.4.21",
  122
+              "dependencies": {
  123
+                "commander": {
  124
+                  "version": "0.6.1"
  125
+                },
  126
+                "tinycolor": {
  127
+                  "version": "0.0.1"
  128
+                },
  129
+                "options": {
  130
+                  "version": "0.0.3"
  131
+                }
  132
+              }
  133
+            },
  134
+            "xmlhttprequest": {
  135
+              "version": "1.4.2"
  136
+            },
  137
+            "active-x-obfuscator": {
  138
+              "version": "0.0.1",
  139
+              "dependencies": {
  140
+                "zeparser": {
  141
+                  "version": "0.0.5"
  142
+                }
  143
+              }
  144
+            }
  145
+          }
  146
+        },
  147
+        "policyfile": {
  148
+          "version": "0.0.4"
  149
+        },
  150
+        "redis": {
  151
+          "version": "0.7.2"
  152
+        }
  153
+      }
  154
+    },
  155
+    "connect-redis": {
  156
+      "version": "1.4.2",
  157
+      "dependencies": {
  158
+        "redis": {
  159
+          "version": "0.7.2"
  160
+        },
  161
+        "debug": {
  162
+          "version": "0.7.0"
  163
+        }
  164
+      }
  165
+    },
  166
+    "connect-notifo": {
  167
+      "version": "0.0.1",
  168
+      "dependencies": {
  169
+        "notifo": {
  170
+          "version": "0.0.2"
  171
+        }
  172
+      }
  173
+    },
  174
+    "airbrake": {
  175
+      "version": "0.2.9",
  176
+      "dependencies": {
  177
+        "request": {
  178
+          "version": "2.9.202"
  179
+        },
  180
+        "xmlbuilder": {
  181
+          "version": "0.1.5"
  182
+        },
  183
+        "stack-trace": {
  184
+          "version": "0.0.5"
  185
+        },
  186
+        "hashish": {
  187
+          "version": "0.0.4",
  188
+          "dependencies": {
  189
+            "traverse": {
  190
+              "version": "0.6.3"
  191
+            }
  192
+          }
  193
+        }
  194
+      }
  195
+    },
  196
+    "everyauth": {
  197
+      "version": "0.2.32",
  198
+      "dependencies": {
  199
+        "oauth": {
  200
+          "version": "0.9.8",
  201
+          "from": "https://github.com/ciaranj/node-oauth/tarball/master"
  202
+        },
  203
+        "restler": {
  204
+          "version": "2.0.1"
  205
+        },
  206
+        "request": {
  207
+          "version": "2.11.1",
  208
+          "dependencies": {
  209
+            "form-data": {
  210
+              "version": "0.0.3",
  211
+              "dependencies": {
  212
+                "combined-stream": {
  213
+                  "version": "0.0.3",
  214
+                  "dependencies": {
  215
+                    "delayed-stream": {
  216
+                      "version": "0.0.5"
  217
+                    }
  218
+                  }
  219
+                },
  220
+                "async": {
  221
+                  "version": "0.1.9"
  222
+                }
  223
+              }
  224
+            },
  225
+            "mime": {
  226
+              "version": "1.2.7"
  227
+            }
  228
+          }
  229
+        },
  230
+        "openid": {
  231
+          "version": "0.4.2"
  232
+        },
  233
+        "xml2js": {
  234
+          "version": "0.2.0",
  235
+          "dependencies": {
  236
+            "sax": {
  237
+              "version": "0.4.2"
  238
+            }
  239
+          }
  240
+        },
  241
+        "node-swt": {
  242
+          "version": "0.1.1"
  243
+        },
  244
+        "node-wsfederation": {
  245
+          "version": "0.1.1"
  246
+        },
  247
+        "debug": {
  248
+          "version": "0.5.0"
  249
+        }
  250
+      }
  251
+    },
  252
+    "cloudfoundry": {
  253
+      "version": "0.1.0"
  254
+    },
  255
+    "activity-streams-mongoose": {
  256
+      "version": "0.1.1",
  257
+      "dependencies": {
  258
+        "redis": {
  259
+          "version": "0.7.2"
  260
+        },
  261
+        "nodeunit": {
  262
+          "version": "0.7.4",
  263
+          "dependencies": {
  264
+            "tap": {
  265
+              "version": "0.3.1",
  266
+              "dependencies": {
  267
+                "inherits": {
  268
+                  "version": "1.0.0"
  269
+                },
  270
+                "yamlish": {
  271
+                  "version": "0.0.5"
  272
+                },
  273
+                "slide": {
  274
+                  "version": "1.1.3"
  275
+                },
  276
+                "runforcover": {
  277
+                  "version": "0.0.2",
  278
+                  "dependencies": {
  279
+                    "bunker": {
  280
+                      "version": "0.1.2",
  281
+                      "dependencies": {
  282
+                        "burrito": {
  283
+                          "version": "0.2.12",
  284
+                          "dependencies": {
  285
+                            "traverse": {
  286
+                              "version": "0.5.2"
  287
+                            },
  288
+                            "uglify-js": {
  289
+                              "version": "1.1.1"
  290
+                            }
  291
+                          }
  292
+                        }
  293
+                      }
  294
+                    }
  295
+                  }
  296
+                },
  297
+                "nopt": {
  298
+                  "version": "2.0.0",
  299
+                  "dependencies": {
  300
+                    "abbrev": {
  301
+                      "version": "1.0.3"
  302
+                    }
  303
+                  }
  304
+                },
  305
+                "mkdirp": {
  306
+                  "version": "0.3.4"
  307
+                },
  308
+                "difflet": {
  309
+                  "version": "0.2.2",
  310
+                  "dependencies": {
  311
+                    "traverse": {
  312
+                      "version": "0.6.3"
  313
+                    },
  314
+                    "charm": {
  315
+                      "version": "0.0.8"
  316
+                    }
  317
+                  }
  318
+                },
  319
+                "deep-equal": {
  320
+                  "version": "0.0.0"
  321
+                },
  322
+                "buffer-equal": {
  323
+                  "version": "0.0.0"
  324
+                }
  325
+              }
  326
+            }
  327
+          }
  328
+        }
  329
+      }
  330
+    },
  331
+    "hiredis": {
  332
+      "version": "0.1.14"
  333
+    },
  334
+    "underscore": {
  335
+      "version": "1.3.3"
  336
+    },
  337
+    "mongoose-auth": {
  338
+      "version": "0.0.12",
  339
+      "dependencies": {
  340
+        "bcrypt": {
  341
+          "version": "0.7.2",
  342
+          "dependencies": {
  343
+            "bindings": {