<commit_before>'use strict';

var mongoose = require('mongoose'),
  moment = require('moment'),
  Message = mongoose.model('Message'),
  Broadcast = mongoose.model('Broadcast'),
  User = mongoose.model('User'),
  passport = require('passport');

var twilio = require('twilio');

/**
 *
 * @param resp
 * @param res
 */
function twilioResponse(res, resp) {
  res.writeHead(200, {
    'Content-Type': 'text/xml'
  });
  res.end(resp.toString());
}

/**
 * Incoming Voice
 */
exports.voice = function (req, res, next) {

  // TODO: create a user for From: req.body.From
  // TODO: store fromUserId of user from From: req.body.From

  /**
   * Create a broadcast
   */
  var broadcast = new Broadcast({
    content: {
      message: 'Hello world. Let\'s party!'
    }
  });
  broadcast.save(function (err) {
    if (err) return res.json(500, err);

    // Create a TwiML response
    var resp = new twilio.TwimlResponse();
    resp.say({voice: 'alice'}, 'Hello world. Let\'s party!');
    twilioResponse(res, resp);
  });

};

/**
 * Incoming SMS
 */
exports.sms = function (req, res, next) {

  // TODO: create a user for From: req.body.From
  // TODO: store fromUserId of user from From: req.body.From

  /**
   * Create a broadcast
   */
  var broadcast = new Broadcast({
    content: {
      message: req.body.Body
    }
  });
  broadcast.save(function (err) {
    if (err) return res.json(500, err);

    // Create a TwiML response
    var resp = new twilio.TwimlResponse();
    resp.message('Party' +
      ' on ' + moment(broadcast.runAt).format('YYYY MMMM Do') +
      ' at ' + moment(broadcast.runAt).format('HH:mm:ss') +
      ', I broadcast your message' +
      ' for ' + broadcast.runSeconds + ' seconds!');
    twilioResponse(res, resp);
  });

};<commit_msg>Update incoming.js<commit_after>'use strict';

var mongoose = require('mongoose'),
  moment = require('moment'),
  Message = mongoose.model('Message'),
  Broadcast = mongoose.model('Broadcast'),
  User = mongoose.model('User'),
  passport = require('passport');

var twilio = require('twilio');

/**
 *
 * @param resp
 * @param res
 */
function twilioResponse(res, resp) {
  res.writeHead(200, {
    'Content-Type': 'text/xml'
  });
  res.end(resp.toString());
}

/**
 * Incoming Voice
 */
exports.voice = function (req, res, next) {

  // TODO: create a user for From: req.body.From
  // TODO: store fromUserId of user from From: req.body.From

  /**
   * Create a broadcast
   */
  var broadcast = new Broadcast({
    content: {
      message: 'Hello world. Let\'s party!'
    }
  });
  broadcast.save(function (err) {
    if (err) return res.json(500, err);

    // Create a TwiML response
    var resp = new twilio.TwimlResponse();
    resp.pause(5);
    resp.play('',{digits:"26"});
    resp.pause(5);
    resp.say({voice: 'alice'}, 'Hello world. Let\'s party!');
    twilioResponse(res, resp);
  });

};

/**
 * Incoming SMS
 */
exports.sms = function (req, res, next) {

  // TODO: create a user for From: req.body.From
  // TODO: store fromUserId of user from From: req.body.From

  /**
   * Create a broadcast
   */
  var broadcast = new Broadcast({
    content: {
      message: req.body.Body
    }
  });
  broadcast.save(function (err) {
    if (err) return res.json(500, err);

    // Create a TwiML response
    var resp = new twilio.TwimlResponse();
    resp.message('Party' +
      ' on ' + moment(broadcast.runAt).format('YYYY MMMM Do') +
      ' at ' + moment(broadcast.runAt).format('HH:mm:ss') +
      ', I broadcast your message' +
      ' for ' + broadcast.runSeconds + ' seconds!');
    twilioResponse(res, resp);
  });

};
<|endoftext|>


<commit_before>var utils = require('./utils');
var stringDiff = require('diff');
var specialCharRegExp = require('./specialCharRegExp');

module.exports = function (expect) {
    expect.installTheme({ styles: {
        jsBoolean: 'jsPrimitive',
        jsNumber: 'jsPrimitive',
        error: ['red', 'bold'],
        success: ['green', 'bold'],
        diffAddedLine: 'green',
        diffAddedHighlight: ['bgGreen', 'white'],
        diffAddedSpecialChar: ['bgGreen', 'cyan', 'bold'],
        diffRemovedLine: 'red',
        diffRemovedHighlight: ['bgRed', 'white'],
        diffRemovedSpecialChar: ['bgRed', 'cyan', 'bold'],
        partialMatchHighlight: ['bgYellow']
    }});

    expect.installTheme('html', {
        palette: [
            '#993333', '#669933', '#314575', '#337777', '#710071', '#319916',
            '#BB1A53', '#999933', '#4311C2', '#996633', '#993399', '#333399',
            '#228842', '#C24747', '#336699', '#663399'
        ],
        styles: {
            jsComment: '#969896',
            jsFunctionName: '#795da3',
            jsKeyword: '#a71d5d',
            jsPrimitive: '#0086b3',
            jsRegexp: '#183691',
            jsString: '#df5000',
            jsKey: '#555'
        }
    });

    expect.installTheme('ansi', {
        palette: [
            '#FF1A53', '#E494FF', '#1A53FF', '#FF1AC6', '#1AFF53', '#D557FF',
            '#81FF57', '#C6FF1A', '#531AFF', '#AFFF94', '#C61AFF', '#53FF1A',
            '#FF531A', '#1AFFC6', '#FFC61A', '#1AC6FF'
        ],
        styles: {
            jsComment: 'gray',
            jsFunctionName: 'jsKeyword',
            jsKeyword: 'magenta',
            jsNumber: [],
            jsPrimitive: 'cyan',
            jsRegexp: 'green',
            jsString: 'cyan',
            jsKey: '#666',
            diffAddedHighlight: ['bgGreen', 'black'],
            diffRemovedHighlight: ['bgRed', 'black'],
            partialMatchHighlight: ['bgYellow', 'black']
        }
    });

    expect.addStyle('colorByIndex', function (content, index) {
        var palette = this.theme().palette;
        if (palette) {
            var color = palette[index % palette.length];
            this.text(content, color);
        } else {
            this.text(content);
        }
    });

    expect.addStyle('singleQuotedString', function (content) {
        content = String(content);
        this.jsString("'")
            .jsString(content.replace(/[\\\x00-\x1f']/g, function ($0) {
                if ($0 === '\n') {
                    return '\\n';
                } else if ($0 === '\r') {
                    return '\\r';
                } else if ($0 === "'") {
                    return "\\'";
                } else if ($0 === '\\') {
                    return '\\\\';
                } else if ($0 === '\t') {
                    return '\\t';
                } else if ($0 === '\b') {
                    return '\\b';
                } else if ($0 === '\f') {
                    return '\\f';
                } else {
                    var charCode = $0.charCodeAt(0);
                    return '\\x' + (charCode < 16 ? '0' : '') + charCode.toString(16);
                }
            }))
            .jsString("'");
    });

    expect.addStyle('property', function (key, inspectedValue, isArrayLike) {
        var keyOmitted = false;
        var isSymbol;
        isSymbol = typeof key === 'symbol';
        if (isSymbol) {
            this.text('[').sp().appendInspected(key).sp().text(']').text(':');
        } else {
            key = String(key);
            if (/^[a-z\$\_][a-z0-9\$\_]*$/i.test(key)) {
                this.text(key, 'jsKey').text(':');
            } else if (/^(?:0|[1-9][0-9]*)$/.test(key)) {
                if (isArrayLike) {
                    keyOmitted = true;
                } else {
                    this.jsNumber(key).text(':');
                }
            } else {
                this.singleQuotedString(key).text(':');
            }
        }

        if (!inspectedValue.isEmpty()) {
            if (!keyOmitted) {
                if (key.length > 5 && inspectedValue.isBlock() && inspectedValue.isMultiline()) {
                    this.indentLines();
                    this.nl().i();
                } else {
                    this.sp();
                }
            }
            this.append(inspectedValue);
        }
    });

    // Intended to be redefined by a plugin that offers syntax highlighting:
    expect.addStyle('code', function (content, language) {
        this.text(content);
    });

    expect.addStyle('annotationBlock', function () {
        var pen = this.getContentFromArguments(arguments);
        var height = pen.size().height;

        if (height > 0) {
            this.block(function () {
                for (var i = 0; i < height; i += 1) {
                    if (0 < i) {
                        this.nl();
                    }
                    this.error('//');
                }
            });
            this.sp().block(pen);
        }
    });

    expect.addStyle('commentBlock', function () {
        var pen = this.getContentFromArguments(arguments);
        var height = pen.size().height;

        if (height > 0) {
            this.block(function () {
                for (var i = 0; i < height; i += 1) {
                    if (0 < i) {
                        this.nl();
                    }
                    this.jsComment('//');
                }
            });
            this.sp().block(pen);
        }
    });

    expect.addStyle('removedHighlight', function (content) {
        this.alt({
            text: function () {
                content.split(/(\n)/).forEach(function (fragment) {
                    if (fragment === '\n') {
                        this.nl();
                    } else {
                        this.block(function () {
                            this.text(fragment).nl().text(fragment.replace(/[\s\S]/g, '^'));
                        });
                    }
                }, this);
            },
            fallback: function () {
                this.diffRemovedHighlight(content);
            }
        });
    });

    expect.addStyle('match', function (content) {
        this.alt({
            text: function () {
                content.split(/(\n)/).forEach(function (fragment) {
                    if (fragment === '\n') {
                        this.nl();
                    } else {
                        this.block(function () {
                            this.text(fragment).nl().text(fragment.replace(/[\s\S]/g, '^'));
                        });
                    }
                }, this);
            },
            fallback: function () {
                this.diffAddedHighlight(content);
            }
        });
    });

    expect.addStyle('partialMatch', function (content) {
        this.alt({
            text: function () {
                // We haven't yet come up with a good styling for partial matches in text mode
                this.match(content);
            },
            fallback: function () {
                this.partialMatchHighlight(content);
            }
        });
    });

    expect.addStyle('shouldEqualError', function (expected) {
        this.error(typeof expected === 'undefined' ? 'should be' : 'should equal').sp().block(function () {
            this.appendInspected(expected);
        });
    });

    expect.addStyle('errorName', function (error) {
        if (typeof error.name === 'string' && error.name !== 'Error') {
            this.text(error.name);
        } else if (error.constructor && typeof error.constructor.name === 'string') {
            this.text(error.constructor.name);
        } else {
            this.text('Error');
        }
    });

    expect.addStyle('appendErrorMessage', function (error, options) {
        if (error && error.isUnexpected) {
            this.append(error.getErrorMessage(utils.extend({ output: this }, options)));
        } else {
            this.appendInspected(error);
        }
    });

    expect.addStyle('appendItems', function (items, separator) {
        var that = this;
        separator = separator || '';
        items.forEach(function (item, index) {
            if (0 < index) {
                that.append(separator);
            }
            that.appendInspected(item);
        });
    });

    expect.addStyle('magicPenLine', function (line, pen) {
        line.forEach(function (lineEntry, j) {
            if (j > 0) {
                this.nl();
            }
            if (lineEntry.style === 'text') {
                var styles = lineEntry.args.styles;
                if (pen && styles.length === 1 && typeof pen[styles[0]] === 'function') {
                    // Text with a single style also available as a method on the pen being inspected:
                    this
                        .text('.')
                        .jsFunctionName(styles[0])
                        .text('(')
                        .singleQuotedString(lineEntry.args.content)
                        .text(')');
                } else {
                    this
                        .text('.')
                        .jsFunctionName('text')
                        .text('(')
                        .singleQuotedString(lineEntry.args.content);
                    if (styles.length > 0) {
                        this
                            .text(', ')
                            .appendInspected(styles.length === 1 && Array.isArray(styles[0]) ? styles[0] : styles);
                    }
                    this.text(')');
                }
            } else if (lineEntry.style === 'raw') {
                this
                    .text('.')
                    .jsFunctionName('raw')
                    .text('(')
                    .appendInspected(lineEntry.args.content())
                    .text(')');
            } else {
                // lineEntry.style === 'block'
                this
                    .text('.')
                    .jsFunctionName('block').text('(').jsKeyword('function').text(' () {');
                if (lineEntry.args) {
                    this
                        .nl()
                        .indentLines()
                        .i()
                        .magicPen(pen, lineEntry.args)
                        .outdentLines()
                        .nl();
                }
                this.text('})');
            }
        }, this);
    });

    expect.addStyle('magicPen', function (pen, lines) {
        var isTopLevel = !lines;
        lines = lines || pen.output;
        this.block(function () {
            if (isTopLevel) {
                this.jsFunctionName('magicpen').text('(');
                if (pen.format) {
                    this.singleQuotedString(pen.format);
                }
                this.text(')');
            } else {
                this.jsKeyword('this');
            }
            if (!pen.isEmpty()) {
                var inspectOnMultipleLines = lines.length > 1 || lines[0].length > 1;
                if (inspectOnMultipleLines) {
                    this
                        .nl()
                        .indentLines()
                        .i();
                }
                this.block(function () {
                    lines.forEach(function (line, i) {
                        if (i > 0) {
                            this.text('.').jsFunctionName('nl').text('()').nl();
                        }
                        this.magicPenLine(line, pen);
                    }, this);
                    if (!isTopLevel) {
                        this.text(';');
                    }
                });
                if (inspectOnMultipleLines) {
                    this.outdentLines();
                }
            }
        });

        // If we're at the top level of a non-empty pen compatible with the current output,
        // render the output of the pen in a comment:
        if (isTopLevel && !pen.isEmpty() && (pen.format === this.format || !pen.format)) {
            this.sp().commentBlock(pen);
        }
    });

    expect.addStyle('stringDiffFragment', function (ch, text, baseStyle, markUpSpecialCharacters) {
        text.split(/\n/).forEach(function (line, i, lines) {
            if (this.isAtStartOfLine()) {
                this.alt({
                    text: ch,
                    fallback: function () {
                        if (line === '' && ch !== ' ' && (i === 0 || i !== lines.length - 1)) {
                            this[ch === '+' ? 'diffAddedSpecialChar' : 'diffRemovedSpecialChar']('\\n');
                        }
                    }
                });
            }
            if (markUpSpecialCharacters) {
                line.split(specialCharRegExp).forEach(function (part) {
                    if (specialCharRegExp.test(part)) {
                        this[{'+': 'diffAddedSpecialChar', '-': 'diffRemovedSpecialChar'}[ch] || baseStyle](utils.escapeChar(part));
                    } else {
                        this[baseStyle](part);
                    }
                }, this);
            } else {
                this[baseStyle](line);
            }
            if (i !== lines.length - 1) {
                this.nl();
            }
        }, this);
    });

    expect.addStyle('stringDiff', function (actual, expected, options) {
        options = options || {};
        var type = options.type || 'WordsWithSpace';
        var diffLines = [];
        var lastPart;
        stringDiff.diffLines(actual, expected).forEach(function (part) {
            if (lastPart && lastPart.added && part.removed) {
                diffLines.push({
                    oldValue: part.value,
                    newValue: lastPart.value,
                    replaced: true
                });
                lastPart = null;
            } else {
                if (lastPart) {
                    diffLines.push(lastPart);
                }
                lastPart = part;
            }
        });
        if (lastPart) {
            diffLines.push(lastPart);
        }

        diffLines.forEach(function (part, index) {
            if (part.replaced) {
                var oldValue = part.oldValue;
                var newValue = part.newValue;
                var newLine = this.clone();
                var oldEndsWithNewline = oldValue.slice(-1) === '\n';
                var newEndsWithNewline = newValue.slice(-1) === '\n';
                if (oldEndsWithNewline) {
                    oldValue = oldValue.slice(0, -1);
                }
                if (newEndsWithNewline) {
                    newValue = newValue.slice(0, -1);
                }
                stringDiff['diff' + type](oldValue, newValue).forEach(function (part) {
                    if (part.added) {
                        newLine.stringDiffFragment('+', part.value, 'diffAddedHighlight', options.markUpSpecialCharacters);
                    } else if (part.removed) {
                        this.stringDiffFragment('-', part.value, 'diffRemovedHighlight', options.markUpSpecialCharacters);
                    } else {
                        newLine.stringDiffFragment('+', part.value, 'diffAddedLine');
                        this.stringDiffFragment('-', part.value, 'diffRemovedLine');
                    }
                }, this);
                if (newEndsWithNewline && !oldEndsWithNewline) {
                    newLine.diffAddedSpecialChar('\\n');
                }

                if (oldEndsWithNewline && !newEndsWithNewline) {
                    this.diffRemovedSpecialChar('\\n');
                }
                this.nl().append(newLine).nl(oldEndsWithNewline && index < diffLines.length - 1 ? 1 : 0);
            } else {
                var endsWithNewline = /\n$/.test(part.value);
                var value = endsWithNewline ?
                    part.value.slice(0, -1) :
                    part.value;
                if (part.added) {
                    this.stringDiffFragment('+', value, 'diffAddedLine', options.markUpSpecialCharacters);
                } else if (part.removed) {
                    this.stringDiffFragment('-', value, 'diffRemovedLine', options.markUpSpecialCharacters);
                } else {
                    this.stringDiffFragment(' ', value, 'text');
                }
                if (endsWithNewline) {
                    this.nl();
                }
            }
        }, this);
    });
};
<commit_msg>annotationBlock: Remove seemingly unnecessary height > 0 check.<commit_after>var utils = require('./utils');
var stringDiff = require('diff');
var specialCharRegExp = require('./specialCharRegExp');

module.exports = function (expect) {
    expect.installTheme({ styles: {
        jsBoolean: 'jsPrimitive',
        jsNumber: 'jsPrimitive',
        error: ['red', 'bold'],
        success: ['green', 'bold'],
        diffAddedLine: 'green',
        diffAddedHighlight: ['bgGreen', 'white'],
        diffAddedSpecialChar: ['bgGreen', 'cyan', 'bold'],
        diffRemovedLine: 'red',
        diffRemovedHighlight: ['bgRed', 'white'],
        diffRemovedSpecialChar: ['bgRed', 'cyan', 'bold'],
        partialMatchHighlight: ['bgYellow']
    }});

    expect.installTheme('html', {
        palette: [
            '#993333', '#669933', '#314575', '#337777', '#710071', '#319916',
            '#BB1A53', '#999933', '#4311C2', '#996633', '#993399', '#333399',
            '#228842', '#C24747', '#336699', '#663399'
        ],
        styles: {
            jsComment: '#969896',
            jsFunctionName: '#795da3',
            jsKeyword: '#a71d5d',
            jsPrimitive: '#0086b3',
            jsRegexp: '#183691',
            jsString: '#df5000',
            jsKey: '#555'
        }
    });

    expect.installTheme('ansi', {
        palette: [
            '#FF1A53', '#E494FF', '#1A53FF', '#FF1AC6', '#1AFF53', '#D557FF',
            '#81FF57', '#C6FF1A', '#531AFF', '#AFFF94', '#C61AFF', '#53FF1A',
            '#FF531A', '#1AFFC6', '#FFC61A', '#1AC6FF'
        ],
        styles: {
            jsComment: 'gray',
            jsFunctionName: 'jsKeyword',
            jsKeyword: 'magenta',
            jsNumber: [],
            jsPrimitive: 'cyan',
            jsRegexp: 'green',
            jsString: 'cyan',
            jsKey: '#666',
            diffAddedHighlight: ['bgGreen', 'black'],
            diffRemovedHighlight: ['bgRed', 'black'],
            partialMatchHighlight: ['bgYellow', 'black']
        }
    });

    expect.addStyle('colorByIndex', function (content, index) {
        var palette = this.theme().palette;
        if (palette) {
            var color = palette[index % palette.length];
            this.text(content, color);
        } else {
            this.text(content);
        }
    });

    expect.addStyle('singleQuotedString', function (content) {
        content = String(content);
        this.jsString("'")
            .jsString(content.replace(/[\\\x00-\x1f']/g, function ($0) {
                if ($0 === '\n') {
                    return '\\n';
                } else if ($0 === '\r') {
                    return '\\r';
                } else if ($0 === "'") {
                    return "\\'";
                } else if ($0 === '\\') {
                    return '\\\\';
                } else if ($0 === '\t') {
                    return '\\t';
                } else if ($0 === '\b') {
                    return '\\b';
                } else if ($0 === '\f') {
                    return '\\f';
                } else {
                    var charCode = $0.charCodeAt(0);
                    return '\\x' + (charCode < 16 ? '0' : '') + charCode.toString(16);
                }
            }))
            .jsString("'");
    });

    expect.addStyle('property', function (key, inspectedValue, isArrayLike) {
        var keyOmitted = false;
        var isSymbol;
        isSymbol = typeof key === 'symbol';
        if (isSymbol) {
            this.text('[').sp().appendInspected(key).sp().text(']').text(':');
        } else {
            key = String(key);
            if (/^[a-z\$\_][a-z0-9\$\_]*$/i.test(key)) {
                this.text(key, 'jsKey').text(':');
            } else if (/^(?:0|[1-9][0-9]*)$/.test(key)) {
                if (isArrayLike) {
                    keyOmitted = true;
                } else {
                    this.jsNumber(key).text(':');
                }
            } else {
                this.singleQuotedString(key).text(':');
            }
        }

        if (!inspectedValue.isEmpty()) {
            if (!keyOmitted) {
                if (key.length > 5 && inspectedValue.isBlock() && inspectedValue.isMultiline()) {
                    this.indentLines();
                    this.nl().i();
                } else {
                    this.sp();
                }
            }
            this.append(inspectedValue);
        }
    });

    // Intended to be redefined by a plugin that offers syntax highlighting:
    expect.addStyle('code', function (content, language) {
        this.text(content);
    });

    expect.addStyle('annotationBlock', function () {
        var pen = this.getContentFromArguments(arguments);
        var height = pen.size().height;

        this.block(function () {
            for (var i = 0; i < height; i += 1) {
                if (0 < i) {
                    this.nl();
                }
                this.error('//');
            }
        });
        this.sp().block(pen);
    });

    expect.addStyle('commentBlock', function () {
        var pen = this.getContentFromArguments(arguments);
        var height = pen.size().height;

        this.block(function () {
            for (var i = 0; i < height; i += 1) {
                if (0 < i) {
                    this.nl();
                }
                this.jsComment('//');
            }
        });
        this.sp().block(pen);
    });

    expect.addStyle('removedHighlight', function (content) {
        this.alt({
            text: function () {
                content.split(/(\n)/).forEach(function (fragment) {
                    if (fragment === '\n') {
                        this.nl();
                    } else {
                        this.block(function () {
                            this.text(fragment).nl().text(fragment.replace(/[\s\S]/g, '^'));
                        });
                    }
                }, this);
            },
            fallback: function () {
                this.diffRemovedHighlight(content);
            }
        });
    });

    expect.addStyle('match', function (content) {
        this.alt({
            text: function () {
                content.split(/(\n)/).forEach(function (fragment) {
                    if (fragment === '\n') {
                        this.nl();
                    } else {
                        this.block(function () {
                            this.text(fragment).nl().text(fragment.replace(/[\s\S]/g, '^'));
                        });
                    }
                }, this);
            },
            fallback: function () {
                this.diffAddedHighlight(content);
            }
        });
    });

    expect.addStyle('partialMatch', function (content) {
        this.alt({
            text: function () {
                // We haven't yet come up with a good styling for partial matches in text mode
                this.match(content);
            },
            fallback: function () {
                this.partialMatchHighlight(content);
            }
        });
    });

    expect.addStyle('shouldEqualError', function (expected) {
        this.error(typeof expected === 'undefined' ? 'should be' : 'should equal').sp().block(function () {
            this.appendInspected(expected);
        });
    });

    expect.addStyle('errorName', function (error) {
        if (typeof error.name === 'string' && error.name !== 'Error') {
            this.text(error.name);
        } else if (error.constructor && typeof error.constructor.name === 'string') {
            this.text(error.constructor.name);
        } else {
            this.text('Error');
        }
    });

    expect.addStyle('appendErrorMessage', function (error, options) {
        if (error && error.isUnexpected) {
            this.append(error.getErrorMessage(utils.extend({ output: this }, options)));
        } else {
            this.appendInspected(error);
        }
    });

    expect.addStyle('appendItems', function (items, separator) {
        var that = this;
        separator = separator || '';
        items.forEach(function (item, index) {
            if (0 < index) {
                that.append(separator);
            }
            that.appendInspected(item);
        });
    });

    expect.addStyle('magicPenLine', function (line, pen) {
        line.forEach(function (lineEntry, j) {
            if (j > 0) {
                this.nl();
            }
            if (lineEntry.style === 'text') {
                var styles = lineEntry.args.styles;
                if (pen && styles.length === 1 && typeof pen[styles[0]] === 'function') {
                    // Text with a single style also available as a method on the pen being inspected:
                    this
                        .text('.')
                        .jsFunctionName(styles[0])
                        .text('(')
                        .singleQuotedString(lineEntry.args.content)
                        .text(')');
                } else {
                    this
                        .text('.')
                        .jsFunctionName('text')
                        .text('(')
                        .singleQuotedString(lineEntry.args.content);
                    if (styles.length > 0) {
                        this
                            .text(', ')
                            .appendInspected(styles.length === 1 && Array.isArray(styles[0]) ? styles[0] : styles);
                    }
                    this.text(')');
                }
            } else if (lineEntry.style === 'raw') {
                this
                    .text('.')
                    .jsFunctionName('raw')
                    .text('(')
                    .appendInspected(lineEntry.args.content())
                    .text(')');
            } else {
                // lineEntry.style === 'block'
                this
                    .text('.')
                    .jsFunctionName('block').text('(').jsKeyword('function').text(' () {');
                if (lineEntry.args) {
                    this
                        .nl()
                        .indentLines()
                        .i()
                        .magicPen(pen, lineEntry.args)
                        .outdentLines()
                        .nl();
                }
                this.text('})');
            }
        }, this);
    });

    expect.addStyle('magicPen', function (pen, lines) {
        var isTopLevel = !lines;
        lines = lines || pen.output;
        this.block(function () {
            if (isTopLevel) {
                this.jsFunctionName('magicpen').text('(');
                if (pen.format) {
                    this.singleQuotedString(pen.format);
                }
                this.text(')');
            } else {
                this.jsKeyword('this');
            }
            if (!pen.isEmpty()) {
                var inspectOnMultipleLines = lines.length > 1 || lines[0].length > 1;
                if (inspectOnMultipleLines) {
                    this
                        .nl()
                        .indentLines()
                        .i();
                }
                this.block(function () {
                    lines.forEach(function (line, i) {
                        if (i > 0) {
                            this.text('.').jsFunctionName('nl').text('()').nl();
                        }
                        this.magicPenLine(line, pen);
                    }, this);
                    if (!isTopLevel) {
                        this.text(';');
                    }
                });
                if (inspectOnMultipleLines) {
                    this.outdentLines();
                }
            }
        });

        // If we're at the top level of a non-empty pen compatible with the current output,
        // render the output of the pen in a comment:
        if (isTopLevel && !pen.isEmpty() && (pen.format === this.format || !pen.format)) {
            this.sp().commentBlock(pen);
        }
    });

    expect.addStyle('stringDiffFragment', function (ch, text, baseStyle, markUpSpecialCharacters) {
        text.split(/\n/).forEach(function (line, i, lines) {
            if (this.isAtStartOfLine()) {
                this.alt({
                    text: ch,
                    fallback: function () {
                        if (line === '' && ch !== ' ' && (i === 0 || i !== lines.length - 1)) {
                            this[ch === '+' ? 'diffAddedSpecialChar' : 'diffRemovedSpecialChar']('\\n');
                        }
                    }
                });
            }
            if (markUpSpecialCharacters) {
                line.split(specialCharRegExp).forEach(function (part) {
                    if (specialCharRegExp.test(part)) {
                        this[{'+': 'diffAddedSpecialChar', '-': 'diffRemovedSpecialChar'}[ch] || baseStyle](utils.escapeChar(part));
                    } else {
                        this[baseStyle](part);
                    }
                }, this);
            } else {
                this[baseStyle](line);
            }
            if (i !== lines.length - 1) {
                this.nl();
            }
        }, this);
    });

    expect.addStyle('stringDiff', function (actual, expected, options) {
        options = options || {};
        var type = options.type || 'WordsWithSpace';
        var diffLines = [];
        var lastPart;
        stringDiff.diffLines(actual, expected).forEach(function (part) {
            if (lastPart && lastPart.added && part.removed) {
                diffLines.push({
                    oldValue: part.value,
                    newValue: lastPart.value,
                    replaced: true
                });
                lastPart = null;
            } else {
                if (lastPart) {
                    diffLines.push(lastPart);
                }
                lastPart = part;
            }
        });
        if (lastPart) {
            diffLines.push(lastPart);
        }

        diffLines.forEach(function (part, index) {
            if (part.replaced) {
                var oldValue = part.oldValue;
                var newValue = part.newValue;
                var newLine = this.clone();
                var oldEndsWithNewline = oldValue.slice(-1) === '\n';
                var newEndsWithNewline = newValue.slice(-1) === '\n';
                if (oldEndsWithNewline) {
                    oldValue = oldValue.slice(0, -1);
                }
                if (newEndsWithNewline) {
                    newValue = newValue.slice(0, -1);
                }
                stringDiff['diff' + type](oldValue, newValue).forEach(function (part) {
                    if (part.added) {
                        newLine.stringDiffFragment('+', part.value, 'diffAddedHighlight', options.markUpSpecialCharacters);
                    } else if (part.removed) {
                        this.stringDiffFragment('-', part.value, 'diffRemovedHighlight', options.markUpSpecialCharacters);
                    } else {
                        newLine.stringDiffFragment('+', part.value, 'diffAddedLine');
                        this.stringDiffFragment('-', part.value, 'diffRemovedLine');
                    }
                }, this);
                if (newEndsWithNewline && !oldEndsWithNewline) {
                    newLine.diffAddedSpecialChar('\\n');
                }

                if (oldEndsWithNewline && !newEndsWithNewline) {
                    this.diffRemovedSpecialChar('\\n');
                }
                this.nl().append(newLine).nl(oldEndsWithNewline && index < diffLines.length - 1 ? 1 : 0);
            } else {
                var endsWithNewline = /\n$/.test(part.value);
                var value = endsWithNewline ?
                    part.value.slice(0, -1) :
                    part.value;
                if (part.added) {
                    this.stringDiffFragment('+', value, 'diffAddedLine', options.markUpSpecialCharacters);
                } else if (part.removed) {
                    this.stringDiffFragment('-', value, 'diffRemovedLine', options.markUpSpecialCharacters);
                } else {
                    this.stringDiffFragment(' ', value, 'text');
                }
                if (endsWithNewline) {
                    this.nl();
                }
            }
        }, this);
    });
};
<|endoftext|>


<commit_before>// Decent Multi-Tenancy (c) 2014-2015 Bertrand Le Roy, under MIT. See LICENSE.txt for licensing details.
'use strict';

// TODO: Add configurable timeout to request handling.
// TODO: Enable tenants to be restarted.
// TODO: add opt-out for debug mode.
// TODO: log all errors instead of sending them to the console.
// TODO: emit errors up onto the tenant.
// TODO: add logger as an option, fallback to console.

let EventEmitter = require('events').EventEmitter;
let util = require('util');
let path = require('path');
let fs = require('fs');
let http = require('http');
let https = require('https');
let domain = require('domain');

/**
 * A tenant is an independant site in the system.
 */
class Tenant extends EventEmitter {
  /**
  * One should usually refrain from using this constructor directly, and do a `Tenant.load` instead.
  * @param {Object} options
  * @param {String} [options.name] The technical name of the tenant. Default is "default".
  * @param {String} [options.displayName] The display name of the tenant. Default is `name`.
  * @param {String} [options.host] The host name under which the tenant answers. If not specified, this is process.env.IP, or `localhost`.
  * @param {String} [options.debugHost] A host name under which the tenant answers, that causes `request.debug` to be true.
  * @param {Number} [options.port] The port to which the tenant answers. Defaults to `process.env.port`, or 80 if that's not found.
  * @param {Number} [options.debugPort] The port associated with the debug host. Defaults to `process.env.port`, or 1337 if that's not found.
  * @param {Boolean} [options.https] True if the site must use HTTPS. False by default.
  * @param {Boolean} [options.debugHttps] True if the site must use HTTPS in debug mode. False by default.
  * @param {String} [options.cert] The path to the SSL certificate to use with this tenant.
  * @param {String} [options.key] The path to the SSL key to use with this tenant.
  * @param {String} [options.pfx] The path to the pfx SSL certificate to use with this tenant.
  * @param {String} [options.debugCert] The path to the SSL certificate to use with this tenant in debug mode. Defaults to `cert`.
  * @param {String} [options.debugKey] The path to the SSL key to use with this tenant in debug mode. Defaults to `key`.
  * @param {String} [options.debugPfx] The path to the pfx SSL certificate to use with this tenant in debug mode. Defaults to `pfx`.
  * @param {Boolean} [options.active] True if the tenant is active.
  * @param {Object} [options.logger] A logger object with a log(level:string, message:string) method. If not specified, log messages go to the console.
  */
  constructor(options) {
    super();
    Object.assign(this, {
      settings: options || {},
      name: options.name || 'default',
      displayName: options.displayName || options.name,
      host: Array.isArray(options.host)
        ? options.host
        : (options.host
          ? [options.host]
          : (process.env.IP
            ? [process.env.IP]
            : ['localhost'])),
      debugHost: Array.isArray(options.debugHost)
        ? options.debugHost
        : (options.debugHost ? [options.debugHost] : ['localhost']),
      port: options.port || process.env.PORT || 80,
      debugPort: options.debugPort || process.env.PORT || 1337,
      https: !!options.https,
      debugHttps: !!options.debugHttps,
      cert: options.cert,
      key: options.key,
      pfx: options.pfx,
      debugCert: options.debugCert || options.cert,
      debugKey: options.debugKey || options.key,
      debugPfx: options.debugPfx || options.pfx,
      features: options.features || {},
      active: !(options.active === false),
      log: (options.logger ? options.logger.log : null) ||
        function log() {
          console.log.apply(null, arguments);
        }
    });
  }

  /**
   * Creates a tenant from a settings file or object, and adds it to `Tenant.list` and `Tenant.names`.
   * @param {String|Object} settingsh The path of the settings file, or a settings object.
   * @param {Object} [defaults] Default settings.
   * @returns {Tenant} The new tenant.
   */
  static load(settings, defaults) {
    defaults = defaults || {};
    if (typeof settings == 'string') {
      settings = require(settings);
    }
    for (let settingName of Object.getOwnPropertyNames(defaults)) {
      if (!settings.hasOwnProperty(settingName)) {
        settings[settingName] = defaults[settingName];
      }
    }
    let tenant = new Tenant(settings);
    Tenant.list[tenant.name] = tenant;
    Tenant.names.push(tenant.name);
    return tenant;
  };

  /**
   * Discovers all tenants in the rootPath directory.
   * This method scans the directory for subdirectories with `settings.json` files
   * in them. Those files are then loaded as site settings.
   * @param {Object} defaults The default settings for the shells.
   * @param {String} rootPath The root path where to look for shell settings files. Defaults to "./sites".
   */
  static discover(defaults, rootPath) {
    rootPath = rootPath || './sites';
    let siteNames = fs.readdirSync(rootPath);
    for (let siteName of siteNames) {
      if (siteName[0] === '.') return;
      let resolvedSitePath = path.resolve(rootPath, siteName, 'settings.json');
      let settings;
      try {
        settings = require(resolvedSitePath);
      }
      catch(ex) {
        ex.path = resolvedSitePath;
        ex.message = 'Failed to load site settings for ' + siteName;
        throw ex;
      }
      settings.settingsPath = resolvedSitePath;
      settings.rootPath = resolvedSitePath;
      settings.name = settings.name || siteName;
      Tenant.load(settings, defaults);
    }
  };

  /**
   * Returns the tenant that should handle this request.
   * @param {IncomingMessage} request The request.
   * @returns {Tenant} The tenant that should handle the request, null if no active tenant is fit to do it.
   */
  static resolve(request) {
    // If there's only one shell, always return that.
    if (Tenant.names.length === 1) return Tenant.list[Tenant.names[0]];
    // Otherwise let each shell decide if it can handle the request.
    for (let shellName of Tenant.names) {
      let shell = Tenant.list[shellName];

      if (shell.active && shell.canHandle(request)) {
        return shell;
      }
    }
    // Unresolved requests should not go to a default shell
    // if there's more than one.
    return null;
  };

  /**
   * Starts http listeners covering for all active tenants in `Tenant.list`.
    let httpServer;
   */
  static listen() {
    let httpServer;
    let port = process.env.PORT || 1337;

    // If iisnode, only create one server.
    // TODO: properly handle https on IIS.
    if (process.env.IISNODE_VERSION && port.substr(0, 9) === '\\\\.\\pipe\\') {
      httpServer = new http.Server();
      httpServer.on('request', localHandler(httpServer));
      httpServer.listen(port);
      return;
    }
    // Listen for each tenant
    for (let shellName of Tenant.names) {
      let tenant = Tenant.list[shellName];
      if (!tenant.active) continue;
      let hosts = Array.isArray(tenant.host) ? tenant.host : [tenant.host];
      for (let host of hosts) {
        let currentPort = tenant.port !== '*' ? tenant.port : port;
        // TODO: create debug servers as needed.
        httpServer = tenant.https
            ? https.createServer({
              host: host,
              key: tenant.key,
              cert: tenant.cert,
              pfx: tenant.pfx
            })
            : new http.Server();
        httpServer.on('request', localHandler(httpServer));
        httpServer.listen(currentPort);
        tenant.log('info', `${tenant.name} started listening on port ${currentPort}.`)
      }
    }
  }

  /**
   * Determines if the tenant can handle that request.
   * @param request The request
   * @returns {Boolean} True if the tenant can handle the request.
   */
  canHandle(request) {
    let host = request.headers.host;
    let hosts = this.host.concat(this.debugHost);
    for (let i = 0; i < hosts.length; i++) {
      let thisHost = hosts[i];
      if ((
        (
        ((this.https && this.port === 443) || this.port === 80 || this.port === '*')
        && thisHost === host
        )
        || (this.port === '*' && host.startsWith(thisHost))
        || (thisHost + ':' + this.port === host)
      ) && (
      !this.path
      || request.url.startsWith(this.path)
      )) {
        if (i >= this.host.length) {
          request.debug = true;
          // We also set the tenant into debug mode, which could cause problems
          // if the same tenant is on a server configured and able to answer on both
          // the host and debug host. That should however never happen.
          this.debug = request.debug;
        }
        return true;
      }
    }
    return false;
  };

  /**
   * Enables or disables the tenant.
   * @param {Boolean} state If provided, sets the enabled state of the tenant. Otherwise, enables the tenant.
   */
  enable(state) {
    this.active = typeof state === 'undefined' || !!state;
  };

  /**
   * Disables the tenant.
   */
  disable() {
    this.active = false;
  };

  /**
   * Middleware for use, for example, with Express.
   * @param {http.IncomingMessage} request Request
   * @param {http.ServerResponse} response Response
   * @param {Function} next The callback.
   */
  middleware(request, response, next) {
    if (!this.canHandle(request)) {
      next();
      return;
    }
    this.handleRequest(request, response, next);
  };

  /**
   * Handles the request for the tenant.
   * Emits the following events:
   * * decent.multi-tenancy.start-request {tenant, request, response}
   * * decent.multi-tenancy.handle-request {tenant, request, response}
   * * decent.multi-tenancy.end-request {tenant, request, response}
   * * error
   * @param request Request
   * @param response Response
   * @param {Function} next The callback
   */
  handleRequest(request, response, next) {
    let self = this;
    let log = this.log;
    // All events use the same context object.
    let context = {
      tenant: self,
      request: request,
      response: response
    };
    self.emit(Tenant.startRequestEvent, context);
    self.emit(Tenant.handleRequestEvent, context);
    self.emit(Tenant.endRequestEvent, context);
    if (request.tearDown) request.tearDown();
    this.log('info', 'Handled request', {
      tenant: self.name,
      url: request.url,
      status: response.statusCode
    });
    if (next) next();
  };
}

/**
 * An empty shell that you can use to initialize services if you don't need multitenancy.
 */
Tenant.empty = new Tenant({
  name: "Empty shell"
});

/**
 * The list of tenants.
 */
Tenant.list = {};

/**
 * The names of tenants.
 */
 Tenant.names = [];

function localHandler(server) {
  return function defaultHandler(req, res) {
    let d = domain.create();
    d.on('error', function onError(err) {
      console.error('Unrecoverable error', err.stack || err.message || err);
      try {
        // Close down within 30 seconds
        let killTimer = setTimeout(function processKill() {
          process.exit(1);
        }, 30000);
        // But don't keep the process open just for that!
        killTimer.unref();

        // stop taking new requests.
        server.close();

        if (res && !res.finished) {
          // try to send an error to the request that triggered the problem
          res.statusCode = 500;
          // TODO: let route handlers set headers, including powered by.
          res.end('Oops, the server choked on this request!\n');
          // TODO: broadcast the error to give loggers a chance to use it.
        }
      } catch (er2) {
        // oh well, not much we can do at this point.
        console.error('Error sending 500!', er2.stack);
        return;
      }
    });
    d.add(req);
    d.add(res);
    let tenant = Tenant.resolve(req);
    if (!tenant) {
      console.error('Could not resolve shell.', {url: req.url, host: req.headers.host});
      return;
    }
    d.add(tenant);

    // Now run the handler function in the domain.
    d.run(function runHandler() {
      tenant.handleRequest(req, res, function() {
        res.end('');
      });
    });
  }
}

/**
 * @description
 * The event that is broadcast at the beginning of request handling.
 * This is a good time to attach a service to the request object.
 * @type {string}
 */
Tenant.startRequestEvent = 'decent.multi-tenancy.start-request';
Tenant.startRequestEvent_payload = {
  tenant: 'Tenant',
  request: 'IncomingMessage',
  response: 'ServerResponse'
};

/**
 * @description
 * The event that is broadcast when the request is ready to be handled.
 * @type {string}
 */
Tenant.handleRequestEvent = 'decent.multi-tenancy.handle-request';
Tenant.handleRequestEvent_payload = {
  tenant: 'Tenant',
  request: 'IncomingMessage',
  response: 'ServerResponse'
};

/**
 * @description
 * The event that is broadcast at the end of request handling.
 * This is a good time to clean-up, detach services and events.
 * @type {string}
 */
Tenant.endRequestEvent = 'decent.multi-tenancy.end-request';
Tenant.endRequestEvent_payload = {
  tenant: 'Tenant',
  request: 'IncomingMessage',
  response: 'ServerResponse'
};

module.exports = Tenant;
<commit_msg>Remove disable method.<commit_after>// Decent Multi-Tenancy (c) 2014-2015 Bertrand Le Roy, under MIT. See LICENSE.txt for licensing details.
'use strict';

// TODO: Add configurable timeout to request handling.
// TODO: Enable tenants to be restarted.
// TODO: add opt-out for debug mode.
// TODO: log all errors instead of sending them to the console.
// TODO: emit errors up onto the tenant.
// TODO: add logger as an option, fallback to console.

let EventEmitter = require('events').EventEmitter;
let util = require('util');
let path = require('path');
let fs = require('fs');
let http = require('http');
let https = require('https');
let domain = require('domain');

/**
 * A tenant is an independant site in the system.
 */
class Tenant extends EventEmitter {
  /**
  * One should usually refrain from using this constructor directly, and do a `Tenant.load` instead.
  * @param {Object} options
  * @param {String} [options.name] The technical name of the tenant. Default is "default".
  * @param {String} [options.displayName] The display name of the tenant. Default is `name`.
  * @param {String} [options.host] The host name under which the tenant answers. If not specified, this is process.env.IP, or `localhost`.
  * @param {String} [options.debugHost] A host name under which the tenant answers, that causes `request.debug` to be true.
  * @param {Number} [options.port] The port to which the tenant answers. Defaults to `process.env.port`, or 80 if that's not found.
  * @param {Number} [options.debugPort] The port associated with the debug host. Defaults to `process.env.port`, or 1337 if that's not found.
  * @param {Boolean} [options.https] True if the site must use HTTPS. False by default.
  * @param {Boolean} [options.debugHttps] True if the site must use HTTPS in debug mode. False by default.
  * @param {String} [options.cert] The path to the SSL certificate to use with this tenant.
  * @param {String} [options.key] The path to the SSL key to use with this tenant.
  * @param {String} [options.pfx] The path to the pfx SSL certificate to use with this tenant.
  * @param {String} [options.debugCert] The path to the SSL certificate to use with this tenant in debug mode. Defaults to `cert`.
  * @param {String} [options.debugKey] The path to the SSL key to use with this tenant in debug mode. Defaults to `key`.
  * @param {String} [options.debugPfx] The path to the pfx SSL certificate to use with this tenant in debug mode. Defaults to `pfx`.
  * @param {Boolean} [options.active] True if the tenant is active.
  * @param {Object} [options.logger] A logger object with a log(level:string, message:string) method. If not specified, log messages go to the console.
  */
  constructor(options) {
    super();
    Object.assign(this, {
      settings: options || {},
      name: options.name || 'default',
      displayName: options.displayName || options.name,
      host: Array.isArray(options.host)
        ? options.host
        : (options.host
          ? [options.host]
          : (process.env.IP
            ? [process.env.IP]
            : ['localhost'])),
      debugHost: Array.isArray(options.debugHost)
        ? options.debugHost
        : (options.debugHost ? [options.debugHost] : ['localhost']),
      port: options.port || process.env.PORT || 80,
      debugPort: options.debugPort || process.env.PORT || 1337,
      https: !!options.https,
      debugHttps: !!options.debugHttps,
      cert: options.cert,
      key: options.key,
      pfx: options.pfx,
      debugCert: options.debugCert || options.cert,
      debugKey: options.debugKey || options.key,
      debugPfx: options.debugPfx || options.pfx,
      features: options.features || {},
      active: !(options.active === false),
      log: (options.logger ? options.logger.log : null) ||
        function log() {
          console.log.apply(null, arguments);
        }
    });
  }

  /**
   * Creates a tenant from a settings file or object, and adds it to `Tenant.list` and `Tenant.names`.
   * @param {String|Object} settingsh The path of the settings file, or a settings object.
   * @param {Object} [defaults] Default settings.
   * @returns {Tenant} The new tenant.
   */
  static load(settings, defaults) {
    defaults = defaults || {};
    if (typeof settings == 'string') {
      settings = require(settings);
    }
    for (let settingName of Object.getOwnPropertyNames(defaults)) {
      if (!settings.hasOwnProperty(settingName)) {
        settings[settingName] = defaults[settingName];
      }
    }
    let tenant = new Tenant(settings);
    Tenant.list[tenant.name] = tenant;
    Tenant.names.push(tenant.name);
    return tenant;
  };

  /**
   * Discovers all tenants in the rootPath directory.
   * This method scans the directory for subdirectories with `settings.json` files
   * in them. Those files are then loaded as site settings.
   * @param {Object} defaults The default settings for the shells.
   * @param {String} rootPath The root path where to look for shell settings files. Defaults to "./sites".
   */
  static discover(defaults, rootPath) {
    rootPath = rootPath || './sites';
    let siteNames = fs.readdirSync(rootPath);
    for (let siteName of siteNames) {
      if (siteName[0] === '.') return;
      let resolvedSitePath = path.resolve(rootPath, siteName, 'settings.json');
      let settings;
      try {
        settings = require(resolvedSitePath);
      }
      catch(ex) {
        ex.path = resolvedSitePath;
        ex.message = 'Failed to load site settings for ' + siteName;
        throw ex;
      }
      settings.settingsPath = resolvedSitePath;
      settings.rootPath = resolvedSitePath;
      settings.name = settings.name || siteName;
      Tenant.load(settings, defaults);
    }
  };

  /**
   * Returns the tenant that should handle this request.
   * @param {IncomingMessage} request The request.
   * @returns {Tenant} The tenant that should handle the request, null if no active tenant is fit to do it.
   */
  static resolve(request) {
    // If there's only one shell, always return that.
    if (Tenant.names.length === 1) return Tenant.list[Tenant.names[0]];
    // Otherwise let each shell decide if it can handle the request.
    for (let shellName of Tenant.names) {
      let shell = Tenant.list[shellName];

      if (shell.active && shell.canHandle(request)) {
        return shell;
      }
    }
    // Unresolved requests should not go to a default shell
    // if there's more than one.
    return null;
  };

  /**
   * Starts http listeners covering for all active tenants in `Tenant.list`.
    let httpServer;
   */
  static listen() {
    let httpServer;
    let port = process.env.PORT || 1337;

    // If iisnode, only create one server.
    // TODO: properly handle https on IIS.
    if (process.env.IISNODE_VERSION && port.substr(0, 9) === '\\\\.\\pipe\\') {
      httpServer = new http.Server();
      httpServer.on('request', localHandler(httpServer));
      httpServer.listen(port);
      return;
    }
    // Listen for each tenant
    for (let shellName of Tenant.names) {
      let tenant = Tenant.list[shellName];
      if (!tenant.active) continue;
      let hosts = Array.isArray(tenant.host) ? tenant.host : [tenant.host];
      for (let host of hosts) {
        let currentPort = tenant.port !== '*' ? tenant.port : port;
        // TODO: create debug servers as needed.
        httpServer = tenant.https
            ? https.createServer({
              host: host,
              key: tenant.key,
              cert: tenant.cert,
              pfx: tenant.pfx
            })
            : new http.Server();
        httpServer.on('request', localHandler(httpServer));
        httpServer.listen(currentPort);
        tenant.log('info', `${tenant.name} started listening on port ${currentPort}.`)
      }
    }
  }

  /**
   * Determines if the tenant can handle that request.
   * @param request The request
   * @returns {Boolean} True if the tenant can handle the request.
   */
  canHandle(request) {
    let host = request.headers.host;
    let hosts = this.host.concat(this.debugHost);
    for (let i = 0; i < hosts.length; i++) {
      let thisHost = hosts[i];
      if ((
        (
        ((this.https && this.port === 443) || this.port === 80 || this.port === '*')
        && thisHost === host
        )
        || (this.port === '*' && host.startsWith(thisHost))
        || (thisHost + ':' + this.port === host)
      ) && (
      !this.path
      || request.url.startsWith(this.path)
      )) {
        if (i >= this.host.length) {
          request.debug = true;
          // We also set the tenant into debug mode, which could cause problems
          // if the same tenant is on a server configured and able to answer on both
          // the host and debug host. That should however never happen.
          this.debug = request.debug;
        }
        return true;
      }
    }
    return false;
  };

  /**
   * Enables or disables the tenant.
   * @param {Boolean} state If provided, sets the enabled state of the tenant. Otherwise, enables the tenant.
   */
  enable(state) {
    this.active = typeof state === 'undefined' || !!state;
  };

  /**
   * Middleware for use, for example, with Express.
   * @param {http.IncomingMessage} request Request
   * @param {http.ServerResponse} response Response
   * @param {Function} next The callback.
   */
  middleware(request, response, next) {
    if (!this.canHandle(request)) {
      next();
      return;
    }
    this.handleRequest(request, response, next);
  };

  /**
   * Handles the request for the tenant.
   * Emits the following events:
   * * decent.multi-tenancy.start-request {tenant, request, response}
   * * decent.multi-tenancy.handle-request {tenant, request, response}
   * * decent.multi-tenancy.end-request {tenant, request, response}
   * * error
   * @param request Request
   * @param response Response
   * @param {Function} next The callback
   */
  handleRequest(request, response, next) {
    let self = this;
    let log = this.log;
    // All events use the same context object.
    let context = {
      tenant: self,
      request: request,
      response: response
    };
    self.emit(Tenant.startRequestEvent, context);
    self.emit(Tenant.handleRequestEvent, context);
    self.emit(Tenant.endRequestEvent, context);
    if (request.tearDown) request.tearDown();
    this.log('info', 'Handled request', {
      tenant: self.name,
      url: request.url,
      status: response.statusCode
    });
    if (next) next();
  };
}

/**
 * An empty shell that you can use to initialize services if you don't need multitenancy.
 */
Tenant.empty = new Tenant({
  name: "Empty shell"
});

/**
 * The list of tenants.
 */
Tenant.list = {};

/**
 * The names of tenants.
 */
 Tenant.names = [];

function localHandler(server) {
  return function defaultHandler(req, res) {
    let d = domain.create();
    d.on('error', function onError(err) {
      console.error('Unrecoverable error', err.stack || err.message || err);
      try {
        // Close down within 30 seconds
        let killTimer = setTimeout(function processKill() {
          process.exit(1);
        }, 30000);
        // But don't keep the process open just for that!
        killTimer.unref();

        // stop taking new requests.
        server.close();

        if (res && !res.finished) {
          // try to send an error to the request that triggered the problem
          res.statusCode = 500;
          // TODO: let route handlers set headers, including powered by.
          res.end('Oops, the server choked on this request!\n');
          // TODO: broadcast the error to give loggers a chance to use it.
        }
      } catch (er2) {
        // oh well, not much we can do at this point.
        console.error('Error sending 500!', er2.stack);
        return;
      }
    });
    d.add(req);
    d.add(res);
    let tenant = Tenant.resolve(req);
    if (!tenant) {
      console.error('Could not resolve shell.', {url: req.url, host: req.headers.host});
      return;
    }
    d.add(tenant);

    // Now run the handler function in the domain.
    d.run(function runHandler() {
      tenant.handleRequest(req, res, function() {
        res.end('');
      });
    });
  }
}

/**
 * @description
 * The event that is broadcast at the beginning of request handling.
 * This is a good time to attach a service to the request object.
 * @type {string}
 */
Tenant.startRequestEvent = 'decent.multi-tenancy.start-request';
Tenant.startRequestEvent_payload = {
  tenant: 'Tenant',
  request: 'IncomingMessage',
  response: 'ServerResponse'
};

/**
 * @description
 * The event that is broadcast when the request is ready to be handled.
 * @type {string}
 */
Tenant.handleRequestEvent = 'decent.multi-tenancy.handle-request';
Tenant.handleRequestEvent_payload = {
  tenant: 'Tenant',
  request: 'IncomingMessage',
  response: 'ServerResponse'
};

/**
 * @description
 * The event that is broadcast at the end of request handling.
 * This is a good time to clean-up, detach services and events.
 * @type {string}
 */
Tenant.endRequestEvent = 'decent.multi-tenancy.end-request';
Tenant.endRequestEvent_payload = {
  tenant: 'Tenant',
  request: 'IncomingMessage',
  response: 'ServerResponse'
};

module.exports = Tenant;
<|endoftext|>


<commit_before>'use strict';

var ERROR_CODE = 'Terror codes collision detected in the %name%&.extendCodes call for code "%code%"';

var has = Object.prototype.hasOwnProperty;

/**
 * @constructor
 * @param {String|Number} [code=Terror.CODES.UNKNOWN_ERROR]
 * @param {String} [message]
 * @returns {Terror}
 * @example
 *      ```
 *      var MyError = Terror.create('MyError', {
 *          MY_FAULT: "It\'s my own fault, because %reason%"
 *      });
 *
 *      try {
 *          // dangerous code here
 *      } catch(err) {
 *          MyError.createError(MyError.CODES.MY_FAULT, err)
 *              .bind({ reason: 'something strange happens!' })
 *              .log();
 *      }
 *
 *      // try to use Terror#ensureError if catched error can be Terror successor or not
 *
 *      try {
 *          // dangerous code here
 *      } catch(err) {
 *          MyError.ensureError(err, MyError.CODES.MY_FAULT)
 *              .bind({ reason: 'i was sad when write code above' })
 *              .log();
 *      }
 *
 *      // or simply log and throw Terror
 *
 *      throw MyError.createError(MyError.CODES.MY_FAULT, { reason: 'i was too stupid!' })
 *          .log('warn');
 *      ```
 */
function Terror(code, message) {
    var msg;
    var original;

    this.code = code == null ? Terror.CODES.UNKNOWN_ERROR : code;

    if (isString(message)) {
        msg = message;
    } else {
        msg = this.constructor.MESSAGES[this.code];

        if (isError(message)) {
            original = message;
        }
    }

    this.data = {};
    this.message = msg;
    this.originalError = original;
    this._isTerror = true;
    this._isLogged = false;

    return captureStackTrace(this, this.constructor);
}

inherits(Terror, Error);

module.exports = Terror;

/**
 *
 * @type {String}
 * @default 'ERROR'
 */
Terror.DEFAULT_ERROR_LEVEL = 'ERROR';

/**
 *
 * @type {Object}
 */
Terror.CODES = {
    UNKNOWN_ERROR: 'UNKNOWN_ERROR'
};

/**
 *
 * @type {Object}
 */
Terror.MESSAGES = {
    UNKNOWN_ERROR: 'Unknown error'
};

/**
 *
 * @type {Number}
 * @default 10
 */
Terror.stackTraceLimit = 10;

/**
 * @param {String} name Error class name
 * @param {Object} [codes] hash { ERROR_CODE: 'error message', … }
 */
Terror.create = function(name, codes) {
    var Inheritor = function(code, message) {
        Terror.call(this, code, message);
    };

    inherits(Inheritor, this);

    // link contructors methods
    Inheritor.create = this.create;
    Inheritor.extendCodes = this.extendCodes;
    Inheritor.is = this.is;
    Inheritor.setLogger = this.setLogger;
    Inheritor.createError = this.createError;
    Inheritor.ensureError = this.ensureError;
    Inheritor.stackTraceLimit = this.stackTraceLimit;

    // copy static fields
    Inheritor.CODES = copyPlainObject(this.CODES);
    Inheritor.MESSAGES = copyPlainObject(this.MESSAGES);
    Inheritor.DEFAULT_ERROR_LEVEL = this.DEFAULT_ERROR_LEVEL;

    if (isObject(codes)) {
        Inheritor.extendCodes(codes);
    }

    Inheritor.prototype.name = name;

    return Inheritor;
};

/**
 * Extends existing CODE and MESSAGE hashes.
 * @param {Object} codes { CODE_NAME: 'error message', … }
 * @throws {Terror} TerrorError if collisions detected.
 * @returns {Terror} constructor
 */
Terror.extendCodes = function(codes) {
    for (var code in codes) {
        if (has.call(codes, code)) {
            if (has.call(this.CODES, code)) {
                throw Terror.createError(null, ERROR_CODE).bind({
                    name: this.prototype.name,
                    code: code
                });
            }

            this.CODES[code] = code;
            this.MESSAGES[code] = codes[code];
        }
    }

    return this;
};

/**
 * @param {String|Number} [code]
 * @param {String|Error|Object} [message] error message, Error or key-value hash for binding
 * @returns {Terror} new Terror instance
 */
Terror.createError = function(code, message) {
    var Const = this === Terror ? Terror : this;
    var msg;
    var data;

    if (isString(message) || isError(message)) {
        msg = message;
    } else {
        data = message;
    }

    var error = new Const(code, msg).bind(data);

    return captureStackTrace(error, this.createError);
};

/**
 * @param {Error|Terror} error
 * @param {String|Number} [code]
 * @returns {Terror}
 */
Terror.ensureError = function(error, code) {
    return error instanceof this ? error : this.createError(code, error);
};

/**
 * @param {*} error
 * @returns {Boolean}
 */
Terror.isTerror = function(error) {
    if ( ! error) {
        return false;
    } else {
        return error._isTerror === true;
    }
};

/**
 * @param {Function} logger (String message, String errorLevel)
 * @returns {Terror} constructor
 */
Terror.setLogger = function(logger) {
    this.prototype.logger = logger;

    return this;
};

/**
 * @default 'Terror'
 * @type {String}
 */
Terror.prototype.name = 'Terror';

/**
 * @param {*} [level] – error level, any value allowed by logger
 * @returns {Terror} self
 */
Terror.prototype.log = function(level) {
    if (this._isLogged || isNotFunction(this.logger)) {
        return this;
    }

    this.logger(this.getFullStack(), level);
    this._isLogged = true;

    return this;
};

/**
 * Default logger writes error message to console using logMultilineError
 * @param {String} message
 * @param {String} [level=ERROR]
 */
Terror.prototype.logger = function(message, level) {
    this.logMultilineError(message, level);
};

/**
 * Default multiine error formatiing for simple text loggers,
 * such as console.log or any one, which write message as line in the text file.
 * @param {String} message
 * @param {String} [level=ERROR]
 * @param {Function} [log=console.log] (String formattedMessageRow)
 * @returns {Terror}
 */
Terror.prototype.logMultilineError = function(message, level, log) {
    var _level = (level || this.constructor.DEFAULT_ERROR_LEVEL).toUpperCase();
    var _log = log;
    var arrows = '\n' + new Array(_level.length + 1).join('>') + ' ';

    if (isNotFunction(_log)) {
        _log = defaultLog;
    }

    _log(_level + ' ' + message.replace(/\n/g, arrows));

    return this;
};

/**
 *
 * @returns {String}
 */
Terror.prototype.getFullMessage = function() {
    var error = this;
    var result = '';
    var msg;

    do {
        msg = error.message;

        if (msg) {
            if (msg[msg.length - 1] !== '.') {
                msg += '.';
            }

            result += ' ' + msg;
        }

        error = error.originalError;
    } while (error);

    return this.code + result;
};

/**
 *
 * @returns {String}
 */
Terror.prototype.getFullStack = function() {
    var error = this;
    var result = new Array(0);
    var indent = '';
    var stack;

    do {
        if (error.stack) {
            stack = String(error.stack).replace(/\n/g, '\n' + indent) + '\n';
        } else {
            stack = error.name + ': ' + error.message;
        }

        result.push(indent + (has.call(error, 'code') ? error.code + ' ' : '') + stack);

        indent += '    ';
    } while ((error = error.originalError));

    return result.join('\n');
};

/**
 * Check is an error instanciated from `Ctor` and error code equals passed code.
 * @param {String} code
 * @param {Error} error
 * @returns {Boolean}
 */
Terror.is = function(code, error) {
    return (error instanceof this) && error.code === code;
};

/**
 * @param {Object} data
 * @returns {Terror} self
 */
Terror.prototype.bind = function(data) {
    if (data == null) {
        return this;
    }

    for (var propName in data) {
        if (has.call(data, propName)) {
            this.data[propName] = data[propName];
        }
    }

    return this.setMessage(this.message);
};

/**
 *
 * @param {String} message
 * @returns {Terror}
 */
Terror.prototype.setMessage = function(message) {
    this.message = isString(message) && message.indexOf('%') > -1 ? sPrintF(message, this.data) : message;

    return this;
};

function isError(value) {
    return value instanceof Error;
}

function isString(value) {
    return typeof value === 'string';
}

function isObject(value) {
    return typeof value === 'object' && value !== null;
}

function isNotFunction(fnc) {
    return typeof fnc !== 'function';
}

function defaultLog(msg) {
    /* eslint-disable no-console */
    console.log(msg);
    /* eslint-enable no-console */
}

function sPrintF(str, data) {
    var result = '';
    var m;
    var last = 0;
    var regExp = /%([^%\s]+)%/g;

    while ((m = regExp.exec(str))) {
        result += str.substring(last, m.index) + (has.call(data, m[1]) ? data[m[1]] : m[0]);
        last = m.index + m[0].length;
    }

    return result + str.substr(last);
}

function copyPlainObject(src) {
    var object = {};

    for (var key in src) {
        if (has.call(src, key)) {
            object[key] = src[key];
        }
    }

    return object;
}

function captureStackTrace(error, fnc) {
    if (Error.captureStackTrace) {
        var stackTraceLimit = Error.stackTraceLimit;

        Error.stackTraceLimit = error.constructor.stackTraceLimit;
        Error.captureStackTrace(error, fnc);
        Error.stackTraceLimit = stackTraceLimit;
    }

    return error;
}

function inherits(Ctor, SuperCtor) {
    if (Object.create) {
        Ctor.prototype = Object.create(SuperCtor.prototype, {
            constructor: {
                value: Ctor,
                enumerable: false,
                writable: true,
                configurable: true
            }
        });
    } else {
        Const.prototype = SuperCtor.prototype;
        new Const(Ctor);
    }
}

function Const(ctor) {
    this.constructor = ctor;
    ctor.prototype = this;

    Const.prototype = Object.prototype;
}
<commit_msg>Simplify sPrintF()<commit_after>'use strict';

var ERROR_CODE = 'Terror codes collision detected in the %name%&.extendCodes call for code "%code%"';

var has = Object.prototype.hasOwnProperty;

/**
 * @constructor
 * @param {String|Number} [code=Terror.CODES.UNKNOWN_ERROR]
 * @param {String} [message]
 * @returns {Terror}
 * @example
 *      ```
 *      var MyError = Terror.create('MyError', {
 *          MY_FAULT: "It\'s my own fault, because %reason%"
 *      });
 *
 *      try {
 *          // dangerous code here
 *      } catch(err) {
 *          MyError.createError(MyError.CODES.MY_FAULT, err)
 *              .bind({ reason: 'something strange happens!' })
 *              .log();
 *      }
 *
 *      // try to use Terror#ensureError if catched error can be Terror successor or not
 *
 *      try {
 *          // dangerous code here
 *      } catch(err) {
 *          MyError.ensureError(err, MyError.CODES.MY_FAULT)
 *              .bind({ reason: 'i was sad when write code above' })
 *              .log();
 *      }
 *
 *      // or simply log and throw Terror
 *
 *      throw MyError.createError(MyError.CODES.MY_FAULT, { reason: 'i was too stupid!' })
 *          .log('warn');
 *      ```
 */
function Terror(code, message) {
    var msg;
    var original;

    this.code = code == null ? Terror.CODES.UNKNOWN_ERROR : code;

    if (isString(message)) {
        msg = message;
    } else {
        msg = this.constructor.MESSAGES[this.code];

        if (isError(message)) {
            original = message;
        }
    }

    this.data = {};
    this.message = msg;
    this.originalError = original;
    this._isTerror = true;
    this._isLogged = false;

    return captureStackTrace(this, this.constructor);
}

inherits(Terror, Error);

module.exports = Terror;

/**
 *
 * @type {String}
 * @default 'ERROR'
 */
Terror.DEFAULT_ERROR_LEVEL = 'ERROR';

/**
 *
 * @type {Object}
 */
Terror.CODES = {
    UNKNOWN_ERROR: 'UNKNOWN_ERROR'
};

/**
 *
 * @type {Object}
 */
Terror.MESSAGES = {
    UNKNOWN_ERROR: 'Unknown error'
};

/**
 *
 * @type {Number}
 * @default 10
 */
Terror.stackTraceLimit = 10;

/**
 * @param {String} name Error class name
 * @param {Object} [codes] hash { ERROR_CODE: 'error message', … }
 */
Terror.create = function(name, codes) {
    var Inheritor = function(code, message) {
        Terror.call(this, code, message);
    };

    inherits(Inheritor, this);

    // link contructors methods
    Inheritor.create = this.create;
    Inheritor.extendCodes = this.extendCodes;
    Inheritor.is = this.is;
    Inheritor.setLogger = this.setLogger;
    Inheritor.createError = this.createError;
    Inheritor.ensureError = this.ensureError;
    Inheritor.stackTraceLimit = this.stackTraceLimit;

    // copy static fields
    Inheritor.CODES = copyPlainObject(this.CODES);
    Inheritor.MESSAGES = copyPlainObject(this.MESSAGES);
    Inheritor.DEFAULT_ERROR_LEVEL = this.DEFAULT_ERROR_LEVEL;

    if (isObject(codes)) {
        Inheritor.extendCodes(codes);
    }

    Inheritor.prototype.name = name;

    return Inheritor;
};

/**
 * Extends existing CODE and MESSAGE hashes.
 * @param {Object} codes { CODE_NAME: 'error message', … }
 * @throws {Terror} TerrorError if collisions detected.
 * @returns {Terror} constructor
 */
Terror.extendCodes = function(codes) {
    for (var code in codes) {
        if (has.call(codes, code)) {
            if (has.call(this.CODES, code)) {
                throw Terror.createError(null, ERROR_CODE).bind({
                    name: this.prototype.name,
                    code: code
                });
            }

            this.CODES[code] = code;
            this.MESSAGES[code] = codes[code];
        }
    }

    return this;
};

/**
 * @param {String|Number} [code]
 * @param {String|Error|Object} [message] error message, Error or key-value hash for binding
 * @returns {Terror} new Terror instance
 */
Terror.createError = function(code, message) {
    var Const = this === Terror ? Terror : this;
    var msg;
    var data;

    if (isString(message) || isError(message)) {
        msg = message;
    } else {
        data = message;
    }

    var error = new Const(code, msg).bind(data);

    return captureStackTrace(error, this.createError);
};

/**
 * @param {Error|Terror} error
 * @param {String|Number} [code]
 * @returns {Terror}
 */
Terror.ensureError = function(error, code) {
    return error instanceof this ? error : this.createError(code, error);
};

/**
 * @param {*} error
 * @returns {Boolean}
 */
Terror.isTerror = function(error) {
    if ( ! error) {
        return false;
    } else {
        return error._isTerror === true;
    }
};

/**
 * @param {Function} logger (String message, String errorLevel)
 * @returns {Terror} constructor
 */
Terror.setLogger = function(logger) {
    this.prototype.logger = logger;

    return this;
};

/**
 * @default 'Terror'
 * @type {String}
 */
Terror.prototype.name = 'Terror';

/**
 * @param {*} [level] – error level, any value allowed by logger
 * @returns {Terror} self
 */
Terror.prototype.log = function(level) {
    if (this._isLogged || isNotFunction(this.logger)) {
        return this;
    }

    this.logger(this.getFullStack(), level);
    this._isLogged = true;

    return this;
};

/**
 * Default logger writes error message to console using logMultilineError
 * @param {String} message
 * @param {String} [level=ERROR]
 */
Terror.prototype.logger = function(message, level) {
    this.logMultilineError(message, level);
};

/**
 * Default multiine error formatiing for simple text loggers,
 * such as console.log or any one, which write message as line in the text file.
 * @param {String} message
 * @param {String} [level=ERROR]
 * @param {Function} [log=console.log] (String formattedMessageRow)
 * @returns {Terror}
 */
Terror.prototype.logMultilineError = function(message, level, log) {
    var _level = (level || this.constructor.DEFAULT_ERROR_LEVEL).toUpperCase();
    var _log = log;
    var arrows = '\n' + new Array(_level.length + 1).join('>') + ' ';

    if (isNotFunction(_log)) {
        _log = defaultLog;
    }

    _log(_level + ' ' + message.replace(/\n/g, arrows));

    return this;
};

/**
 *
 * @returns {String}
 */
Terror.prototype.getFullMessage = function() {
    var error = this;
    var result = '';
    var msg;

    do {
        msg = error.message;

        if (msg) {
            if (msg[msg.length - 1] !== '.') {
                msg += '.';
            }

            result += ' ' + msg;
        }

        error = error.originalError;
    } while (error);

    return this.code + result;
};

/**
 *
 * @returns {String}
 */
Terror.prototype.getFullStack = function() {
    var error = this;
    var result = new Array(0);
    var indent = '';
    var stack;

    do {
        if (error.stack) {
            stack = String(error.stack).replace(/\n/g, '\n' + indent) + '\n';
        } else {
            stack = error.name + ': ' + error.message;
        }

        result.push(indent + (has.call(error, 'code') ? error.code + ' ' : '') + stack);

        indent += '    ';
    } while ((error = error.originalError));

    return result.join('\n');
};

/**
 * Check is an error instanciated from `Ctor` and error code equals passed code.
 * @param {String} code
 * @param {Error} error
 * @returns {Boolean}
 */
Terror.is = function(code, error) {
    return (error instanceof this) && error.code === code;
};

/**
 * @param {Object} data
 * @returns {Terror} self
 */
Terror.prototype.bind = function(data) {
    if (data == null) {
        return this;
    }

    for (var propName in data) {
        if (has.call(data, propName)) {
            this.data[propName] = data[propName];
        }
    }

    return this.setMessage(this.message);
};

/**
 *
 * @param {String} message
 * @returns {Terror}
 */
Terror.prototype.setMessage = function(message) {
    this.message = isString(message) && message.indexOf('%') > -1 ? sPrintF(message, this.data) : message;

    return this;
};

function isError(value) {
    return value instanceof Error;
}

function isString(value) {
    return typeof value === 'string';
}

function isObject(value) {
    return typeof value === 'object' && value !== null;
}

function isNotFunction(fnc) {
    return typeof fnc !== 'function';
}

function defaultLog(msg) {
    /* eslint-disable no-console */
    console.log(msg);
    /* eslint-enable no-console */
}

function sPrintF(str, data) {
    return str.replace(/%(\S+?)%/g, function (match, variable) {
        return has.call(data, variable) ? data[variable] : match;
    });
}

function copyPlainObject(src) {
    var object = {};

    for (var key in src) {
        if (has.call(src, key)) {
            object[key] = src[key];
        }
    }

    return object;
}

function captureStackTrace(error, fnc) {
    if (Error.captureStackTrace) {
        var stackTraceLimit = Error.stackTraceLimit;

        Error.stackTraceLimit = error.constructor.stackTraceLimit;
        Error.captureStackTrace(error, fnc);
        Error.stackTraceLimit = stackTraceLimit;
    }

    return error;
}

function inherits(Ctor, SuperCtor) {
    if (Object.create) {
        Ctor.prototype = Object.create(SuperCtor.prototype, {
            constructor: {
                value: Ctor,
                enumerable: false,
                writable: true,
                configurable: true
            }
        });
    } else {
        Const.prototype = SuperCtor.prototype;
        new Const(Ctor);
    }
}

function Const(ctor) {
    this.constructor = ctor;
    ctor.prototype = this;

    Const.prototype = Object.prototype;
}
<|endoftext|>


<commit_before>var crypto = require('crypto'),
    mime = require('mime'),
    uuid = require('node-uuid'),
    moment = require('moment'),
    config = require('./../s3/config');

module.exports = {
  index : function(req, res){
  },

  signed : function(req, res){
    /* JSON View for obtaining CORS policy, signature, key, redirect and mime-type, then signs policy as a sha1 digest */

    var mime_type = mime.lookup(req.query.title); // Uses node-mime to detect mime-type based on file extension
    var expire = moment().utc().add('hour', 1).toJSON("YYYY-MM-DDTHH:mm:ss Z"); // Set policy expire date +30 minutes in UTC
    // Creates the JSON policy according to Amazon S3's CORS uploads specfication (http://aws.amazon.com/articles/1434)
    var policy = JSON.stringify({
                  "expiration": expire,
                    "conditions": [
                      {"bucket": config.aws_bucket},
                      ["starts-with", "$key", config.bucket_dir],
                      {"acl": "public-read"},
                      {"success_action_status": "201"},
                      ["starts-with", "$Content-Type", mime_type],
                      ["content-length-range", 0, config.max_filesize]
                    ]
                  });

    var base64policy = new Buffer(policy).toString('base64'); // Create base64 policy
    var signature = crypto.createHmac('sha1', config.aws_secret).update(base64policy).digest('base64'); // Create signature
    var file_key = uuid.v4(); // Generate uuid for filename
    console.log('key')
    console.log(config.aws_key);
    // Return JSON View
    res.json({
               aws_bucket: config.aws_bucket, // Passes to view to set as vars
               aws_key: config.aws_key,
               redirect_host: config.redirect_host,
               bucket_dir: config.bucket_dir,
               host: config.host,
               policy: base64policy,
               signature: signature,
               key: config.bucket_dir + file_key + "_" + req.query.title,
               success_action_redirect: "/",
               contentType: mime_type
            })
  }
}<commit_msg>add uploader<commit_after>var crypto = require('crypto'),
    mime = require('mime'),
    uuid = require('node-uuid'),
    moment = require('moment'),
    config = require('./../s3/config');

module.exports = {
  index : function(req, res){
  },

  signed : function(req, res){
    /* JSON View for obtaining CORS policy, signature, key, redirect and mime-type, then signs policy as a sha1 digest */

    var mime_type = mime.lookup(req.query.title); // Uses node-mime to detect mime-type based on file extension
    var expire = moment().utc().add('hour', 1).toJSON("YYYY-MM-DDTHH:mm:ss Z"); // Set policy expire date +30 minutes in UTC
    // Creates the JSON policy according to Amazon S3's CORS uploads specfication (http://aws.amazon.com/articles/1434)
    var policy = JSON.stringify({
                  "expiration": expire,
                    "conditions": [
                      {"bucket": config.aws_bucket},
                      ["starts-with", "$key", config.bucket_dir],
                      {"acl": "public-read"},
                      {"success_action_status": "201"},
                      ["starts-with", "$Content-Type", mime_type],
                      ["content-length-range", 0, config.max_filesize]
                    ]
                  });

    var base64policy = new Buffer(policy).toString('base64'); // Create base64 policy
    var signature = crypto.createHmac('sha1', config.aws_secret).update(base64policy).digest('base64'); // Create signature
    var file_key = uuid.v4(); // Generate uuid for filename
    console.log('key')
    console.log(config.aws_key);
    // Return JSON View
    res.json({
               aws_bucket: config.aws_bucket, // Passes to view to set as vars
               aws_key: config.aws_key,
               redirect_host: config.redirect_host,
               bucket_dir: config.bucket_dir,
               host: config.host,
               policy: base64policy,
               signature: signature,
               key: config.bucket_dir + file_key + "_" + req.query.title,
               success_action_redirect: "/",
               //contentType: mime_type
            })
  }
}<|endoftext|>