-
Notifications
You must be signed in to change notification settings - Fork 0
/
ssr-server.js
152 lines (115 loc) · 4.23 KB
/
ssr-server.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
import fs from 'fs'
import express from 'express'
import path from 'path'
import React from 'react'
import { renderToString } from 'react-dom/server'
import { Provider } from 'react-redux'
import { Router, RoutingContext, match } from 'react-router'
import routes from './app/routes'
import fetchComponentData from './app/utils/fetchComponentData'
import configureStore from './app/configureStore'
import Html from './app/html'
import zlib from 'zlib'
console.log( 'env: ', process.env.NODE_ENV )
const PORT = process.env.PORT || 3000
const app = express();
const morgan = require('morgan')
app.use(express.static('public'))
app.use(morgan('dev'))
app.get('/static/bundle.js', function(req, res) {
var raw = fs.createReadStream(path.join(__dirname, 'dist', 'bundle.js'));
var acceptEncoding = req.headers['accept-encoding'];
if (!acceptEncoding) {
acceptEncoding = '';
}
// Note: this is not a conformant accept-encoding parser.
// See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3
if (acceptEncoding.match(/\bdeflate\b/)) {
res.writeHead(200, { 'content-encoding': 'deflate' });
raw.pipe(zlib.createDeflate()).pipe(res)
} else if (acceptEncoding.match(/\bgzip\b/)) {
res.writeHead(200, { 'content-encoding': 'gzip' })
raw.pipe(zlib.createGzip()).pipe(res)
} else {
res.writeHead(200, {})
raw.pipe(res)
}
})
app.get('/static/styles.css', function(req, res) {
const raw = fs.createReadStream(path.join(__dirname, 'dist', 'styles.css'));
var acceptEncoding = req.headers['accept-encoding'];
if (!acceptEncoding) {
acceptEncoding = '';
}
// Note: this is not a conformant accept-encoding parser.
// See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3
if (acceptEncoding.match(/\bdeflate\b/)) {
res.writeHead(200, { 'content-encoding': 'deflate' });
raw.pipe(zlib.createDeflate()).pipe(res)
} else if (acceptEncoding.match(/\bgzip\b/)) {
res.writeHead(200, { 'content-encoding': 'gzip' })
raw.pipe(zlib.createGzip()).pipe(res)
} else {
res.writeHead(200, {})
raw.pipe(res)
}
})
// server rendering
app.use((req, res, next) => {
const store = configureStore()
// react-router
match({routes, location: req.url}, (error, redirectLocation, renderProps) => {
if (error) {
return res.status(500).send(error.message)
}
if (redirectLocation) {
return res.redirect(302, redirectLocation.pathname + redirectLocation.search)
}
if (renderProps === null) {
return next('err msg: route not found'); // yield control to next middleware to handle the request
}
// console.log(renderProps)
// console.log(renderProps.components)
// this is where universal rendering happens,
// fetchComponentData() will trigger actions listed in static "needs" props in each container component
// and wait for all of them to complete before continuing rendering the page,
// hence ensuring all data needed was fetched before proceeding
//
// renderProps: contains all necessary data, e.g: routes, router, history, components...
fetchComponentData(store.dispatch, renderProps.components, renderProps)
.then(() => {
const state = JSON.stringify(store.getState());
const innerHtml = renderToString((
<Provider store={store}>
<RoutingContext {...renderProps} />
</Provider>
))
const initView = renderToString((
<Html
state={state}
html={innerHtml} />
))
// console.log(initView);
// console.log( '\nstate: ', state )
const page = `<!doctype html> ${initView}`
// console.log( '\npage:\n', page );
return page;
})
.then(page => res.status(200).send(page))
.catch(err => res.end(err.message));
})
})
// <script src="/static/bundle.js"></script>
// example of handling 404 pages
// global error catcher, need four arguments
app.use((err, req, res, next) => {
console.error("Error on request %s %s", req.method, req.url);
console.error(err.stack);
res.status(500).send("Server error");
});
process.on('uncaughtException', evt => {
console.log( 'uncaughtException: ', evt );
})
app.listen(PORT, () => {
console.log('Listening on port ' + PORT);
});