<commit_before>'use strict';

var gulp = require('gulp');
var bump = require('gulp-bump');
var git = require('gulp-git');
var excludeGitignore = require('gulp-exclude-gitignore');
var concat = require('gulp-concat');
var rename = require('gulp-rename');
var uglify = require('gulp-uglify');

var DEST = 'dist/';
var version = require('./package.json').version;

gulp.task('default', build);
gulp.task('bumpMajor', bumpVersion('major'));
gulp.task('bumpMinor', bumpVersion('minor'));
gulp.task('bumpPatch', bumpVersion('patch'));
gulp.task('release', release);
gulp.task('build', build);

function build() {
    return gulp.src(['src/sg.ui.js', 'src/*.js'])
        .pipe(concat('sg.ui.js'))
        // This will output the non-minified version
        .pipe(gulp.dest(DEST))
        // This will minify and rename to foo.min.js
        .pipe(uglify())
        .pipe(rename({extname: '.min.js'}))
        .pipe(gulp.dest(DEST));
}

function release() {
    return gulp.src('./*')
        .pipe(excludeGitignore())
        .pipe(git.add())
        .pipe(git.commit('Dist files for version ' + version), function(err){
            if (err) throw err;
        })
        .on('end', function () {
            git.tag('v' + version, 'Release ' + version, function (err) {
                if (err) throw err;
                git.push('origin', 'master', {args: '--tags'});
            });
        });
}

function bumpVersion(bumpType) {
    return function () {
        gulp.src('./package.json')
            .pipe(bump({type: bumpType}))
            .pipe(gulp.dest('./'))
            .pipe(git.commit("Bump package version"));
    }
}<commit_msg>Dist files for version 0.6.0<commit_after>'use strict';

var gulp = require('gulp');
var bump = require('gulp-bump');
var git = require('gulp-git');
var excludeGitignore = require('gulp-exclude-gitignore');
var concat = require('gulp-concat');
var rename = require('gulp-rename');
var uglify = require('gulp-uglify');

var DEST = 'dist/';
var version = require('./package.json').version;

gulp.task('default', build);
gulp.task('bumpMajor', bumpVersion('major'));
gulp.task('bumpMinor', bumpVersion('minor'));
gulp.task('bumpPatch', bumpVersion('patch'));
gulp.task('release', release);
gulp.task('build', build);

function build() {
    return gulp.src(['src/sg.ui.js', 'src/*.js'])
        .pipe(concat('sg.ui.js'))
        // This will output the non-minified version
        .pipe(gulp.dest(DEST))
        // This will minify and rename to foo.min.js
        .pipe(uglify())
        .pipe(rename({extname: '.min.js'}))
        .pipe(gulp.dest(DEST));
}

function release() {
    return gulp.src('./*')
        .pipe(excludeGitignore())
        .pipe(git.add())
        .pipe(git.commit('Dist files for version ' + version), function(err){
            if (err) {
                process.stdout.write(err);
                throw err;
            }
        })
        .on('end', function () {
            git.tag('v' + version, 'Release ' + version, function (err) {
                if (err) throw err;
                git.push('origin', 'master', {args: '--tags'});
            });
        });
}

function bumpVersion(bumpType) {
    return function () {
        gulp.src('./package.json')
            .pipe(bump({type: bumpType}))
            .pipe(gulp.dest('./'))
            .pipe(git.commit("Bump package version"));
    }
}<|endoftext|>


<commit_before>"use strict";

/*
 * HyperSwitch web service entry point
 *
 * Sets up a HyperSwitch instance by loading and setting up handlers and the
 * storage layer, and then dispatches requests to it.
 */

var P = require('bluebird');
var Busboy = require('busboy');
var qs = require('querystring');
var url = require('url');
var http = require('http');
var zlib = require('zlib');
var stream = require('stream');

var URI = require('swagger-router').URI;

var exports = require('./exports');
var HyperSwitch = require('./hyperswitch');
var Router = require('./router');
var utils = require('./utils');

// Should make it into 0.12, see https://github.com/joyent/node/pull/7878
var SIMPLE_PATH = /^(\/(?!\/)[^\?#\s]*)(?:\?([^#\s]*))?$/;
function parseURL(uri) {
    // Fast path for simple path uris
    var fastMatch = SIMPLE_PATH.exec(uri);
    if (fastMatch) {
        return {
            protocol: null,
            slashes: null,
            auth: null,
            host: null,
            port: null,
            hostname: null,
            hash: null,
            search: fastMatch[2] || '',
            pathname: fastMatch[1],
            path: fastMatch[1],
            query: fastMatch[2] && qs.parse(fastMatch[2]) || {},
            href: uri
        };
    } else {
        return url.parse(uri, true);
    }
}

/**
 * Parse a POST request into request.body with BusBoy
 * Drops file uploads on the floor without creating temporary files
 *
 * @param {Request} req HTTP request
 * @returns {Promise<>}
 */
function read(req) {
    return new P(function(resolve) {
        var chunks = [];
        req.on('data', function(chunk) {
            chunks.push(chunk);
        });

        req.on('end', function() {
            resolve(Buffer.concat(chunks));
        });
    });
}

function parsePOST(req) {
    var readIt = (req.method === 'PUT') ||
        (req.method === 'POST' && req.headers &&
            (/^application\/json/i.test(req.headers['content-type'])
            || !req.headers['content-type']));

    if (readIt) {
        return read(req);
    } else if (req.method !== 'POST') {
        return P.resolve();
    } else {
        // Parse the POST
        return new P(function(resolve) {
            // Parse POST data
            var bboy = new Busboy({
                headers: req.headers,
                // Increase the form field size limit from the 1M default.
                limits: { fieldSize: 15 * 1024 * 1024 }
            });
            var body = {};
            bboy.on('field', function(field, val) {
                body[field] = val;
            });
            bboy.on('finish', function() {
                resolve(body);
            });
            req.pipe(bboy);
        });
    }
}

function logResponse(opts, response, startTime) {
    var latency = Date.now() - startTime;
    var logLevel = 'trace/request';
    if (response.status >= 500) {
        logLevel = 'error/request';
    } else if (response.status >= 400) {
        logLevel = 'info/request';
    } else if (latency > 5000) {
        logLevel = 'trace/request/slow';
    }
    opts.log(logLevel, {
        message: response.message,
        res: response,
        stack: response.stack,
        latency: latency,
    });
}

/**
 * Set up basic CORS in response headers
 *
 * @param {Object} rh Response headers object
 * @private
 */
function setCORSHeaders(rh) {
    rh['access-control-allow-origin'] = '*';
    rh['access-control-allow-methods'] = 'GET';
    rh['access-control-allow-headers'] = 'accept, content-type';
    rh['access-control-expose-headers'] = 'etag';
}

function handleResponse(opts, req, resp, response) {
    if (response && response.status) {
        var rh = response.headers = response.headers || {};

        utils.removeHopToHopHeaders(rh, false);
        setCORSHeaders(rh);

        // Default to no server-side caching
        rh['cache-control'] = rh['cache-control']
            || 'private, max-age=0, s-maxage=0, must-revalidate';

        // Set up security headers
        // https://www.owasp.org/index.php/List_of_useful_HTTP_headers
        rh['x-content-type-options'] = 'nosniff';
        rh['x-frame-options'] = 'SAMEORIGIN';

        exports.misc.addCSPHeaders(response, { domain: req.params && req.params.domain });

        // Propagate the request id header
        rh['x-request-id'] = opts.reqId;

        logResponse(opts, response, opts.startTime);

        var body;
        // Prepare error responses for the client
        if (response.status >= 400) {
            rh['content-type'] = rh['content-type']
                || 'application/problem+json';

            // Whitelist fields to avoid leaking sensitive info
            var rBody = response.body || {};
            body = {
                type: rBody.type,
                title: rBody.title,
                method: rBody.method || req.method,
                detail: rBody.detail || rBody.description,
                uri: rBody.uri || req.uri
            };
            if (response.status === 404) {
                body.type = body.type || 'not_found';
                body.title = body.title || 'Not found.';
            }
            body.type = body.type || 'unknown_error';

            // Prefix error base URL
            if (!/^https?:\/\//.test(body.type)) {
                body.type = (opts.conf.default_error_uri
                        || 'https://mediawiki.org/wiki/HyperSwitch/errors/')
                    + body.type;
            }
            response.body = body;
        }

        if (req.method === 'head') {
            response.body = null;
        }

        if (opts.metrics) {
            opts.metrics.endTiming([
                'ALL.ALL',
                req.method.toUpperCase() + '.' + response.status.toString()
            ], opts.startTime);
        }

        if (response.body) {
            body = response.body;
            var bodyIsStream = body instanceof stream.Readable;
            if (!Buffer.isBuffer(body) && !bodyIsStream) {
                // Convert to a buffer
                if (typeof body === 'object') {
                    rh['content-type'] = rh['content-type'] || 'application/json';
                    body = new Buffer(JSON.stringify(body));
                } else {
                    body = new Buffer(body);
                }
            }

            var cType = rh['content-type'];
            if (/\bgzip\b/.test(req.headers['accept-encoding'])
                    && /^application\/json\b|^text\//.test(cType)
                    && rh['content-encoding'] !== 'gzip') {
                rh['content-encoding'] = 'gzip';
                resp.writeHead(response.status, '', rh);
                var zStream = zlib.createGzip({ level: 3 });
                zStream.pipe(resp);
                if (bodyIsStream) {
                    body.pipe(zStream);
                } else {
                    zStream.end(body);
                }
            } else if (rh['content-encoding'] === 'gzip'
                    && !/\bgzip\b/.test(req.headers['accept-encoding'])) {
                delete rh['content-encoding'];
                resp.writeHead(response.status, '', rh);
                var unzStream = zlib.createGunzip();
                unzStream.pipe(resp);
                if (bodyIsStream) {
                    body.pipe(unzStream);
                } else {
                    unzStream.end(body);
                }
            } else if (bodyIsStream) {
                resp.writeHead(response.status, '', rh);
                body.pipe(resp);
            } else {
                rh['content-length'] = body.length;
                resp.writeHead(response.status, '', rh);
                resp.end(body);
            }
        } else {
            resp.writeHead(response.status, '', rh);
            resp.end();
        }
    } else {
        opts.log('error/request', {
            root_req: req,
            msg: "No content returned"
        });

        response = response || {};
        response.headers = response.headers || {};
        response.headers['content-type'] = 'application/problem+json';

        resp.writeHead(response.status || 500, '', response.headers);
        resp.end(req.method === 'head' ? undefined : JSON.stringify({
            type: 'server_error#empty_response',
            title: 'HyperSwitch error: No content returned by backend.',
            uri: req.url,
            method: req.method
        }));
    }
}

// Handle a single request
function handleRequest(opts, req, resp) {
    // Set the request ID early on for external requests
    req.headers = req.headers || {};
    req.headers['x-request-id'] = req.headers['x-request-id'] || utils.generateRequestId();

    var remoteAddr = req.headers['x-client-ip'] || req.socket.remoteAddress;
    req.headers['x-client-ip'] = remoteAddr;
    req.headers['x-forwarded-for'] = remoteAddr;

    var reqOpts = {
        conf: opts.conf,
        log: null,
        reqId: req.headers['x-request-id'],
        metrics: opts.metrics,
        ratelimiter: opts.ratelimiter,
        startTime: Date.now()
    };

    /**
     * We use separate metric trees for requests from private networks, so
     * that we can separately study performance for those different use cases.
     * We implement this by passing a specialized prefixed metric producer to
     * the handler. All derived metrics associated with a private / update
     * request are thus recorded in .internal or .internal_update prefixed
     * metric trees.
     */
    if (/^(?:::ffff:)?(?:10|127)\./.test(remoteAddr)) {
        if (req.headers['cache-control'] && /no-cache/i.test(req.headers['cache-control'])) {
            reqOpts.metrics = opts.child_metrics.internal_update;
            req.headers['x-request-class'] = 'internal_update';
        } else {
            reqOpts.metrics = opts.child_metrics.internal;
            req.headers['x-request-class'] = 'internal';
        }
    } else {
        reqOpts.metrics = opts.child_metrics.external;
        req.headers['x-request-class'] = 'external';
    }

    // Create a child logger with selected request information.
    reqOpts.logger = opts.logger && opts.logger.child({
        root_req: {
            method: req.method.toLowerCase(),
            uri: req.url,
            headers: {
                'cache-control': req.headers['cache-control'],
                'content-length': req.headers['content-length'],
                'content-type': req.headers['content-type'],
                'if-match': req.headers['if-match'],
                'user-agent': req.headers['user-agent'],
                'x-client-ip': req.headers['x-client-ip'],
                'x-request-id': req.headers['x-request-id'],
                'x-request-class': req.headers['x-request-class'],
            },
        },
        request_id: req.headers['x-request-id']
    });
    reqOpts.log = reqOpts.logger && reqOpts.logger.log.bind(reqOpts.logger) || function() {};

    // Create a new, clean request object
    var urlData = parseURL(req.url);

    var newReq = {
        uri: new URI(urlData.pathname),
        query: urlData.query,
        method: req.method.toLowerCase(),
        headers: req.headers
    };

    // Start off by parsing any POST data with BusBoy
    return parsePOST(req)
    .catchThrow(new exports.HTTPError({
        status: 400,
        body: {
            type: 'invalid_request'
        }
    }))

    // Then process the request
    .then(function(body) {

        if (body && /^application\/json/i.test(req.headers['content-type'])) {
            try {
                body = JSON.parse(body.toString());
            } catch (e) {
                reqOpts.log('error/request/json-parsing', e);
            }
        }

        newReq.body = body;

        // Quick hack to set up general CORS
        if (newReq.method === 'options') {
            return P.resolve({
                status: 200
            });
        } else {
            return opts.hyper.request(newReq, reqOpts);
        }
    })

    // And finally handle the response
    .then(function(result) {
        return handleResponse(reqOpts, newReq, resp, result);
    })
    .catch(function(e) {
        if (!e || e.name !== 'HTTPError') {
            var originalError = e;
            var stack = e && e.stack;
            e = new exports.HTTPError({
                status: 500,
                body: {
                    type: 'internal_error',
                    description: e + ''
                    // Probably better to keep this private for now
                    // stack: e.stack
                }
            });
            // Log this internally
            e.stack = stack;
            e.innerError = originalError;
        }
        if (!e.status) {
            e.status = 500;
        }
        return handleResponse(reqOpts, newReq, resp, e);
    });
}

// Main app setup
function main(options) {
    var conf = options.config || {};
    // Set up the global options object with a logger
    conf.service_name = options.name;
    var opts = {
        appBasePath: options.appBasePath,
        conf: conf,
        logger: options.logger,
        log: options.logger && options.logger.log.bind(options.logger) || function() {},
        metrics: options.metrics,
        ratelimiter: options.ratelimiter,
        child_metrics: {
            external: options.metrics && options.metrics.makeChild('external'),
            internal: options.metrics && options.metrics.makeChild('internal'),
            internal_update: options.metrics && options.metrics.makeChild('internal_update'),
        }
    };

    opts.router = new Router(opts);
    opts.hyper = new HyperSwitch(opts);
    // Use a child HyperSwitch instance to sidestep the security protection for
    // direct requests to /sys
    var childHyperSwitch = opts.hyper.makeChild({ uri: '#internal-startup' }, {
        metrics: options.metrics && options.metrics.makeChild('internal_startup')
    });

    // Main app startup happens during async spec loading:
    return opts.router.loadSpec(conf.spec, childHyperSwitch)
    .then(function() {
        var server = http.createServer(handleRequest.bind(null, opts));
        // Use a large listen queue
        // Also, echo 1024 | sudo tee /proc/sys/net/core/somaxconn
        // (from 128 default)
        var port = conf.port || 7231;
        var host = conf.host;
        // Apply some back-pressure.
        server.maxConnections = 500;
        server.listen(port, host);
        opts.log('info', 'listening on ' + (host || '*') + ':' + port);
        return server;
    })
    .catch(function(e) {
        opts.log('fatal/startup', {
            status: e.status,
            err: e,
            stack: e.body && e.body.stack || e.stack
        });
        // Delay exiting to avoid heavy restart load & let the logger finish its business
        setTimeout(function() {
            process.exit(1);
        }, 2000);
    });
}

if (module.parent === null) {
    main();
} else {
    module.exports = main;
}
<commit_msg>Logging tweaks<commit_after>"use strict";

/*
 * HyperSwitch web service entry point
 *
 * Sets up a HyperSwitch instance by loading and setting up handlers and the
 * storage layer, and then dispatches requests to it.
 */

var P = require('bluebird');
var Busboy = require('busboy');
var qs = require('querystring');
var url = require('url');
var http = require('http');
var zlib = require('zlib');
var stream = require('stream');

var URI = require('swagger-router').URI;

var exports = require('./exports');
var HyperSwitch = require('./hyperswitch');
var Router = require('./router');
var utils = require('./utils');

// Should make it into 0.12, see https://github.com/joyent/node/pull/7878
var SIMPLE_PATH = /^(\/(?!\/)[^\?#\s]*)(?:\?([^#\s]*))?$/;
function parseURL(uri) {
    // Fast path for simple path uris
    var fastMatch = SIMPLE_PATH.exec(uri);
    if (fastMatch) {
        return {
            protocol: null,
            slashes: null,
            auth: null,
            host: null,
            port: null,
            hostname: null,
            hash: null,
            search: fastMatch[2] || '',
            pathname: fastMatch[1],
            path: fastMatch[1],
            query: fastMatch[2] && qs.parse(fastMatch[2]) || {},
            href: uri
        };
    } else {
        return url.parse(uri, true);
    }
}

/**
 * Parse a POST request into request.body with BusBoy
 * Drops file uploads on the floor without creating temporary files
 *
 * @param {Request} req HTTP request
 * @returns {Promise<>}
 */
function read(req) {
    return new P(function(resolve) {
        var chunks = [];
        req.on('data', function(chunk) {
            chunks.push(chunk);
        });

        req.on('end', function() {
            resolve(Buffer.concat(chunks));
        });
    });
}

function parsePOST(req) {
    var readIt = (req.method === 'PUT') ||
        (req.method === 'POST' && req.headers &&
            (/^application\/json/i.test(req.headers['content-type'])
            || !req.headers['content-type']));

    if (readIt) {
        return read(req);
    } else if (req.method !== 'POST') {
        return P.resolve();
    } else {
        // Parse the POST
        return new P(function(resolve) {
            // Parse POST data
            var bboy = new Busboy({
                headers: req.headers,
                // Increase the form field size limit from the 1M default.
                limits: { fieldSize: 15 * 1024 * 1024 }
            });
            var body = {};
            bboy.on('field', function(field, val) {
                body[field] = val;
            });
            bboy.on('finish', function() {
                resolve(body);
            });
            req.pipe(bboy);
        });
    }
}

function logResponse(opts, response, startTime) {
    var latency = Date.now() - startTime;
    var logLevel = 'trace/request';
    if (response.status >= 500) {
        logLevel = 'error/request';
    } else if (response.status >= 400) {
        logLevel = 'info/request';
    } else if (latency > 5000) {
        logLevel = 'trace/request/slow';
    }
    opts.log(logLevel, {
        message: response.message,
        res: /^error\//.test(logLevel) ? response : {
            status: response.status,
            headers: response.headers,
        },
        stack: response.stack,
        latency: latency,
    });
}

/**
 * Set up basic CORS in response headers
 *
 * @param {Object} rh Response headers object
 * @private
 */
function setCORSHeaders(rh) {
    rh['access-control-allow-origin'] = '*';
    rh['access-control-allow-methods'] = 'GET';
    rh['access-control-allow-headers'] = 'accept, content-type';
    rh['access-control-expose-headers'] = 'etag';
}

function handleResponse(opts, req, resp, response) {
    if (response && response.status) {
        var rh = response.headers = response.headers || {};

        utils.removeHopToHopHeaders(rh, false);
        setCORSHeaders(rh);

        // Default to no server-side caching
        rh['cache-control'] = rh['cache-control']
            || 'private, max-age=0, s-maxage=0, must-revalidate';

        // Set up security headers
        // https://www.owasp.org/index.php/List_of_useful_HTTP_headers
        rh['x-content-type-options'] = 'nosniff';
        rh['x-frame-options'] = 'SAMEORIGIN';

        exports.misc.addCSPHeaders(response, { domain: req.params && req.params.domain });

        // Propagate the request id header
        rh['x-request-id'] = opts.reqId;

        logResponse(opts, response, opts.startTime);

        var body;
        // Prepare error responses for the client
        if (response.status >= 400) {
            rh['content-type'] = rh['content-type']
                || 'application/problem+json';

            // Whitelist fields to avoid leaking sensitive info
            var rBody = response.body || {};
            body = {
                type: rBody.type,
                title: rBody.title,
                method: rBody.method || req.method,
                detail: rBody.detail || rBody.description,
                uri: rBody.uri || req.uri
            };
            if (response.status === 404) {
                body.type = body.type || 'not_found';
                body.title = body.title || 'Not found.';
            }
            body.type = body.type || 'unknown_error';

            // Prefix error base URL
            if (!/^https?:\/\//.test(body.type)) {
                body.type = (opts.conf.default_error_uri
                        || 'https://mediawiki.org/wiki/HyperSwitch/errors/')
                    + body.type;
            }
            response.body = body;
        }

        if (req.method === 'head') {
            response.body = null;
        }

        if (opts.metrics) {
            opts.metrics.endTiming([
                'ALL.ALL',
                req.method.toUpperCase() + '.' + response.status.toString()
            ], opts.startTime);
        }

        if (response.body) {
            body = response.body;
            var bodyIsStream = body instanceof stream.Readable;
            if (!Buffer.isBuffer(body) && !bodyIsStream) {
                // Convert to a buffer
                if (typeof body === 'object') {
                    rh['content-type'] = rh['content-type'] || 'application/json';
                    body = new Buffer(JSON.stringify(body));
                } else {
                    body = new Buffer(body);
                }
            }

            var cType = rh['content-type'];
            if (/\bgzip\b/.test(req.headers['accept-encoding'])
                    && /^application\/json\b|^text\//.test(cType)
                    && rh['content-encoding'] !== 'gzip') {
                rh['content-encoding'] = 'gzip';
                resp.writeHead(response.status, '', rh);
                var zStream = zlib.createGzip({ level: 3 });
                zStream.pipe(resp);
                if (bodyIsStream) {
                    body.pipe(zStream);
                } else {
                    zStream.end(body);
                }
            } else if (rh['content-encoding'] === 'gzip'
                    && !/\bgzip\b/.test(req.headers['accept-encoding'])) {
                delete rh['content-encoding'];
                resp.writeHead(response.status, '', rh);
                var unzStream = zlib.createGunzip();
                unzStream.pipe(resp);
                if (bodyIsStream) {
                    body.pipe(unzStream);
                } else {
                    unzStream.end(body);
                }
            } else if (bodyIsStream) {
                resp.writeHead(response.status, '', rh);
                body.pipe(resp);
            } else {
                rh['content-length'] = body.length;
                resp.writeHead(response.status, '', rh);
                resp.end(body);
            }
        } else {
            resp.writeHead(response.status, '', rh);
            resp.end();
        }
    } else {
        opts.log('error/request', {
            root_req: req,
            msg: "No content returned"
        });

        response = response || {};
        response.headers = response.headers || {};
        response.headers['content-type'] = 'application/problem+json';

        resp.writeHead(response.status || 500, '', response.headers);
        resp.end(req.method === 'head' ? undefined : JSON.stringify({
            type: 'server_error#empty_response',
            title: 'HyperSwitch error: No content returned by backend.',
            uri: req.url,
            method: req.method
        }));
    }
}

// Handle a single request
function handleRequest(opts, req, resp) {
    // Set the request ID early on for external requests
    req.headers = req.headers || {};
    req.headers['x-request-id'] = req.headers['x-request-id'] || utils.generateRequestId();

    var remoteAddr = req.headers['x-client-ip'] || req.socket.remoteAddress;
    req.headers['x-client-ip'] = remoteAddr;
    req.headers['x-forwarded-for'] = remoteAddr;

    var reqOpts = {
        conf: opts.conf,
        log: null,
        reqId: req.headers['x-request-id'],
        metrics: opts.metrics,
        ratelimiter: opts.ratelimiter,
        startTime: Date.now()
    };

    /**
     * We use separate metric trees for requests from private networks, so
     * that we can separately study performance for those different use cases.
     * We implement this by passing a specialized prefixed metric producer to
     * the handler. All derived metrics associated with a private / update
     * request are thus recorded in .internal or .internal_update prefixed
     * metric trees.
     */
    if (/^(?:::ffff:)?(?:10|127)\./.test(remoteAddr)) {
        if (req.headers['cache-control'] && /no-cache/i.test(req.headers['cache-control'])) {
            reqOpts.metrics = opts.child_metrics.internal_update;
            req.headers['x-request-class'] = 'internal_update';
        } else {
            reqOpts.metrics = opts.child_metrics.internal;
            req.headers['x-request-class'] = 'internal';
        }
    } else {
        reqOpts.metrics = opts.child_metrics.external;
        req.headers['x-request-class'] = 'external';
    }

    // Create a child logger with selected request information.
    reqOpts.logger = opts.logger && opts.logger.child({
        root_req: {
            method: req.method.toLowerCase(),
            uri: req.url,
            headers: {
                'cache-control': req.headers['cache-control'],
                'content-length': req.headers['content-length'],
                'content-type': req.headers['content-type'],
                'if-match': req.headers['if-match'],
                'user-agent': req.headers['user-agent'],
                'x-client-ip': req.headers['x-client-ip'],
                'x-request-id': req.headers['x-request-id'],
                'x-request-class': req.headers['x-request-class'],
            },
        },
        request_id: req.headers['x-request-id']
    });
    reqOpts.log = reqOpts.logger && reqOpts.logger.log.bind(reqOpts.logger) || function() {};

    // Create a new, clean request object
    var urlData = parseURL(req.url);

    var newReq = {
        uri: new URI(urlData.pathname),
        query: urlData.query,
        method: req.method.toLowerCase(),
        headers: req.headers
    };

    // Start off by parsing any POST data with BusBoy
    return parsePOST(req)
    .catchThrow(new exports.HTTPError({
        status: 400,
        body: {
            type: 'invalid_request'
        }
    }))

    // Then process the request
    .then(function(body) {

        if (body && /^application\/json/i.test(req.headers['content-type'])) {
            try {
                body = JSON.parse(body.toString());
            } catch (e) {
                reqOpts.log('error/request/json-parsing', e);
            }
        }

        newReq.body = body;

        // Quick hack to set up general CORS
        if (newReq.method === 'options') {
            return P.resolve({
                status: 200
            });
        } else {
            return opts.hyper.request(newReq, reqOpts);
        }
    })

    // And finally handle the response
    .then(function(result) {
        return handleResponse(reqOpts, newReq, resp, result);
    })
    .catch(function(e) {
        if (!e || e.name !== 'HTTPError') {
            var originalError = e;
            var stack = e && e.stack;
            e = new exports.HTTPError({
                status: 500,
                body: {
                    type: 'internal_error',
                    description: e + ''
                    // Probably better to keep this private for now
                    // stack: e.stack
                }
            });
            // Log this internally
            e.stack = stack;
            e.innerError = originalError;
        }
        if (!e.status) {
            e.status = 500;
        }
        return handleResponse(reqOpts, newReq, resp, e);
    });
}

// Main app setup
function main(options) {
    var conf = options.config || {};
    // Set up the global options object with a logger
    conf.service_name = options.name;
    var opts = {
        appBasePath: options.appBasePath,
        conf: conf,
        logger: options.logger,
        log: options.logger && options.logger.log.bind(options.logger) || function() {},
        metrics: options.metrics,
        ratelimiter: options.ratelimiter,
        child_metrics: {
            external: options.metrics && options.metrics.makeChild('external'),
            internal: options.metrics && options.metrics.makeChild('internal'),
            internal_update: options.metrics && options.metrics.makeChild('internal_update'),
        }
    };

    opts.router = new Router(opts);
    opts.hyper = new HyperSwitch(opts);
    // Use a child HyperSwitch instance to sidestep the security protection for
    // direct requests to /sys
    var childHyperSwitch = opts.hyper.makeChild({ uri: '#internal-startup' }, {
        metrics: options.metrics && options.metrics.makeChild('internal_startup')
    });

    // Main app startup happens during async spec loading:
    return opts.router.loadSpec(conf.spec, childHyperSwitch)
    .then(function() {
        var server = http.createServer(handleRequest.bind(null, opts));
        // Use a large listen queue
        // Also, echo 1024 | sudo tee /proc/sys/net/core/somaxconn
        // (from 128 default)
        var port = conf.port || 7231;
        var host = conf.host;
        // Apply some back-pressure.
        server.maxConnections = 500;
        server.listen(port, host);
        opts.log('warn/startup', 'listening on ' + (host || '*') + ':' + port);
        return server;
    })
    .catch(function(e) {
        opts.log('fatal/startup', {
            status: e.status,
            err: e,
            stack: e.body && e.body.stack || e.stack
        });
        // Delay exiting to avoid heavy restart load & let the logger finish its business
        setTimeout(function() {
            process.exit(1);
        }, 2000);
    });
}

if (module.parent === null) {
    main();
} else {
    module.exports = main;
}
<|endoftext|>


<commit_before>window.onload = function () {
    // Get container elements
    var current = document.getElementById('currentCurator');
    var next = document.getElementById('nextCurator');
    var following = document.getElementById('followingCurator');
    var successive = document.getElementById('successiveCurators');
    var alertClose = document.getElementById('alert-close');

    // Get file path, add time-stamp to prevent caching
    var filePath = 'http://scratchtools.tk/fpc/api/v1/json/?u=' + new Date().getTime();

    // Get date info
    var weekStart;
    var monthNames = ['Jan',
                      'Feb',
                      'Mar',
                      'Apr',
                      'May',
                      'Jun',
                      'Jul',
                      'Aug',
                      'Sep',
                      'Oct',
                      'Nov',
                      'Dec'];

    // Init http request
    var xmlHttp = new XMLHttpRequest();

    xmlHttp.ontimeout = function() {
        console.log('Timeout');
        error();
    };

    xmlHttp.onerror = function() {
        console.log('Error');
        error();
    };

    xmlHttp.onreadystatechange = function() {
        if (xmlHttp.readyState === 4 && xmlHttp.status === 200) {
            downloaded(JSON.parse(xmlHttp.responseText));
        }
    };

    xmlHttp.open('GET', filePath, true);
    xmlHttp.timeout = 10000;

    // Send http request
    xmlHttp.send(null);

    // Interpret downloaded data
    function downloaded(result) {
        // Get start date
        weekStart = new Date(result['date']);

        // Load current fpc
        load(result['current'], current, 0);

        if (result['following'].length > 0) {
            // Load next fpc
            load(result['following'][0], next, 1);

            if (result['following'].length > 1) {
                // Load following fpc
                load(result['following'][1], following, 2);

                if (result['following'].length > 2) {
                    // There are successive curators

                    // For each successive curator
                    for (var i = 2; i < result['following'].length; i++) {
                        // Add date
                        successive.innerHTML += '' + getWeekRange(i + 1) + '';

                        // Check if curator has been suggested
                        if (result['following'][i] !== '') {
                            // Curator has been suggested

                            // Check if last character is a '?'
                            // Thus indicating if the FPC is confirmed
                            if (result['following'][i][result['following'][i].length - 1] === '?') {
                                // Curator is not yet confirmed

                                // Add curator with TBC warning and no link
                                successive.innerHTML +=
                                        '@' +
                                        result['following'][i].substr(0, result['following'][i].length - 1) +
                                        ' (TBC)';
                            } else {
                                // Curator is confirmed

                                // Add curator with link
                                successive.innerHTML +=
                                        '@' +
                                        result['following'][i] + '';
                            }
                        } else {
                            // No known FPC for this period

                            // Display unknown message
                            successive.innerHTML +=
                                    'Unknown';
                        }
                    }

                    // Show container for successive curators
                    document.getElementById('successiveContainer').className = 'box hover-box';
                }
            } else {
                // Following FPC not known

                unknown(following, 2);
            }
        } else {
            // Next and following FPCs are unknown
            // (Ideally this case shouldn't happen)

            unknown(next, 1);
            unknown(following, 2);
        }

        // Show notice
        if (result['message']) {
            document.getElementById('alert-text').innerHTML = result['message'];
            document.getElementById('alert').className = 'box';
        }
    }

    // Display error messages in every container
    function error() {
        current.innerText = 'Error';
        next.innerText = 'Error';
        following.innerText = 'Error';
    }

    // Loads the curator name into the specified container
    function load(user, element, index) {
        // Check if curator has been suggested

        if (user !== '') {
            // Curator has been suggested for this period

            var date = document.createElement('span');

            date.className = 'date';
            date.innerText = getWeekRange(index);

            element.parentElement.insertBefore(date, element.parentElement.childNodes[0]);

            if (user.endsWith('?')) {
                //FPC not confirmed

                element.innerHTML =
                        '@' +
                        user.substring(0, user.length - 1) +
                        ' (TBC)';
                element.className = 'gray';
            } else {
                //FPC was confirmed

                element.innerHTML = '@' + user + '';
                element.parentElement.href = 'https://scratch.mit.edu/users/' + user + '/';
                element.className = '';
            }
        } else {
            // Curator is not known for this period
            unknown(element, index);
        }
    }

    // Marks the fpc for the specified element as unknown
    function unknown(element, index) {
        var date = document.createElement('span');

        date.className = 'date';
        date.innerText = getWeekRange(index);

        element.parentElement.insertBefore(date, element.parentElement.childNodes[0]);

        element.title = 'The FPC during this period is currently unknown';
        element.innerText = 'Unknown';
    }

    // Gets a human readable range representing the specified week (0 = this week)
    function getWeekRange(week) {
        return formatDate(offsetDate(weekStart, week * 7)) +
                ' - ' +
                formatDate(offsetDate(weekStart, (week + 1) * 7));
    }

    // Adds the specified amount of days to a specified date
    function offsetDate(d, offset) {
        var newDate = new Date();

        newDate.setMonth(d.getMonth());
        newDate.setDate(d.getDate() + offset);

        return newDate;
    }

    // Makes a date human readable
    function formatDate(d) {
        return d.getDate() + ' ' + monthNames[d.getMonth()];
    }

    // Add close button click event
    alertClose.onclick = function() {
        document.getElementById('alert').className = 'hidden';
    };
};<commit_msg>Handle server error responses fix<commit_after>window.onload = function () {
    // Get container elements
    var current = document.getElementById('currentCurator');
    var next = document.getElementById('nextCurator');
    var following = document.getElementById('followingCurator');
    var successive = document.getElementById('successiveCurators');
    var alertClose = document.getElementById('alert-close');

    // Get file path, add time-stamp to prevent caching
    var filePath = 'http://scratchtools/fpc/api/v1/json/?u=' + new Date().getTime();

    // Get date info
    var weekStart;
    var monthNames = ['Jan',
                      'Feb',
                      'Mar',
                      'Apr',
                      'May',
                      'Jun',
                      'Jul',
                      'Aug',
                      'Sep',
                      'Oct',
                      'Nov',
                      'Dec'];

    // Init http request
    var xmlHttp = new XMLHttpRequest();

    xmlHttp.ontimeout = function() {
        console.log('Timeout');
        error();
    };

    xmlHttp.onerror = function() {
        console.log('Error');
        error();
    };

    xmlHttp.onreadystatechange = function() {
        if (xmlHttp.readyState === 4 && xmlHttp.status === 200) {
            downloaded(JSON.parse(xmlHttp.responseText));
        }
    };

    xmlHttp.open('GET', filePath, true);
    xmlHttp.timeout = 10000;

    // Send http request
    xmlHttp.send(null);

    // Interpret downloaded data
    function downloaded(result) {
        if (typeof result['error'] === 'undefined') {// Get start date
            weekStart = new Date(result['date']);

            // Load current fpc
            load(result['current'], current, 0);

            if (result['following'].length > 0) {
                // Load next fpc
                load(result['following'][0], next, 1);

                if (result['following'].length > 1) {
                    // Load following fpc
                    load(result['following'][1], following, 2);

                    if (result['following'].length > 2) {
                        // There are successive curators

                        // For each successive curator
                        for (var i = 2; i < result['following'].length; i++) {
                            // Add date
                            successive.innerHTML += '' + getWeekRange(i + 1) + '';

                            // Check if curator has been suggested
                            if (result['following'][i] !== '') {
                                // Curator has been suggested

                                // Check if last character is a '?'
                                // Thus indicating if the FPC is confirmed
                                if (result['following'][i][result['following'][i].length - 1] === '?') {
                                    // Curator is not yet confirmed

                                    // Add curator with TBC warning and no link
                                    successive.innerHTML +=
                                            '@' +
                                            result['following'][i].substr(0, result['following'][i].length - 1) +
                                            ' (TBC)';
                                } else {
                                    // Curator is confirmed

                                    // Add curator with link
                                    successive.innerHTML +=
                                            '@' +
                                            result['following'][i] + '';
                                }
                            } else {
                                // No known FPC for this period

                                // Display unknown message
                                successive.innerHTML +=
                                        'Unknown';
                            }
                        }

                        // Show container for successive curators
                        document.getElementById('successiveContainer').className = 'box hover-box';
                    }
                } else {
                    // Following FPC not known

                    unknown(following, 2);
                }
            } else {
                // Next and following FPCs are unknown
                // (Ideally this case shouldn't happen)

                unknown(next, 1);
                unknown(following, 2);
            }

            // Show notice
            if (result['message']) {
                document.getElementById('alert-text').innerHTML = result['message'];
                document.getElementById('alert').className = 'box';
            }
        } else {
            error();
        }
    }

    // Display error messages in every container
    function error() {
        current.innerText = 'Error';
        next.innerText = 'Error';
        following.innerText = 'Error';
    }

    // Loads the curator name into the specified container
    function load(user, element, index) {
        // Check if curator has been suggested

        if (user !== '') {
            // Curator has been suggested for this period

            var date = document.createElement('span');

            date.className = 'date';
            date.innerText = getWeekRange(index);

            element.parentElement.insertBefore(date, element.parentElement.childNodes[0]);

            if (user.endsWith('?')) {
                //FPC not confirmed

                element.innerHTML =
                        '@' +
                        user.substring(0, user.length - 1) +
                        ' (TBC)';
                element.className = 'gray';
            } else {
                //FPC was confirmed

                element.innerHTML = '@' + user + '';
                element.parentElement.href = 'https://scratch.mit.edu/users/' + user + '/';
                element.className = '';
            }
        } else {
            // Curator is not known for this period
            unknown(element, index);
        }
    }

    // Marks the fpc for the specified element as unknown
    function unknown(element, index) {
        var date = document.createElement('span');

        date.className = 'date';
        date.innerText = getWeekRange(index);

        element.parentElement.insertBefore(date, element.parentElement.childNodes[0]);

        element.title = 'The FPC during this period is currently unknown';
        element.innerText = 'Unknown';
    }

    // Gets a human readable range representing the specified week (0 = this week)
    function getWeekRange(week) {
        return formatDate(offsetDate(weekStart, week * 7)) +
                ' - ' +
                formatDate(offsetDate(weekStart, (week + 1) * 7));
    }

    // Adds the specified amount of days to a specified date
    function offsetDate(d, offset) {
        var newDate = new Date();

        newDate.setMonth(d.getMonth());
        newDate.setDate(d.getDate() + offset);

        return newDate;
    }

    // Makes a date human readable
    function formatDate(d) {
        return d.getDate() + ' ' + monthNames[d.getMonth()];
    }

    // Add close button click event
    alertClose.onclick = function() {
        document.getElementById('alert').className = 'hidden';
    };
};<|endoftext|>


<commit_before>var cluster = require("cluster")
var config = require("./config")
var url = require("url")

if(cluster.isMaster) {
	var numCpus = require("os").cpus().length

	for(var i = 0; i < numCpus; i++) {
		cluster.fork()
	}

	cluster.on('exit', function(worker, code, signal) {
		return console.log('Worker ' + worker.id + ' died')
	})

	return
}

var logger = function(from, id) {
	return function(req, res, next) {
		console.log("[%s] (%d) => %s %s", from, id, req.method, req.url)

		return next()
	}
}

config.proxy = config.proxy || {}
config.proxy.host = config.proxy.host || "localhost"
config.proxy.port = (config.proxy.port === undefined) ? 80 : config.proxy.port
config.proxy.api = config.proxy.api || {}
config.api.uri = config.api.uri || "localhost:8080/api"
config.api.path = config.api.path || "/api"
config.files.host = config.files.host || "localhost:8081"
config.files.port = (config.files.port === undefined) ? 8081 : config.files.port

var id = cluster.worker.id;

var http = require("http")
var path = require("path")
var lactate = require("lactate")
var Proxy = require("./lib/proxy")

var errorLog = function(req, res, err) {
	console.log(err)
}

var getStaticServer = function(id) {
	var root = path.join(__dirname, config.files.root)

	var log = logger("STATIC", id)

	var files = lactate.Lactate({
		root: root,
	})

	var staticServe = function(req, res) {
		console.log("[" + id + "] (STATIC) => " + req.url)

		files.serve(req, res)
	}

	return function(req, res) {

		var originalUrl = req.url
		var path = url.parse(originalUrl).pathname
		var folder = path.match(/^\/[^\/]*\//)

		if(folder === null) {
			req.url = "/index.html"
			return staticServe(req, res)
		}

		var root = folder[0]
		switch(root) {
			case "/css/":
			case "/js/":
			case "/partials/":
			case "/font/":
			case "/img/":
				req.url = path
				return staticServe(req, res)
		}

		req.url = "/index.html"
		return staticServe(req, res)
	}
}

var getProxyServer = function(id) {

	var files = config.files.host + ":" + config.files.port

	var serveStatic = getStaticServer(id)

	var proxy = Proxy(config.api.url)

	var func = function(req, res) {
		// All errosr from the proxy are sent back to here
		req.on("error", function(err) {
			errorLog(req, res, err)
			res.writeHead(500);
			res.end();
		});

		var path = url.parse(req.url).pathname
		if(path !== null && path.match(/^\/api/)) {
			console.log("[" + id + "] (PROXY) => " + req.url)
			return proxy(req, res)
		}

		return serveStatic(req, res)
	}

	var httpServer
	if(config.proxy.https) {
		httpServer = require("https").createServer(config.proxy.https, func)
	} else {
		httpServer = require("http").createServer(func)
	}

	return httpServer
}

var server = getProxyServer(cluster.worker.id)
if(config.proxy.https) {
	var host = config.proxy.host === "0.0.0.0" ? "localhost" : config.proxy.host
	http.createServer(function(req, res) {
		var target = url.parse(req.url)
		target.protocol = "https"
		target.host = req.headers.host || host

		res.statusCode = 301
		res.setHeader("Location", url.format(target))
		res.end()
	}).listen(80, config.proxy.host)
}

server.listen(config.proxy.https ? 443 : config.proxy.port, config.proxy.host)
console.log("Listening on port " + (config.proxy.https ? 443 : config.proxy.port))
<commit_msg>Remove unused config items<commit_after>var cluster = require("cluster")
var config = require("./config")
var url = require("url")

if(cluster.isMaster) {
	var numCpus = require("os").cpus().length

	for(var i = 0; i < numCpus; i++) {
		cluster.fork()
	}

	cluster.on('exit', function(worker, code, signal) {
		return console.log('Worker ' + worker.id + ' died')
	})

	return
}

var logger = function(from, id) {
	return function(req, res, next) {
		console.log("[%s] (%d) => %s %s", from, id, req.method, req.url)

		return next()
	}
}

config.proxy = config.proxy || {}
config.proxy.host = config.proxy.host || "localhost"
config.proxy.port = (config.proxy.port === undefined) ? 80 : config.proxy.port
config.proxy.api = config.proxy.api || {}

var id = cluster.worker.id;

var http = require("http")
var path = require("path")
var lactate = require("lactate")
var Proxy = require("./lib/proxy")

var errorLog = function(req, res, err) {
	console.log(err)
}

var getStaticServer = function(id) {
	var root = path.join(__dirname, config.files.root)

	var log = logger("STATIC", id)

	var files = lactate.Lactate({
		root: root,
	})

	var staticServe = function(req, res) {
		console.log("[" + id + "] (STATIC) => " + req.url)

		files.serve(req, res)
	}

	return function(req, res) {

		var originalUrl = req.url
		var path = url.parse(originalUrl).pathname
		var folder = path.match(/^\/[^\/]*\//)

		if(folder === null) {
			req.url = "/index.html"
			return staticServe(req, res)
		}

		var root = folder[0]
		switch(root) {
			case "/css/":
			case "/js/":
			case "/partials/":
			case "/font/":
			case "/img/":
				req.url = path
				return staticServe(req, res)
		}

		req.url = "/index.html"
		return staticServe(req, res)
	}
}

var getProxyServer = function(id) {

	var files = config.files.host + ":" + config.files.port

	var serveStatic = getStaticServer(id)

	var proxy = Proxy(config.api.url)

	var func = function(req, res) {
		// All errosr from the proxy are sent back to here
		req.on("error", function(err) {
			errorLog(req, res, err)
			res.writeHead(500);
			res.end();
		});

		var path = url.parse(req.url).pathname
		if(path !== null && path.match(/^\/api/)) {
			console.log("[" + id + "] (PROXY) => " + req.url)
			return proxy(req, res)
		}

		return serveStatic(req, res)
	}

	var httpServer
	if(config.proxy.https) {
		httpServer = require("https").createServer(config.proxy.https, func)
	} else {
		httpServer = require("http").createServer(func)
	}

	return httpServer
}

var server = getProxyServer(cluster.worker.id)
if(config.proxy.https) {
	var host = config.proxy.host === "0.0.0.0" ? "localhost" : config.proxy.host
	http.createServer(function(req, res) {
		var target = url.parse(req.url)
		target.protocol = "https"
		target.host = req.headers.host || host

		res.statusCode = 301
		res.setHeader("Location", url.format(target))
		res.end()
	}).listen(80, config.proxy.host)
}

server.listen(config.proxy.https ? 443 : config.proxy.port, config.proxy.host)
console.log("Listening on port " + (config.proxy.https ? 443 : config.proxy.port))
<|endoftext|>


<commit_before>var PackageStore = require('./package-store');
var PublicPackageStore = require('./public-package-store');
var RepoCacheHandler = require('./repo-cache-handler');

var argv = require('optimist').argv;
var fs = require('fs');
var path = require('path');
var spawn = require('child_process').spawn;
var log4js = require('log4js');
var bodyParser = require('body-parser');
var express = require('express');
var app = express();

var logger = require('./logger');

var _config, _configPath, _configDirectory;
var _repoCacheEnabled;
var _packageStore, _publicPackageStore, _repoCacheHandler;

module.exports.run = _init;
function _init() {
    if(argv.h || argv.help) {
        logger.logHelp();

        process.exit();
    }

    _loadConfig();

    _packageStore = new PackageStore({
        persistFilePath: _config.registryFile
    });

    if(!_config.disablePublic) {
        _publicPackageStore = new PublicPackageStore(_config);

        if(_config.repositoryCache) {
            initPublicRepoCache();
        }
    }

    _initService();

    function initPublicRepoCache() {
        var repoCacheOptions = {};

        if(_config.repositoryCache.svn && _config.repositoryCache.svn.enabled) {
            repoCacheOptions.svn = {
                repoCacheRoot: path.resolve(_config.repositoryCache.svn.cacheDirectory || path.join(_configDirectory, './svnRepoCache')),
                hostName: _config.repositoryCache.svn.host || 'localhost',
                port: _config.repositoryCache.svn.port || 7891,
                refreshTimeout: _config.repositoryCache.svn.refreshTimeout || 10,
                parameters: _config.repositoryCache.svn.parameters
            };
        }

        if(_config.repositoryCache.git && _config.repositoryCache.git.enabled) {
            repoCacheOptions.git = {
                repoCacheRoot: path.resolve(_config.repositoryCache.git.cacheDirectory || path.join(_configDirectory, './gitRepoCache')),
                hostName: _config.repositoryCache.git.host || 'localhost',
                publicAccessURL: _config.repositoryCache.git.publicAccessURL || null,
                port: _config.repositoryCache.git.port || 6789,
                refreshTimeout: _config.repositoryCache.git.refreshTimeout || 10,
                parameters: _config.repositoryCache.git.parameters
            };
        }

        if(repoCacheOptions.svn || repoCacheOptions.git) {
            _repoCacheEnabled = true;

            _repoCacheHandler = new RepoCacheHandler(repoCacheOptions);
        }
    }
}

function _loadConfig() {
    var defaultConfigPath = path.join(__dirname, '../bower.conf.json');
    _configPath = path.resolve(argv.config || defaultConfigPath);

    if(!fs.existsSync(_configPath)) {
        logger.error('config file not found at ' + _configPath);
    }

    _configDirectory = path.join(_configPath, '../');

    var json = fs.readFileSync(_configPath).toString();
    _config = JSON.parse(json);

    //defaults
    _config.port = _config.port || 5678;
    _config.registryFile = path.resolve(_config.registryFile || path.join(_configDirectory, './bowerRepository.json'));

    _config.timeout = _config.timeout || 2 * 60 * 1200;

    if(_config.log4js && _config.log4js.enabled)  {
        log4js.configure(_config.log4js.configPath);
    }
}

function _initService() {
    app.use(bodyParser());
    app.use(express.static(path.join(__dirname, '../site')));

    app.post('/registerPackage', function(req, res) {
        if(!authenticate(req, res)) {
            return;
        }

        _packageStore.registerPackages([
            {
                name: req.body.name,
                repo: req.body.repo
            }
        ]);

        res.send('ok');
    });

    app.post('/packages', function(req, res) {
        if(!authenticate(req, res)) {
            return;
        }

        _packageStore.registerPackages([
            {
                name: req.body.name,
                repo: req.body.url
            }
        ]);

        res.send(201);
    });

    app.post('/registerPackages', function(req, res) {
        if(!authenticate(req, res)) {
            return;
        }

        _packageStore.registerPackages(req.body.packages);

        res.send('ok');
    });

    app.post('/removePackage', function(req, res) {
        if(!authenticate(req, res)) {
            return;
        }

        var packages = [ req.body.name ];

        _packageStore.removePackages(packages);

        if(_repoCacheHandler) {
            _removePackagesFromRepoCaches(packages);
        }

        res.send('ok');
    });

    app.post('/removePackages', function(req, res) {
        if(!authenticate(req, res)) {
            return;
        }

        var packages = req.body.packages;

        _packageStore.removePackages(packages);

        if(_repoCacheHandler) {
            _removePackagesFromRepoCaches(packages);
        }

        res.send('ok');
    });

    function _removePackagesFromRepoCaches(packages) {
        var repoCaches = _repoCacheHandler.getRepoAllCaches();

        repoCaches.forEach(function(repoCache) {
            packages.forEach(function(packageName) {
                repoCache.removeRepo(packageName);
            });
        });
    }

    app.get('/packages', function(req, res) {
        var packages = [];

        for(var packageName in _packageStore.packages) {
            if(_packageStore.packages.hasOwnProperty(packageName)) {
                var item = _packageStore.packages[packageName];

                packages.push({
                    name: packageName,
                    repo: item.repo,
                    hits: item.hits
                });
            }
        }

        res.send(packages);
    });

    app.get('/packages/search/:name', function(req, res) {
        var searchName = req.params.name;
        var packages = _packageStore.searchPackage(searchName);

        if(!_config.disablePublic) {
            Array.prototype.push.apply(packages, _publicPackageStore.searchPackage(searchName));
        }

        res.send(packages);
    });

    //bower service
    app.get('/packages/:name', function(req, res) {
        var packageName = req.params.name;
        var privatePackage = _packageStore.getPackage(packageName);

        if(privatePackage) {
            handlePrivatePackage();
        }
        else if(!_config.disablePublic) {
            handlePublicPackage();
        }
        else {
            res.status(404).send('Not found');
        }

        function handlePrivatePackage() {
            if(_config.repositoryCache && _config.repositoryCache.cachePrivate) {
                if(privatePackage.cachedRepo) {
                    res.send({
                        name: packageName,
                        url: privatePackage.cachedRepo,
                        hits: privatePackage.hits
                    });
                }
                else if(_repoCacheHandler) {
                    cachePrivateRepoAndSend();
                }
                else {
                    sendPrivatePackage();
                }
            }
            else {
                sendPrivatePackage();
            }

            function cachePrivateRepoAndSend() {
                var repoCache = _repoCacheHandler.getRepoCache(privatePackage.repo);

                repoCache.cacheRepo(packageName, privatePackage.repo)
                    .then(function(pack) {
                        privatePackage.cachedRepo = pack.repo;
                        _packageStore.persistPackages();

                        res.send({
                            name: packageName,
                            url: privatePackage.cachedRepo,
                            hits: privatePackage.hits
                        });
                    })
                    .fail(sendPrivatePackage);
            }

            function sendPrivatePackage() {
                res.send({
                    name: packageName,
                    url: privatePackage.repo,
                    hits: privatePackage.hits
                });
            }
        }

        function handlePublicPackage() {
            var publicPackage = _publicPackageStore.getPackage(packageName);
            if(publicPackage) {
                if(_repoCacheEnabled) {
                    cachePublicRepo();
                }
                else {
                    res.send(publicPackage);
                }
            }
            else {
                res.status(404).send('Not found');
            }

            function cachePublicRepo() {
                var repoCache = _repoCacheHandler.getRepoCache(publicPackage.url);

                repoCache.cacheRepo(packageName, publicPackage.url)
                    .then(function(pack) {
                        var privatePackage = {
                            name: packageName,
                            repo: pack.repo,
                            hits: publicPackage.hits
                        };

                        _packageStore.registerPackages([ privatePackage ]);

                        res.send({
                            name: privatePackage.name,
                            url: privatePackage.repo,
                            hits: privatePackage.hits
                        });
                    })
                    .fail(function() {
                        res.send(publicPackage);
                    });
            }
        }

    });

    app.post('/restart', function(req, res) {
        if(!authenticate(req, res)) {
            return;
        }

        logger.log('Shutting down server for restart');

        res.send('ok');
        server.close();

        logger.log('Restarting private-bower with config set to ' + _configPath);

        _shutDown();

        _startDetachedChildProcess('private-bower', ['--config',  _configPath]);
    });

    var server = app.listen(_config.port, function() {
        logger.log('Bower server started on port ' + _config.port);
    });
    server.timeout = _config.timeout;

    process.on('SIGINT', _doShutDown);

    function authenticate(req, res) {
        if(!_config.authentication || !_config.authentication.enabled) {
            return true;
        }

        if(req.get('Auth-Key') === _config.authentication.key) {
            return true;
        }

        res.status(401);
        res.send('Unauthorized');

        return false;
    }
}

function _startDetachedChildProcess(command, args) {
    var child = spawn(command, args, {
        detached: true
    });

    child.stdout.pipe(process.stdout);
    child.stderr.pipe(process.stderr);

    child.unref();
}

function _doShutDown() {
    _shutDown();

    process.exit();
}

function _shutDown() {
    logger.log('Shutting down private-bower');

    if(_repoCacheHandler) {
        _repoCacheHandler.getRepoAllCaches().forEach(function(repoCache) {
            repoCache.shutDown();
        });
    }
}

process.on('uncaughtException', function(err) {
    logger.log('Exception message:' + (err.stack || err.message));

    _shutDown();
});
<commit_msg>Fixed packages name to reference the passed object name property<commit_after>var PackageStore = require('./package-store');
var PublicPackageStore = require('./public-package-store');
var RepoCacheHandler = require('./repo-cache-handler');

var argv = require('optimist').argv;
var fs = require('fs');
var path = require('path');
var spawn = require('child_process').spawn;
var log4js = require('log4js');
var bodyParser = require('body-parser');
var express = require('express');
var app = express();

var logger = require('./logger');

var _config, _configPath, _configDirectory;
var _repoCacheEnabled;
var _packageStore, _publicPackageStore, _repoCacheHandler;

module.exports.run = _init;
function _init() {
    if(argv.h || argv.help) {
        logger.logHelp();

        process.exit();
    }

    _loadConfig();

    _packageStore = new PackageStore({
        persistFilePath: _config.registryFile
    });

    if(!_config.disablePublic) {
        _publicPackageStore = new PublicPackageStore(_config);

        if(_config.repositoryCache) {
            initPublicRepoCache();
        }
    }

    _initService();

    function initPublicRepoCache() {
        var repoCacheOptions = {};

        if(_config.repositoryCache.svn && _config.repositoryCache.svn.enabled) {
            repoCacheOptions.svn = {
                repoCacheRoot: path.resolve(_config.repositoryCache.svn.cacheDirectory || path.join(_configDirectory, './svnRepoCache')),
                hostName: _config.repositoryCache.svn.host || 'localhost',
                port: _config.repositoryCache.svn.port || 7891,
                refreshTimeout: _config.repositoryCache.svn.refreshTimeout || 10,
                parameters: _config.repositoryCache.svn.parameters
            };
        }

        if(_config.repositoryCache.git && _config.repositoryCache.git.enabled) {
            repoCacheOptions.git = {
                repoCacheRoot: path.resolve(_config.repositoryCache.git.cacheDirectory || path.join(_configDirectory, './gitRepoCache')),
                hostName: _config.repositoryCache.git.host || 'localhost',
                publicAccessURL: _config.repositoryCache.git.publicAccessURL || null,
                port: _config.repositoryCache.git.port || 6789,
                refreshTimeout: _config.repositoryCache.git.refreshTimeout || 10,
                parameters: _config.repositoryCache.git.parameters
            };
        }

        if(repoCacheOptions.svn || repoCacheOptions.git) {
            _repoCacheEnabled = true;

            _repoCacheHandler = new RepoCacheHandler(repoCacheOptions);
        }
    }
}

function _loadConfig() {
    var defaultConfigPath = path.join(__dirname, '../bower.conf.json');
    _configPath = path.resolve(argv.config || defaultConfigPath);

    if(!fs.existsSync(_configPath)) {
        logger.error('config file not found at ' + _configPath);
    }

    _configDirectory = path.join(_configPath, '../');

    var json = fs.readFileSync(_configPath).toString();
    _config = JSON.parse(json);

    //defaults
    _config.port = _config.port || 5678;
    _config.registryFile = path.resolve(_config.registryFile || path.join(_configDirectory, './bowerRepository.json'));

    _config.timeout = _config.timeout || 2 * 60 * 1200;

    if(_config.log4js && _config.log4js.enabled)  {
        log4js.configure(_config.log4js.configPath);
    }
}

function _initService() {
    app.use(bodyParser());
    app.use(express.static(path.join(__dirname, '../site')));

    app.post('/registerPackage', function(req, res) {
        if(!authenticate(req, res)) {
            return;
        }

        _packageStore.registerPackages([
            {
                name: req.body.name,
                repo: req.body.repo
            }
        ]);

        res.send('ok');
    });

    app.post('/packages', function(req, res) {
        if(!authenticate(req, res)) {
            return;
        }

        _packageStore.registerPackages([
            {
                name: req.body.name,
                repo: req.body.url
            }
        ]);

        res.send(201);
    });

    app.post('/registerPackages', function(req, res) {
        if(!authenticate(req, res)) {
            return;
        }

        _packageStore.registerPackages(req.body.packages);

        res.send('ok');
    });

    app.post('/removePackage', function(req, res) {
        if(!authenticate(req, res)) {
            return;
        }

        var packages = [ req.body.name ];

        _packageStore.removePackages(packages);

        if(_repoCacheHandler) {
            _removePackagesFromRepoCaches(packages);
        }

        res.send('ok');
    });

    app.post('/removePackages', function(req, res) {
        if(!authenticate(req, res)) {
            return;
        }

        var packages = req.body.packages;

        _packageStore.removePackages(packages);

        if(_repoCacheHandler) {
            _removePackagesFromRepoCaches(packages);
        }

        res.send('ok');
    });

    function _removePackagesFromRepoCaches(packages) {
        var repoCaches = _repoCacheHandler.getRepoAllCaches();

        repoCaches.forEach(function(repoCache) {
            packages.forEach(function(packageName) {
                repoCache.removeRepo(packageName);
            });
        });
    }

    app.get('/packages', function(req, res) {
        var packages = [];

        for(var packageName in _packageStore.packages) {
            if(_packageStore.packages.hasOwnProperty(packageName)) {
                var item = _packageStore.packages[packageName];

                packages.push({
                    name: item.name,
                    repo: item.repo,
                    hits: item.hits
                });
            }
        }

        res.send(packages);
    });

    app.get('/packages/search/:name', function(req, res) {
        var searchName = req.params.name;
        var packages = _packageStore.searchPackage(searchName);

        if(!_config.disablePublic) {
            Array.prototype.push.apply(packages, _publicPackageStore.searchPackage(searchName));
        }

        res.send(packages);
    });

    //bower service
    app.get('/packages/:name', function(req, res) {
        var packageName = req.params.name;
        var privatePackage = _packageStore.getPackage(packageName);

        if(privatePackage) {
            handlePrivatePackage();
        }
        else if(!_config.disablePublic) {
            handlePublicPackage();
        }
        else {
            res.status(404).send('Not found');
        }

        function handlePrivatePackage() {
            if(_config.repositoryCache && _config.repositoryCache.cachePrivate) {
                if(privatePackage.cachedRepo) {
                    res.send({
                        name: packageName,
                        url: privatePackage.cachedRepo,
                        hits: privatePackage.hits
                    });
                }
                else if(_repoCacheHandler) {
                    cachePrivateRepoAndSend();
                }
                else {
                    sendPrivatePackage();
                }
            }
            else {
                sendPrivatePackage();
            }

            function cachePrivateRepoAndSend() {
                var repoCache = _repoCacheHandler.getRepoCache(privatePackage.repo);

                repoCache.cacheRepo(packageName, privatePackage.repo)
                    .then(function(pack) {
                        privatePackage.cachedRepo = pack.repo;
                        _packageStore.persistPackages();

                        res.send({
                            name: packageName,
                            url: privatePackage.cachedRepo,
                            hits: privatePackage.hits
                        });
                    })
                    .fail(sendPrivatePackage);
            }

            function sendPrivatePackage() {
                res.send({
                    name: packageName,
                    url: privatePackage.repo,
                    hits: privatePackage.hits
                });
            }
        }

        function handlePublicPackage() {
            var publicPackage = _publicPackageStore.getPackage(packageName);
            if(publicPackage) {
                if(_repoCacheEnabled) {
                    cachePublicRepo();
                }
                else {
                    res.send(publicPackage);
                }
            }
            else {
                res.status(404).send('Not found');
            }

            function cachePublicRepo() {
                var repoCache = _repoCacheHandler.getRepoCache(publicPackage.url);

                repoCache.cacheRepo(packageName, publicPackage.url)
                    .then(function(pack) {
                        var privatePackage = {
                            name: packageName,
                            repo: pack.repo,
                            hits: publicPackage.hits
                        };

                        _packageStore.registerPackages([ privatePackage ]);

                        res.send({
                            name: privatePackage.name,
                            url: privatePackage.repo,
                            hits: privatePackage.hits
                        });
                    })
                    .fail(function() {
                        res.send(publicPackage);
                    });
            }
        }

    });

    app.post('/restart', function(req, res) {
        if(!authenticate(req, res)) {
            return;
        }

        logger.log('Shutting down server for restart');

        res.send('ok');
        server.close();

        logger.log('Restarting private-bower with config set to ' + _configPath);

        _shutDown();

        _startDetachedChildProcess('private-bower', ['--config',  _configPath]);
    });

    var server = app.listen(_config.port, function() {
        logger.log('Bower server started on port ' + _config.port);
    });
    server.timeout = _config.timeout;

    process.on('SIGINT', _doShutDown);

    function authenticate(req, res) {
        if(!_config.authentication || !_config.authentication.enabled) {
            return true;
        }

        if(req.get('Auth-Key') === _config.authentication.key) {
            return true;
        }

        res.status(401);
        res.send('Unauthorized');

        return false;
    }
}

function _startDetachedChildProcess(command, args) {
    var child = spawn(command, args, {
        detached: true
    });

    child.stdout.pipe(process.stdout);
    child.stderr.pipe(process.stderr);

    child.unref();
}

function _doShutDown() {
    _shutDown();

    process.exit();
}

function _shutDown() {
    logger.log('Shutting down private-bower');

    if(_repoCacheHandler) {
        _repoCacheHandler.getRepoAllCaches().forEach(function(repoCache) {
            repoCache.shutDown();
        });
    }
}

process.on('uncaughtException', function(err) {
    logger.log('Exception message:' + (err.stack || err.message));

    _shutDown();
});
<|endoftext|>