diff --git a/package.json b/package.json index 8067637..ae63347 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "ansi-html": "0.0.7", "bcrypt": "^5.0.0", "classnames": "2.2.6", + "concat-stream-p": "0.1.2", "connect-redis": "^5.0.0", "contempo": "1.x", "debug": "^4.1.1", diff --git a/src/client/index.ejs b/src/client/index.ejs index bf8c18d..e203835 100644 --- a/src/client/index.ejs +++ b/src/client/index.ejs @@ -5,6 +5,7 @@ <%= htmlWebpackPlugin.options.title %> +
diff --git a/src/server/http.js b/src/server/http.js index 0a7ad0d..595e53a 100644 --- a/src/server/http.js +++ b/src/server/http.js @@ -1,14 +1,31 @@ /* eslint-disable import/no-extraneous-dependencies */ -import express from 'express'; +import {createReadStream} from 'fs'; import http, {ServerResponse} from 'http'; -import httpProxy from 'http-proxy'; import {join} from 'path'; +import concat from 'concat-stream-p'; +import express from 'express'; +import httpProxy from 'http-proxy'; +import {invokeHookFlat} from 'scwp'; + import userRoutes from './routes/user'; import passport from './passport'; import session from './session'; -export function createHttpServer() { +const hydration = async (req, buffer) => { + const hydration = JSON.stringify( + (await Promise.all(invokeHookFlat('hydration', req))) + .reduce((hydration, result) => ({...hydration, ...result}), {}), + ); + return buffer + .toString() + .replace( + 'window.__HYDRATION__ = {};', + `window.__HYDRATION__ = ${hydration};`, + ); +}; + +export async function createHttpServer() { const app = express(); app.use(express.urlencoded({extended: true})); app.use(session()); @@ -22,19 +39,23 @@ export function createHttpServer() { secure: false, target: 'http://127.0.0.1:31345', }); + proxy.on('proxyRes', async (proxyRes, req, res) => { + const buffer = await proxyRes.pipe(concat()); + res.end(await hydration(req, buffer)); + }); proxy.on('error', (err, req, res) => { if (res instanceof ServerResponse) { res.status(502).end('Bad Gateway (WDS)'); } }); - app.get('*', (req, res) => proxy.web(req, res)); + app.get('*', (req, res) => proxy.web(req, res, {selfHandleResponse: true})); httpServer.on('close', () => proxy.close()); } else { app.use(express.static(join(__dirname, '..', 'client'))); - app.get('*', (req, res) => { - res.sendFile(join(__dirname, '..', 'client', 'index.html')); - }); + const stream = createReadStream(join(__dirname, '..', 'client', 'index.html')); + const buffer = await stream.pipe(concat()); + app.get('*', async (req, res) => res.end(await hydration(req, buffer))); } return httpServer; } diff --git a/src/server/models/base.js b/src/server/models/base.js index 3c9e7f2..adf5ee2 100644 --- a/src/server/models/base.js +++ b/src/server/models/base.js @@ -2,11 +2,10 @@ import {Model} from 'sequelize'; class BaseModel extends Model { - // eslint-disable-next-line no-unused-vars - static associate(map) {} + static associate(/* map */) {} - static attributes() { - throw new ReferenceError('You must define a static attributes() method in your model.'); + static get attributes() { + return {}; } } diff --git a/src/server/models/user.model.js b/src/server/models/user.model.js index 3552888..b70b634 100644 --- a/src/server/models/user.model.js +++ b/src/server/models/user.model.js @@ -3,9 +3,10 @@ import {randomBytes} from 'crypto'; import bcrypt from 'bcrypt'; import {registerHooks} from 'scwp'; -import {DataTypes as Types} from 'sequelize'; +import {DataTypes as Types, Op} from 'sequelize'; import BaseModel from './base'; +import {allModels} from './registrar'; class User extends BaseModel { @@ -33,6 +34,19 @@ class User extends BaseModel { }); } + async friends() { + const {Friendship} = allModels(); + const friendships = await Friendship.findAll({ + where: { + [Op.or]: [ + {adderId: this.id}, + {addeeId: this.id}, + ], + }, + }); + return friendships.map(({adderId, addeeId}) => (adderId === this.id ? addeeId : adderId)); + } + validatePassword(plaintext) { return bcrypt.compare(plaintext, this.hash); } diff --git a/src/server/session.js b/src/server/session.js index 7b473d4..2dab20e 100644 --- a/src/server/session.js +++ b/src/server/session.js @@ -1,5 +1,6 @@ -import redis from 'redis'; import session from 'express-session'; +import redis from 'redis'; +import {registerHooks} from 'scwp'; const { REDIS_HOST, @@ -20,3 +21,18 @@ export default (options = {}) => ( ...options, }) ); + +registerHooks({ + hydration: async (req) => { + const hydration = {}; + const {user} = req; + if (user) { + hydration.redditUsername = user.redditUsername; + hydration.friends = await user.friends(); + } + else { + hydration.user = null; + } + return hydration; + }, +}, module.id); diff --git a/yarn.lock b/yarn.lock index 66553ee..4ed5fc6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1924,6 +1924,11 @@ block-stream@*: dependencies: inherits "~2.0.0" +bluebird@^2.10.0: + version "2.11.0" + resolved "https://npm.i12e.cha0s.io/bluebird/-/bluebird-2.11.0.tgz#534b9033c022c9579c56ba3b3e5a5caafbb650e1" + integrity sha1-U0uQM8AiyVecVro7Plpcqvu2UOE= + bluebird@^3.5.5, bluebird@^3.7.2: version "3.7.2" resolved "https://npm.i12e.cha0s.io/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" @@ -2600,6 +2605,14 @@ concat-map@0.0.1: resolved "https://npm.i12e.cha0s.io/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= +concat-stream-p@0.1.2: + version "0.1.2" + resolved "https://npm.i12e.cha0s.io/concat-stream-p/-/concat-stream-p-0.1.2.tgz#5de0386a3d0a27e2013627f58cddb5c46d8e2d22" + integrity sha1-XeA4aj0KJ+IBNif1jN21xG2OLSI= + dependencies: + bluebird "^2.10.0" + concat-stream "^1.5.0" + concat-stream@^1.5.0: version "1.6.2" resolved "https://npm.i12e.cha0s.io/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"