diff --git a/client/src/actions/show.js b/client/src/actions/show.js
index ee1d0b7e..b4ed7719 100644
--- a/client/src/actions/show.js
+++ b/client/src/actions/show.js
@@ -2,10 +2,13 @@ import * as actions from '../constants/show_action_types';
import { CHANNEL, ASSET_LITE, ASSET_DETAILS } from '../constants/show_request_types';
// basic request parsing
-export function onHandleShowPageUri (params) {
+export function onHandleShowPageUri (params, url) {
return {
type: actions.HANDLE_SHOW_URI,
- data: params,
+ data: {
+ ...params,
+ url,
+ }
};
}
diff --git a/package-lock.json b/package-lock.json
index 87374bf1..d625325d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1259,6 +1259,14 @@
"integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=",
"dev": true
},
+ "async-hook-jl": {
+ "version": "1.7.6",
+ "resolved": "https://registry.npmjs.org/async-hook-jl/-/async-hook-jl-1.7.6.tgz",
+ "integrity": "sha512-gFaHkFfSxTjvoxDMYqDuGHlcRyUuamF8s+ZTtJdDzqjws4mCt7v0vuV79/E2Wr2/riMQgtG4/yUtXWs1gZ7JMg==",
+ "requires": {
+ "stack-chain": "1.3.7"
+ }
+ },
"async-limiter": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
@@ -2690,6 +2698,16 @@
"shimmer": "1.2.0"
}
},
+ "cls-hooked": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/cls-hooked/-/cls-hooked-4.2.2.tgz",
+ "integrity": "sha512-J4Xj5f5wq/4jAvcdgoGsL3G103BtWpZrMo8NEinRltN+xpTZdI+M38pyQqhuFU/P792xkMFvnKSf+Lm81U1bxw==",
+ "requires": {
+ "async-hook-jl": "1.7.6",
+ "emitter-listener": "1.1.1",
+ "semver": "5.5.1"
+ }
+ },
"co": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
@@ -3640,6 +3658,14 @@
"minimalistic-crypto-utils": "1.0.1"
}
},
+ "emitter-listener": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/emitter-listener/-/emitter-listener-1.1.1.tgz",
+ "integrity": "sha1-6Lu+gkS8jg0LTvcc0UKUx/JBx+w=",
+ "requires": {
+ "shimmer": "1.2.0"
+ }
+ },
"emojis-list": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz",
@@ -4365,6 +4391,14 @@
"promise": "7.3.1"
}
},
+ "express-http-context": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/express-http-context/-/express-http-context-1.1.0.tgz",
+ "integrity": "sha512-LS47HseitRIxzBHDEQrlVwZkEkMaViM+nhRCrlWYxPNIu7W8KUZyNUOxiD93OghHesl7y+DhBYuz3yfaNHDvVA==",
+ "requires": {
+ "cls-hooked": "4.2.2"
+ }
+ },
"extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
@@ -12893,6 +12927,11 @@
"tweetnacl": "0.14.5"
}
},
+ "stack-chain": {
+ "version": "1.3.7",
+ "resolved": "https://registry.npmjs.org/stack-chain/-/stack-chain-1.3.7.tgz",
+ "integrity": "sha1-0ZLJ/06moiyUxN1FkXHj8AzqEoU="
+ },
"stack-trace": {
"version": "0.0.10",
"resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
diff --git a/package.json b/package.json
index 3d1cf596..23f23980 100644
--- a/package.json
+++ b/package.json
@@ -54,6 +54,7 @@
"cookie-session": "^2.0.0-beta.3",
"express": "^4.15.2",
"express-handlebars": "^3.0.0",
+ "express-http-context": "^1.1.0",
"get-video-dimensions": "^1.0.0",
"helmet": "^3.13.0",
"image-size": "^0.6.3",
diff --git a/server/index.js b/server/index.js
index 85222a71..097fd9ce 100644
--- a/server/index.js
+++ b/server/index.js
@@ -7,6 +7,7 @@ const cookieSession = require('cookie-session');
const http = require('http');
const logger = require('winston');
const Path = require('path');
+const httpContext = require('express-http-context');
// load local modules
const db = require('./models');
@@ -26,6 +27,14 @@ const {
},
} = require('@config/siteConfig');
+function setRouteDataInContextMiddleware(routePath, routeData) {
+ return function (req, res, next) {
+ httpContext.set('routePath', routePath);
+ httpContext.set('routeData', routeData);
+ next();
+ };
+}
+
function Server () {
this.initialize = () => {
// configure logging
@@ -43,6 +52,9 @@ function Server () {
// set HTTP headers to protect against well-known web vulnerabilties
app.use(helmet());
+ // Support per-request http-context
+ app.use(httpContext.middleware);
+
// 'express.static' to serve static files from public directory
const publicPath = Path.resolve(process.cwd(), 'public');
app.use(express.static(publicPath));
@@ -78,11 +90,15 @@ function Server () {
app.set('view engine', 'handlebars');
// set the routes on the app
- require('./routes/auth/index')(app);
- require('./routes/api/index')(app);
- require('./routes/pages/index')(app);
- require('./routes/assets/index')(app);
- require('./routes/fallback/index')(app);
+ const routes = require('./routes');
+
+ Object.keys(routes).map((routePath) => {
+ let routeData = routes[routePath];
+ let routeMethod = routeData.hasOwnProperty('method') ? routeData.method : 'get';
+ let controllers = Array.isArray(routeData.controller) ? routeData.controller : [routeData.controller];
+
+ app[routeMethod](routePath, setRouteDataInContextMiddleware(routePath, routeData), ...controllers);
+ });
this.app = app;
};
diff --git a/server/render/build/handleShowRender.js b/server/render/build/handleShowRender.js
index 98f433ca..2236d1bb 100644
--- a/server/render/build/handleShowRender.js
+++ b/server/render/build/handleShowRender.js
@@ -18,6 +18,8 @@ var _effects = require("redux-saga/effects");
var _reactHelmet = _interopRequireDefault(require("react-helmet"));
+var httpContext = _interopRequireWildcard(require("express-http-context"));
+
var _reducers = _interopRequireDefault(require("@reducers"));
var _GAListener = _interopRequireDefault(require("@components/GAListener"));
@@ -28,6 +30,8 @@ var _sagas = _interopRequireDefault(require("@sagas"));
var _actions = _interopRequireDefault(require("@actions"));
+function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var returnSagaWithParams = function returnSagaWithParams(saga, params) {
@@ -52,19 +56,17 @@ var returnSagaWithParams = function returnSagaWithParams(saga, params) {
};
module.exports = function (req, res) {
- var context = {}; // create and apply middleware
+ var context = {};
- var sagaMiddleware = (0, _reduxSaga.default)();
- var middleware = (0, _redux.applyMiddleware)(sagaMiddleware); // create a new Redux store instance
+ var _httpContext$get = httpContext.get('routeData'),
+ _httpContext$get$acti = _httpContext$get.action,
+ action = _httpContext$get$acti === void 0 ? false : _httpContext$get$acti,
+ _httpContext$get$saga = _httpContext$get.saga,
+ saga = _httpContext$get$saga === void 0 ? false : _httpContext$get$saga;
- var store = (0, _redux.createStore)(_reducers.default, middleware); // create an action to handle the given url,
- // and create a the saga needed to handle that action
+ var runSaga = action !== false && saga !== false;
- var action = _actions.default.onHandleShowPageUri(req.params);
-
- var saga = returnSagaWithParams(_sagas.default.handleShowPageUri, action); // run the saga middleware with the saga call
-
- sagaMiddleware.run(saga).done.then(function () {
+ var renderPage = function renderPage(store) {
// render component to a string
var html = (0, _server.renderToString)(_react.default.createElement(_reactRedux.Provider, {
store: store
@@ -84,5 +86,27 @@ module.exports = function (req, res) {
var preloadedState = store.getState(); // send the rendered page back to the client
res.send((0, _renderFullPage.default)(helmet, html, preloadedState));
- });
+ };
+
+ console.log(httpContext.get('routePath'), runSaga, httpContext.get('routeData'), action, saga);
+
+ if (runSaga) {
+ // create and apply middleware
+ var sagaMiddleware = (0, _reduxSaga.default)();
+ var middleware = (0, _redux.applyMiddleware)(sagaMiddleware); // create a new Redux store instance
+
+ var store = (0, _redux.createStore)(_reducers.default, middleware); // create an action to handle the given url,
+ // and create a the saga needed to handle that action
+
+ var boundAction = action(req.params, req.url);
+ var boundSaga = returnSagaWithParams(saga, boundAction); // run the saga middleware with the saga call
+
+ sagaMiddleware.run(boundSaga).done.then(function () {
+ return renderPage(store);
+ });
+ } else {
+ var _store = (0, _redux.createStore)(_reducers.default);
+
+ renderPage(_store);
+ }
};
\ No newline at end of file
diff --git a/server/render/src/handleShowRender.jsx b/server/render/src/handleShowRender.jsx
index 2a8809f2..f96d8015 100644
--- a/server/render/src/handleShowRender.jsx
+++ b/server/render/src/handleShowRender.jsx
@@ -7,6 +7,7 @@ import renderFullPage from '../renderFullPage';
import createSagaMiddleware from 'redux-saga';
import { call } from 'redux-saga/effects';
import Helmet from 'react-helmet';
+import * as httpContext from 'express-http-context';
import Reducers from '@reducers';
import GAListener from '@components/GAListener';
@@ -23,46 +24,60 @@ const returnSagaWithParams = (saga, params) => {
module.exports = (req, res) => {
let context = {};
- // create and apply middleware
- const sagaMiddleware = createSagaMiddleware();
- const middleware = applyMiddleware(sagaMiddleware);
+ const {
+ action = false,
+ saga = false,
+ } = httpContext.get('routeData');
- // create a new Redux store instance
- const store = createStore(Reducers, middleware);
+ const runSaga = (action !== false && saga !== false);
- // create an action to handle the given url,
- // and create a the saga needed to handle that action
- const action = Actions.onHandleShowPageUri(req.params);
- const saga = returnSagaWithParams(Sagas.handleShowPageUri, action);
+ const renderPage = (store) => {
+ // render component to a string
+ const html = renderToString(
+
+
+
+
+
+
+
+ );
- // run the saga middleware with the saga call
- sagaMiddleware
- .run(saga)
- .done
- .then(() => {
- // render component to a string
- const html = renderToString(
-
-
-
-
-
-
-
- );
+ // get head tags from helmet
+ const helmet = Helmet.renderStatic();
- // get head tags from helmet
- const helmet = Helmet.renderStatic();
+ // check for a redirect
+ if (context.url) {
+ return res.redirect(301, context.url);
+ }
- // check for a redirect
- if (context.url) {
- return res.redirect(301, context.url);
- }
+ // get the initial state from our Redux store
+ const preloadedState = store.getState();
- // get the initial state from our Redux store
- const preloadedState = store.getState();
+ // send the rendered page back to the client
+ res.send(renderFullPage(helmet, html, preloadedState));
+ };
- // send the rendered page back to the client
- res.send(renderFullPage(helmet, html, preloadedState));
- });
+ if (runSaga) {
+ // create and apply middleware
+ const sagaMiddleware = createSagaMiddleware();
+ const middleware = applyMiddleware(sagaMiddleware);
+
+ // create a new Redux store instance
+ const store = createStore(Reducers, middleware);
+
+ // create an action to handle the given url,
+ // and create a the saga needed to handle that action
+ const boundAction = action(req.params, req.url);
+ const boundSaga = returnSagaWithParams(saga, boundAction);
+
+ // run the saga middleware with the saga call
+ sagaMiddleware
+ .run(boundSaga)
+ .done
+ .then(() => renderPage(store) );
+ } else {
+ const store = createStore(Reducers);
+ renderPage(store);
+ }
};
diff --git a/server/routes/api/index.js b/server/routes/api/index.js
index b5a0c466..f4220179 100644
--- a/server/routes/api/index.js
+++ b/server/routes/api/index.js
@@ -21,31 +21,31 @@ const getTorList = require('../../controllers/api/tor');
const getBlockedList = require('../../controllers/api/blocked');
const getOEmbedData = require('../../controllers/api/oEmbed');
-module.exports = (app) => {
+module.exports = {
// channel routes
- app.get('/api/channel/availability/:name', torCheckMiddleware, channelAvailability);
- app.get('/api/channel/short-id/:longId/:name', torCheckMiddleware, channelShortId);
- app.get('/api/channel/data/:channelName/:channelClaimId', torCheckMiddleware, channelData);
- app.get('/api/channel/claims/:channelName/:channelClaimId/:page', torCheckMiddleware, channelClaims);
+ '/api/channel/availability/:name': { controller: [ torCheckMiddleware, channelAvailability ] },
+ '/api/channel/short-id/:longId/:name': { controller: [ torCheckMiddleware, channelShortId ] },
+ '/api/channel/data/:channelName/:channelClaimId': { controller: [ torCheckMiddleware, channelData ] },
+ '/api/channel/claims/:channelName/:channelClaimId/:page': { controller: [ torCheckMiddleware, channelClaims ] },
// claim routes
- app.get('/api/claim/availability/:name', torCheckMiddleware, claimAvailability);
- app.get('/api/claim/data/:claimName/:claimId', torCheckMiddleware, claimData);
- app.get('/api/claim/get/:name/:claimId', torCheckMiddleware, claimGet);
- app.get('/api/claim/list/:name', torCheckMiddleware, claimList);
- app.post('/api/claim/long-id', torCheckMiddleware, claimLongId); // note: should be a 'get'
- app.post('/api/claim/publish', torCheckMiddleware, multipartMiddleware, claimPublish);
- app.get('/api/claim/resolve/:name/:claimId', torCheckMiddleware, claimResolve);
- app.get('/api/claim/short-id/:longId/:name', torCheckMiddleware, claimShortId);
+ '/api/claim/availability/:name': { controller: [ torCheckMiddleware, claimAvailability ] },
+ '/api/claim/data/:claimName/:claimId': { controller: [ torCheckMiddleware, claimData ] },
+ '/api/claim/get/:name/:claimId': { controller: [ torCheckMiddleware, claimGet ] },
+ '/api/claim/list/:name': { controller: [ torCheckMiddleware, claimList ] },
+ '/api/claim/long-id': { method: 'post', controller: [ torCheckMiddleware, claimLongId ] }, // note: should be a 'get'
+ '/api/claim/publish': { method: 'post', controller: [ torCheckMiddleware, multipartMiddleware, claimPublish ] },
+ '/api/claim/resolve/:name/:claimId': { controller: [ torCheckMiddleware, claimResolve ] },
+ '/api/claim/short-id/:longId/:name': { controller: [ torCheckMiddleware, claimShortId ] },
// file routes
- app.get('/api/file/availability/:name/:claimId', torCheckMiddleware, fileAvailability);
+ '/api/file/availability/:name/:claimId': { controller: [ torCheckMiddleware, fileAvailability ] },
// user routes
- app.put('/api/user/password/', torCheckMiddleware, userPassword);
+ '/api/user/password/': { method: 'put', controller: [ torCheckMiddleware, userPassword ] },
// configs
- app.get('/api/config/site/publishing', torCheckMiddleware, publishingConfig);
+ '/api/config/site/publishing': { controller: [ torCheckMiddleware, publishingConfig ] },
// tor
- app.get('/api/tor', torCheckMiddleware, getTorList);
+ '/api/tor': { controller: [ torCheckMiddleware, getTorList ] },
// blocked
- app.get('/api/blocked', torCheckMiddleware, getBlockedList);
+ '/api/blocked': { controller: [ torCheckMiddleware, getBlockedList ] },
// open embed
- app.get('/api/oembed', torCheckMiddleware, getOEmbedData);
+ '/api/oembed': { controller: [ torCheckMiddleware, getOEmbedData ] },
};
diff --git a/server/routes/assets/index.js b/server/routes/assets/index.js
index ef70ea3f..411a4f64 100644
--- a/server/routes/assets/index.js
+++ b/server/routes/assets/index.js
@@ -1,7 +1,11 @@
const serveByClaim = require('../../controllers/assets/serveByClaim');
const serveByIdentifierAndClaim = require('../../controllers/assets/serveByIdentifierAndClaim');
-module.exports = (app) => {
- app.get('/:identifier/:claim', serveByIdentifierAndClaim);
- app.get('/:claim', serveByClaim);
+// TODO: Adjust build & sources to use import/export everywhere
+const Actions = require('@actions').default;
+const Sagas = require('@sagas').default;
+
+module.exports = {
+ '/:identifier/:claim': { controller: serveByIdentifierAndClaim, action: Actions.onHandleShowPageUri, saga: Sagas.handleShowPageUri },
+ '/:claim': { controller: serveByClaim, action: Actions.onHandleShowPageUri, saga: Sagas.handleShowPageUri },
};
diff --git a/server/routes/auth/index.js b/server/routes/auth/index.js
index 9754f5b2..8bdbe1b6 100644
--- a/server/routes/auth/index.js
+++ b/server/routes/auth/index.js
@@ -4,9 +4,9 @@ const handleLoginRequest = require('../../controllers/auth/login');
const handleLogoutRequest = require('../../controllers/auth/logout');
const handleUserRequest = require('../../controllers/auth/user');
-module.exports = (app) => {
- app.post('/signup', speechPassport.authenticate('local-signup'), handleSignupRequest);
- app.post('/login', handleLoginRequest);
- app.get('/logout', handleLogoutRequest);
- app.get('/user', handleUserRequest);
+module.exports = {
+ '/signup': { method: 'post', controller: [ speechPassport.authenticate('local-signup'), handleSignupRequest ] },
+ '/login': { method: 'post', controller: handleLoginRequest },
+ '/logout': { controller: handleLogoutRequest },
+ '/user': { controller: handleUserRequest },
};
diff --git a/server/routes/fallback/index.js b/server/routes/fallback/index.js
index cc453b81..d46b3ae8 100644
--- a/server/routes/fallback/index.js
+++ b/server/routes/fallback/index.js
@@ -1,5 +1,5 @@
const handlePageRequest = require('../../controllers/pages/sendReactApp');
-module.exports = (app) => {
- app.get('*', handlePageRequest);
+module.exports = {
+ '*': { controller: handlePageRequest, action: 'fallback' },
};
diff --git a/server/routes/index.js b/server/routes/index.js
new file mode 100644
index 00000000..73756955
--- /dev/null
+++ b/server/routes/index.js
@@ -0,0 +1,7 @@
+module.exports = {
+ ...require('./pages'),
+ ...require('./api'),
+ ...require('./assets'),
+ ...require('./auth'),
+ ...require('./fallback'),
+};
diff --git a/server/routes/pages/index.js b/server/routes/pages/index.js
index 2dff606b..0d097ddf 100644
--- a/server/routes/pages/index.js
+++ b/server/routes/pages/index.js
@@ -2,13 +2,13 @@ const handlePageRequest = require('../../controllers/pages/sendReactApp');
const handleVideoEmbedRequest = require('../../controllers/pages/sendVideoEmbedPage');
const redirect = require('../../controllers/utils/redirect');
-module.exports = (app) => {
- app.get('/', handlePageRequest);
- app.get('/login', handlePageRequest);
- app.get('/about', handlePageRequest);
- app.get('/trending', redirect('/popular'));
- app.get('/popular', handlePageRequest);
- app.get('/new', handlePageRequest);
- app.get('/multisite', handlePageRequest);
- app.get('/video-embed/:name/:claimId', handleVideoEmbedRequest); // for twitter
+module.exports = {
+ '/': { controller: handlePageRequest },
+ '/login': { controller: handlePageRequest },
+ '/about': { controller: handlePageRequest },
+ '/trending': { controller: redirect('/popular') },
+ '/popular': { controller: handlePageRequest },
+ '/new': { controller: handlePageRequest },
+ '/multisite': { controller: handlePageRequest },
+ '/video-embed/:name/:claimId': { controller: handleVideoEmbedRequest }, // for twitter
};