react-ssr-test/Server.js

143 lines
3.9 KiB
JavaScript
Raw Normal View History

import http from 'http';
import url from 'url';
import path from 'path';
import fs from 'fs';
//import pg from 'pg';
import React from 'react';
import TestRenderer from 'react-test-renderer';
import ReactDOMServer from 'react-dom/server';
import { App } from './App.js';
import { StateTree } from './StateTree.js';
function promisify(f)
{
return new Promise((resolve, reject) => f((error, result) => error ? reject(error) : resolve(result)));
}
export class SSRServer
{
static defaultConfig = {
host: '127.0.0.1',
port: 9223,
};
start(cfg)
{
this.cfg = cfg;
for (let k in this.constructor.defaultConfig)
{
this.cfg[k] = this.cfg[k] || this.constructor.defaultConfig[k];
}
this.server = http.Server((req, resp) => this.handleRequest(req, resp));
this.server.listen(this.cfg.port, this.cfg.host);
if (this.cfg.db)
{
this.connectPg();
}
this.dir = __dirname;
}
async connectPg()
{
this.db = new pg.Client(this.cfg.db);
this.db.on('error', () => setTimeout(() => this.connectPg(), 30000));
this.db.on('notification', (msg) => this.onNotification(msg));
await this.db.connect();
}
onNotification({ name, length, processId, channel, payload })
{
}
async handleRequest(req, resp)
{
const u = url.parse(req.url, true);
const path = u.pathname.replace(/^\/+|\/+$/g, '');
if (!path)
{
resp.writeHead(200, {
'Content-Type': 'text/html; charset=utf-8',
});
let pending = 0;
let st, testr;
let finish = () =>
{
st.update();
const data = st.export();
testr.unmount();
testr = null;
st = new StateTree({ doQuery: () => {} });
st.import(data);
const html =
'<!DOCTYPE html><html><head>'+
'<meta http-equiv="content-type" content="text/html; charset=utf-8" />'+
'</head><body><div id="app">'+
ReactDOMServer.renderToString(<App state={st} />)+
'</div></body><script src="main.c.js"></script>'+
'<script>initApp('+JSON.stringify(data)+')</script></html>';
resp.write(html);
resp.end();
};
st = new StateTree({
doQuery: (cb) =>
{
pending++;
this.handle('data', (r) =>
{
cb(r);
pending--;
if (!pending)
{
testr.update(<App state={st} />);
finish();
}
});
}
});
testr = TestRenderer.create(<App state={st} />);
if (testr && !pending)
{
finish();
}
}
else if (path == 'main.c.js')
{
const data = await promisify(h => fs.readFile(this.dir + '/main.c.js', h));
resp.writeHead(200, {
'Content-Type': 'application/javascript',
});
resp.write(data);
resp.end();
}
else
{
this.handle(path, (r) =>
{
resp.writeHead(200, {
'Content-Type': 'application/json',
});
resp.write(JSON.stringify(r));
resp.end();
});
}
}
handle(path, cb)
{
let r;
if (path == 'data')
{
// Data
r = { text: 'Hello world!' };
}
else
r = { error: 'Unknown action' };
setTimeout(() =>
{
cb(r);
}, 100);
}
}