diff --git a/app.js b/app.js
index 2bf7e93fe..2d679b0f7 100644
--- a/app.js
+++ b/app.js
@@ -4,13 +4,11 @@ const morgan = require('morgan');
const path = require('path');
const helmet = require('helmet');
const passport = require('./services/passport');
-const session = require('express-session');
const enabled = require('debug').enabled;
-const RedisStore = require('connect-redis')(session);
-const redis = require('./services/redis');
const csrf = require('csurf');
const errors = require('./errors');
-const graph = require('./graph');
+const session = require('./services/session');
+const {createGraphOptions} = require('./graph');
const apollo = require('graphql-server-express');
const app = express();
@@ -42,34 +40,7 @@ app.set('view engine', 'ejs');
// SESSION MIDDLEWARE
//==============================================================================
-const session_opts = {
- secret: process.env.TALK_SESSION_SECRET,
- httpOnly: true,
- rolling: true,
- saveUninitialized: true,
- resave: true,
- unset: 'destroy',
- name: 'talk.sid',
- cookie: {
- secure: false,
- maxAge: 8.64e+7, // 24 hours for session token expiry
- },
- store: new RedisStore({
- client: redis.createClient(),
- })
-};
-
-if (app.get('env') === 'production') {
-
- // Enable the secure cookie when we are in production mode.
- session_opts.cookie.secure = true;
-} else if (app.get('env') === 'test') {
-
- // Add in the secret during tests.
- session_opts.secret = 'keyboard cat';
-}
-
-app.use(session(session_opts));
+app.use(session);
//==============================================================================
// PASSPORT MIDDLEWARE
@@ -83,10 +54,8 @@ app.use(passport.session());
// GraphQL Router
//==============================================================================
-graph.createSubscriptionManager(app, '/api/v1/live');
-
// GraphQL endpoint.
-app.use('/api/v1/graph/ql', apollo.graphqlExpress(graph.createGraphOptions));
+app.use('/api/v1/graph/ql', apollo.graphqlExpress(createGraphOptions));
// Only include the graphiql tool if we aren't in production mode.
if (app.get('env') !== 'production') {
diff --git a/bin/cli-serve b/bin/cli-serve
index ae5da3d3b..eef6526bd 100755
--- a/bin/cli-serve
+++ b/bin/cli-serve
@@ -1,14 +1,14 @@
#!/usr/bin/env node
-const app = require('../app');
const program = require('./commander');
+const app = require('../app');
const {createServer} = require('http');
const scraper = require('../services/scraper');
const mailer = require('../services/mailer');
const kue = require('../services/kue');
const mongoose = require('../services/mongoose');
const util = require('./util');
-const {createSubscriptionManager} = require('../graph');
+const subscriptions = require('../services/subscriptions');
/**
* Get port from environment and store in Express.
@@ -91,7 +91,9 @@ function startApp() {
* Listen on provided port, on all network interfaces.
*/
server.listen(port, () => {
- createSubscriptionManager(server, '/api/v1/live');
+
+ // Mount the subscriptions server on the application server.
+ subscriptions.mount(server);
});
server.on('error', onError);
diff --git a/client/coral-embed-live-stream/src/Embed.js b/client/coral-embed-live-stream/src/Embed.js
index 9301e2a21..6be149007 100644
--- a/client/coral-embed-live-stream/src/Embed.js
+++ b/client/coral-embed-live-stream/src/Embed.js
@@ -3,6 +3,8 @@ import React from 'react';
import {graphql} from 'react-apollo';
import gql from 'graphql-tag';
+import PubDate from 'coral-plugin-pubdate/PubDate';
+
class Embed extends React.Component {
static propTypes = {
@@ -20,18 +22,24 @@ class Embed extends React.Component {
}
render() {
- console.log(this.props);
const {asset, loading} = this.props.data;
return (
);
}
@@ -40,12 +48,13 @@ class Embed extends React.Component {
const COMMENT_QUERY = gql`
query Comment($assetID: ID!) {
asset(id: $assetID) {
- comments {
+ comments(limit: 50) {
id
body
user {
username
}
+ created_at
}
}
}
@@ -59,6 +68,7 @@ const COMMENTS_SUBSCRIPTION = gql`
user {
username
}
+ created_at
}
}
`;
@@ -95,8 +105,6 @@ const withData = graphql(COMMENT_QUERY, {
}
};
- console.log('updateQuery', before, after);
-
return after;
}
});
diff --git a/client/coral-embed-live-stream/src/index.js b/client/coral-embed-live-stream/src/index.js
index c546ccd2e..564ea0ed6 100644
--- a/client/coral-embed-live-stream/src/index.js
+++ b/client/coral-embed-live-stream/src/index.js
@@ -10,7 +10,7 @@ import Embed from './Embed';
render(
-
+
, document.querySelector('#coralStream')
);
diff --git a/client/coral-framework/services/client.js b/client/coral-framework/services/client.js
index d1974df96..7744ea9bd 100644
--- a/client/coral-framework/services/client.js
+++ b/client/coral-framework/services/client.js
@@ -2,7 +2,7 @@ import ApolloClient, {addTypename} from 'apollo-client';
import getNetworkInterface from './transport';
import {SubscriptionClient, addGraphQLSubscriptions} from 'subscriptions-transport-ws';
-const wsClient = new SubscriptionClient('ws://localhost:3001/api/v1/live', {
+const wsClient = new SubscriptionClient('ws://localhost:3000/api/v1/live', {
reconnect: true
});
const networkInterface = getNetworkInterface();
diff --git a/graph/context.js b/graph/context.js
index 3d2e13991..ce66c4bcd 100644
--- a/graph/context.js
+++ b/graph/context.js
@@ -1,5 +1,6 @@
const loaders = require('./loaders');
const mutators = require('./mutators');
+const uuid = require('uuid');
const plugins = require('../plugins');
const debug = require('debug')('talk:graph:context');
@@ -33,6 +34,9 @@ const decorateContextPlugins = (context, contextPlugins) => contextPlugins.reduc
class Context {
constructor({user = null}, pubsub) {
+ // Generate a new context id for the request.
+ this.id = uuid.v4();
+
// Load the current logged in user to `user`, otherwise this'll be null.
if (user) {
this.user = user;
diff --git a/graph/index.js b/graph/index.js
index b690ec22c..78d9edc9a 100644
--- a/graph/index.js
+++ b/graph/index.js
@@ -19,7 +19,7 @@ module.exports = {
// the lifespan of this request.
context: new Context(req, pubsub)
}),
- createSubscriptionManager: (server, path) => new SubscriptionServer({
+ createSubscriptionManager: (server, path, sessionFactory) => new SubscriptionServer({
subscriptionManager: new SubscriptionManager({
schema,
pubsub,
@@ -31,9 +31,19 @@ module.exports = {
}),
}
}),
- // onConnect: (connectionParams, webSocket) => {
- // console.log(webSocket.upgradeReq.headers);
- // }
+ onSubscribe: (parsedMessage, baseParams, connection) => {
+
+ // Attach the context per request.
+ baseParams.context = () => sessionFactory(connection.upgradeReq)
+ .then((req) => new Context(req, pubsub))
+ .catch((err) => {
+ console.error(err);
+
+ return new Context({}, pubsub);
+ });
+
+ return baseParams;
+ }
}, {
server,
path
diff --git a/graph/resolvers/date.js b/graph/resolvers/date.js
index 2335b9713..af79e3eb4 100644
--- a/graph/resolvers/date.js
+++ b/graph/resolvers/date.js
@@ -5,6 +5,10 @@ module.exports = new GraphQLScalarType({
name: 'Date',
description: 'Date represented as an ISO8601 string',
serialize(value) {
+ if (typeof value === 'string') {
+ return value;
+ }
+
return value.toISOString();
},
parseValue(value) {
diff --git a/services/session.js b/services/session.js
new file mode 100644
index 000000000..5697e026d
--- /dev/null
+++ b/services/session.js
@@ -0,0 +1,36 @@
+const session = require('express-session');
+const RedisStore = require('connect-redis')(session);
+const redis = require('./services/redis');
+
+//==============================================================================
+// SESSION MIDDLEWARE
+//==============================================================================
+
+const session_opts = {
+ secret: process.env.TALK_SESSION_SECRET,
+ httpOnly: true,
+ rolling: true,
+ saveUninitialized: true,
+ resave: true,
+ unset: 'destroy',
+ name: 'talk.sid',
+ cookie: {
+ secure: false,
+ maxAge: 8.64e+7, // 24 hours for session token expiry
+ },
+ store: new RedisStore({
+ client: redis.createClient(),
+ })
+};
+
+if (process.env.NODE_ENV === 'production') {
+
+ // Enable the secure cookie when we are in production mode.
+ session_opts.cookie.secure = true;
+} else if (process.env.NODE_ENV === 'test') {
+
+ // Add in the secret during tests.
+ session_opts.secret = 'keyboard cat';
+}
+
+module.exports = session(session_opts);
diff --git a/services/subscriptions.js b/services/subscriptions.js
new file mode 100644
index 000000000..b24d6d69b
--- /dev/null
+++ b/services/subscriptions.js
@@ -0,0 +1,35 @@
+const {createSubscriptionManager} = require('../graph');
+const session = require('./session');
+const passport = require('./passport');
+
+module.exports = class Subscriptions {
+
+ static deserializeUser(req) {
+ return new Promise((resolve, reject) => {
+ session(req, {}, () => {
+
+ if ('session' in req && 'passport' in req.session && 'user' in req.session.passport) {
+ passport.deserializeUser(req.session.passport.user, (err, user) => {
+ if (err) {
+ return reject(err);
+ }
+
+ req.user = user;
+
+ return resolve(req);
+ });
+ } else {
+ resolve(req);
+ }
+
+ });
+ });
+ }
+
+ static mount(server) {
+
+ // Create the SubscriptionManager and mount it on the specified route with
+ // this deserializer.
+ createSubscriptionManager(server, '/api/v1/live', Subscriptions.deserializeUser);
+ }
+};
diff --git a/yarn.lock b/yarn.lock
index 8f7116ad3..9da6badee 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4251,10 +4251,6 @@ jsbn@~0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.0.tgz#650987da0dd74f4ebf5a11377a2aa2d273e97dfd"
-jsdom-global@^2.1.1:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/jsdom-global/-/jsdom-global-2.1.1.tgz#47d46fe77f6167baf5d34431d3bb59fc41b0915a"
-
jsdom@^7.0.2:
version "7.2.2"
resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-7.2.2.tgz#40b402770c2bda23469096bee91ab675e3b1fc6e"
@@ -4275,7 +4271,7 @@ jsdom@^7.0.2:
whatwg-url-compat "~0.6.5"
xml-name-validator ">= 2.0.1 < 3.0.0"
-jsdom@^9.12.0:
+jsdom@^9.8.3:
version "9.12.0"
resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-9.12.0.tgz#e8c546fffcb06c00d4833ca84410fed7f8a097d4"
dependencies: