Add context & dynamic route sagas, req for Osprey features
This commit is contained in:
parent
ec2aea5972
commit
158f0b4863
12 changed files with 200 additions and 91 deletions
|
@ -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,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
39
package-lock.json
generated
39
package-lock.json
generated
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
|
@ -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(
|
||||
<Provider store={store}>
|
||||
<StaticRouter location={req.url} context={context}>
|
||||
<GAListener>
|
||||
<App />
|
||||
</GAListener>
|
||||
</StaticRouter>
|
||||
</Provider>
|
||||
);
|
||||
|
||||
// run the saga middleware with the saga call
|
||||
sagaMiddleware
|
||||
.run(saga)
|
||||
.done
|
||||
.then(() => {
|
||||
// render component to a string
|
||||
const html = renderToString(
|
||||
<Provider store={store}>
|
||||
<StaticRouter location={req.url} context={context}>
|
||||
<GAListener>
|
||||
<App />
|
||||
</GAListener>
|
||||
</StaticRouter>
|
||||
</Provider>
|
||||
);
|
||||
// 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);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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 ] },
|
||||
};
|
||||
|
|
|
@ -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 },
|
||||
};
|
||||
|
|
|
@ -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 },
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const handlePageRequest = require('../../controllers/pages/sendReactApp');
|
||||
|
||||
module.exports = (app) => {
|
||||
app.get('*', handlePageRequest);
|
||||
module.exports = {
|
||||
'*': { controller: handlePageRequest, action: 'fallback' },
|
||||
};
|
||||
|
|
7
server/routes/index.js
Normal file
7
server/routes/index.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
module.exports = {
|
||||
...require('./pages'),
|
||||
...require('./api'),
|
||||
...require('./assets'),
|
||||
...require('./auth'),
|
||||
...require('./fallback'),
|
||||
};
|
|
@ -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
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue