<commit_before>
"use strict";

var EventEmitter = require('events').EventEmitter;
var WritableStream = require('stream').Writable;
var util = require( './util' );

/**
 * @summary Input stream for sending audio to Mumble.
 *
 * @description
 * The stream implements the `WritableStream` interface.
 *
 * The input data format can be specified with the constructor options. The
 * final audio will be converted to mono 16-bit PCM at 48 kHz.
 *
 * Currently the packets are sent to murmur in 10ms packets. The sample rate
 * should be such that it can divide the audio to packets of that size.
 *
 * @constructor
 * @this {MumbleInputStream}
 * @param {MumbleConnection} connection Mumble connection to write to
 * @param {Number} options.sampleRate - Input sample rate. Defaults to 48000.
 * @param {Number} options.channels - Input channels. Defaults to 1.
 * @param {Number} options.gain - Volume multiplier. Defaults to 1.
 */
var MumbleInputStream = function( connection, options ) {
    var self = this;
    options = options || {};

    this.connection = connection;
    this.channels = options.channels || 1;
    this.whisperId = options.whisperId;
    this.sampleRate = options.sampleRate || 48000;
    this.gain = options.gain || 1;

    if( options && options.decodeStrings === false ) {
        throw new TypeError( "MumbleInputStream does not support decoded string writes" );
    }

    this.processInterval = setInterval(
        this._processBuffer.bind( this ),
        this.connection.FRAME_LENGTH );

    this.processObserver = new EventEmitter();
    this.frameQueue = [];

    this.lastFrame = this._createFrameBuffer();

    this.lastFrameWritten = 0;
    this.queuedForPlay = 0;
    this.lastWrite = null;
    this.sent = 0;

    // Call the Writable constructor
    WritableStream.call( this, options );
};
MumbleInputStream.prototype = Object.create( WritableStream.prototype );

MumbleInputStream.prototype.LOCAL_BUFFER_SIZE = 10;
MumbleInputStream.prototype.PLAY_BUFFER_SIZE = 5;

/**
 * @summary Closes the stream
 */
MumbleInputStream.prototype.close = function () {
    clearInterval( this.processInterval );
};

/**
 * @summary Change the volume multiplier
 *
 * @param {Number} gain - New gain value.
 */
MumbleInputStream.prototype.setGain = function( gain ) {
    if( gain <= 0 )
        throw new Error( 'Gain must be non-negative.' );

    this.gain = gain;
};

/**
 * Create new frame buffer
 *
 * @private
 */
MumbleInputStream.prototype._createFrameBuffer = function() {
    return new Buffer(
        this.sampleRate /
        1000 * this.connection.FRAME_LENGTH *
        2 * ( this.channels) );
};

/**
 * Processes the frameQueue by sending frames to the connection
 *
 * @private
 */
MumbleInputStream.prototype._processBuffer = function() {

    // If there's been a gap in the audio, reset the sequence id.
    if( !this.lastWrite ||
        this.lastWrite + 20*this.connection.FRAME_LENGTH < Date.now() ) {
        this.voiceSequence = this.connection.voiceSequence;
        this.lastWrite = Date.now();
        return;
    }

    while( this.lastWrite + this.connection.FRAME_LENGTH < Date.now() ) {
        if( this.frameQueue.length > 0 ) {

            // Deque the first sample from the queue.
            var frame = this.frameQueue.shift();

            // Process the sample to fit the Mumble expectations.
            if( this.gain !== 1 )
                frame = util.applyGain( frame, this.gain );
            if( this.channels > 1 )
                frame = util.downmixChannels( frame, this.channels );
            if( this.sampleRate !== 48000 )
                frame = util.resample( frame, this.sampleRate, 48000 );

            // Now the sample should be 48kHz 16-bit mono audio.
            // We can send it to Mumble.
            this.voiceSequence +=
                this.connection.sendVoiceFrame( frame, this.whisperId, this.voiceSequence );
            this.sent += frame.length / 2;

            // Raise 'written' event to notify the _write implementation that
            // it might be able to write more chunks to the internal buffer.
            this.processObserver.emit( 'written' );
        }

        this.lastWrite += this.connection.FRAME_LENGTH;
    }

    return;
};

/**
 * Writes incoming chunks to the voice channel
 *
 * This method is called by the WritableStream when data is being written.
 *
 * @private
 *
 * @param {Buffer} chunk The chunk to be written
 * @param {String} encoding Not used
 * @param {Function} callback Callback to signal writing the chunk is done.
 */
MumbleInputStream.prototype._write = function( chunk, encoding, callback ) {

    while( true ) {

        // If we are at the buffer cap, wait until the buffer is emptied
        // before writing the rest.
        if( this.frameQueue.length >= this.LOCAL_BUFFER_SIZE ) {
            var self = this;
            this.processObserver.once( 'written', function () {
                self._write( chunk, encoding, callback );
            });
            return;
        }

        // Write the chunk to the current buffer.
        var writtenBefore = this.lastFrameWritten;
        chunk.copy( this.lastFrame, this.lastFrameWritten, 0 );
        var written = writtenBefore + chunk.length;

        // Check if we've written the last frame full.
        if( written >= this.lastFrame.length ) {

            // Frame is full.
            // Fix the 'written' value and queue the frame.
            written = this.lastFrame.length;
            this.frameQueue.push( this.lastFrame );
            this.lastFrame = this._createFrameBuffer();
            this.lastFrameWritten = 0;

        } else {

            // Frame not full. Advance the lastFrameWritten.
            this.lastFrameWritten = written;

        }

        // Check if the chunk was written in full or if some remains.
        if( chunk.length > (written - writtenBefore) ) {

            // Chunk was larger than remaining space in the last frame.
            chunk = chunk.slice( written - writtenBefore );

        } else {

            // Chunk was written completely.
            return callback();

        }

    }
};

module.exports = MumbleInputStream;
<commit_msg>rescale sample, if not 16 Bit, signed, LE<commit_after>
"use strict";

var EventEmitter = require('events').EventEmitter;
var WritableStream = require('stream').Writable;
var util = require( './util' );

/**
 * @summary Input stream for sending audio to Mumble.
 *
 * @description
 * The stream implements the `WritableStream` interface.
 *
 * The input data format can be specified with the constructor options. The
 * final audio will be converted to mono 16-bit PCM at 48 kHz.
 *
 * Currently the packets are sent to murmur in 10ms packets. The sample rate
 * should be such that it can divide the audio to packets of that size.
 *
 * @constructor
 * @this {MumbleInputStream}
 * @param {MumbleConnection} connection Mumble connection to write to
 * @param {Number} options.sampleRate - Input sample rate. Defaults to 48000.
 * @param {Number} options.channels - Input channels. Defaults to 1.
 * @param {Number} options.gain - Volume multiplier. Defaults to 1.
 */
var MumbleInputStream = function( connection, options ) {
    var self = this;
    options = options || {};

    this.connection = connection;
    this.channels = options.channels || 1;
    this.whisperId = options.whisperId;
    this.sampleRate = options.sampleRate || 48000;
    this.gain = options.gain || 1;
    this.bitDepth = options.bitDepth || 16;
    this.signed = options.signed == true || options.signed == undefined;
    this.endianness = options.endianness || 'LE';

    if( options && options.decodeStrings === false ) {
        throw new TypeError( "MumbleInputStream does not support decoded string writes" );
    }

    this.processInterval = setInterval(
        this._processBuffer.bind( this ),
        this.connection.FRAME_LENGTH );

    this.processObserver = new EventEmitter();
    this.frameQueue = [];

    this.lastFrame = this._createFrameBuffer();

    this.lastFrameWritten = 0;
    this.queuedForPlay = 0;
    this.lastWrite = null;
    this.sent = 0;

    // Call the Writable constructor
    WritableStream.call( this, options );
};
MumbleInputStream.prototype = Object.create( WritableStream.prototype );

MumbleInputStream.prototype.LOCAL_BUFFER_SIZE = 10;
MumbleInputStream.prototype.PLAY_BUFFER_SIZE = 5;

/**
 * @summary Closes the stream
 */
MumbleInputStream.prototype.close = function () {
    clearInterval( this.processInterval );
};

/**
 * @summary Change the volume multiplier
 *
 * @param {Number} gain - New gain value.
 */
MumbleInputStream.prototype.setGain = function( gain ) {
    if( gain <= 0 )
        throw new Error( 'Gain must be non-negative.' );

    this.gain = gain;
};

/**
 * Create new frame buffer
 *
 * @private
 */
MumbleInputStream.prototype._createFrameBuffer = function() {
    return new Buffer(
        this.sampleRate /
        1000 * this.connection.FRAME_LENGTH *
        2 * ( this.channels) );
};

/**
 * Processes the frameQueue by sending frames to the connection
 *
 * @private
 */
MumbleInputStream.prototype._processBuffer = function() {

    // If there's been a gap in the audio, reset the sequence id.
    if( !this.lastWrite ||
        this.lastWrite + 20*this.connection.FRAME_LENGTH < Date.now() ) {
        this.voiceSequence = this.connection.voiceSequence;
        this.lastWrite = Date.now();
        return;
    }

    while( this.lastWrite + this.connection.FRAME_LENGTH < Date.now() ) {
        if( this.frameQueue.length > 0 ) {

            // Deque the first sample from the queue.
            var frame = this.frameQueue.shift();

            // Process the sample to fit the Mumble expectations.
            if( this.bitDepth !== 16 || !this.signed)
                frame = util.rescaleToUInt16LE( frame, this.bitDepth, !this.signed,
                    this.endianness === 'BE' );
            if( this.gain !== 1 )
                frame = util.applyGain( frame, this.gain );
            if( this.channels > 1 )
                frame = util.downmixChannels( frame, this.channels );
            if( this.sampleRate !== 48000 )
                frame = util.resample( frame, this.sampleRate, 48000 );

            // Now the sample should be 48kHz 16-bit mono audio.
            // We can send it to Mumble.
            this.voiceSequence +=
                this.connection.sendVoiceFrame( frame, this.whisperId, this.voiceSequence );
            this.sent += frame.length / 2;

            // Raise 'written' event to notify the _write implementation that
            // it might be able to write more chunks to the internal buffer.
            this.processObserver.emit( 'written' );
        }

        this.lastWrite += this.connection.FRAME_LENGTH;
    }

    return;
};

/**
 * Writes incoming chunks to the voice channel
 *
 * This method is called by the WritableStream when data is being written.
 *
 * @private
 *
 * @param {Buffer} chunk The chunk to be written
 * @param {String} encoding Not used
 * @param {Function} callback Callback to signal writing the chunk is done.
 */
MumbleInputStream.prototype._write = function( chunk, encoding, callback ) {

    while( true ) {

        // If we are at the buffer cap, wait until the buffer is emptied
        // before writing the rest.
        if( this.frameQueue.length >= this.LOCAL_BUFFER_SIZE ) {
            var self = this;
            this.processObserver.once( 'written', function () {
                self._write( chunk, encoding, callback );
            });
            return;
        }

        // Write the chunk to the current buffer.
        var writtenBefore = this.lastFrameWritten;
        chunk.copy( this.lastFrame, this.lastFrameWritten, 0 );
        var written = writtenBefore + chunk.length;

        // Check if we've written the last frame full.
        if( written >= this.lastFrame.length ) {

            // Frame is full.
            // Fix the 'written' value and queue the frame.
            written = this.lastFrame.length;
            this.frameQueue.push( this.lastFrame );
            this.lastFrame = this._createFrameBuffer();
            this.lastFrameWritten = 0;

        } else {

            // Frame not full. Advance the lastFrameWritten.
            this.lastFrameWritten = written;

        }

        // Check if the chunk was written in full or if some remains.
        if( chunk.length > (written - writtenBefore) ) {

            // Chunk was larger than remaining space in the last frame.
            chunk = chunk.slice( written - writtenBefore );

        } else {

            // Chunk was written completely.
            return callback();

        }

    }
};

module.exports = MumbleInputStream;
<|endoftext|>


<commit_before>const path = require('path');

/**
 * PackageJsonParser
 */
export default class PackageJsonParser {
    /**
     * Construct PackageJsonParser
     *
     * @param {?Object|string} [PackageJsonOrDirectory] package.json Object or absolute or relative path to package.json
     */
    constructor(PackageJsonOrDirectory) {
        this.packageJson = {};
        this.setPackageJson(PackageJsonOrDirectory);
    }

    setPackageJson(packageJsonOrDirectory) {
        // If it is a package.json object
        if (!!packageJsonOrDirectory && typeof packageJsonOrDirectory === 'object' && !(packageJsonOrDirectory instanceof Array)) {
            // maybe add "package-json-validator"
            this.packageJson = packageJsonOrDirectory;

        // If it is a string(directory) or empty we have to calculate the path and get the package.json from there
        } else if (typeof packageJsonOrDirectory === 'string' || !packageJsonOrDirectory) {
            let directoryOfPackageJson = '';

            // Without parameter use current path
            if (!packageJsonOrDirectory) {
                directoryOfPackageJson = process.cwd();
            } else {
                // if path is like "path/to/package.json", slice package.json, because we just need the path
                if (path.basename(packageJsonOrDirectory) === 'package.json') {
                    packageJsonOrDirectory = packageJsonOrDirectory.slice(0, -12); // eslint-disable-line no-param-reassign
                }

                // if path is absolute
                if (path.resolve(packageJsonOrDirectory) === path.normalize(packageJsonOrDirectory)) {
                    directoryOfPackageJson = packageJsonOrDirectory;

                // if path is relative
                } else {
                    directoryOfPackageJson = path.join(process.cwd(), packageJsonOrDirectory);
                }
            }

            try {
                this.packageJson = require(path.join(directoryOfPackageJson, 'package.json')); // eslint-disable-line global-require
            } catch (err) {
                throw new ReferenceError(`No package.json was found in "${directoryOfPackageJson}"`);
            }

        // If it is neither an object or string or empty, throw error
        } else {
            throw new TypeError('Parameter must be a package.json Object, an directory or empty.');
        }

        return this;
    }

    /**
     * Removes "npm run foo" and replaces it with foo command
     *
     * @param  {Object} [scriptsObject]            Object that contains "scripts" part of package.json
     * @return {Object} concatenatedScriptsObject
     */
    concatenateNpmScripts(scriptsObject = this.packageJson.scripts) {
        const concatenatedScriptsObject = {};

        /**
         * regex to find script invocations (npm run)
         *
         * matches:
         * 1: opening character (like "(" or just "")
         * 2: complete script invocation (like "npm run -s foo")
         * 3: script name that is called (in the example above it would be "foo")
         * 4: closing character (like ")" or just "")
         */
        const runScriptRegexp = /(^|\W)(npm run(?:-script)? (?:-s )?([^-][\w:\-]+)(?: -s)?)($|\W(?!-))/;

        // exclude scripts values if they match the following regular expression
        const excludeScriptRegexp = /onchange/; // issue that onchange works with one command only
        let atLeastOneValueCouldBeReplaced = false;

        // successive replace script invocations with their reference content and successive move them from scriptsObject to concatenatedScriptsObject
        do {
            atLeastOneValueCouldBeReplaced = false;

            for (const key in scriptsObject) {
                if (scriptsObject.hasOwnProperty(key)) {
                    const match = runScriptRegexp.exec(scriptsObject[key]);

                    // check if value contains script invocation, but does not match the excludeScriptRegexp
                    if (!!match && !excludeScriptRegexp.test(scriptsObject[key])) {
                        // check if name of the found script already exists in concatenatedScriptsObject so it can be replaced
                        if (match[3] in concatenatedScriptsObject) {
                            scriptsObject[key] = scriptsObject[key].replace(runScriptRegexp, `$1${concatenatedScriptsObject[match[3]]}$4`);
                            atLeastOneValueCouldBeReplaced = true;
                        }

                    // if it does not contain a script invocation, a modification is not required
                    } else {
                        // move from scriptsObject to concatenatedScriptsObject
                        concatenatedScriptsObject[key] = scriptsObject[key];
                        delete scriptsObject[key];

                        atLeastOneValueCouldBeReplaced = true;
                    }
                }
            }
        } while (atLeastOneValueCouldBeReplaced);

        // if there still is something in scriptsObject throw an Error, because everything should be in concatenatedScriptsObject now
        if (Object.keys(scriptsObject).length !== 0) {
            throw new Error('Not all script invocations could be replaced.');
        }

        return concatenatedScriptsObject;
    }
}
<commit_msg>added more comments, JSDoc<commit_after>const path = require('path');

/**
 * PackageJsonParser
 */
export default class PackageJsonParser {
    /**
     * Construct PackageJsonParser
     *
     * @param {?Object|string} [PackageJsonOrDirectory] package.json Object or absolute or relative path to package.json
     */
    constructor(PackageJsonOrDirectory) {
        /**
         * package.json Object
         *
         * @private
         * @type {Object}
         */
        this.packageJson = {};

        this.setPackageJson(PackageJsonOrDirectory);
    }

    /**
     * Set packageJson
     *
     * @throws {ReferenceError}                          Throw error if no package.json was found
     * @throws {TypeError}                               Throw error if given parameter is neither an object or string or empty
     * @param  {?Object|string} [packageJsonOrDirectory] package.json Object or absolute or relative path to package.json
     * @return {PackageJsonParser}                       Self reference
     */
    setPackageJson(packageJsonOrDirectory) {
        // If it is a package.json object we just have to assign it to this.packageJson, that's it
        if (!!packageJsonOrDirectory && typeof packageJsonOrDirectory === 'object' && !(packageJsonOrDirectory instanceof Array)) {
            this.packageJson = packageJsonOrDirectory;

        // If it is a string(directory) or empty we have to calculate the path and get the package.json from there
        } else if (typeof packageJsonOrDirectory === 'string' || !packageJsonOrDirectory) {
            let directoryOfPackageJson = '';

            // Without parameter use current path
            if (!packageJsonOrDirectory) {
                directoryOfPackageJson = process.cwd();
            } else {
                // if path is like "path/to/package.json", slice package.json, because we just need the path
                if (path.basename(packageJsonOrDirectory) === 'package.json') {
                    packageJsonOrDirectory = packageJsonOrDirectory.slice(0, -12); // eslint-disable-line no-param-reassign
                }

                // if path is absolute
                if (path.resolve(packageJsonOrDirectory) === path.normalize(packageJsonOrDirectory)) {
                    directoryOfPackageJson = packageJsonOrDirectory;

                // if path is relative
                } else {
                    directoryOfPackageJson = path.join(process.cwd(), packageJsonOrDirectory);
                }
            }

            try {
                this.packageJson = require(path.join(directoryOfPackageJson, 'package.json')); // eslint-disable-line global-require
            } catch (err) {
                throw new ReferenceError(`No package.json was found in "${directoryOfPackageJson}"`);
            }

        // If it is neither an object or string or empty, throw error
        } else {
            throw new TypeError('Parameter must be a package.json Object, an directory or empty.');
        }

        return this;
    }

    /**
     * Removes "npm run foo" and replaces it with foo command
     *
     * @param  {Object} [scriptsObject]            Object that contains "scripts" part of package.json
     * @return {Object} concatenatedScriptsObject
     */
    concatenateNpmScripts(scriptsObject = this.packageJson.scripts) {
        const concatenatedScriptsObject = {};

        /**
         * regex to find script invocations (npm run)
         *
         * matches:
         * 1: opening character (like "(" or just "")
         * 2: complete script invocation (like "npm run -s foo")
         * 3: script name that is called (in the example above it would be "foo")
         * 4: closing character (like ")" or just "")
         */
        const runScriptRegexp = /(^|\W)(npm run(?:-script)? (?:-s )?([^-][\w:\-]+)(?: -s)?)($|\W(?!-))/;

        // exclude scripts values if they match the following regular expression
        const excludeScriptRegexp = /onchange/; // issue that onchange works with one command only
        let atLeastOneValueCouldBeReplaced = false;

        // successive replace script invocations with their reference content and successive move them from scriptsObject to concatenatedScriptsObject
        do {
            atLeastOneValueCouldBeReplaced = false;

            for (const key in scriptsObject) {
                if (scriptsObject.hasOwnProperty(key)) {
                    const match = runScriptRegexp.exec(scriptsObject[key]);

                    // check if value contains script invocation, but does not match the excludeScriptRegexp
                    if (!!match && !excludeScriptRegexp.test(scriptsObject[key])) {
                        // check if name of the found script already exists in concatenatedScriptsObject so it can be replaced
                        if (match[3] in concatenatedScriptsObject) {
                            scriptsObject[key] = scriptsObject[key].replace(runScriptRegexp, `$1${concatenatedScriptsObject[match[3]]}$4`);
                            atLeastOneValueCouldBeReplaced = true;
                        }

                    // if it does not contain a script invocation, a modification is not required
                    } else {
                        // move from scriptsObject to concatenatedScriptsObject
                        concatenatedScriptsObject[key] = scriptsObject[key];
                        delete scriptsObject[key];

                        atLeastOneValueCouldBeReplaced = true;
                    }
                }
            }
        } while (atLeastOneValueCouldBeReplaced);

        // if there still is something in scriptsObject throw an Error, because everything should be in concatenatedScriptsObject now
        if (Object.keys(scriptsObject).length !== 0) {
            throw new Error('Not all script invocations could be replaced.');
        }

        return concatenatedScriptsObject;
    }
}
<|endoftext|>


<commit_before>var express       = require('express'),
    Error404      = require('../errors').Error404,
    path          = require('path'),
    mkdirp        = require('mkdirp'),
    fs            = require('fs'),
    requireUser   = require('../middleware/route').requireUser,
    image         = require('./image'),
    _             = require('underscore');

module.exports = function () {

  var app = express.createServer();

  app.mounted(function(parent){

    var app = this;

    app.get('/', function (req,res) {

      var query = req.popit.model('Organisation').find().asc('name');

      query.run(function(err, docs) {
        if (err) throw err;

        res.local('organisations', docs);
        res.render('organisation');
      });
    });

    function organisation_new_display_form (req, res, next) {
      if( ! res.local('errors') ) res.local('errors', {} );
      if( ! res.local('organisation') ) res.local('organisation', {} );
      res.render( 'organisation/new' );
    }

    app.get( '/new', requireUser, organisation_new_display_form );

    app.post('/new', requireUser, function(req, res) {
      var OrganisationModel = req.popit.model('Organisation');

      var organisation = new OrganisationModel({
        name: req.param('name', ''),
        slug: req.param('slug', ''),
      });

      if (organisation.name && !organisation.slug) {
        res.locals({ require_slug: true, organisation: organisation });
        return organisation_new_display_form(req,res);
      }

      organisation.save(function(err, obj){
        if ( err ) {
          res.local( 'errors', err['errors'] );
          return organisation_new_display_form(req,res);
        } else {
          res.redirect( obj.slug_url );
        }
      });
    });

    app.param('organisationSlug', function loadOrganisation (req, res, next, slug) {
      req.popit.model('Organisation').findOne({slug: slug}, function(err, doc) {
        if (err) console.log( err );
        if (!doc) {
          next( new Error404() );
        } else {
          res.local('organisation', doc);
          req.object = doc;
          next();
        }
      });
    });

    app.get('/:organisationSlug', function(req,res) {
      res.render('organisation/view');
    });

    app.get('/:organisationSlug/json', function(req,res) {
      res.json( res.local('organisation') );
    });

    app.get(  '/:organisationSlug/images/upload', requireUser, image.upload_image );
    app.post( '/:organisationSlug/images/upload', requireUser, image.upload_image );
    app.get(  '/:organisationSlug/images/:image_spec', image.get );
    app.post( '/:organisationSlug/images/:image_spec/delete', requireUser, image.delete );

  });

  return app;
};
<commit_msg>Added image proxy helper to organisation app<commit_after>var express       = require('express'),
    Error404      = require('../errors').Error404,
    path          = require('path'),
    mkdirp        = require('mkdirp'),
    fs            = require('fs'),
    requireUser   = require('../middleware/route').requireUser,
    image         = require('./image'),
    _             = require('underscore');

module.exports = function () {

  var app = express.createServer();

  app.helpers({
    image_proxy: utils.image_proxy_helper
  });

  app.mounted(function(parent){

    var app = this;

    app.get('/', function (req,res) {

      var query = req.popit.model('Organisation').find().asc('name');

      query.run(function(err, docs) {
        if (err) throw err;

        res.local('organisations', docs);
        res.render('organisation');
      });
    });

    function organisation_new_display_form (req, res, next) {
      if( ! res.local('errors') ) res.local('errors', {} );
      if( ! res.local('organisation') ) res.local('organisation', {} );
      res.render( 'organisation/new' );
    }

    app.get( '/new', requireUser, organisation_new_display_form );

    app.post('/new', requireUser, function(req, res) {
      var OrganisationModel = req.popit.model('Organisation');

      var organisation = new OrganisationModel({
        name: req.param('name', ''),
        slug: req.param('slug', ''),
      });

      if (organisation.name && !organisation.slug) {
        res.locals({ require_slug: true, organisation: organisation });
        return organisation_new_display_form(req,res);
      }

      organisation.save(function(err, obj){
        if ( err ) {
          res.local( 'errors', err['errors'] );
          return organisation_new_display_form(req,res);
        } else {
          res.redirect( obj.slug_url );
        }
      });
    });

    app.param('organisationSlug', function loadOrganisation (req, res, next, slug) {
      req.popit.model('Organisation').findOne({slug: slug}, function(err, doc) {
        if (err) console.log( err );
        if (!doc) {
          next( new Error404() );
        } else {
          res.local('organisation', doc);
          req.object = doc;
          next();
        }
      });
    });

    app.get('/:organisationSlug', function(req,res) {
      res.render('organisation/view');
    });

    app.get('/:organisationSlug/json', function(req,res) {
      res.json( res.local('organisation') );
    });

    app.get(  '/:organisationSlug/images/upload', requireUser, image.upload_image );
    app.post( '/:organisationSlug/images/upload', requireUser, image.upload_image );
    app.get(  '/:organisationSlug/images/:image_spec', image.get );
    app.post( '/:organisationSlug/images/:image_spec/delete', requireUser, image.delete );

  });

  return app;
};
<|endoftext|>


<commit_before>var gulp   = require('gulp');

var Task   = require('../Task');
var utils  = require('../utils');

module.exports = Task.extend({
  paths: {
    source: '',
    dest:   '',
  },

  watch: '',

  pipes: {
    dev: function(pipe) {
      var stylus         = require('gulp-stylus');
      var plumber        = require('gulp-plumber');
      var sourcemaps     = require('gulp-sourcemaps');
      var postcss        = require('gulp-postcss');

      var pAutoprefixer  = require('autoprefixer');
      var pMqpacker      = require('css-mqpacker');
      var pMatches       = require('postcss-selector-matches');
      var pQuantity      = require('postcss-quantity-queries');

      // Useful stuff for docs generation: mdcss[https://github.com/jonathantneal/mdcss]

      return pipe
        .pipe(global.WATCHING ? plumber({
            errorHandler: utils.errorHandler,
          }) : utils.noop())
        .pipe(sourcemaps.init())
          .pipe(stylus({
              pretty: global.DEBUG,
              'include css': true,
              compress: global.DEBUG,
            }))
          .pipe(postcss([
              pAutoprefixer({
                  browsers: ['> 0.5%']
                }),
              pMqpacker,
              pMatches,
              pQuantity,
            ]))
        .pipe(sourcemaps.write('./'));
    },

    build: function(pipe) {
      var csso = require('gulp-csso');

      return this.pipes.dev(pipe).pipe(csso(true));
    }
  }
});
<commit_msg>CSSO in stylus build pipe fix.<commit_after>var gulp   = require('gulp');

var Task   = require('../Task');
var utils  = require('../utils');

module.exports = Task.extend({
  paths: {
    source: '',
    dest:   '',
  },

  watch: '',

  pipes: {
    dev: function(pipe, scmap) {
      var stylus         = require('gulp-stylus');
      var plumber        = require('gulp-plumber');
      var sourcemaps     = require('gulp-sourcemaps');
      var postcss        = require('gulp-postcss');

      var pAutoprefixer  = require('autoprefixer');
      var pMqpacker      = require('css-mqpacker');
      var pMatches       = require('postcss-selector-matches');
      var pQuantity      = require('postcss-quantity-queries');
      scmap = typeof scmap == 'undefined' ? true : false;

      // Useful stuff for docs generation: mdcss[https://github.com/jonathantneal/mdcss]

      return pipe
        .pipe(global.WATCHING ? plumber({
            errorHandler: utils.errorHandler,
          }) : utils.noop())
        .pipe(scmap ? sourcemaps.init() : utils.noop())
          .pipe(stylus({
              pretty: global.DEBUG,
              'include css': true,
              compress: global.DEBUG,
            }))
          .pipe(postcss([
              pAutoprefixer({
                  browsers: ['> 0.5%']
                }),
              pMqpacker,
              pMatches,
              pQuantity,
            ]))
        .pipe(scmap ? sourcemaps.write() : utils.noop());
    },

    build: function(pipe) {
      var csso = require('gulp-csso');

      return this.pipes.dev(pipe, false)
        .pipe(csso(true));
    }
  }
});
<|endoftext|>


<commit_before>var MAPS = require('./../../maps'),
    MAP64 = MAPS.arrs[64],
    MAPASCII = MAPS.arrs['ascii'];

var EMPTY_BYTE = '00000000',
    PAD_CHAR = '=';

/**
 * Returns the 6 bit representation of the ascii character value
 *
 * @param b10 {int}  Ascii character value
 * @return {string}
 */
function b10To6Bit(b10){
  var bits = '',
      pref_count;

  while(b10 != 0){
    bits = (b10 % 2) + bits;
    b10 = Math.floor(b10 / 2);
  }

  pref_count = ( (6-bits.length%6) == 6 ? 0 : (6-bits.length%6));

  for(var i=0; i<pref_count; i+=1)
    bits = '0' + bits;

  return bits;
}

/**
 * @param byte {string}
 * @return b10 {int}
 */
function byteToB10(byte){
  var b10 = 0,
      rev_byte_arr = byte.toString().split('').reverse();

  for(var i=0; i<rev_byte_arr.length; i+=1)
    b10 += rev_byte_arr[i] * Math.pow(2, i);

  return b10;
}

module.exports = {

  to: {

    'ascii': function (b64){
      var ascii = '',
          bit_pattern = '',
          empty_bit = '0',
          pad_len = (b64.match(/=/g) || []).length,
          remainder;

      // Remove padding characters
      b64 = b64.replace(/=/g, '');

      // Create bit_pattern from base64
      for(var b64_i=0; b64_i<b64.length; b64_i+=1)
        bit_pattern += b10To6Bit(MAP64.indexOf(b64[b64_i]));

      // Append empty bytes so we get byte blocks in the bit_pattern
      remainder = (8-bit_pattern.length%8) == 8 ? 0 : (8-bit_pattern.length%8);
      for(var p_i=0; p_i<remainder; p_i+=1)
        bit_pattern += empty_bit;

      // Remove empty bytes
      // p_l_i = pad_length_iterator
      for(var p_l_i=0; p_l_i<pad_len; p_l_i+=1)
        bit_pattern = bit_pattern.replace(/00000000$/, '');

      // Create ascii string
      for(var b_i=0; b_i<bit_pattern.length; b_i+=8)
        ascii += MAPASCII[byteToB10(bit_pattern.substr(b_i, 8)) - 32];

      return ascii;
    }

  }

};
<commit_msg>Comments and cleaning<commit_after>var MAPS = require('./../../maps'),
    MAP64 = MAPS.arrs[64],
    MAPASCII = MAPS.arrs['ascii'];

var EMPTY_BYTE = '00000000',
    PAD_CHAR = '=';

/**
 * Returns the 6 bit representation of the ascii character value
 *
 * @param b10 {int}  Ascii character value
 * @return {string}
 */
function b10To6Bit(b10){
  var bits = '',
      pref_count;

  while(b10 != 0){
    bits = (b10 % 2) + bits;
    b10 = Math.floor(b10 / 2);
  }

  pref_count = ( (6-bits.length%6) == 6 ? 0 : (6-bits.length%6));

  for(var i=0; i<pref_count; i+=1)
    bits = '0' + bits;

  return bits;
}

/**
 * @param byte {string}
 * @return b10 {int}
 */
function byteToB10(byte){
  var b10 = 0,
      rev_byte_arr = byte.toString().split('').reverse();

  for(var i=0; i<rev_byte_arr.length; i+=1)
    b10 += rev_byte_arr[i] * Math.pow(2, i);

  return b10;
}

module.exports = {

  to: {
    /**
     * @param b64    {string}
     * @return ascii {string}
     */
    'ascii': function (b64){
      var ascii = '',
          bit_pattern = '',
          empty_bit = '0',
          pad_len = (b64.match(/=/g) || []).length,
          remainder;

      // Remove padding characters
      b64 = b64.replace(/=/g, '');

      // Create bit_pattern from base64
      for(var b64_i=0; b64_i<b64.length; b64_i+=1)
        bit_pattern += b10To6Bit(MAP64.indexOf(b64[b64_i]));

      // Append empty bytes so we get byte blocks in the bit_pattern
      remainder = (8-bit_pattern.length%8) == 8 ? 0 : (8-bit_pattern.length%8);
      for(var p_i=0; p_i<remainder; p_i+=1)
        bit_pattern += empty_bit;

      // Remove empty bytes
      // p_l_i = pad_length_iterator
      for(var p_l_i=0; p_l_i<pad_len; p_l_i+=1)
        bit_pattern = bit_pattern.replace(/00000000$/, '');

      // Create ascii string
      for(var b_i=0; b_i<bit_pattern.length; b_i+=8)
        ascii += MAPASCII[byteToB10(bit_pattern.substr(b_i, 8)) - 32];

      return ascii;
    }

  }

};
<|endoftext|>


<commit_before>const crypto = require('crypto')
const os = require('os')
const path = require('path')
const cp = require('child_process')
const fs = require('fs')

const _ = require('lodash/fp')

const logger = require('console-log-level')({level: 'info'})

module.exports = {
  es,
  writeSupervisorConfig,
  firewall,
  randomPass,
  fetchAndInstall,
  logger,
  isInstalledSoftware,
  writeFile
}

const BINARIES = {
  BTC: {
    url: 'https://bitcoincore.org/bin/bitcoin-core-0.20.1/bitcoin-0.20.1-x86_64-linux-gnu.tar.gz',
    dir: 'bitcoin-0.20.1/bin'
  },
  ETH: {
    url: 'https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.6-576681f2.tar.gz',
    dir: 'geth-linux-amd64-1.10.6-576681f2'
  },
  ZEC: {
    url: 'https://z.cash/downloads/zcash-4.4.1-linux64-debian-stretch.tar.gz',
    dir: 'zcash-4.4.1/bin'
  },
  DASH: {
    url: 'https://github.com/dashpay/dash/releases/download/v0.17.0.3/dashcore-0.17.0.3-x86_64-linux-gnu.tar.gz',
    dir: 'dashcore-0.17.0/bin'
  },
  LTC: {
    url: 'https://download.litecoin.org/litecoin-0.18.1/linux/litecoin-0.18.1-x86_64-linux-gnu.tar.gz',
    dir: 'litecoin-0.18.1/bin'
  },
  BCH: {
    url: 'https://github.com/bitcoin-cash-node/bitcoin-cash-node/releases/download/v23.0.0/bitcoin-cash-node-23.0.0-x86_64-linux-gnu.tar.gz',
    dir: 'bitcoin-cash-node-23.0.0/bin',
    files: [['bitcoind', 'bitcoincashd'], ['bitcoin-cli', 'bitcoincash-cli']]
  }
}

function firewall (ports) {
  if (!ports || ports.length === 0) throw new Error('No ports supplied')
  const portsString = ports.join(',')
  es(`sudo ufw allow ${portsString}`)
}

function randomPass () {
  return crypto.randomBytes(32).toString('hex')
}

function es (cmd) {
  const env = {HOME: os.userInfo().homedir}
  const options = {encoding: 'utf8', env}
  const res = cp.execSync(cmd, options)
  logger.debug(res)
  return res.toString()
}

function writeSupervisorConfig (coinRec, cmd) {
  if (isInstalledSoftware(coinRec)) return

  const blockchain = coinRec.code

  const supervisorConfig = `[program:${blockchain}]
command=nice ${cmd}
autostart=true
autorestart=true
stderr_logfile=/var/log/supervisor/${blockchain}.err.log
stdout_logfile=/var/log/supervisor/${blockchain}.out.log
environment=HOME="/root"
`

  writeFile(`/etc/supervisor/conf.d/${coinRec.code}.conf`, supervisorConfig)
}

function isInstalledSoftware (coinRec) {
  return fs.existsSync(`/etc/supervisor/conf.d/${coinRec.code}.conf`)
}

function fetchAndInstall (coinRec) {
  if (isInstalledSoftware(coinRec)) return

  const binaries = BINARIES[coinRec.cryptoCode]
  if (!binaries) throw new Error(`No such coin: ${coinRec.code}`)

  const url = binaries.url
  const downloadFile = path.basename(url)
  const binDir = binaries.dir

  es(`wget -q ${url}`)
  es(`tar -xzf ${downloadFile}`)

  if (_.isEmpty(binaries.files)) {
    es(`sudo cp ${binDir}/* /usr/local/bin`)
    return
  }

  _.forEach(([source, target]) => {
    es(`sudo cp ${binDir}/${source} /usr/local/bin/${target}`)
  }, binaries.files)
}

function writeFile (path, content) {
  try {
    fs.writeFileSync(path, content)
  } catch (err) {
    if (err.code === 'EEXIST') {
      logger.info(`${path} exists, skipping.`)
      return
    }

    throw err
  }
}
<commit_msg>chore: update BCH, ETH nodes<commit_after>const crypto = require('crypto')
const os = require('os')
const path = require('path')
const cp = require('child_process')
const fs = require('fs')

const _ = require('lodash/fp')

const logger = require('console-log-level')({level: 'info'})

module.exports = {
  es,
  writeSupervisorConfig,
  firewall,
  randomPass,
  fetchAndInstall,
  logger,
  isInstalledSoftware,
  writeFile
}

const BINARIES = {
  BTC: {
    url: 'https://bitcoincore.org/bin/bitcoin-core-0.20.1/bitcoin-0.20.1-x86_64-linux-gnu.tar.gz',
    dir: 'bitcoin-0.20.1/bin'
  },
  ETH: {
    url: 'https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.10.7-12f0ff40.tar.gz',
    dir: 'geth-linux-amd64-1.10.7-12f0ff40'
  },
  ZEC: {
    url: 'https://z.cash/downloads/zcash-4.4.1-linux64-debian-stretch.tar.gz',
    dir: 'zcash-4.4.1/bin'
  },
  DASH: {
    url: 'https://github.com/dashpay/dash/releases/download/v0.17.0.3/dashcore-0.17.0.3-x86_64-linux-gnu.tar.gz',
    dir: 'dashcore-0.17.0/bin'
  },
  LTC: {
    url: 'https://download.litecoin.org/litecoin-0.18.1/linux/litecoin-0.18.1-x86_64-linux-gnu.tar.gz',
    dir: 'litecoin-0.18.1/bin'
  },
  BCH: {
    url: 'https://github.com/bitcoin-cash-node/bitcoin-cash-node/releases/download/v23.1.0/bitcoin-cash-node-23.1.0-x86_64-linux-gnu.tar.gz',
    dir: 'bitcoin-cash-node-23.1.0/bin',
    files: [['bitcoind', 'bitcoincashd'], ['bitcoin-cli', 'bitcoincash-cli']]
  }
}

function firewall (ports) {
  if (!ports || ports.length === 0) throw new Error('No ports supplied')
  const portsString = ports.join(',')
  es(`sudo ufw allow ${portsString}`)
}

function randomPass () {
  return crypto.randomBytes(32).toString('hex')
}

function es (cmd) {
  const env = {HOME: os.userInfo().homedir}
  const options = {encoding: 'utf8', env}
  const res = cp.execSync(cmd, options)
  logger.debug(res)
  return res.toString()
}

function writeSupervisorConfig (coinRec, cmd) {
  if (isInstalledSoftware(coinRec)) return

  const blockchain = coinRec.code

  const supervisorConfig = `[program:${blockchain}]
command=nice ${cmd}
autostart=true
autorestart=true
stderr_logfile=/var/log/supervisor/${blockchain}.err.log
stdout_logfile=/var/log/supervisor/${blockchain}.out.log
environment=HOME="/root"
`

  writeFile(`/etc/supervisor/conf.d/${coinRec.code}.conf`, supervisorConfig)
}

function isInstalledSoftware (coinRec) {
  return fs.existsSync(`/etc/supervisor/conf.d/${coinRec.code}.conf`)
}

function fetchAndInstall (coinRec) {
  if (isInstalledSoftware(coinRec)) return

  const binaries = BINARIES[coinRec.cryptoCode]
  if (!binaries) throw new Error(`No such coin: ${coinRec.code}`)

  const url = binaries.url
  const downloadFile = path.basename(url)
  const binDir = binaries.dir

  es(`wget -q ${url}`)
  es(`tar -xzf ${downloadFile}`)

  if (_.isEmpty(binaries.files)) {
    es(`sudo cp ${binDir}/* /usr/local/bin`)
    return
  }

  _.forEach(([source, target]) => {
    es(`sudo cp ${binDir}/${source} /usr/local/bin/${target}`)
  }, binaries.files)
}

function writeFile (path, content) {
  try {
    fs.writeFileSync(path, content)
  } catch (err) {
    if (err.code === 'EEXIST') {
      logger.info(`${path} exists, skipping.`)
      return
    }

    throw err
  }
}
<|endoftext|>


<commit_before>'use strict';

var $ = require('preconditions').singleton();
var _ = require('lodash');
var async = require('async');
var log = require('npmlog');
log.debug = log.verbose;

var BlockchainExplorer = require('./blockchainexplorer');
var Storage = require('./storage');
var MessageBroker = require('./messagebroker');
var Lock = require('./lock');

var Notification = require('./model/notification');

var WalletService = require('./server');

function BlockchainMonitor() {};

BlockchainMonitor.prototype.start = function(opts, cb) {
  opts = opts || {};

  var self = this;

  async.parallel([

    function(done) {
      self.explorers = _.map(['livenet', 'testnet'], function(network) {
        var explorer;
        if (opts.blockchainExplorers) {
          explorer = opts.blockchainExplorers[network];
        } else {
          var config = {}
          if (opts.blockchainExplorerOpts && opts.blockchainExplorerOpts[network]) {
            config = opts.blockchainExplorerOpts[network];
          }
          var explorer = new BlockchainExplorer({
            provider: config.provider,
            network: network,
            url: config.url,
            userAgent: WalletService.getServiceVersion(),
          });
        }
        $.checkState(explorer);
        self._initExplorer(explorer);
        return explorer;
      });
      done();
    },
    function(done) {
      if (opts.storage) {
        self.storage = opts.storage;
        done();
      } else {
        self.storage = new Storage();
        self.storage.connect(opts.storageOpts, done);
      }
    },
    function(done) {
      self.messageBroker = opts.messageBroker || new MessageBroker(opts.messageBrokerOpts);
      self.messageBroker.onMessage(_.bind(self._handleIncommingTx, self));
      done();
    },
    function(done) {
      self.lock = opts.lock || new Lock(opts.lockOpts);
      done();
    },
  ], function(err) {
    if (err) {
      log.error(err);
    }
    return cb(err);
  });
};

BlockchainMonitor.prototype._initExplorer = function(explorer) {
  var self = this;

  var socket = explorer.initSocket();

  socket.on('connect', function() {
    log.info('Connected to ' + explorer.getConnectionInfo());
    socket.emit('subscribe', 'inv');
  });
  socket.on('connect_error', function() {
    log.error('Error connecting to ' + explorer.getConnectionInfo());
  });
  socket.on('tx', _.bind(self._handleIncommingTx, self));
  socket.on('block', _.bind(self._handleNewBlock, self, explorer.network));
};

BlockchainMonitor.prototype._handleTxId = function(data, processIt) {
  var self = this;
  if (!data || !data.txid) return;

  self.storage.fetchTxByHash(data.txid, function(err, txp) {
    if (err) {
      log.error('Could not fetch tx from the db');
      return;
    }
    if (!txp || txp.status != 'accepted') return;

    var walletId = txp.walletId;

    if (!processIt) {
      log.info('Detected broadcast ' + data.txid + ' of an accepted txp [' + txp.id + '] for wallet ' + walletId + ' [' + txp.amount + 'sat ]');
      return setTimeout(self._handleTxId.bind(self, data, true), 20 * 1000);
    }

    log.info('Processing accepted txp [' + txp.id + '] for wallet ' + walletId + ' [' + txp.amount + 'sat ]');

    txp.setBroadcasted();
    self.storage.storeTx(self.walletId, txp, function(err) {
      if (err)
        log.error('Could not save TX');

      var args = {
        txProposalId: txp.id,
        txid: data.txid,
        amount: txp.getTotalAmount(),
      };

      var notification = Notification.create({
        type: 'NewOutgoingTxByThirdParty',
        data: args,
        walletId: walletId,
      });
      self._storeAndBroadcastNotification(notification);
    });
  });
};

BlockchainMonitor.prototype._handleTxOuts = function(data) {
  var self = this;

  if (!data || !data.vout) return;

  var outs = _.compact(_.map(data.vout, function(v) {
    var addr = _.keys(v)[0];

    return {
      address: addr,
      amount: +v[addr]
    };
  }));
  if (_.isEmpty(outs)) return;

  async.each(outs, function(out, next) {
    self.storage.fetchAddress(out.address, function(err, address) {
      if (err) {
        log.error('Could not fetch addresses from the db');
        return next(err);
      }
      if (!address || address.isChange) return next();

      var walletId = address.walletId;
      log.info('Incoming tx for wallet ' + walletId + ' [' + out.amount + 'sat -> ' + out.address + ']');

      var notification = Notification.create({
        type: 'NewIncomingTx',
        data: {
          txid: data.txid,
          address: out.address,
          amount: out.amount,
        },
        walletId: walletId,
      });
      self._updateActiveAddresses(address, function() {
        self._storeAndBroadcastNotification(notification, next);
      });
    });
  }, function(err) {
    return;
  });
};

BlockchainMonitor.prototype._updateActiveAddresses = function(address, cb) {
  var self = this;

  self.storage.storeActiveAddresses(address.walletId, address.address, function(err) {
    if (err) {
      log.warn('Could not update wallet cache', err);
    }
    return cb(err);
  });
};

BlockchainMonitor.prototype._handleIncommingTx = function(data) {
  this._handleTxId(data);
  this._handleTxOuts(data);
};

BlockchainMonitor.prototype._handleNewBlock = function(network, hash) {
  var self = this;

  log.info('New ' + network + ' block: ', hash);
  var notification = Notification.create({
    type: 'NewBlock',
    walletId: network, // use network name as wallet id for global notifications
    data: {
      hash: hash,
    },
  });
  self._storeAndBroadcastNotification(notification, function(err) {
    return;
  });
};

BlockchainMonitor.prototype._storeAndBroadcastNotification = function(notification, cb) {
  var self = this;

  self.storage.storeNotification(notification.walletId, notification, function() {
    self.messageBroker.send(notification)
    if (cb) return cb();
  });
};

module.exports = BlockchainMonitor;
<commit_msg>remove subscription to message broker<commit_after>'use strict';

var $ = require('preconditions').singleton();
var _ = require('lodash');
var async = require('async');
var log = require('npmlog');
log.debug = log.verbose;

var BlockchainExplorer = require('./blockchainexplorer');
var Storage = require('./storage');
var MessageBroker = require('./messagebroker');
var Lock = require('./lock');

var Notification = require('./model/notification');

var WalletService = require('./server');

function BlockchainMonitor() {};

BlockchainMonitor.prototype.start = function(opts, cb) {
  opts = opts || {};

  var self = this;

  async.parallel([

    function(done) {
      self.explorers = _.map(['livenet', 'testnet'], function(network) {
        var explorer;
        if (opts.blockchainExplorers) {
          explorer = opts.blockchainExplorers[network];
        } else {
          var config = {}
          if (opts.blockchainExplorerOpts && opts.blockchainExplorerOpts[network]) {
            config = opts.blockchainExplorerOpts[network];
          }
          var explorer = new BlockchainExplorer({
            provider: config.provider,
            network: network,
            url: config.url,
            userAgent: WalletService.getServiceVersion(),
          });
        }
        $.checkState(explorer);
        self._initExplorer(explorer);
        return explorer;
      });
      done();
    },
    function(done) {
      if (opts.storage) {
        self.storage = opts.storage;
        done();
      } else {
        self.storage = new Storage();
        self.storage.connect(opts.storageOpts, done);
      }
    },
    function(done) {
      self.messageBroker = opts.messageBroker || new MessageBroker(opts.messageBrokerOpts);
      done();
    },
    function(done) {
      self.lock = opts.lock || new Lock(opts.lockOpts);
      done();
    },
  ], function(err) {
    if (err) {
      log.error(err);
    }
    return cb(err);
  });
};

BlockchainMonitor.prototype._initExplorer = function(explorer) {
  var self = this;

  var socket = explorer.initSocket();

  socket.on('connect', function() {
    log.info('Connected to ' + explorer.getConnectionInfo());
    socket.emit('subscribe', 'inv');
  });
  socket.on('connect_error', function() {
    log.error('Error connecting to ' + explorer.getConnectionInfo());
  });
  socket.on('tx', _.bind(self._handleIncommingTx, self));
  socket.on('block', _.bind(self._handleNewBlock, self, explorer.network));
};

BlockchainMonitor.prototype._handleTxId = function(data, processIt) {
  var self = this;
  if (!data || !data.txid) return;

  self.storage.fetchTxByHash(data.txid, function(err, txp) {
    if (err) {
      log.error('Could not fetch tx from the db');
      return;
    }
    if (!txp || txp.status != 'accepted') return;

    var walletId = txp.walletId;

    if (!processIt) {
      log.info('Detected broadcast ' + data.txid + ' of an accepted txp [' + txp.id + '] for wallet ' + walletId + ' [' + txp.amount + 'sat ]');
      return setTimeout(self._handleTxId.bind(self, data, true), 20 * 1000);
    }

    log.info('Processing accepted txp [' + txp.id + '] for wallet ' + walletId + ' [' + txp.amount + 'sat ]');

    txp.setBroadcasted();
    self.storage.storeTx(self.walletId, txp, function(err) {
      if (err)
        log.error('Could not save TX');

      var args = {
        txProposalId: txp.id,
        txid: data.txid,
        amount: txp.getTotalAmount(),
      };

      var notification = Notification.create({
        type: 'NewOutgoingTxByThirdParty',
        data: args,
        walletId: walletId,
      });
      self._storeAndBroadcastNotification(notification);
    });
  });
};

BlockchainMonitor.prototype._handleTxOuts = function(data) {
  var self = this;

  if (!data || !data.vout) return;

  var outs = _.compact(_.map(data.vout, function(v) {
    var addr = _.keys(v)[0];

    return {
      address: addr,
      amount: +v[addr]
    };
  }));
  if (_.isEmpty(outs)) return;

  async.each(outs, function(out, next) {
    self.storage.fetchAddress(out.address, function(err, address) {
      if (err) {
        log.error('Could not fetch addresses from the db');
        return next(err);
      }
      if (!address || address.isChange) return next();

      var walletId = address.walletId;
      log.info('Incoming tx for wallet ' + walletId + ' [' + out.amount + 'sat -> ' + out.address + ']');

      var notification = Notification.create({
        type: 'NewIncomingTx',
        data: {
          txid: data.txid,
          address: out.address,
          amount: out.amount,
        },
        walletId: walletId,
      });
      self._updateActiveAddresses(address, function() {
        self._storeAndBroadcastNotification(notification, next);
      });
    });
  }, function(err) {
    return;
  });
};

BlockchainMonitor.prototype._updateActiveAddresses = function(address, cb) {
  var self = this;

  self.storage.storeActiveAddresses(address.walletId, address.address, function(err) {
    if (err) {
      log.warn('Could not update wallet cache', err);
    }
    return cb(err);
  });
};

BlockchainMonitor.prototype._handleIncommingTx = function(data) {
  this._handleTxId(data);
  this._handleTxOuts(data);
};

BlockchainMonitor.prototype._handleNewBlock = function(network, hash) {
  var self = this;

  log.info('New ' + network + ' block: ', hash);
  var notification = Notification.create({
    type: 'NewBlock',
    walletId: network, // use network name as wallet id for global notifications
    data: {
      hash: hash,
    },
  });
  self._storeAndBroadcastNotification(notification, function(err) {
    return;
  });
};

BlockchainMonitor.prototype._storeAndBroadcastNotification = function(notification, cb) {
  var self = this;

  self.storage.storeNotification(notification.walletId, notification, function() {
    self.messageBroker.send(notification)
    if (cb) return cb();
  });
};

module.exports = BlockchainMonitor;
<|endoftext|>


<commit_before>'use strict';

const resolve = require('resolve');

/**
 * Create a drop in replacement for the built in require function.
 * Modules will be looked for as if the current directory is the
 * directory specified by `rootpath`.
 *
 * @param String rootpath Path to resolve modules relative to.
 * @returns function A function with the same interface as the built in require function.
 *
 * @example
 *
 * let cachelessRequire = require('./lib/cacheless-require')(__dirname);
 *
 * let _ = cachelessRequire('lodash');
 */
module.exports = function createCachelessRequire(rootpath) {

  let resolveRelative = filePath => resolve.sync(filePath, {
    basedir: rootpath,
  });

  function cachelessRequire(filePath = '') {
    let modulePath = resolveRelative(filePath);

    delete require.cache[modulePath];
    return require(modulePath);
  }

  cachelessRequire.resolve = resolveRelative;

  return cachelessRequire;
}<commit_msg>Indirectly required modules are now no longer cached<commit_after>'use strict';

const resolve = require('resolve');

/**
 * Create a drop in replacement for the built in require function.
 * Modules will be looked for as if the current directory is the
 * directory specified by `rootpath`.
 *
 * @param String rootpath Path to resolve modules relative to.
 * @returns function A function with the same interface as the built in require function.
 *
 * @example
 *
 * let cachelessRequire = require('./lib/cacheless-require')(__dirname);
 *
 * let _ = cachelessRequire('lodash');
 */
module.exports = function createCachelessRequire(rootpath) {

  let resolveRelative = filePath => resolve.sync(filePath, {
    basedir: rootpath,
  });

  function cachelessRequire(filePath = '') {

    for (const modulePath : Object.keys(require.cache)) {
      delete require.cache[modulePath];
    }

    return require(resolveRelative(filePath));
  }

  cachelessRequire.resolve = resolveRelative;

  return cachelessRequire;
}<|endoftext|>


<commit_before>(function(carto) {
var tree = require('./tree');
var _ = global._ || require('underscore');

function CartoCSS(style, options) {
  this.options = options || {};
  this.imageURLs = [];
  if(style) {
    this.setStyle(style);
  }
}

CartoCSS.Layer = function(shader, options) {
  this.options = options;
  this.shader = shader;
};

CartoCSS.Layer.prototype = {

  fullName: function() {
    return this.shader.attachment;
  },

  name: function() {
    return this.fullName().split('::')[0];
  },

  // frames this layer need to be rendered
  frames: function() {
    return this.shader.frames;
  },

  attachment: function() {
    return this.fullName().split('::')[1];
  },

  eval: function(prop) {
    var p = this.shader[prop];
    if (!p || !p.style) return;
    return p.style({}, { zoom: 0, 'frame-offset': 0 });
  },

  /*
   * `props`: feature properties
   * `context`: rendering properties, i.e zoom
   */
  getStyle: function(props, context) {
    var style = {};
    for(var i in this.shader) {
      if(i !== 'attachment' && i !== 'zoom' && i !== 'frames' && i !== 'symbolizers') {
        style[i] = this.shader[i].style(props, context);
      }
    }
    return style;
  },

  /**
   * return the symbolizers that need to be rendered with
   * this style. The order is the rendering order.
   * @returns a list with 3 possible values 'line', 'marker', 'polygon'
   */
  getSymbolizers: function() {
    return this.shader.symbolizers;
  },

  /**
   * returns if the style varies with some feature property.
   * Useful to optimize rendering
   */
  isVariable: function() {
    for(var i in this.shader) {
      if(i !== 'attachment' && i !== 'zoom' && i !== 'frames' && i !== 'symbolizers') {
        if (!this.shader[i].constant) {
          return true;
        }
      }
    }
    return false;
  },

  getShader: function() {
    return this.shader;
  },

  /**
   * returns true if a feature needs to be rendered
   */
  filter: function(featureType, props, context) {
    for(var i in this.shader) {
     var s = this.shader[i](props, context);
     if(s) {
       return true;
     }
    }
    return false;
  },

  //
  // given a geoemtry type returns the transformed one acording the CartoCSS
  // For points there are two kind of types: point and sprite, the first one
  // is a circle, second one is an image sprite
  //
  // the other geometry types are the same than geojson (polygon, linestring...)
  //
  transformGeometry: function(type) {
    return type;
  },

  transformGeometries: function(geojson) {
    return geojson;
  }

};

CartoCSS.prototype = {

  setStyle: function(style) {
    var layers = this.parse(style);
    if(!layers) {
      throw new Error(this.parse_env.errors);
    }
    this.layers = layers.map(function(shader) {
        return new CartoCSS.Layer(shader);
    });
  },

  getLayers: function() {
    return this.layers;
  },

  getDefault: function() {
    return this.findLayer({ attachment: '__default__' });
  },

  findLayer: function(where) {
    return _.find(this.layers, function(value) {
      for (var key in where) {
        var v = value[key];
        if (typeof(v) === 'function') {
          v = v.call(value);
        }
        if (where[key] !== v) return false;
      }
      return true;
    });
  },

  _createFn: function(ops) {
    var body = ops.join('\n');
    if(this.options.debug) console.log(body);
    return Function("data","ctx", "var _value = null; " +  body + "; return _value; ");
  },

  _compile: function(shader) {
    if(typeof shader === 'string') {
        shader = eval("(function() { return " + shader +"; })()");
    }
    this.shader_src = shader;
    for(var attr in shader) {
        var c = mapper[attr];
        if(c) {
            this.compiled[c] = eval("(function() { return shader[attr]; })();");
        }
    }
  },
  getImageURLs: function(){
    return this.imageURLs;
  },

  parse: function(cartocss) {
    var parse_env = {
      frames: [],
      errors: [],
      error: function(obj) {
        this.errors.push(obj);
      }
    };
    this.parse_env = parse_env;

    var ruleset = null;
    try {
      ruleset = (new carto.Parser(parse_env)).parse(cartocss);
    } catch(e) {
      // add the style.mss string to match the response from the server
      parse_env.errors.push(e.message);
      return;
    }
    if(ruleset) {

      function defKey(def) {
        return def.elements[0] + "::" + def.attachment;
      }
      var defs = ruleset.toList(parse_env);
      defs.reverse();
      // group by elements[0].value::attachment
      var layers = {};
      for(var i = 0; i < defs.length; ++i) {
        var def = defs[i];
        var key = defKey(def);
        var layer = layers[key] = (layers[key] || {
          symbolizers: []
        });

        for(var u = 0; u<def.rules.length; u++){
          var rule = def.rules[u];
            if(rule.name === "marker-file" || rule.name === "point-file"){
                var value = rule.value.value[0].value[0].value.value;
                this.imageURLs.push(value);
            }

            rule.toXML(parse_env, {});
        }

        if (this.options.strict && parse_env.errors.message) {
          throw new Error(parse_env.errors.message);
        }

        layer.frames = [];
        layer.zoom = tree.Zoom.all;
        var props = def.toJS(parse_env);
        if (this.options.debug) console.log("props", props);
        for(var v in props) {
          var lyr = layer[v] = layer[v] || {
            constant: false,
            symbolizer: null,
            js: [],
            index: 0
          };
          // build javascript statements
          lyr.js.push(props[v].map(function(a) { return a.js; }).join('\n'));
          // get symbolizer for prop
          lyr.symbolizer = _.first(props[v].map(function(a) { return a.symbolizer; }));
          // serach the max index to know rendering order
          lyr.index = _.max(props[v].map(function(a) { return a.index; }).concat(lyr.index));
          lyr.constant = !_.any(props[v].map(function(a) { return !a.constant; }));
        }
      }

      var ordered_layers = [];
      if (this.options.debug) console.log(layers);

      var done = {};
      for(var i = 0; i < defs.length; ++i) {
        var def = defs[i];
        var k = defKey(def);
        var layer = layers[k];
        if(!done[k]) {
          if(this.options.debug) console.log("**", k);
          for(var prop in layer) {
            if (prop !== 'zoom' && prop !== 'frames' && prop !== 'symbolizers') {
              if(this.options.debug) console.log("*", prop);
              layer[prop].style = this._createFn(layer[prop].js);
              layer.symbolizers.push(layer[prop].symbolizer);
              layer.symbolizers = _.uniq(layer.symbolizers);
            }
          }
          layer.attachment = k;
          ordered_layers.push(layer);
          done[k] = true;
        }
        layer.zoom |= def.zoom;
        layer.frames.push(def.frame_offset);
      }

      // uniq the frames
      for(i = 0; i < ordered_layers.length; ++i) {
        ordered_layers[i].frames = _.uniq(ordered_layers[i].frames);
      }

      return ordered_layers;

    }
    return null;
  }
};

carto.RendererJS = function (options) {
    this.options = options || {};
    this.options.mapnik_version = this.options.mapnik_version || 'latest';
    this.reference = this.options.reference || require('./torque-reference').version.latest;
    this.options.strict = this.options.hasOwnProperty('strict') ? this.options.strict : false;
};

// Prepare a javascript object which contains the layers
carto.RendererJS.prototype.render = function render(cartocss, callback) {
    tree.Reference.setData(this.reference);
    return new CartoCSS(cartocss, this.options);
}

if(typeof(module) !== 'undefined') {
  module.exports = carto.RendererJS;
}

})(require('../carto'));
<commit_msg>Make the validation in the correct place<commit_after>(function(carto) {
var tree = require('./tree');
var _ = global._ || require('underscore');

function CartoCSS(style, options) {
  this.options = options || {};
  this.imageURLs = [];
  if(style) {
    this.setStyle(style);
  }
}

CartoCSS.Layer = function(shader, options) {
  this.options = options;
  this.shader = shader;
};

CartoCSS.Layer.prototype = {

  fullName: function() {
    return this.shader.attachment;
  },

  name: function() {
    return this.fullName().split('::')[0];
  },

  // frames this layer need to be rendered
  frames: function() {
    return this.shader.frames;
  },

  attachment: function() {
    return this.fullName().split('::')[1];
  },

  eval: function(prop) {
    var p = this.shader[prop];
    if (!p || !p.style) return;
    return p.style({}, { zoom: 0, 'frame-offset': 0 });
  },

  /*
   * `props`: feature properties
   * `context`: rendering properties, i.e zoom
   */
  getStyle: function(props, context) {
    var style = {};
    for(var i in this.shader) {
      if(i !== 'attachment' && i !== 'zoom' && i !== 'frames' && i !== 'symbolizers') {
        style[i] = this.shader[i].style(props, context);
      }
    }
    return style;
  },

  /**
   * return the symbolizers that need to be rendered with
   * this style. The order is the rendering order.
   * @returns a list with 3 possible values 'line', 'marker', 'polygon'
   */
  getSymbolizers: function() {
    return this.shader.symbolizers;
  },

  /**
   * returns if the style varies with some feature property.
   * Useful to optimize rendering
   */
  isVariable: function() {
    for(var i in this.shader) {
      if(i !== 'attachment' && i !== 'zoom' && i !== 'frames' && i !== 'symbolizers') {
        if (!this.shader[i].constant) {
          return true;
        }
      }
    }
    return false;
  },

  getShader: function() {
    return this.shader;
  },

  /**
   * returns true if a feature needs to be rendered
   */
  filter: function(featureType, props, context) {
    for(var i in this.shader) {
     var s = this.shader[i](props, context);
     if(s) {
       return true;
     }
    }
    return false;
  },

  //
  // given a geoemtry type returns the transformed one acording the CartoCSS
  // For points there are two kind of types: point and sprite, the first one
  // is a circle, second one is an image sprite
  //
  // the other geometry types are the same than geojson (polygon, linestring...)
  //
  transformGeometry: function(type) {
    return type;
  },

  transformGeometries: function(geojson) {
    return geojson;
  }

};

CartoCSS.prototype = {

  setStyle: function(style) {
    var layers = this.parse(style);
    if(!layers) {
      throw new Error(this.parse_env.errors);
    }
    this.layers = layers.map(function(shader) {
        return new CartoCSS.Layer(shader);
    });
  },

  getLayers: function() {
    return this.layers;
  },

  getDefault: function() {
    return this.findLayer({ attachment: '__default__' });
  },

  findLayer: function(where) {
    return _.find(this.layers, function(value) {
      for (var key in where) {
        var v = value[key];
        if (typeof(v) === 'function') {
          v = v.call(value);
        }
        if (where[key] !== v) return false;
      }
      return true;
    });
  },

  _createFn: function(ops) {
    var body = ops.join('\n');
    if(this.options.debug) console.log(body);
    return Function("data","ctx", "var _value = null; " +  body + "; return _value; ");
  },

  _compile: function(shader) {
    if(typeof shader === 'string') {
        shader = eval("(function() { return " + shader +"; })()");
    }
    this.shader_src = shader;
    for(var attr in shader) {
        var c = mapper[attr];
        if(c) {
            this.compiled[c] = eval("(function() { return shader[attr]; })();");
        }
    }
  },
  getImageURLs: function(){
    return this.imageURLs;
  },

  parse: function(cartocss) {
    var parse_env = {
      frames: [],
      errors: [],
      error: function(obj) {
        this.errors.push(obj);
      }
    };
    this.parse_env = parse_env;

    var ruleset = null;
    try {
      ruleset = (new carto.Parser(parse_env)).parse(cartocss);
    } catch(e) {
      // add the style.mss string to match the response from the server
      parse_env.errors.push(e.message);
      return;
    }
    if(ruleset) {

      function defKey(def) {
        return def.elements[0] + "::" + def.attachment;
      }
      var defs = ruleset.toList(parse_env);
      defs.reverse();
      // group by elements[0].value::attachment
      var layers = {};
      for(var i = 0; i < defs.length; ++i) {
        var def = defs[i];
        var key = defKey(def);
        var layer = layers[key] = (layers[key] || {
          symbolizers: []
        });

        for(var u = 0; u<def.rules.length; u++){
          var rule = def.rules[u];
            if(rule.name === "marker-file" || rule.name === "point-file"){
                var value = rule.value.value[0].value[0].value.value;
                this.imageURLs.push(value);
            }
        }

        layer.frames = [];
        layer.zoom = tree.Zoom.all;
        var props = def.toJS(parse_env);
        if (this.options.debug) console.log("props", props);
        for(var v in props) {
          var lyr = layer[v] = layer[v] || {
            constant: false,
            symbolizer: null,
            js: [],
            index: 0
          };
          // build javascript statements
          lyr.js.push(props[v].map(function(a) { return a.js; }).join('\n'));
          // get symbolizer for prop
          lyr.symbolizer = _.first(props[v].map(function(a) { return a.symbolizer; }));
          // serach the max index to know rendering order
          lyr.index = _.max(props[v].map(function(a) { return a.index; }).concat(lyr.index));
          lyr.constant = !_.any(props[v].map(function(a) { return !a.constant; }));
        }
      }

      var ordered_layers = [];
      if (this.options.debug) console.log(layers);

      var done = {};
      for(var i = 0; i < defs.length; ++i) {
        var def = defs[i];

        def.toXML(parse_env, {});

        if (this.options.strict && parse_env.errors.message) {
          throw new Error(parse_env.errors.message);
        }

        var k = defKey(def);
        var layer = layers[k];
        if(!done[k]) {
          if(this.options.debug) console.log("**", k);
          for(var prop in layer) {
            if (prop !== 'zoom' && prop !== 'frames' && prop !== 'symbolizers') {
              if(this.options.debug) console.log("*", prop);
              layer[prop].style = this._createFn(layer[prop].js);
              layer.symbolizers.push(layer[prop].symbolizer);
              layer.symbolizers = _.uniq(layer.symbolizers);
            }
          }
          layer.attachment = k;
          ordered_layers.push(layer);
          done[k] = true;
        }
        layer.zoom |= def.zoom;
        layer.frames.push(def.frame_offset);
      }

      // uniq the frames
      for(i = 0; i < ordered_layers.length; ++i) {
        ordered_layers[i].frames = _.uniq(ordered_layers[i].frames);
      }

      return ordered_layers;

    }
    return null;
  }
};

carto.RendererJS = function (options) {
    this.options = options || {};
    this.options.mapnik_version = this.options.mapnik_version || 'latest';
    this.reference = this.options.reference || require('./torque-reference').version.latest;
    this.options.strict = this.options.hasOwnProperty('strict') ? this.options.strict : false;
};

// Prepare a javascript object which contains the layers
carto.RendererJS.prototype.render = function render(cartocss, callback) {
    tree.Reference.setData(this.reference);
    return new CartoCSS(cartocss, this.options);
}

if(typeof(module) !== 'undefined') {
  module.exports = carto.RendererJS;
}

})(require('../carto'));
<|endoftext|>


<commit_before>const models = require('../../../models');
const _ = require('lodash');
const log = require('../../../utils/logger');
const importDomain = require('./utils').importDomain;
const importCommunity = require('./utils').importCommunity;
const importGroup = require('./utils').importGroup;
const importPost = require('./utils').importPost;
const importPoint = require('./utils').importPoint;
const request = require('request');

const updateDomain = (domainId, done) => {
  log.info('updateDomain');

  models.Domain.unscoped().findOne({
    where: {
      id: domainId
    },
    attributes: ['id','name','description','default_locale','created_at', 'updated_at'],
    order: [
      ['id', 'asc' ]
    ]
  }).then((domain) => {
    if (domain) {
      importDomain(domain, done);
    } else {
      done("Can't find domain for similarities import");
    }
  }).catch((error) => {
    done(error);
  });
};

const updateCommunity = (communityId, done) => {
  log.info('updateCommunity');

  models.Community.unscoped().findOne({
    where: {
      id: communityId
    },
    include: [
      {
        model: models.Domain,
        attributes: ['id','default_locale'],
        required: true
      }
    ],
    attributes: ['id','name','description','default_locale','created_at', 'updated_at'],
    order: [
      ['id', 'asc' ]
    ]
  }).then((community) => {
    if (community) {
      importCommunity(community, done);
    } else {
      done("Can't find community for similarities import");
    }
  }).catch(function (error) {
    done(error);
  });
};

const updateGroup = (groupId, done) => {
  log.info('updateGroup');

  models.Group.unscoped().findOne({
    where: {
      id: groupId
    },
    include: [
      {
        model: models.Community,
        attributes: ['id','access','status','default_locale','created_at', 'updated_at'],
        required: true,
        include: [
          {
            model: models.Domain,
            attributes: ['id','default_locale'],
            required: true
          }
        ]
      }
    ],
    attributes: ['id','name','created_at', 'objectives', 'updated_at'],
    order: [
      ['id', 'asc' ]
    ]
  }).then((group) => {
    if (group) {
      importGroup(group, done);
    } else {
      done("Can't find group for similarities import");
    }
  }).catch(function (error) {
    done(error);
  });
};

const updatePost = (postId, done) => {
  log.info('updatePost');

  models.Post.unscoped().findOne(
    {
      where: {
        id: postId
      },
      include: [
        {
          model: models.Point,
          required: false,
          attributes: ['id','content'],
        },
        {
          model: models.Group,
          required: true,
          attributes: ['id','access','status','configuration'],
          include: [
            {
              attributes: ['id','formats'],
              model: models.Image, as: 'GroupLogoImages',
              required: false
            },
            {
              model: models.Community,
              attributes: ['id','access','status','default_locale'],
              required: true,
              include: [
                {
                  attributes: ['id','formats'],
                  model: models.Image, as: 'CommunityLogoImages',
                  required: false
                },
                {
                  model: models.Domain,
                  attributes: ['id','default_locale'],
                  required: true
                }
              ]
            }
          ]
        },
        {
          model: models.Image,
          required: false,
          as: 'PostHeaderImages',
          attributes: ['id','formats']
        },
        {
          model: models.Video,
          required: false,
          attributes: ['id','formats','updated_at','viewable','public_meta'],
          as: 'PostVideos',
          include: [
            {
              model: models.Image,
              as: 'VideoImages',
              attributes:["formats",'updated_at'],
              required: false
            },
          ]
        },
        {
          model: models.Audio,
          required: false,
          attributes: ['id','formats','updated_at','listenable'],
          as: 'PostAudios',
        }
      ],
      order: [
        ['id', 'desc' ],
        [ { model: models.Image, as: 'PostHeaderImages' } ,'updated_at', 'asc' ],
        [ { model: models.Group }, { model: models.Image, as: 'GroupLogoImages' } , 'created_at', 'desc' ],
        [ { model: models.Group }, { model: models.Community }, { model: models.Image, as: 'CommunityLogoImages' } , 'created_at', 'desc' ]
      ],
      attributes: ['id','name','description','group_id','category_id','status','deleted','language','created_at', 'updated_at',
        'user_id','official_status','public_data','cover_media_type',
        'counter_endorsements_up','counter_endorsements_down','counter_points','counter_flags']
    }).then((post) => {
      if (post) {
        importPost(post, done);
      } else {
        done("Can't find post for similarities import");
      }
  }).catch(function (error) {
    done(error);
  });
};

const updatePoint = (pointId, done) => {
  log.info('updatePoint');

  models.Point.unscoped().findOne({
    where: {
      id: pointId
    },
    attributes: ['id', 'name', 'content', 'user_id', 'post_id', 'value', 'status', 'counter_quality_up', 'counter_quality_down', 'language','created_at', 'updated_at'],
    order: [
      [models.PointRevision, 'created_at', 'asc'],
      [{model: models.Video, as: "PointVideos"}, 'updated_at', 'desc'],
      [{model: models.Audio, as: "PointAudios"}, 'updated_at', 'desc'],
    ],
    include: [
      {
        model: models.PointRevision,
        attributes: ['content', 'value', 'created_at'],
        required: false
      },
      {
        model: models.Video,
        required: false,
        attributes: ['id', 'formats'],
        as: 'PointVideos'
      },
      {
        model: models.Audio,
        required: false,
        attributes: ['id', 'formats'],
        as: 'PointAudios'
      },
      {
        model: models.Post,
        attributes: ['id', 'group_id','created_at','category_id','official_status','status'],
        required: true,
        include: [
          {
            model: models.Group,
            attributes: ['id','access','status','configuration'],
            required: true,
            include: [
              {
                model: models.Community,
                attributes: ['id','access','status','default_locale'],
                required: true,
                include: [
                  {
                    model: models.Domain,
                    attributes: ['id','default_locale'],
                    required: true
                  }
                ]
              }
            ]
          }
        ]
      }
    ]
  }).then(function (point) {
    if (point) {
      importPoint(point, done);
    } else {
      done("Can't find point for similarities import");
    }
  }).catch(function (error) {
    done(error);
  });
};

const updateCollection = (workPackage, done) => {
  if (process.env.AC_ANALYTICS_KEY &&
      process.env.AC_ANALYTICS_CLUSTER_ID &&
      process.env.AC_ANALYTICS_BASE_URL) {
    if (workPackage.domainId) {
      updateDomain(workPackage.domainId, done)
    } else if (workPackage.communityId) {
      updateCommunity(workPackage.communityId, done)
    } else if (workPackage.groupId) {
      updateGroup(workPackage.groupId, done)
    } else if (workPackage.postId) {
      updatePost(workPackage.postId, done)
    } else if (workPackage.pointId) {
      updatePoint(workPackage.pointId, done)
    } else {
      done("Couldn't find any collection to update similarities", { workPackage });
    }
  } else {
    log.warn("Can't find AC_ANALYTICS_KEY, AC_ANALYTICS_CLUSTER_ID & AC_ANALYTICS_BASE_URL for the similarities engine");
    done();
  }
};

const getFromAnalyticsApi = (req, featureType, collectionType, collectionId, done) => {
  const redisKey = "cachev5:getAnalytics:"+featureType+":"+collectionType+":"+collectionId;
  req.redisClient.get(redisKey, (error, content) => {
    if (!error && content) {
      done(null, JSON.parse(content));
    } else {
      if (error) {
        log.error('Could not get pages for group from redis', {
          err: error,
          context: 'redis',
          userId: req.user ? req.user.id : null
        });
      }

      const options = {
        url: process.env["AC_ANALYTICS_BASE_URL"]+featureType+"/"+collectionType+"/"+process.env.AC_ANALYTICS_CLUSTER_ID+"/"+collectionId,
        headers: {
          'X-API-KEY': process.env["AC_ANALYTICS_KEY"]
        }
      };

      request.get(options, (error, content) => {
        if (content && content.statusCode!=200) {
          error = content.statusCode;
        } else {
          req.redisClient.setex(redisKey, process.env.SIMILARITIES_CACHE_TTL ? parseInt(process.env.SIMILARITIES_CACHE_TTL) : 60*60, JSON.stringify(content));
        }
        done(error, content);
      });
    }
  });
};

const triggerSimilaritiesTraining = (req, collectionType, collectionId, done) => {
  const options = {
    url: process.env["AC_ANALYTICS_BASE_URL"]+"trigger_similarities_training"+"/"+collectionType+"/"+process.env.AC_ANALYTICS_CLUSTER_ID+"/"+collectionId,
    headers: {
      'X-API-KEY': process.env["AC_ANALYTICS_KEY"]
    },
    json: {}
  };

  request.put(options, (error, content) => {
    if (content && content.statusCode!=200) {
      error = content.statusCode;
    }
    done(error, content);
  });
};

const getParsedSimilaritiesContent = (content) => {
  let results;
  if (content && content.body) {
    try {
      results = JSON.parse(content.body);
    } catch (error) {
      log.error("Can't parse JSON from similarities");
    }
  }
  return results;
};

const sendBackAnalyticsResultsOrError = (req,res,error,results) => {
  if (error) {
    log.error("Analytics Error", { id: req.params.id, url: req.url, userId: req.user ? req.user.id : null, errorStatus:  500, error });
    res.send({ nodata: true });
  } else {
    res.send(results);
  }
};

const convertDocxSurveyToJson = (formData, done) => {
  const options = {
    url: process.env["AC_ANALYTICS_BASE_URL"]+"convert_doc_x_survey_to_json",
    headers: {
      'X-API-KEY': process.env["AC_ANALYTICS_KEY"]
    },
    formData: formData,
  };

  request.put(options, (error, content) => {
    if (content && content.statusCode!=200) {
      error = content.statusCode;
    }
    done(error, content);
  });
}

module.exports = {
  updateCollection,
  getFromAnalyticsApi,
  triggerSimilaritiesTraining,
  sendBackAnalyticsResultsOrError,
  getParsedSimilaritiesContent,
  convertDocxSurveyToJson
};<commit_msg>Reduce cache time for simularities & wordcloud data<commit_after>const models = require('../../../models');
const _ = require('lodash');
const log = require('../../../utils/logger');
const importDomain = require('./utils').importDomain;
const importCommunity = require('./utils').importCommunity;
const importGroup = require('./utils').importGroup;
const importPost = require('./utils').importPost;
const importPoint = require('./utils').importPoint;
const request = require('request');

const updateDomain = (domainId, done) => {
  log.info('updateDomain');

  models.Domain.unscoped().findOne({
    where: {
      id: domainId
    },
    attributes: ['id','name','description','default_locale','created_at', 'updated_at'],
    order: [
      ['id', 'asc' ]
    ]
  }).then((domain) => {
    if (domain) {
      importDomain(domain, done);
    } else {
      done("Can't find domain for similarities import");
    }
  }).catch((error) => {
    done(error);
  });
};

const updateCommunity = (communityId, done) => {
  log.info('updateCommunity');

  models.Community.unscoped().findOne({
    where: {
      id: communityId
    },
    include: [
      {
        model: models.Domain,
        attributes: ['id','default_locale'],
        required: true
      }
    ],
    attributes: ['id','name','description','default_locale','created_at', 'updated_at'],
    order: [
      ['id', 'asc' ]
    ]
  }).then((community) => {
    if (community) {
      importCommunity(community, done);
    } else {
      done("Can't find community for similarities import");
    }
  }).catch(function (error) {
    done(error);
  });
};

const updateGroup = (groupId, done) => {
  log.info('updateGroup');

  models.Group.unscoped().findOne({
    where: {
      id: groupId
    },
    include: [
      {
        model: models.Community,
        attributes: ['id','access','status','default_locale','created_at', 'updated_at'],
        required: true,
        include: [
          {
            model: models.Domain,
            attributes: ['id','default_locale'],
            required: true
          }
        ]
      }
    ],
    attributes: ['id','name','created_at', 'objectives', 'updated_at'],
    order: [
      ['id', 'asc' ]
    ]
  }).then((group) => {
    if (group) {
      importGroup(group, done);
    } else {
      done("Can't find group for similarities import");
    }
  }).catch(function (error) {
    done(error);
  });
};

const updatePost = (postId, done) => {
  log.info('updatePost');

  models.Post.unscoped().findOne(
    {
      where: {
        id: postId
      },
      include: [
        {
          model: models.Point,
          required: false,
          attributes: ['id','content'],
        },
        {
          model: models.Group,
          required: true,
          attributes: ['id','access','status','configuration'],
          include: [
            {
              attributes: ['id','formats'],
              model: models.Image, as: 'GroupLogoImages',
              required: false
            },
            {
              model: models.Community,
              attributes: ['id','access','status','default_locale'],
              required: true,
              include: [
                {
                  attributes: ['id','formats'],
                  model: models.Image, as: 'CommunityLogoImages',
                  required: false
                },
                {
                  model: models.Domain,
                  attributes: ['id','default_locale'],
                  required: true
                }
              ]
            }
          ]
        },
        {
          model: models.Image,
          required: false,
          as: 'PostHeaderImages',
          attributes: ['id','formats']
        },
        {
          model: models.Video,
          required: false,
          attributes: ['id','formats','updated_at','viewable','public_meta'],
          as: 'PostVideos',
          include: [
            {
              model: models.Image,
              as: 'VideoImages',
              attributes:["formats",'updated_at'],
              required: false
            },
          ]
        },
        {
          model: models.Audio,
          required: false,
          attributes: ['id','formats','updated_at','listenable'],
          as: 'PostAudios',
        }
      ],
      order: [
        ['id', 'desc' ],
        [ { model: models.Image, as: 'PostHeaderImages' } ,'updated_at', 'asc' ],
        [ { model: models.Group }, { model: models.Image, as: 'GroupLogoImages' } , 'created_at', 'desc' ],
        [ { model: models.Group }, { model: models.Community }, { model: models.Image, as: 'CommunityLogoImages' } , 'created_at', 'desc' ]
      ],
      attributes: ['id','name','description','group_id','category_id','status','deleted','language','created_at', 'updated_at',
        'user_id','official_status','public_data','cover_media_type',
        'counter_endorsements_up','counter_endorsements_down','counter_points','counter_flags']
    }).then((post) => {
      if (post) {
        importPost(post, done);
      } else {
        done("Can't find post for similarities import");
      }
  }).catch(function (error) {
    done(error);
  });
};

const updatePoint = (pointId, done) => {
  log.info('updatePoint');

  models.Point.unscoped().findOne({
    where: {
      id: pointId
    },
    attributes: ['id', 'name', 'content', 'user_id', 'post_id', 'value', 'status', 'counter_quality_up', 'counter_quality_down', 'language','created_at', 'updated_at'],
    order: [
      [models.PointRevision, 'created_at', 'asc'],
      [{model: models.Video, as: "PointVideos"}, 'updated_at', 'desc'],
      [{model: models.Audio, as: "PointAudios"}, 'updated_at', 'desc'],
    ],
    include: [
      {
        model: models.PointRevision,
        attributes: ['content', 'value', 'created_at'],
        required: false
      },
      {
        model: models.Video,
        required: false,
        attributes: ['id', 'formats'],
        as: 'PointVideos'
      },
      {
        model: models.Audio,
        required: false,
        attributes: ['id', 'formats'],
        as: 'PointAudios'
      },
      {
        model: models.Post,
        attributes: ['id', 'group_id','created_at','category_id','official_status','status'],
        required: true,
        include: [
          {
            model: models.Group,
            attributes: ['id','access','status','configuration'],
            required: true,
            include: [
              {
                model: models.Community,
                attributes: ['id','access','status','default_locale'],
                required: true,
                include: [
                  {
                    model: models.Domain,
                    attributes: ['id','default_locale'],
                    required: true
                  }
                ]
              }
            ]
          }
        ]
      }
    ]
  }).then(function (point) {
    if (point) {
      importPoint(point, done);
    } else {
      done("Can't find point for similarities import");
    }
  }).catch(function (error) {
    done(error);
  });
};

const updateCollection = (workPackage, done) => {
  if (process.env.AC_ANALYTICS_KEY &&
      process.env.AC_ANALYTICS_CLUSTER_ID &&
      process.env.AC_ANALYTICS_BASE_URL) {
    if (workPackage.domainId) {
      updateDomain(workPackage.domainId, done)
    } else if (workPackage.communityId) {
      updateCommunity(workPackage.communityId, done)
    } else if (workPackage.groupId) {
      updateGroup(workPackage.groupId, done)
    } else if (workPackage.postId) {
      updatePost(workPackage.postId, done)
    } else if (workPackage.pointId) {
      updatePoint(workPackage.pointId, done)
    } else {
      done("Couldn't find any collection to update similarities", { workPackage });
    }
  } else {
    log.warn("Can't find AC_ANALYTICS_KEY, AC_ANALYTICS_CLUSTER_ID & AC_ANALYTICS_BASE_URL for the similarities engine");
    done();
  }
};

const getFromAnalyticsApi = (req, featureType, collectionType, collectionId, done) => {
  const redisKey = "cachev5:getAnalytics:"+featureType+":"+collectionType+":"+collectionId;
  req.redisClient.get(redisKey, (error, content) => {
    if (!error && content) {
      done(null, JSON.parse(content));
    } else {
      if (error) {
        log.error('Could not get pages for group from redis', {
          err: error,
          context: 'redis',
          userId: req.user ? req.user.id : null
        });
      }

      const options = {
        url: process.env["AC_ANALYTICS_BASE_URL"]+featureType+"/"+collectionType+"/"+process.env.AC_ANALYTICS_CLUSTER_ID+"/"+collectionId,
        headers: {
          'X-API-KEY': process.env["AC_ANALYTICS_KEY"]
        }
      };

      request.get(options, (error, content) => {
        if (content && content.statusCode!=200) {
          error = content.statusCode;
        } else {
          req.redisClient.setex(redisKey, process.env.SIMILARITIES_CACHE_TTL ? parseInt(process.env.SIMILARITIES_CACHE_TTL) : 15*60, JSON.stringify(content));
        }
        done(error, content);
      });
    }
  });
};

const triggerSimilaritiesTraining = (req, collectionType, collectionId, done) => {
  const options = {
    url: process.env["AC_ANALYTICS_BASE_URL"]+"trigger_similarities_training"+"/"+collectionType+"/"+process.env.AC_ANALYTICS_CLUSTER_ID+"/"+collectionId,
    headers: {
      'X-API-KEY': process.env["AC_ANALYTICS_KEY"]
    },
    json: {}
  };

  request.put(options, (error, content) => {
    if (content && content.statusCode!=200) {
      error = content.statusCode;
    }
    done(error, content);
  });
};

const getParsedSimilaritiesContent = (content) => {
  let results;
  if (content && content.body) {
    try {
      results = JSON.parse(content.body);
    } catch (error) {
      log.error("Can't parse JSON from similarities");
    }
  }
  return results;
};

const sendBackAnalyticsResultsOrError = (req,res,error,results) => {
  if (error) {
    log.error("Analytics Error", { id: req.params.id, url: req.url, userId: req.user ? req.user.id : null, errorStatus:  500, error });
    res.send({ nodata: true });
  } else {
    res.send(results);
  }
};

const convertDocxSurveyToJson = (formData, done) => {
  const options = {
    url: process.env["AC_ANALYTICS_BASE_URL"]+"convert_doc_x_survey_to_json",
    headers: {
      'X-API-KEY': process.env["AC_ANALYTICS_KEY"]
    },
    formData: formData,
  };

  request.put(options, (error, content) => {
    if (content && content.statusCode!=200) {
      error = content.statusCode;
    }
    done(error, content);
  });
}

module.exports = {
  updateCollection,
  getFromAnalyticsApi,
  triggerSimilaritiesTraining,
  sendBackAnalyticsResultsOrError,
  getParsedSimilaritiesContent,
  convertDocxSurveyToJson
};<|endoftext|>