Web refactoring

This commit is contained in:
calzoneman 2015-10-26 22:56:53 -07:00
parent 566e932e7e
commit 50ca141f1d
10 changed files with 106 additions and 75 deletions

View File

@ -3,6 +3,7 @@ try {
} catch (err) {
console.error('FATAL: Failed to require() lib/server.js');
console.error('Have you run `npm run build-server` yet to generate it?');
console.error(err.stack);
process.exit(1);
}
var Config = require("./lib/config");

View File

@ -1,4 +1,9 @@
import createError from 'create-error';
import * as HTTPStatus from './web/httpstatus';
export const ChannelStateSizeError = createError('ChannelStateSizeError');
export const ChannelNotFoundError = createError('ChannelNotFoundError');
export const CSRFError = createError('CSRFError');
export const HTTPError = createError('HTTPError', {
status: HTTPStatus.INTERNAL_SERVER_ERROR
});

View File

@ -42,6 +42,9 @@ var $util = require("./utilities");
var db = require("./database");
var Flags = require("./flags");
var sio = require("socket.io");
import LocalChannelIndex from './web/localchannelindex';
import IOConfiguration from './configuration/ioconfig';
import NullClusterClient from './io/cluster/nullclusterclient';
var Server = function () {
var self = this;
@ -60,8 +63,14 @@ var Server = function () {
ChannelStore.init();
// webserver init -----------------------------------------------------
const ioConfig = IOConfiguration.fromOldConfig(Config);
const clusterClient = new NullClusterClient(ioConfig);
const channelIndex = new LocalChannelIndex();
self.express = express();
require("./web/webserver").init(self.express);
require("./web/webserver").init(self.express,
ioConfig,
clusterClient,
channelIndex);
// http/https/sio server init -----------------------------------------
var key = "", cert = "", ca = undefined;

View File

@ -2,8 +2,9 @@
* Adapted from https://github.com/expressjs/csurf
*/
import { CSRFError } from '../errors';
var csrf = require("csrf");
var createError = require("http-errors");
var tokens = csrf();
@ -39,8 +40,6 @@ exports.verify = function csrfVerify(req) {
var token = req.body._csrf || req.query._csrf;
if (!tokens.verify(secret, token)) {
throw createError(403, 'invalid csrf token', {
code: 'EBADCSRFTOKEN'
});
throw new CSRFError('Invalid CSRF token');
}
};

3
src/web/httpstatus.js Normal file
View File

@ -0,0 +1,3 @@
export const BAD_REQUEST = 400;
export const FORBIDDEN = 403;
export const INTERNAL_SERVER_ERROR = 500;

View File

@ -0,0 +1,14 @@
import Promise from 'bluebird';
import Server from '../server';
var SERVER = null;
export default class LocalChannelIndex {
listPublicChannels() {
if (SERVER === null) {
SERVER = require('../server').getServer();
}
return Promise.resolve(SERVER.packChannelList(true));
}
}

25
src/web/routes/channel.js Normal file
View File

@ -0,0 +1,25 @@
import CyTubeUtil from '../../utilities';
import { sanitizeText } from '../../xss';
import { sendJade } from '../jade';
import * as HTTPStatus from '../httpstatus';
import { HTTPError } from '../../errors';
export default function initialize(app, ioConfig) {
app.get('/r/:channel', (req, res) => {
if (!req.params.channel || !CyTubeUtil.isValidChannelName(req.params.channel)) {
throw new HTTPError(`"${sanitizeText(req.params.channel)} is not a valid ` +
'channel name.', { status: HTTPStatus.BAD_REQUEST });
}
const endpoints = ioConfig.getSocketEndpoints();
if (endpoints.length === 0) {
throw new HTTPError('No socket.io endpoints configured');
}
const socketBaseURL = endpoints[0].url;
sendJade(res, 'channel', {
channelName: req.params.channel,
sioSource: `${socketBaseURL}/socket.io/socket.io.js`
});
});
}

19
src/web/routes/index.js Normal file
View File

@ -0,0 +1,19 @@
import { sendJade } from '../jade';
export default function initialize(app, channelIndex) {
app.get('/', (req, res) => {
channelIndex.listPublicChannels().then((channels) => {
channels.sort((a, b) => {
if (a.usercount === b.usercount) {
return a.uniqueName > b.uniqueName ? -1 : 1;
}
return b.usercount - a.usercount;
});
sendJade(res, 'index', {
channels: channels
});
});
});
}

View File

@ -1,16 +1,12 @@
import IOConfiguration from '../../configuration/ioconfig';
import NullClusterClient from '../../io/cluster/nullclusterclient';
import Config from '../../config';
import CyTubeUtil from '../../utilities';
import Logger from '../../logger';
import * as HTTPStatus from '../httpstatus';
export default function initialize(app) {
const ioConfig = IOConfiguration.fromOldConfig(Config);
const clusterClient = new NullClusterClient(ioConfig);
export default function initialize(app, clusterClient) {
app.get('/socketconfig/:channel.json', (req, res) => {
if (!req.params.channel || !CyTubeUtil.isValidChannelName(req.params.channel)) {
return res.status(404).json({
return res.status(HTTPStatus.NOT_FOUND).json({
error: `Channel "${req.params.channel}" does not exist.`
});
}

View File

@ -16,6 +16,8 @@ var morgan = require("morgan");
var session = require("../session");
var csrf = require("./csrf");
var XSS = require("../xss");
import * as HTTPStatus from './httpstatus';
import { CSRFError } from '../errors';
const LOG_FORMAT = ':real-address - :remote-user [:date] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"';
morgan.token('real-address', function (req) { return req._ip; });
@ -71,51 +73,6 @@ function redirectHttp(req, res) {
return false;
}
/**
* Handles a GET request for /r/:channel - serves channel.html
*/
function handleChannel(req, res) {
if (!$util.isValidChannelName(req.params.channel)) {
res.status(404);
res.send("Invalid channel name '" + XSS.sanitizeText(req.params.channel) + "'");
return;
}
var sio;
if (net.isIPv6(ipForRequest(req))) {
sio = Config.get("io.ipv6-default");
}
if (!sio) {
sio = Config.get("io.ipv4-default");
}
sio += "/socket.io/socket.io.js";
sendJade(res, "channel", {
channelName: req.params.channel,
sioSource: sio
});
}
/**
* Handles a request for the index page
*/
function handleIndex(req, res) {
var channels = Server.getServer().packChannelList(true);
channels.sort(function (a, b) {
if (a.usercount === b.usercount) {
return a.uniqueName > b.uniqueName ? -1 : 1;
}
return b.usercount - a.usercount;
});
sendJade(res, "index", {
channels: channels
});
}
/**
* Legacy socket.io configuration endpoint. This is being migrated to
* /socketconfig/<channel name>.json (see ./routes/socketconfig.js)
@ -185,7 +142,7 @@ module.exports = {
/**
* Initializes webserver callbacks
*/
init: function (app) {
init: function (app, ioConfig, clusterClient, channelIndex) {
app.use(function (req, res, next) {
req._ip = ipForRequest(req);
next();
@ -241,10 +198,10 @@ module.exports = {
Logger.syslog.log("Enabled express-minify for CSS and JS");
}
app.get("/r/:channel", handleChannel);
app.get("/", handleIndex);
require("./routes/channel")(app, ioConfig);
require("./routes/index")(app, channelIndex);
app.get("/sioconfig(.json)?", handleSocketConfig);
require("./routes/socketconfig")(app);
require("./routes/socketconfig")(app, clusterClient);
app.get("/useragreement", handleUserAgreement);
app.get("/contact", handleContactPage);
require("./auth").init(app);
@ -256,21 +213,24 @@ module.exports = {
}));
app.use(function (err, req, res, next) {
if (err) {
if (err.message && err.message.match(/failed to decode param/i)) {
return res.status(400).send("Malformed path: " + req.path);
} else if (err.message && err.message.match(/range not satisfiable/i)) {
return res.status(416).end();
} else if (err.message && err.message.match(/request entity too large/i)) {
return res.status(413).end();
} else if (err.message && err.message.match(/bad request/i)) {
return res.status(400).end("Bad Request");
} else if (err.message && err.message.match(/invalid csrf token/i)) {
res.status(403);
sendJade(res, 'csrferror', { path: req.path });
return;
if (err instanceof CSRFError) {
res.status(HTTPStatus.FORBIDDEN);
return sendJade(res, 'csrferror', { path: req.path });
}
Logger.errlog.log(err.stack);
res.status(500).end();
let { message, status } = err;
if (!status) {
status = HTTPStatus.INTERNAL_SERVER_ERROR;
}
if (!message) {
message = 'An unknown error occurred.';
}
if (Math.floor(status / 100) === 5) {
Logger.errlog.log(err.stack);
}
return res.status(status).send(message);
} else {
next();
}