Skip to content
This repository has been archived by the owner on Dec 28, 2018. It is now read-only.

Commit

Permalink
Auto merge of #639 - saneyuki:app, r=saneyuki
Browse files Browse the repository at this point in the history
refactor(server): create 'KarenServer' class

<!-- Reviewable:start -->
This change is [<img src="https://reviewable.io/review_button.svg" height="35" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/karen-irc/karen/639)
<!-- Reviewable:end -->
  • Loading branch information
dokidokivisual committed May 3, 2016
2 parents 8b22a94 + 759a3b9 commit ea17f5a
Show file tree
Hide file tree
Showing 4 changed files with 322 additions and 197 deletions.
139 changes: 139 additions & 0 deletions src/server/app/application.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/**
* @license MIT License
*
* Copyright (c) 2015 Tetsuharu OHZEKI <saneyuki.snyk@gmail.com>
* Copyright (c) 2015 Yusuke Suzuki <utatane.tea@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/// <reference path='../../../typings/main.d.ts'/>

import compression from 'compression';
import * as fs from 'fs';
import express from 'express';
import * as http from 'http';
import * as spdy from 'spdy';

import ConfigDriver from '../adapter/ConfigDriver';
import {SocketIoServerDriver} from '../adapter/SocketIoServerDriver';

import {routeIndex} from '../route/router';
import {confirmAuth, initializeConnection} from '../route/socketio';

import {ClientManager} from '../ClientManager';

import {applyGenericSecurityHeader} from './security';

export class KarenServer {

constructor(options) {
const config = ConfigDriver.getConfig();

this._config = Object.assign(config, options);
this._express = createExpress();
this._server = createServer(this._express, this._config);
this._socketIo = createSocketIo(this._server, this._config);
this._manager = new ClientManager();

this._subscription = this._init();
}

_init() {
this._initRouting();
this._manager.sockets = this._socketIo.getServer();

const subscription = this._socketIo.connect().subscribe((gateway) => {
if (this._config.public) {
this._initConnection(gateway);
} else {
this._confirmAuth(gateway);
}
});
return subscription;
}

_initRouting() {
const app = this._express;
app.use((req, res, next) => {
routeIndex(this._config, req, res, next);
});
app.use('/dist', express.static('dist/client'));
app.use(express.static('src/client'));
}

_initConnection(clientGateway) {
initializeConnection(this._config, this._manager, clientGateway, this._socketIo);
}

_confirmAuth(clientGateway) {
confirmAuth(this._config, this._manager, clientGateway);
}

config() {
return this._config;
}

server() {
return this._server;
}

socketIo() {
return this._socketIo;
}

clientManager() {
return this._manager;
}
}

function createExpress() {
const app = express();
app.set('x-powered-by', false);
app.use(applyGenericSecurityHeader);
app.use(compression());
app.enable('trust proxy');
return app;
}

function createServer(express, config) {
const httpsOptions = config.https || {};
const port = config.port;
const host = config.host;

let server = null;
if (!httpsOptions.enable){
server = http.createServer(express).listen(port, host);
} else {
/*eslint-disable no-sync*/
// We can wait the synchronous call in the start up time.
server = spdy.createServer({
key: fs.readFileSync(httpsOptions.key),
cert: fs.readFileSync(httpsOptions.certificate)
}, express).listen(port, host);
/*eslint-enable */
}

return server;
}

function createSocketIo(server, config) {
const transports = config.transports || ['websocket', 'polling'];
const driver = new SocketIoServerDriver(server, transports);
return driver;
}
56 changes: 56 additions & 0 deletions src/server/route/router.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/**
* @license MIT License
*
* Copyright (c) 2015 Tetsuharu OHZEKI <saneyuki.snyk@gmail.com>
* Copyright (c) 2015 Yusuke Suzuki <utatane.tea@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

import * as React from 'react';
import * as ReactDOMServer from 'react-dom/server';

import Package from '../adapter/Package';
import {applyHtmlSecurtyHeader} from '../app/security';

import { KarenAppIndex as IndexTemplate } from '../view/classic/Index';
import {RizeIndex} from '../view/rize/RizeIndex';

const isEnableRize = process.env.ENABLE_RIZE === '1';

export function routeIndex(config, req, res, next) {
if (req.url.split('?')[0] !== '/') {
next();
return;
}

let data = Object.assign({}, Package.getPackage());
data = Object.assign(data, config);
res.setHeader('Content-Type', 'text/html');
applyHtmlSecurtyHeader(req, res);
res.writeHead(200);

const view = isEnableRize ?
React.createElement(RizeIndex, null) :
React.createElement(IndexTemplate, {
data: data,
});
const html = '<!DOCTYPE html>' + ReactDOMServer.renderToStaticMarkup(view);
res.end(html);
}
121 changes: 121 additions & 0 deletions src/server/route/socketio.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/**
* @license MIT License
*
* Copyright (c) 2015 Tetsuharu OHZEKI <saneyuki.snyk@gmail.com>
* Copyright (c) 2015 Yusuke Suzuki <utatane.tea@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

import bcrypt from 'bcrypt-nodejs';

import {Client} from '../Client';

export function confirmAuth(config, clientManager, clientGateway) {
clientGateway.auth().subscribe(function (data) {
tryLogin(config, clientManager, clientGateway, data);
});

clientGateway.emitAuth();
}

export function initializeConnection(config, clientManager, clientGateway, serverGateway) {
const client = new Client(serverGateway.getServer());
clientManager.addClient(client);

clientGateway.disconnect().subscribe(function() {
clientManager.removeClient(client);
client.quit();
});

subscribeFromClient(config, clientGateway, client);
}

function tryLogin(config, clientManager, clientGateway, data) {
const tryAll = Array.from(clientManager.clients).map(function(client){
const trying = new Promise(function(resolve, reject){
if (!!data.token && data.token === client.token) {
resolve(true);
} else if (client.config.user === data.user) {
bcrypt.compare((data.password || ''), client.config.password, function(err, res) {
if (!!err) {
reject(err);
return;
}
resolve(res);
});
}
else {
resolve(false);
}
});
const auth = trying.then(function(isSuccess){
if (!isSuccess) {
return false;
}
else {
let token = '';
if (data.remember || data.token) {
token = client.token;
}
subscribeFromClient(config, clientGateway, client, token);
return true;
}
});
return auth;
});

Promise.all(tryAll).then(function(result) {
const isSuccess = result.some((ok) => ok);
if (!isSuccess) {
// There are no succeeded clients, use authentication.
clientGateway.emitAuth();
}
});
}

function subscribeFromClient(config, clientGateway, client, token) {
clientGateway.input().subscribe(function (data) {
client.input(data);
});

clientGateway.more().subscribe(function (data) {
client.more(data);
});

clientGateway.connect().subscribe(function (data) {
client.connect(data);
});

clientGateway.open().subscribe(function (data) {
client.open(data);
});

clientGateway.sort().subscribe(function (data) {
client.sort(data);
});

clientGateway.joinClient(client.id);

const key = (token !== undefined) ? token : '';
clientGateway.emitInitialize(client.activeChannel,
client.networks,
key,
[config.defaults]);
}
Loading

0 comments on commit ea17f5a

Please sign in to comment.