143 lines
3.9 KiB
JavaScript
143 lines
3.9 KiB
JavaScript
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);
|
|
}
|
|
}
|