server.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. /*
  2. Copyright (C) 2017 Cloudbase Solutions SRL
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Affero General Public License as
  5. published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Affero General Public License for more details.
  11. You should have received a copy of the GNU Affero General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. */
  14. import 'babel-polyfill';
  15. import path from 'path';
  16. import express from 'express';
  17. import cookieParser from 'cookie-parser';
  18. import bodyParser from 'body-parser';
  19. import expressJwt from 'express-jwt';
  20. import jwt from 'jsonwebtoken';
  21. import ReactDOM from 'react-dom/server';
  22. import PrettyError from 'pretty-error';
  23. import passport from './core/passport';
  24. import Router from './routes';
  25. import assets from './assets';
  26. import { port, auth, analytics } from './config';
  27. const server = global.server = express();
  28. //
  29. // Tell any CSS tooling (such as Material UI) to use all vendor prefixes if the
  30. // user agent is not known.
  31. // -----------------------------------------------------------------------------
  32. global.navigator = global.navigator || {};
  33. global.navigator.userAgent = global.navigator.userAgent || 'all';
  34. //
  35. // Register Node.js middleware
  36. // -----------------------------------------------------------------------------
  37. server.use(express.static(path.join(__dirname, 'public')));
  38. server.use(cookieParser());
  39. server.use(bodyParser.urlencoded({ extended: true }));
  40. server.use(bodyParser.json());
  41. //
  42. // Authentication
  43. // -----------------------------------------------------------------------------
  44. server.use(expressJwt({
  45. secret: auth.jwt.secret,
  46. credentialsRequired: false,
  47. /* jscs:disable requireCamelCaseOrUpperCaseIdentifiers */
  48. getToken: req => req.cookies.id_token,
  49. /* jscs:enable requireCamelCaseOrUpperCaseIdentifiers */
  50. }));
  51. server.use(passport.initialize());
  52. server.get('/login/facebook',
  53. passport.authenticate('facebook', { scope: ['email', 'user_location'], session: false })
  54. );
  55. server.get('/login/facebook/return',
  56. passport.authenticate('facebook', { failureRedirect: '/login', session: false }),
  57. (req, res) => {
  58. const expiresIn = 60 * 60 * 24 * 180; // 180 days
  59. const token = jwt.sign(req.user, auth.jwt.secret, { expiresIn });
  60. res.cookie('id_token', token, { maxAge: 1000 * expiresIn, httpOnly: true });
  61. res.redirect('/');
  62. }
  63. );
  64. //
  65. // Register server-side rendering middleware
  66. // -----------------------------------------------------------------------------
  67. server.get('*', async (req, res, next) => {
  68. try {
  69. let statusCode = 200;
  70. const template = require('./views/index.jade');
  71. const data = { title: '', description: '', css: '', body: '', entry: assets.main.js };
  72. if (process.env.NODE_ENV === 'production') {
  73. data.trackingId = analytics.google.trackingId;
  74. }
  75. const css = [];
  76. const context = {
  77. insertCss: styles => css.push(styles._getCss()),
  78. onSetTitle: value => (data.title = value),
  79. onSetMeta: (key, value) => (data[key] = value),
  80. onPageNotFound: () => (statusCode = 404),
  81. };
  82. await Router.dispatch({ path: req.path, query: req.query, context }, (state, component) => {
  83. data.body = ReactDOM.renderToString(component);
  84. data.css = css.join('');
  85. });
  86. res.status(statusCode);
  87. res.send(template(data));
  88. } catch (err) {
  89. next(err);
  90. }
  91. });
  92. //
  93. // Error handling
  94. // -----------------------------------------------------------------------------
  95. const pe = new PrettyError();
  96. pe.skipNodeFiles();
  97. pe.skipPackage('express');
  98. server.use((err, req, res, next) => { // eslint-disable-line no-unused-vars
  99. console.log(pe.render(err)); // eslint-disable-line no-console
  100. const template = require('./views/error.jade');
  101. const statusCode = err.status || 500;
  102. res.status(statusCode);
  103. res.send(template({
  104. message: err.message,
  105. stack: process.env.NODE_ENV === 'production' ? '' : err.stack,
  106. }));
  107. });
  108. //
  109. // Launch the server
  110. // -----------------------------------------------------------------------------
  111. server.listen(port, () => {
  112. /* eslint-disable no-console */
  113. console.log(`The server is running at http://localhost:${port}/`);
  114. });