<commit_before>'use strict';

/**
 * Rhizome - The API that feeds grassroots movements
 *
 * @file user.js
 * @description USER API specification
 * @module API
 * @author Chris Bates-Keegan
 *
 */

var Route = require('../route');
var Model = require('../../model');
var Logging = require('../../logging');
var Helpers = require('../../helpers');

var routes = [];

/**
 * @class GetUserList
 */
class GetUserList extends Route {
  constructor() {
    super('user', 'GET USER LIST');
    this.verb = Route.Constants.Verbs.GET;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.LIST;
  }

  _validate() {
    return Promise.resolve(true);
  }

  _exec() {
    return Model.User.getAll();
  }
}
routes.push(GetUserList);

/**
 * @class GetUser
 */
class GetUser extends Route {
  constructor() {
    super('user/:id', 'GET USER');
    this.verb = Route.Constants.Verbs.GET;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.READ;

    this._user = false;
  }

  _validate() {
    return new Promise((resolve, reject) => {
      if (!this.req.params.id) {
        this.log('ERROR: Missing required field', Route.LogLevel.ERR);
        reject({statusCode: 400});
        return;
      }
      Model.User.findById(this.req.params.id).populate('_person').then(user => {
        if (!user) {
          this.log('ERROR: Invalid User ID', Route.LogLevel.ERR);
          reject({statusCode: 400});
          return;
        }

        this._user = user;
        resolve(true);
      });
    });
  }

  _exec() {
    return Promise.resolve(this._user.details);
  }
}
routes.push(GetUser);

/**
 * @class FindUser
 */
class FindUser extends Route {
  constructor() {
    super('user/:app(twitter|facebook|google)/:id', 'FIND USER');
    this.verb = Route.Constants.Verbs.GET;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.READ;

    this._user = false;
  }

  _validate() {
    return new Promise((resolve, reject) => {
      Model.User.getByAppId(this.req.params.app, this.req.params.id).then(user => {
        Logging.log(`User: ${user}`, Logging.Constants.LogLevel.DEBUG);
        this._user = user;
        resolve(true);
      });
    });
  }

  _exec() {
    return Promise.resolve(this._user ? {id: this._user.id} : false);
  }
}
routes.push(FindUser);

/**
 * @class UpdateUserToken
 */
class UpdateUserToken extends Route {
  constructor() {
    super('user/:id/:app(twitter|facebook|google)/token', 'UPDATE USER APP TOKEN');
    this.verb = Route.Constants.Verbs.PUT;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.READ;

    this._user = false;
  }

  _validate() {
    return new Promise((resolve, reject) => {
      if (!this.req.body ||
        !this.req.body.token ||
        !this.req.body.tokenSecret) {
        this.log('ERROR: Missing required field', Route.LogLevel.ERR);
        reject({statusCode: 400});
        return;
      }

      Model.User.findById(this.req.params.id).select('-metadata').then(user => {
        Logging.log(`User: ${user ? user.id : null}`, Logging.Constants.LogLevel.DEBUG);
        this._user = user;
        if (this._user) {
          resolve(true);
        } else {
          this.log('ERROR: Invalid User ID', Route.LogLevel.ERR);
          resolve({statusCode: 400});
        }
      });
    });
  }

  _exec() {
    return this._user.updateToken(this.req.params.app, this.req.body);
  }
}
routes.push(UpdateUserToken);

/**
 * @class AddUser
 */
class AddUser extends Route {
  constructor() {
    super('user/:app?', 'ADD USER');
    this.verb = Route.Constants.Verbs.POST;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.ADD;
  }

  _validate() {
    return new Promise((resolve, reject) => {
      Logging.log(this.req.body, Logging.Constants.LogLevel.DEBUG);

      if (!this.req.body.username ||
          !this.req.body.app ||
          !this.req.body.id ||
          !this.req.body.token ||
          !this.req.body.tokenSecret ||
          !this.req.body.profileUrl ||
          !this.req.body.profileImgUrl) {
        this.log('ERROR: Missing required field', Route.LogLevel.ERR);
        reject({statusCode: 400});
        return;
      }

      Model.Person.findByDetails(this.req.body)
        .then(person => {
          this._person = person;
          resolve(true);
        });
    });
  }

  _exec() {
    return Model.User.add(this.req.body)
      .then(Logging.Promise.logProp('Added User', 'username', Route.LogLevel.VERBOSE))
      .then(Helpers.Promise.prop('details'));
  }
}
routes.push(AddUser);

/**
 * @class DeleteUser
 */
class DeleteUser extends Route {
  constructor() {
    super('user/:id', 'DELETE USER');
    this.verb = Route.Constants.Verbs.DEL;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.DELETE;
    this._user = false;
  }

  _validate() {
    return new Promise((resolve, reject) => {
      if (!this.req.params.id) {
        this.log('ERROR: Missing required field', Route.LogLevel.ERR);
        reject({statusCode: 400});
        return;
      }
      Model.User.findById(this.req.params.id).select('-metadata').then(user => {
        if (!user) {
          this.log('ERROR: Invalid User ID', Route.LogLevel.ERR);
          reject({statusCode: 400});
          return;
        }
        this._user = user;
        resolve(true);
      });
    });
  }

  _exec() {
    return Model.User.rm(this._user).then(() => true);
  }
}
routes.push(DeleteUser);

class AttachToPerson extends Route {
  constructor() {
    super('user/:id/person', 'ATTACH USER TO PERSON');
    this.verb = Route.Constants.Verbs.PUT;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.ADD;

    this._user = false;
    this._person = false;
  }

  _validate() {
    return new Promise((resolve, reject) => {
      Model.User
        .findById(this.req.params.id).select('-metadata').then(user => {
          if (!user) {
            this.log('ERROR: Invalid User ID', Route.LogLevel.ERR);
            reject({statusCode: 400});
            return;
          }
          this._user = user;

          if (this._user._person) {
            this.log('ERROR: Already attached to a person', Route.LogLevel.ERR);
            reject({statusCode: 400});
            return;
          }

          if (!this.req.body.name || !this.req.body.email) {
            this.log('ERROR: Missing required field', Route.LogLevel.ERR);
            reject({statusCode: 400});
            return;
          }

          Model.Person
            .findByDetails(this.req.body)
            .then(person => {
              this._person = person;
              if (person) {
                return Model.User.findOne({_person: person});
              }
              return Promise.resolve(null);
            })
            .then(user => {
              if (user && user._id !== this._user._id) {
                this.log('ERROR: Person attached to a different user', Route.LogLevel.ERR);
                reject({statusCode: 400});
                return;
              }
              resolve(true);
            })
            .catch(err => {
              this.log(`ERROR: ${err.message}`, Route.LogLevel.ERR);
              reject({statusCode: 400});
            });
        })
        .catch(Logging.Promise.logError());
    });
  }

  _exec() {
    return this._user.attachToPerson(this._person, this.req.body)
      .then(Helpers.Promise.prop('details'));
  }
}
routes.push(AttachToPerson);

/**
 * @class AddUserMetadata
 */
class AddUserMetadata extends Route {
  constructor() {
    super('user/:id/metadata/:key', 'ADD USER METADATA');
    this.verb = Route.Constants.Verbs.POST;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.ADD;

    this._user = false;
  }

  _validate() {
    return new Promise((resolve, reject) => {
      Model.User.findById(this.req.params.id).then(user => {
        if (!user) {
          this.log('ERROR: Invalid User ID', Route.LogLevel.ERR);
          reject({statusCode: 400});
          return;
        }
        try {
          JSON.parse(this.req.body.value);
        } catch (e) {
          this.log(`ERROR: ${e.message}`, Route.LogLevel.ERR);
          this.log(this.req.body.value, Route.LogLevel.ERR);
          reject({statusCode: 400});
          return;
        }

        this._user = user;
        resolve(true);
      });
    });
  }

  _exec() {
    return this._user.addOrUpdateMetadata(this.req.params.key, this.req.body.value);
  }
}
routes.push(AddUserMetadata);

/**
 * @class UpdateUserMetadata
 */
class UpdateUserMetadata extends Route {
  constructor() {
    super('user/:id/metadata/:key', 'UPDATE USER METADATA');
    this.verb = Route.Constants.Verbs.PUT;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.ADD;

    this._app = false;
  }

  _validate() {
    return new Promise((resolve, reject) => {
      Model.User.findById(this.req.params.id).then(user => {
        if (!user) {
          this.log('ERROR: Invalid App ID', Route.LogLevel.ERR);
          reject({statusCode: 400});
          return;
        }
        if (user.findMetadata(this.req.params.key) === false) {
          this.log('ERROR: Metadata does not exist', Route.LogLevel.ERR);
          reject({statusCode: 400});
          return;
        }
        try {
          JSON.parse(this.req.body.value);
        } catch (e) {
          this.log(`ERROR: ${e.message}`, Route.LogLevel.ERR);
          reject({statusCode: 400});
          return;
        }

        this._user = user;
        resolve(true);
      });
    });
  }

  _exec() {
    return this._user.addOrUpdateMetadata(this.req.params.key, this.req.body.value);
  }
}
routes.push(UpdateUserMetadata);

/**
 * @class GetUserMetadata
 */
class GetUserMetadata extends Route {
  constructor() {
    super('user/:id/metadata/:key', 'GET USER METADATA');
    this.verb = Route.Constants.Verbs.GET;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.GET;

    this._metadata = false;
  }

  _validate() {
    return new Promise((resolve, reject) => {
      Model.User.findById(this.req.params.id).then(user => {
        if (!user) {
          this.log('ERROR: Invalid User ID', Route.LogLevel.ERR);
          reject({statusCode: 400});
          return;
        }

        this._metadata = user.findMetadata(this.req.params.key);
        if (this._metadata === undefined) {
          this.log('WARN: App Metadata Not Found', Route.LogLevel.ERR);
          reject({statusCode: 404});
          return;
        }

        resolve(true);
      });
    });
  }

  _exec() {
    return this._metadata.value;
  }
}
routes.push(GetUserMetadata);

/**
 * @type {*[]}
 */
module.exports = routes;
<commit_msg>- REFACTORED: Add user api allowing for the auth app to be passed via the url<commit_after>'use strict';

/**
 * Rhizome - The API that feeds grassroots movements
 *
 * @file user.js
 * @description USER API specification
 * @module API
 * @author Chris Bates-Keegan
 *
 */

var Route = require('../route');
var Model = require('../../model');
var Logging = require('../../logging');
var Helpers = require('../../helpers');

var routes = [];

/**
 * @class GetUserList
 */
class GetUserList extends Route {
  constructor() {
    super('user', 'GET USER LIST');
    this.verb = Route.Constants.Verbs.GET;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.LIST;
  }

  _validate() {
    return Promise.resolve(true);
  }

  _exec() {
    return Model.User.getAll();
  }
}
routes.push(GetUserList);

/**
 * @class GetUser
 */
class GetUser extends Route {
  constructor() {
    super('user/:id', 'GET USER');
    this.verb = Route.Constants.Verbs.GET;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.READ;

    this._user = false;
  }

  _validate() {
    return new Promise((resolve, reject) => {
      if (!this.req.params.id) {
        this.log('ERROR: Missing required field', Route.LogLevel.ERR);
        reject({statusCode: 400});
        return;
      }
      Model.User.findById(this.req.params.id).populate('_person').then(user => {
        if (!user) {
          this.log('ERROR: Invalid User ID', Route.LogLevel.ERR);
          reject({statusCode: 400});
          return;
        }

        this._user = user;
        resolve(true);
      });
    });
  }

  _exec() {
    return Promise.resolve(this._user.details);
  }
}
routes.push(GetUser);

/**
 * @class FindUser
 */
class FindUser extends Route {
  constructor() {
    super('user/:app(twitter|facebook|google)/:id', 'FIND USER');
    this.verb = Route.Constants.Verbs.GET;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.READ;

    this._user = false;
  }

  _validate() {
    return new Promise((resolve, reject) => {
      Model.User.getByAppId(this.req.params.app, this.req.params.id).then(user => {
        Logging.log(`User: ${user}`, Logging.Constants.LogLevel.DEBUG);
        this._user = user;
        resolve(true);
      });
    });
  }

  _exec() {
    return Promise.resolve(this._user ? {id: this._user.id} : false);
  }
}
routes.push(FindUser);

/**
 * @class UpdateUserToken
 */
class UpdateUserToken extends Route {
  constructor() {
    super('user/:id/:app(twitter|facebook|google)/token', 'UPDATE USER APP TOKEN');
    this.verb = Route.Constants.Verbs.PUT;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.READ;

    this._user = false;
  }

  _validate() {
    return new Promise((resolve, reject) => {
      if (!this.req.body ||
        !this.req.body.token ||
        !this.req.body.tokenSecret) {
        this.log('ERROR: Missing required field', Route.LogLevel.ERR);
        reject({statusCode: 400});
        return;
      }

      Model.User.findById(this.req.params.id).select('-metadata').then(user => {
        Logging.log(`User: ${user ? user.id : null}`, Logging.Constants.LogLevel.DEBUG);
        this._user = user;
        if (this._user) {
          resolve(true);
        } else {
          this.log('ERROR: Invalid User ID', Route.LogLevel.ERR);
          resolve({statusCode: 400});
        }
      });
    });
  }

  _exec() {
    return this._user.updateToken(this.req.params.app, this.req.body);
  }
}
routes.push(UpdateUserToken);

/**
 * @class AddUser
 */
class AddUser extends Route {
  constructor() {
    super('user/:app?', 'ADD USER');
    this.verb = Route.Constants.Verbs.POST;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.ADD;
  }

  _validate() {
    return new Promise((resolve, reject) => {
      Logging.log(this.req.body, Logging.Constants.LogLevel.DEBUG);
      var app = this.req.body.app ? this.req.body.app : this.req.params.app;

      if (!this.req.body.username || !app ||
          !this.req.body.id ||
          !this.req.body.token ||
          !this.req.body.tokenSecret ||
          !this.req.body.profileUrl ||
          !this.req.body.profileImgUrl) {
        this.log('ERROR: Missing required field', Route.LogLevel.ERR);
        reject({statusCode: 400});
        return;
      }

      Model.Person.findByDetails(this.req.body)
        .then(person => {
          this._person = person;
          resolve(true);
        });
    });
  }

  _exec() {
    return Model.User.add(this.req.body)
      .then(Logging.Promise.logProp('Added User', 'username', Route.LogLevel.VERBOSE))
      .then(Helpers.Promise.prop('details'));
  }
}
routes.push(AddUser);

/**
 * @class DeleteUser
 */
class DeleteUser extends Route {
  constructor() {
    super('user/:id', 'DELETE USER');
    this.verb = Route.Constants.Verbs.DEL;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.DELETE;
    this._user = false;
  }

  _validate() {
    return new Promise((resolve, reject) => {
      if (!this.req.params.id) {
        this.log('ERROR: Missing required field', Route.LogLevel.ERR);
        reject({statusCode: 400});
        return;
      }
      Model.User.findById(this.req.params.id).select('-metadata').then(user => {
        if (!user) {
          this.log('ERROR: Invalid User ID', Route.LogLevel.ERR);
          reject({statusCode: 400});
          return;
        }
        this._user = user;
        resolve(true);
      });
    });
  }

  _exec() {
    return Model.User.rm(this._user).then(() => true);
  }
}
routes.push(DeleteUser);

class AttachToPerson extends Route {
  constructor() {
    super('user/:id/person', 'ATTACH USER TO PERSON');
    this.verb = Route.Constants.Verbs.PUT;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.ADD;

    this._user = false;
    this._person = false;
  }

  _validate() {
    return new Promise((resolve, reject) => {
      Model.User
        .findById(this.req.params.id).select('-metadata').then(user => {
          if (!user) {
            this.log('ERROR: Invalid User ID', Route.LogLevel.ERR);
            reject({statusCode: 400});
            return;
          }
          this._user = user;

          if (this._user._person) {
            this.log('ERROR: Already attached to a person', Route.LogLevel.ERR);
            reject({statusCode: 400});
            return;
          }

          if (!this.req.body.name || !this.req.body.email) {
            this.log('ERROR: Missing required field', Route.LogLevel.ERR);
            reject({statusCode: 400});
            return;
          }

          Model.Person
            .findByDetails(this.req.body)
            .then(person => {
              this._person = person;
              if (person) {
                return Model.User.findOne({_person: person});
              }
              return Promise.resolve(null);
            })
            .then(user => {
              if (user && user._id !== this._user._id) {
                this.log('ERROR: Person attached to a different user', Route.LogLevel.ERR);
                reject({statusCode: 400});
                return;
              }
              resolve(true);
            })
            .catch(err => {
              this.log(`ERROR: ${err.message}`, Route.LogLevel.ERR);
              reject({statusCode: 400});
            });
        })
        .catch(Logging.Promise.logError());
    });
  }

  _exec() {
    return this._user.attachToPerson(this._person, this.req.body)
      .then(Helpers.Promise.prop('details'));
  }
}
routes.push(AttachToPerson);

/**
 * @class AddUserMetadata
 */
class AddUserMetadata extends Route {
  constructor() {
    super('user/:id/metadata/:key', 'ADD USER METADATA');
    this.verb = Route.Constants.Verbs.POST;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.ADD;

    this._user = false;
  }

  _validate() {
    return new Promise((resolve, reject) => {
      Model.User.findById(this.req.params.id).then(user => {
        if (!user) {
          this.log('ERROR: Invalid User ID', Route.LogLevel.ERR);
          reject({statusCode: 400});
          return;
        }
        try {
          JSON.parse(this.req.body.value);
        } catch (e) {
          this.log(`ERROR: ${e.message}`, Route.LogLevel.ERR);
          this.log(this.req.body.value, Route.LogLevel.ERR);
          reject({statusCode: 400});
          return;
        }

        this._user = user;
        resolve(true);
      });
    });
  }

  _exec() {
    return this._user.addOrUpdateMetadata(this.req.params.key, this.req.body.value);
  }
}
routes.push(AddUserMetadata);

/**
 * @class UpdateUserMetadata
 */
class UpdateUserMetadata extends Route {
  constructor() {
    super('user/:id/metadata/:key', 'UPDATE USER METADATA');
    this.verb = Route.Constants.Verbs.PUT;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.ADD;

    this._app = false;
  }

  _validate() {
    return new Promise((resolve, reject) => {
      Model.User.findById(this.req.params.id).then(user => {
        if (!user) {
          this.log('ERROR: Invalid App ID', Route.LogLevel.ERR);
          reject({statusCode: 400});
          return;
        }
        if (user.findMetadata(this.req.params.key) === false) {
          this.log('ERROR: Metadata does not exist', Route.LogLevel.ERR);
          reject({statusCode: 400});
          return;
        }
        try {
          JSON.parse(this.req.body.value);
        } catch (e) {
          this.log(`ERROR: ${e.message}`, Route.LogLevel.ERR);
          reject({statusCode: 400});
          return;
        }

        this._user = user;
        resolve(true);
      });
    });
  }

  _exec() {
    return this._user.addOrUpdateMetadata(this.req.params.key, this.req.body.value);
  }
}
routes.push(UpdateUserMetadata);

/**
 * @class GetUserMetadata
 */
class GetUserMetadata extends Route {
  constructor() {
    super('user/:id/metadata/:key', 'GET USER METADATA');
    this.verb = Route.Constants.Verbs.GET;
    this.auth = Route.Constants.Auth.ADMIN;
    this.permissions = Route.Constants.Permissions.GET;

    this._metadata = false;
  }

  _validate() {
    return new Promise((resolve, reject) => {
      Model.User.findById(this.req.params.id).then(user => {
        if (!user) {
          this.log('ERROR: Invalid User ID', Route.LogLevel.ERR);
          reject({statusCode: 400});
          return;
        }

        this._metadata = user.findMetadata(this.req.params.key);
        if (this._metadata === undefined) {
          this.log('WARN: App Metadata Not Found', Route.LogLevel.ERR);
          reject({statusCode: 404});
          return;
        }

        resolve(true);
      });
    });
  }

  _exec() {
    return this._metadata.value;
  }
}
routes.push(GetUserMetadata);

/**
 * @type {*[]}
 */
module.exports = routes;
<|endoftext|>


<commit_before>'use strict';

/**
 * Watcher process.
 *
 * **watcher.js** is an independant process and can be run without
 * running openveo publish. By default it is run as a sub process
 * while launching the application server.
 *
 * **watcher.js** can be run as a standalone script or as a child
 * process.
 *
 * node watcher.js --root ""/home/veo-labs/openveo" --rootPublish
 * "/home/veo-labs/openveo/node_modules/@openveo/publish" --databaseConf
 * "~/.openveo/publish/databaseConf.json"
 *
 * @module publish-watcher
 * @class watcher.js
 */

// Module dependencies
var path = require('path');
var fs = require('fs');
var util = require('util');
var chokidar = require('chokidar');
var openVeoAPI = require('@openveo/api');
var configDir = openVeoAPI.fileSystem.getConfDir();
var databaseConf;

for (var i = 0; i < process.argv.length; i++) {

  switch (process.argv[i]) {
    case '--rootPublish':
      process.rootPublish = process.argv[i + 1] || null;
      break;
    case '--databaseConf':
      databaseConf = require(path.normalize(process.argv[i + 1])) || null;
      break;
    case '--anonymousUserId':
      openVeoAPI.applicationStorage.setAnonymousUserId(process.argv[i + 1] || '1');
      break;
    default:
      break;
  }

}

// Validate rootPublish argument
if (!process.rootPublish)
  throw new Error('--rootPublish argument must be passed to the watcher.js script and point to the ' +
                  '@openveo/publish module root directory');

// Define a function to easily require files inside the module
process.requirePublish = function(filePath) {
  return require(path.normalize(process.rootPublish + '/' + filePath));
};

var async = require('async');

// Module files
var watcherConf = require(path.join(configDir, 'publish/watcherConf.json'));
var publishConf = require(path.join(configDir, 'publish/publishConf.json'));
var PublishManager = process.requirePublish('app/server/PublishManager.js');

// Create logger
process.logger = openVeoAPI.logger.add('openveo', require(path.join(configDir, 'publish/loggerConf.json')).watcher);

// hotFolders and videoTmpDir must be defined
if (!watcherConf.hotFolders || !publishConf.videoTmpDir) {
  process.logger.error('You must configure a hot folder and a video temporary directory in watcherConf.json ' +
               'and publishConf.json files');
  return;
}

// hotFolders must be a non empty Array
if (!util.isArray(watcherConf.hotFolders) || !watcherConf.hotFolders.length) {
  process.logger.error('hotFolders property must be an array of objects');
  return;
}

// videoTmpDir must be an String
if (typeof publishConf.videoTmpDir !== 'string') {
  process.logger.error('videoTmpDir property must be a String');
  return;
}

// databaseConf must be an Object
if (typeof databaseConf !== 'object') {
  process.logger.error('--databaseConf argument must point to a JSON file');
  return;
}

// Get a Database instance
var db = openVeoAPI.Database.getDatabase(databaseConf);

// Establish connection to the database
db.connect(function(error) {
  var publishManager = new PublishManager(db);

  /**
   * Publishes a package by its name.
   *
   * @param {String} filePath The file of the path to publish
   */
  function publishPackage(filePath) {
    var pathDescriptor = path.parse(filePath);

    // Only files with tar or mp4 extensions are accepted
    if (pathDescriptor.ext === '.tar' || pathDescriptor.ext === '.mp4') {
      var packageInfo = null;

      // Find the hot folder in which the file was added
      watcherConf.hotFolders.forEach(function(hotFolder) {
        if (path.normalize(pathDescriptor.dir).indexOf(path.normalize(hotFolder.path)) === 0) {
          packageInfo = JSON.parse(JSON.stringify(hotFolder));
          return;
        }
      });

      packageInfo['originalPackagePath'] = filePath;
      packageInfo['originalFileName'] = pathDescriptor.name;
      publishManager.publish(packageInfo);
    } else
      process.logger.warn('File ' + filePath + ' is not a valid package file (mp4 or tar)');
  }

  // Connection to database failed
  if (error) {
    process.logger.error(error && error.message);
    throw new Error('Connection to database failed');
  } else {

    // Connection to database done
    // If process is a child process
    if (process.connected) {

      // Exit process if disconnected from its parent
      process.on('disconnect', function() {
        throw new Error('Watcher has been disconnected from its parent process');
      });

      // Handle messages from parent process
      process.on('message', function(data) {
        if (!data)
          return;

        // Retry publication of a package
        if (data.action === 'retry' && data.id)
          publishManager.retry(data.id);

        // Force the upload of a package
        else if (data.action === 'upload' && data.id && data.platform)
          publishManager.upload(data.id, data.platform);

      });

    }

    // Retrieve the list of hot folders paths from configuration
    var hotFoldersPaths = [];
    watcherConf.hotFolders.forEach(function(hotFolder) {

      if (
        typeof hotFolder === 'object' &&
        typeof hotFolder.path === 'string'
      )
        hotFoldersPaths.push(path.normalize(hotFolder.path));

    });

    // Listen to errors dispatched by the publish manager
    publishManager.on('error', function(error) {
      process.logger.error(error && error.message, {code: error.code});
    });

    // Listen to complete publications dispatched by the publish manager
    publishManager.on('complete', function(videoPackage) {
      process.logger.info('Publish complete for video ' + videoPackage.id);
    });

    // Listen to retry dispatched by the publish manager when package retry has started
    publishManager.on('retry', function(videoPackage) {
      process.logger.info('Retry publishing package ' + videoPackage.id + ' started');
      process.send({action: 'retry'});
    });

    // Listen to upload dispatched by the publish manager when package waiting for upload starts uploading
    publishManager.on('upload', function(videoPackage) {
      process.logger.info('Force uploading package ' + videoPackage.id + ' started');
      process.send({action: 'upload'});
    });

    async.filter(hotFoldersPaths, fs.exists, function(results) {
      hotFoldersPaths = results;

      // Start watching the hot folders
      var watcher = chokidar.watch(hotFoldersPaths, {
        ignoreInitial: false,
        followSymlinks: false,
        awaitWriteFinish: {
          stabilityThreshold: 10000
        }
      });

      // Listen to files added to the hot folder
      watcher.on('add', function(filePath) {
        process.logger.verbose('File detected : ' + filePath);
        publishPackage(filePath);
      });

      watcher.on('ready', function() {
        process.logger.info('Initial scan complete');

        // If process is a child process
        if (process.connected)
          process.send({status: 'started'});

        // Retry all packages in a non stable state
        publishManager.retryAll();

      });

      watcher.on('error', function(error) {
        process.logger.error(error && error.message);
      });
    });
  }

});
<commit_msg>Update async.filter calls due to async upgrade<commit_after>'use strict';

/**
 * Watcher process.
 *
 * **watcher.js** is an independant process and can be run without
 * running openveo publish. By default it is run as a sub process
 * while launching the application server.
 *
 * **watcher.js** can be run as a standalone script or as a child
 * process.
 *
 * node watcher.js --root ""/home/veo-labs/openveo" --rootPublish
 * "/home/veo-labs/openveo/node_modules/@openveo/publish" --databaseConf
 * "~/.openveo/publish/databaseConf.json"
 *
 * @module publish-watcher
 * @class watcher.js
 */

// Module dependencies
var path = require('path');
var fs = require('fs');
var util = require('util');
var chokidar = require('chokidar');
var openVeoAPI = require('@openveo/api');
var configDir = openVeoAPI.fileSystem.getConfDir();
var databaseConf;

for (var i = 0; i < process.argv.length; i++) {

  switch (process.argv[i]) {
    case '--rootPublish':
      process.rootPublish = process.argv[i + 1] || null;
      break;
    case '--databaseConf':
      databaseConf = require(path.normalize(process.argv[i + 1])) || null;
      break;
    case '--anonymousUserId':
      openVeoAPI.applicationStorage.setAnonymousUserId(process.argv[i + 1] || '1');
      break;
    default:
      break;
  }

}

// Validate rootPublish argument
if (!process.rootPublish)
  throw new Error('--rootPublish argument must be passed to the watcher.js script and point to the ' +
                  '@openveo/publish module root directory');

// Define a function to easily require files inside the module
process.requirePublish = function(filePath) {
  return require(path.normalize(process.rootPublish + '/' + filePath));
};

var async = require('async');

// Module files
var watcherConf = require(path.join(configDir, 'publish/watcherConf.json'));
var publishConf = require(path.join(configDir, 'publish/publishConf.json'));
var PublishManager = process.requirePublish('app/server/PublishManager.js');

// Create logger
process.logger = openVeoAPI.logger.add('openveo', require(path.join(configDir, 'publish/loggerConf.json')).watcher);

// hotFolders and videoTmpDir must be defined
if (!watcherConf.hotFolders || !publishConf.videoTmpDir) {
  process.logger.error('You must configure a hot folder and a video temporary directory in watcherConf.json ' +
               'and publishConf.json files');
  return;
}

// hotFolders must be a non empty Array
if (!util.isArray(watcherConf.hotFolders) || !watcherConf.hotFolders.length) {
  process.logger.error('hotFolders property must be an array of objects');
  return;
}

// videoTmpDir must be an String
if (typeof publishConf.videoTmpDir !== 'string') {
  process.logger.error('videoTmpDir property must be a String');
  return;
}

// databaseConf must be an Object
if (typeof databaseConf !== 'object') {
  process.logger.error('--databaseConf argument must point to a JSON file');
  return;
}

// Get a Database instance
var db = openVeoAPI.Database.getDatabase(databaseConf);

// Establish connection to the database
db.connect(function(error) {
  var publishManager = new PublishManager(db);

  /**
   * Publishes a package by its name.
   *
   * @param {String} filePath The file of the path to publish
   */
  function publishPackage(filePath) {
    var pathDescriptor = path.parse(filePath);

    // Only files with tar or mp4 extensions are accepted
    if (pathDescriptor.ext === '.tar' || pathDescriptor.ext === '.mp4') {
      var packageInfo = null;

      // Find the hot folder in which the file was added
      watcherConf.hotFolders.forEach(function(hotFolder) {
        if (path.normalize(pathDescriptor.dir).indexOf(path.normalize(hotFolder.path)) === 0) {
          packageInfo = JSON.parse(JSON.stringify(hotFolder));
          return;
        }
      });

      packageInfo['originalPackagePath'] = filePath;
      packageInfo['originalFileName'] = pathDescriptor.name;
      publishManager.publish(packageInfo);
    } else
      process.logger.warn('File ' + filePath + ' is not a valid package file (mp4 or tar)');
  }

  // Connection to database failed
  if (error) {
    process.logger.error(error && error.message);
    throw new Error('Connection to database failed');
  } else {

    // Connection to database done
    // If process is a child process
    if (process.connected) {

      // Exit process if disconnected from its parent
      process.on('disconnect', function() {
        throw new Error('Watcher has been disconnected from its parent process');
      });

      // Handle messages from parent process
      process.on('message', function(data) {
        if (!data)
          return;

        // Retry publication of a package
        if (data.action === 'retry' && data.id)
          publishManager.retry(data.id);

        // Force the upload of a package
        else if (data.action === 'upload' && data.id && data.platform)
          publishManager.upload(data.id, data.platform);

      });

    }

    // Retrieve the list of hot folders paths from configuration
    var hotFoldersPaths = [];
    watcherConf.hotFolders.forEach(function(hotFolder) {

      if (
        typeof hotFolder === 'object' &&
        typeof hotFolder.path === 'string'
      )
        hotFoldersPaths.push(path.normalize(hotFolder.path));

    });

    // Listen to errors dispatched by the publish manager
    publishManager.on('error', function(error) {
      process.logger.error(error && error.message, {code: error.code});
    });

    // Listen to complete publications dispatched by the publish manager
    publishManager.on('complete', function(videoPackage) {
      process.logger.info('Publish complete for video ' + videoPackage.id);
    });

    // Listen to retry dispatched by the publish manager when package retry has started
    publishManager.on('retry', function(videoPackage) {
      process.logger.info('Retry publishing package ' + videoPackage.id + ' started');
      process.send({action: 'retry'});
    });

    // Listen to upload dispatched by the publish manager when package waiting for upload starts uploading
    publishManager.on('upload', function(videoPackage) {
      process.logger.info('Force uploading package ' + videoPackage.id + ' started');
      process.send({action: 'upload'});
    });

    async.filter(hotFoldersPaths, fs.exists, function(error, results) {
      hotFoldersPaths = results;

      // Start watching the hot folders
      var watcher = chokidar.watch(hotFoldersPaths, {
        ignoreInitial: false,
        followSymlinks: false,
        awaitWriteFinish: {
          stabilityThreshold: 10000
        }
      });

      // Listen to files added to the hot folder
      watcher.on('add', function(filePath) {
        process.logger.verbose('File detected : ' + filePath);
        publishPackage(filePath);
      });

      watcher.on('ready', function() {
        process.logger.info('Initial scan complete');

        // If process is a child process
        if (process.connected)
          process.send({status: 'started'});

        // Retry all packages in a non stable state
        publishManager.retryAll();

      });

      watcher.on('error', function(error) {
        process.logger.error(error && error.message);
      });
    });
  }

});
<|endoftext|>


<commit_before>"use strict";
var util           = require('util');
var async          = require('async');
var _              = require('underscore');
var Q              = require('q');
var events         = require('events');
var logger         = require('../lib/logger')('peering');
var base58         = require('../lib/base58');
var sha1           = require('sha1');
var moment         = require('moment');
var rawer          = require('../lib/rawer');
var constants      = require('../lib/constants');
var localValidator = require('../lib/localValidator');

function PeeringService(peerserver, pair, signFunc, dal) {

  var conf = peerserver.conf;
  var currency = conf.currency;

  var Peer        = require('../lib/entity/peer');

  var selfPubkey;
  this.pubkey = selfPubkey;

  var peer = null, BlockchainService = null;
  var that = this;

  this.setKeyPair = function(keypair) {
    if (keypair) {
      pair = keypair;
      selfPubkey = base58.encode(pair.publicKey);
      that.pubkey = selfPubkey;
    }
  };

  this.setSignFunc = function(f) {
    signFunc = f;
  };

  this.setBlockchainService = function(service) {
    BlockchainService = service;
  };

  this.peer = function (newPeer) {
    if (newPeer) {
      peer = newPeer;
    }
    return Peer.statics.peerize(peer);
  };

  this.load = function (done) {
  };

  this.submit = function(peering, done){
    var peer = new Peer(peering);
    var sp = peer.block.split('-');
    var number = sp[0];
    var sigTime = 0;
    async.waterfall([
      function (next) {
        localValidator(null).checkPeerSignature(peer, next);
      },
      function (next) {
        if (peer.block == constants.PEER.SPECIAL_BLOCK) {
          peer.statusTS = 0;
          peer.status = 'UP';
          next(null, null);
        }
        // Check if document is based upon an existing block as time reference
        else dal.getBlockOrNull(number, next);
      },
      function (block, next){
        sigTime = block ? block.medianTime : 0;
        peer.statusTS = sigTime;
        dal.getPeerOrNull(peer.pubkey, next);
      },
      function (found, next){
        var peerEntity = Peer.statics.peerize(found || peer);
        var previousHash = null;
        if(found){
          // Already existing peer
          var sp2 = found.block.split('-');
          var number2 = sp2[0];
          if(number <= number2){
            next(constants.ERROR.PEER.ALREADY_RECORDED);
            return;
          }
          peerEntity = Peer.statics.peerize(found);
          previousHash = peerEntity.hash;
          peer.copyValues(peerEntity);
          peerEntity.sigDate = new Date(sigTime*1000);
        }
        // Set the peer as UP again
        peerEntity.status = 'UP';
        dal.savePeer(peerEntity, function (err) {
          next(err, peerEntity, previousHash);
        });
      },
      function (recordedPR, previousHash, next) {
        dal.updateMerkleForPeers(function(err) {
          next(err, Peer.statics.peerize(recordedPR));
        });
      }
    ], done);
  };

  var peerFifo = async.queue(function (task, callback) {
    task(callback);
  }, 1);
  var peerInterval = null;
  this.regularPeerSignal = function (done) {
    if (peerInterval)
      clearInterval(peerInterval);
    peerInterval = setInterval(function () {
      peerFifo.push(_.partial(generateSelfPeer, peerserver.conf));
    }, 1000*conf.avgGenTime*constants.NETWORK.STATUS_INTERVAL.UPDATE);
    generateSelfPeer(peerserver.conf, done);
  };

  var syncBlockFifo = async.queue(function (task, callback) {
    task(callback);
  }, 1);
  var syncBlockInterval = null;
  this.regularSyncBlock = function (done) {
    if (syncBlockInterval)
      clearInterval(syncBlockInterval);
    syncBlockInterval = setInterval(function () {
      syncBlockFifo.push(syncBlock);
    }, 1000*conf.avgGenTime*constants.NETWORK.SYNC_BLOCK_INTERVAL);
    syncBlock(done);
  };

  function generateSelfPeer(conf, done) {
    var currency = conf.currency;
    var current = null;
    async.waterfall([
      function (next) {
        peerserver.BlockchainService.current(next);
      },
      function (currentBlock, next) {
        current = currentBlock;
        peerserver.dal.findPeers(peerserver.PeeringService.pubkey, next);
      },
      function (peers, next) {
        var p1 = { version: 1, currency: currency };
        if(peers.length != 0){
          p1 = _(peers[0]).extend({ version: 1, currency: currency });
        }
        var endpoint = 'BASIC_MERKLED_API';
        if (conf.remotehost) {
          endpoint += ' ' + conf.remotehost;
        }
        if (conf.remoteipv4) {
          endpoint += ' ' + conf.remoteipv4;
        }
        if (conf.remoteipv6) {
          endpoint += ' ' + conf.remoteipv6;
        }
        if (conf.remoteport) {
          endpoint += ' ' + conf.remoteport;
        }
        var p2 = {
          version: 1,
          currency: currency,
          pubkey: peerserver.PeeringService.pubkey,
          block: current ? [current.number, current.hash].join('-') : constants.PEER.SPECIAL_BLOCK,
          endpoints: [endpoint]
        };
        var raw1 = new Peer(p1).getRaw().dos2unix();
        var raw2 = new Peer(p2).getRaw().dos2unix();
        if (raw1 != raw2) {
          logger.debug('Generating server\'s peering entry...');
          async.waterfall([
            function (next){
              peerserver.sign(raw2, next);
            },
            function (signature, next) {
              p2.signature = signature;
              p2.pubkey = peerserver.PeeringService.pubkey;
              peerserver.submit(p2, false, next);
            }
          ], function (err) {
            next(err);
          });
        } else {
          peerserver.push(p1);
          next();
        }
      },
      function (next){
        peerserver.dal.getPeer(peerserver.PeeringService.pubkey, next);
      },
      function (peer, next){
        // Set peer's statut to UP
        peerserver.PeeringService.peer(peer);
        peerserver.push(peer);
        next();
      }
    ], function(err) {
      done(err);
    });
  }

  this.testPeers = function(done) {
    dal.getAllPeers()
      .then(function(peers){
        peers = _.filter(peers, function(p){ return p.pubkey != selfPubkey; });
        return Q.all(peers.map(function(p) {
          var peer = new Peer(p);
          return Q.nfcall(peer.connect)
            .then(function(node){
              // Get peering record of each peer
              return Q.nfcall(node.network.peering.get)
                .then(function(peering){
                  // Submit the peering
                  return Q.nfcall(that.submit, peering)
                    .fail(function(err) {
                      // Not a problem!
                      logger.warn("Peer record %s: %s", peer.pubkey, err.code || err.message || err);
                    });
                })
                .fail(function(err){
                  logger.warn("Peer %s: %s", peer.pubkey, err.code || err.message || err);
                  // The peer is unreachable: set it DOWN
                  peer.status = 'DOWN';
                  return dal.savePeer(peer);
                });
            });
        }));
      })
      .then(function(){
        done();
      })
      .fail(done);
  };

  function syncBlock(callback) {
    var lastAdded = null;
    async.waterfall([
      function(next) {
        next('Do not want to sync');
      },
      function (next) {
        dal.getCurrentBlockOrNull(next);
      },
      function(current, next) {
        if (current) {
          logger.info("Check network for new blocks...");
          dal.findAllPeersNEWUPBut([selfPubkey])
            .then(function(peers){
              peers = _.shuffle(peers);
              return peers.reduce(function(promise, peer) {
                return promise
                  .fail(function(err){
                    if (err) {
                      logger.warn(err.message || err);
                    }
                    var p = new Peer(peer);
                    logger.info("Try with %s", p.getURL());
                    return Q.Promise(function(resolve, reject){
                      async.waterfall([
                        function(next) {
                          p.connect(next);
                        },
                        function(node, next) {
                          var errorWithPeer = false;
                          async.whilst(
                            function () {
                              return !errorWithPeer;
                            },
                            function (callback) {
                              async.waterfall([
                                function (next) {
                                  node.blockchain.current(function(err) {
                                    if (err) {
                                      return dal.setPeerDown(p.pubkey, function(err2) {
                                        next(err2 || err);
                                      });
                                    }
                                    next();
                                  });
                                },
                                function (next) {
                                  node.blockchain.block(current.number + 1, next);
                                },
                                function (block, next) {
                                  // Rawification of transactions
                                  block.transactions.forEach(function (tx) {
                                    tx.raw = ["TX", "1", tx.signatories.length, tx.inputs.length, tx.outputs.length, tx.comment ? '1' : '0'].join(':') + '\n';
                                    tx.raw += tx.signatories.join('\n') + '\n';
                                    tx.raw += tx.inputs.join('\n') + '\n';
                                    tx.raw += tx.outputs.join('\n') + '\n';
                                    if (tx.comment)
                                      tx.raw += tx.comment + '\n';
                                    tx.raw += tx.signatures.join('\n') + '\n';
                                    tx.version = 1;
                                    tx.currency = conf.currency;
                                    tx.issuers = tx.signatories;
                                    tx.hash = ("" + sha1(rawer.getTransaction(tx))).toUpperCase();
                                  });
                                  logger.info("Downloaded block #%s from peer ", block.number, p.getNamedURL());
                                  BlockchainService.submitBlock(block, true, next);
                                },
                                function(block, next) {
                                  current = block;
                                  lastAdded = block;
                                  next();
                                }
                              ], callback);
                            },
                            function (err) {
                              errorWithPeer = err ? true : false;
                              if (errorWithPeer) {
                                next(err);
                              }
                            }
                          );
                        }
                      ], function(err) {
                        err ? reject(err) : resolve();
                      });
                    });
                  })
              }, Q.reject())
                .fail(function(){
                  logger.info("No new block found");
                  if (lastAdded) {
                    peerserver.router().write(_.extend({ type: 'Block' }, lastAdded));
                  }
                  next();
                });
            });
        }
        else next();
      }
    ], function(err) {
      if (err) {
        logger.warn(err.code || err.message || err);
      }
      callback();
    });
  }
}

util.inherits(PeeringService, events.EventEmitter);

module.exports = function (peerserver, pair, signFunc, dal) {
  return new PeeringService(peerserver, pair, signFunc, dal);
};
<commit_msg>Fix #107: peers were never pulling blocks<commit_after>"use strict";
var util           = require('util');
var async          = require('async');
var _              = require('underscore');
var Q              = require('q');
var events         = require('events');
var logger         = require('../lib/logger')('peering');
var base58         = require('../lib/base58');
var sha1           = require('sha1');
var moment         = require('moment');
var rawer          = require('../lib/rawer');
var constants      = require('../lib/constants');
var localValidator = require('../lib/localValidator');

function PeeringService(peerserver, pair, signFunc, dal) {

  var conf = peerserver.conf;
  var currency = conf.currency;

  var Peer        = require('../lib/entity/peer');

  var selfPubkey;
  this.pubkey = selfPubkey;

  var peer = null, BlockchainService = null;
  var that = this;

  this.setKeyPair = function(keypair) {
    if (keypair) {
      pair = keypair;
      selfPubkey = base58.encode(pair.publicKey);
      that.pubkey = selfPubkey;
    }
  };

  this.setSignFunc = function(f) {
    signFunc = f;
  };

  this.setBlockchainService = function(service) {
    BlockchainService = service;
  };

  this.peer = function (newPeer) {
    if (newPeer) {
      peer = newPeer;
    }
    return Peer.statics.peerize(peer);
  };

  this.load = function (done) {
  };

  this.submit = function(peering, done){
    var peer = new Peer(peering);
    var sp = peer.block.split('-');
    var number = sp[0];
    var sigTime = 0;
    async.waterfall([
      function (next) {
        localValidator(null).checkPeerSignature(peer, next);
      },
      function (next) {
        if (peer.block == constants.PEER.SPECIAL_BLOCK) {
          peer.statusTS = 0;
          peer.status = 'UP';
          next(null, null);
        }
        // Check if document is based upon an existing block as time reference
        else dal.getBlockOrNull(number, next);
      },
      function (block, next){
        sigTime = block ? block.medianTime : 0;
        peer.statusTS = sigTime;
        dal.getPeerOrNull(peer.pubkey, next);
      },
      function (found, next){
        var peerEntity = Peer.statics.peerize(found || peer);
        var previousHash = null;
        if(found){
          // Already existing peer
          var sp2 = found.block.split('-');
          var number2 = sp2[0];
          if(number <= number2){
            next(constants.ERROR.PEER.ALREADY_RECORDED);
            return;
          }
          peerEntity = Peer.statics.peerize(found);
          previousHash = peerEntity.hash;
          peer.copyValues(peerEntity);
          peerEntity.sigDate = new Date(sigTime*1000);
        }
        // Set the peer as UP again
        peerEntity.status = 'UP';
        dal.savePeer(peerEntity, function (err) {
          next(err, peerEntity, previousHash);
        });
      },
      function (recordedPR, previousHash, next) {
        dal.updateMerkleForPeers(function(err) {
          next(err, Peer.statics.peerize(recordedPR));
        });
      }
    ], done);
  };

  var peerFifo = async.queue(function (task, callback) {
    task(callback);
  }, 1);
  var peerInterval = null;
  this.regularPeerSignal = function (done) {
    if (peerInterval)
      clearInterval(peerInterval);
    peerInterval = setInterval(function () {
      peerFifo.push(_.partial(generateSelfPeer, peerserver.conf));
    }, 1000*conf.avgGenTime*constants.NETWORK.STATUS_INTERVAL.UPDATE);
    generateSelfPeer(peerserver.conf, done);
  };

  var syncBlockFifo = async.queue(function (task, callback) {
    task(callback);
  }, 1);
  var syncBlockInterval = null;
  this.regularSyncBlock = function (done) {
    if (syncBlockInterval)
      clearInterval(syncBlockInterval);
    syncBlockInterval = setInterval(function () {
      syncBlockFifo.push(syncBlock);
    }, 1000*conf.avgGenTime*constants.NETWORK.SYNC_BLOCK_INTERVAL);
    syncBlock(done);
  };

  function generateSelfPeer(conf, done) {
    var currency = conf.currency;
    var current = null;
    async.waterfall([
      function (next) {
        peerserver.BlockchainService.current(next);
      },
      function (currentBlock, next) {
        current = currentBlock;
        peerserver.dal.findPeers(peerserver.PeeringService.pubkey, next);
      },
      function (peers, next) {
        var p1 = { version: 1, currency: currency };
        if(peers.length != 0){
          p1 = _(peers[0]).extend({ version: 1, currency: currency });
        }
        var endpoint = 'BASIC_MERKLED_API';
        if (conf.remotehost) {
          endpoint += ' ' + conf.remotehost;
        }
        if (conf.remoteipv4) {
          endpoint += ' ' + conf.remoteipv4;
        }
        if (conf.remoteipv6) {
          endpoint += ' ' + conf.remoteipv6;
        }
        if (conf.remoteport) {
          endpoint += ' ' + conf.remoteport;
        }
        var p2 = {
          version: 1,
          currency: currency,
          pubkey: peerserver.PeeringService.pubkey,
          block: current ? [current.number, current.hash].join('-') : constants.PEER.SPECIAL_BLOCK,
          endpoints: [endpoint]
        };
        var raw1 = new Peer(p1).getRaw().dos2unix();
        var raw2 = new Peer(p2).getRaw().dos2unix();
        if (raw1 != raw2) {
          logger.debug('Generating server\'s peering entry...');
          async.waterfall([
            function (next){
              peerserver.sign(raw2, next);
            },
            function (signature, next) {
              p2.signature = signature;
              p2.pubkey = peerserver.PeeringService.pubkey;
              peerserver.submit(p2, false, next);
            }
          ], function (err) {
            next(err);
          });
        } else {
          peerserver.push(p1);
          next();
        }
      },
      function (next){
        peerserver.dal.getPeer(peerserver.PeeringService.pubkey, next);
      },
      function (peer, next){
        // Set peer's statut to UP
        peerserver.PeeringService.peer(peer);
        peerserver.push(peer);
        next();
      }
    ], function(err) {
      done(err);
    });
  }

  this.testPeers = function(done) {
    dal.getAllPeers()
      .then(function(peers){
        peers = _.filter(peers, function(p){ return p.pubkey != selfPubkey; });
        return Q.all(peers.map(function(p) {
          var peer = new Peer(p);
          return Q.nfcall(peer.connect)
            .then(function(node){
              // Get peering record of each peer
              return Q.nfcall(node.network.peering.get)
                .then(function(peering){
                  // Submit the peering
                  return Q.nfcall(that.submit, peering)
                    .fail(function(err) {
                      // Not a problem!
                      logger.warn("Peer record %s: %s", peer.pubkey, err.code || err.message || err);
                    });
                })
                .fail(function(err){
                  logger.warn("Peer %s: %s", peer.pubkey, err.code || err.message || err);
                  // The peer is unreachable: set it DOWN
                  peer.status = 'DOWN';
                  return dal.savePeer(peer);
                });
            });
        }));
      })
      .then(function(){
        done();
      })
      .fail(done);
  };

  function syncBlock(callback) {
    var lastAdded = null;
    async.waterfall([
      function (next) {
        dal.getCurrentBlockOrNull(next);
      },
      function(current, next) {
        if (current) {
          logger.info("Check network for new blocks...");
          dal.findAllPeersNEWUPBut([selfPubkey])
            .then(function(peers){
              peers = _.shuffle(peers);
              return peers.reduce(function(promise, peer) {
                return promise
                  .fail(function(err){
                    if (err) {
                      logger.warn(err.message || err);
                    }
                    var p = new Peer(peer);
                    logger.info("Try with %s", p.getURL());
                    return Q.Promise(function(resolve, reject){
                      async.waterfall([
                        function(next) {
                          p.connect(next);
                        },
                        function(node, next) {
                          var errorWithPeer = false;
                          async.whilst(
                            function () {
                              return !errorWithPeer;
                            },
                            function (callback) {
                              async.waterfall([
                                function (next) {
                                  node.blockchain.current(function(err) {
                                    if (err) {
                                      return dal.setPeerDown(p.pubkey, function(err2) {
                                        next(err2 || err);
                                      });
                                    }
                                    next();
                                  });
                                },
                                function (next) {
                                  node.blockchain.block(current.number + 1, next);
                                },
                                function (block, next) {
                                  // Rawification of transactions
                                  block.transactions.forEach(function (tx) {
                                    tx.raw = ["TX", "1", tx.signatories.length, tx.inputs.length, tx.outputs.length, tx.comment ? '1' : '0'].join(':') + '\n';
                                    tx.raw += tx.signatories.join('\n') + '\n';
                                    tx.raw += tx.inputs.join('\n') + '\n';
                                    tx.raw += tx.outputs.join('\n') + '\n';
                                    if (tx.comment)
                                      tx.raw += tx.comment + '\n';
                                    tx.raw += tx.signatures.join('\n') + '\n';
                                    tx.version = 1;
                                    tx.currency = conf.currency;
                                    tx.issuers = tx.signatories;
                                    tx.hash = ("" + sha1(rawer.getTransaction(tx))).toUpperCase();
                                  });
                                  logger.info("Downloaded block #%s from peer ", block.number, p.getNamedURL());
                                  BlockchainService.submitBlock(block, true, next);
                                },
                                function(block, next) {
                                  current = block;
                                  lastAdded = block;
                                  next();
                                }
                              ], callback);
                            },
                            function (err) {
                              errorWithPeer = err ? true : false;
                              if (errorWithPeer) {
                                next(err);
                              }
                            }
                          );
                        }
                      ], function(err) {
                        err ? reject(err) : resolve();
                      });
                    });
                  })
              }, Q.reject())
                .fail(function(){
                  logger.info("No new block found");
                  if (lastAdded) {
                    peerserver.router().write(_.extend({ type: 'Block' }, lastAdded));
                  }
                  next();
                });
            });
        }
        else next();
      }
    ], function(err) {
      if (err) {
        logger.warn(err.code || err.message || err);
      }
      callback();
    });
  }
}

util.inherits(PeeringService, events.EventEmitter);

module.exports = function (peerserver, pair, signFunc, dal) {
  return new PeeringService(peerserver, pair, signFunc, dal);
};
<|endoftext|>


<commit_before>import Service from '@ember/service';
import {inject as service} from '@ember/service';

export default class MembersUtilsService extends Service {
    @service settings;

    get isStripeEnabled() {
        let membersSubscriptionSettings = this.settings.parseSubscriptionSettings(this.settings.get('membersSubscriptionSettings'));
        let stripeEnabled = membersSubscriptionSettings && !!(membersSubscriptionSettings.paymentProcessors[0].config.secret_token) && !!(membersSubscriptionSettings.paymentProcessors[0].config.public_token);
        return stripeEnabled;
    }
}
<commit_msg>Updated isStripeEnabled to use stripeConnectIntegration<commit_after>import Service from '@ember/service';
import {inject as service} from '@ember/service';

export default class MembersUtilsService extends Service {
    @service settings;

    get isStripeEnabled() {
        let stripeConnectIntegration;

        try {
            stripeConnectIntegration = JSON.parse(this.settings.get('stripeConnectIntegration'));
        } catch (err) {
            stripeConnectIntegration = null;
        }

        let stripeConnectEnabled = stripeConnectIntegration && stripeConnectIntegration.account_id;

        let membersSubscriptionSettings = this.settings.parseSubscriptionSettings(this.settings.get('membersSubscriptionSettings'));
        let stripeEnabled = membersSubscriptionSettings && !!(membersSubscriptionSettings.paymentProcessors[0].config.secret_token) && !!(membersSubscriptionSettings.paymentProcessors[0].config.public_token);

        return stripeEnabled || stripeConnectEnabled;
    }
}
<|endoftext|>


<commit_before>var data = {
    "title": "Programmeringspraxis",
    "questions": [
        {
            "description": "En funktion som har bieffekter kan ej anses vara korrekt",
            "alternatives": ["Sant", "Falskt"],
            "answer": "Falskt"
        }, {
            "description": "Något annat än indata, utdata och bieffekter behöver ej beskrivas i funktionens kontrakt",
            "alternatives": ["Sant", "Falskt"],
            "answer": "Sant"
        }, {
            "description": "En 'single line'-kommentar ska beskriva vad koden gör, inte varför",
            "alternatives": ["Sant", "Falskt"],
            "answer": "Falskt"
        }, {
            "description": "En docstring ska sammanfatta vad funktionen gör, inte varför funktionen gör det",
            "alternatives": ["Sant", "Falskt"],
            "answer": "Sant"
        }, {
            "description": "Samtliga kommentarer i en funktion utgör funktionens kontrakt",
            "alternatives": ["Sant", "Falskt"],
            "answer": "Falskt"
        }, {
            "description": "Alla kommentarer ska skrivas med fullständiga meningar",
            "alternatives": ["Sant", "Falskt"],
            "answer": "Sant"
        }
    ]
}
<commit_msg>Remove question, and minor correction<commit_after>var data = {
    "title": "Programmeringspraxis",
    "questions": [
        {
            "description": "En funktion som har bieffekter kan ej anses vara korrekt",
            "alternatives": ["Sant", "Falskt"],
            "answer": "Falskt"
        }, {
            "description": "En enradskommentar ska beskriva vad koden gör, inte varför",
            "alternatives": ["Sant", "Falskt"],
            "answer": "Falskt"
        }, {
            "description": "En docstring ska sammanfatta vad funktionen gör, inte varför funktionen gör det",
            "alternatives": ["Sant", "Falskt"],
            "answer": "Sant"
        }, {
            "description": "Samtliga kommentarer i en funktion utgör funktionens kontrakt",
            "alternatives": ["Sant", "Falskt"],
            "answer": "Falskt"
        }, {
            "description": "Alla kommentarer ska skrivas med fullständiga meningar",
            "alternatives": ["Sant", "Falskt"],
            "answer": "Sant"
        }
    ]
}
<|endoftext|>


<commit_before>/* eslint-env greasemonkey, browser */
// ==UserScript==
// @name         byr-signature
// @namespace    weibo.com/flowmemo
// @version      0.2.0
// @description  为北邮人论坛发帖添加个性签名
// @author       flowmemo
// @match        *://bbs.byr.cn/*
// @match        *://bbs6.byr.cn/*
// @grant        GM_getValue
// @grant        GM_setValue
// @license      MIT
// @supportURL   https://github.com/flowmemo/byr-signature
// ==/UserScript==

; (function () {
  'use strict'
  const _DEBUG = false
  const log = (function () {
    if (_DEBUG) return console.log.bind(console)
    else return Function.prototype
  })()

  log('init')
  const added = new WeakSet()

  const defaultSig =
    `————
示例签名: 微博 [url=http://weibo.com/flowmemo][color=#0000FF]@flowmemo[/color][/url] , 现在主要写JavaScript. 关注广泛, 欢迎交流.
修改签名请到[url=https://bbs.byr.cn/#!article/WWWTechnology/post][color=#0000FF]这里[/color][/url]
[url=https://github.com/flowmemo/byr-signature][color=#0000FF]此签名通过「北邮人签名档」脚本发送[/color][/url]`

  function getUsername () {
    const user = document.querySelector('.u-login-id a')
    if (!user) {
      log('cannot find username')
      return false
    }
    const username = user.title
    return username
  }

  function getSig () {
    const username = getUsername()
    if (!username) return false
    const signature = GM_getValue('sig-' + username, defaultSig)
    return signature
  }

  function addPostSig () {
    log('addPostSig')
    const div = document.getElementById('post_content')
    if (!div || added.has(div)) return false
    added.add(div)
    div.value += '\n' + getSig()
    addSigPanel()
    return true
  }

  function addQuickSig () {
    log('addQuickSig')
    const div = document.getElementsByName('content')[0]
    if (!div || added.has(div)) return false
    added.add(div)
    div.value += '\n' + getSig()
    log('add value')
    return true
  }

  function addSigPanel () {
    log('sigPanel')
    const div = document.createElement('div')
    const postItems = document.getElementsByClassName('post-list-item')
    const referNode = postItems[postItems.length - 2]
    referNode.appendChild(div)
    div.outerHTML =
      `

byr-signature自定义签名

       <textarea class="post-textarea" name="sig-content" placeholder="在此输入你的签名"></textarea>

<input name="saveSig" type="button" value="保存"> 保存后刷新页面生效（注意保存你的发帖内容）

`

    const userSig = document.getElementsByName('sig-content')[0]
    userSig.value = getSig()
    const saveButton = document.getElementsByName('saveSig')[0]

    const username = getUsername()
    if (!username) return false
    saveButton.addEventListener('click', function () {
      log('save')
      log('username', username)
      GM_setValue('sig-' + username, userSig.value)
    })
  }

  function onPageChange () {
    log('onPageChange')
    if (window.location.href.match(/'#!article'/)) return
    if (window.location.href.match(/edit|post/)) {
      log(addPostSig())
    } else log(addQuickSig())
  }
  onPageChange()
  const observer = new MutationObserver(onPageChange)
  observer.observe(document.body, {childList: true, subtree: true})
})()
<commit_msg>update url matching<commit_after>/* eslint-env greasemonkey, browser */
// ==UserScript==
// @name         byr-signature
// @namespace    weibo.com/flowmemo
// @version      0.2.1
// @description  为北邮人论坛发帖添加个性签名
// @author       flowmemo
// @match        *://bbs.byr.cn/*
// @match        *://bbs6.byr.cn/*
// @grant        GM_getValue
// @grant        GM_setValue
// @license      MIT
// @supportURL   https://github.com/flowmemo/byr-signature
// ==/UserScript==

; (function () {
  'use strict'
  const _DEBUG = false
  const log = (function () {
    if (_DEBUG) return console.log.bind(console)
    else return Function.prototype
  })()

  log('init')
  const added = new WeakSet()

  const defaultSig =
    `————
示例签名: 微博 [url=http://weibo.com/flowmemo][color=#0000FF]@flowmemo[/color][/url] , 现在主要写JavaScript. 关注广泛, 欢迎交流.
修改签名请到[url=https://bbs.byr.cn/#!article/WWWTechnology/post][color=#0000FF]这里[/color][/url]
[url=https://github.com/flowmemo/byr-signature][color=#0000FF]此签名通过「北邮人签名档」脚本发送[/color][/url]`

  function getUsername () {
    const user = document.querySelector('.u-login-id a')
    if (!user) {
      log('cannot find username')
      return false
    }
    const username = user.title
    return username
  }

  function getSig () {
    const username = getUsername()
    if (!username) return false
    const signature = GM_getValue('sig-' + username, defaultSig)
    return signature
  }

  function addPostSig () {
    log('addPostSig')
    const div = document.getElementById('post_content')
    if (!div || added.has(div)) return false
    added.add(div)
    div.value += '\n' + getSig()
    addSigPanel()
    return true
  }

  function addQuickSig () {
    log('addQuickSig')
    const div = document.getElementsByName('content')[0]
    if (!div || added.has(div)) return false
    added.add(div)
    div.value += '\n' + getSig()
    log('add value')
    return true
  }

  function addSigPanel () {
    log('sigPanel')
    const div = document.createElement('div')
    const postItems = document.getElementsByClassName('post-list-item')
    const referNode = postItems[postItems.length - 2]
    referNode.appendChild(div)
    div.outerHTML =
      `

byr-signature自定义签名

       <textarea class="post-textarea" name="sig-content" placeholder="在此输入你的签名"></textarea>

<input name="saveSig" type="button" value="保存"> 保存后刷新页面生效（注意保存你的发帖内容）

`

    const userSig = document.getElementsByName('sig-content')[0]
    userSig.value = getSig()
    const saveButton = document.getElementsByName('saveSig')[0]

    const username = getUsername()
    if (!username) return false
    saveButton.addEventListener('click', function () {
      log('save')
      log('username', username)
      GM_setValue('sig-' + username, userSig.value)
    })
  }

  function onPageChange () {
    log('onPageChange')
    const URL = window.location.href
    if (URL.indexOf('#!article') === -1 || URL.indexOf('edit') > -1) return
    if (window.location.href.match(/post/)) {
      log(addPostSig())
    } else log(addQuickSig())
  }
  onPageChange()
  const observer = new MutationObserver(onPageChange)
  observer.observe(document.body, {childList: true, subtree: true})
})()
<|endoftext|>


<commit_before>// ==UserScript==
// @name         byr-signature
// @namespace    https://github.com/flowmemo/byr-signature
// @version      0.1.4
// @description  为北邮人论坛发帖添加个性签名
// @author       flowmemo
// @match        https://bbs.byr.cn/*
// @grant        GM_getValue
// @grant        GM_setValue
// @license      MIT
// ==/UserScript==

// JavaScript Standard Style: https://github.com/feross/standard

;(function () {
  var _DEBUG = 0
  function log () {
    if (_DEBUG === 1) console.log.apply(console, arguments)
  }
  'use strict'
  log('hello')
  var defaultSig = '————\n' +
        '示例签名:\n' +
        '微博 [url=http://weibo.com/flowmemo][color=#0000FF]@flowmemo[/color][/url] , 现在主要写JavaScript. 关注广泛, 欢迎交流.\n' +
        '\n' +
        '[url=https://github.com/flowmemo/byr-signature][color=#0000FF]此签名通过「北邮人签名档」脚本发送[/color][/url]'
  var id // timeout id
  function addPostSig () {
    log('addPostSig')
    var signature = GM_getValue('sig', defaultSig)
    var div = document.getElementById('post_content')
    if (!div) return
    div.value += '\n' + signature
    return true
  }
  function addQuickSig () {
    log('addQuickSig')
    var signature = GM_getValue('sig', defaultSig)
    var page = document.querySelector('.page-select a')
    if (!page) return
    var curPage = page.text
    if (!window.location.href.match(/\?p=(\d+)/)) {
      if (curPage !== '1') return
    } else {
      var hrefPage = window.location.href.match(/\?p=(\d+)/)[1]
      if (hrefPage !== curPage) return
    }

    var div = document.getElementsByName('content')[0]
    if (!div) return
    div.value += '\n' + signature
    log('add value')
    return true
  }
  function polling () {
    log('polling')
    if (window.location.href.indexOf('#!article') === -1) return
    window.clearTimeout(id) // clear timeout if it already exists

    function cb () {
      if (window.location.href.indexOf('/post') > -1) {
        if (addPostSig()) {
          sigPanel()
          return
        }
      } else if (addQuickSig()) return
      id = setTimeout(cb, 300)
    }
    cb()
  }
  function sigPanel () {
    log('sigPanel')

    var div = document.createElement('div')
    var postItems = document.getElementsByClassName('post-list-item')
    var referNode = postItems[postItems.length - 2]
    referNode.appendChild(div)

    div.outerHTML = '

' +
            '
byr-signature自定义签名
' +
            '
' +
            '<textarea class="post-textarea" name="sig-content" placeholder="在此输入你的签名"></textarea>
' +
            '

<input name="saveSig" type="button" value="保存"> 保存后刷新页面生效（注意保存你的发帖内容）

'
    var userSig = document.getElementsByName('sig-content')[0]
    userSig.value = GM_getValue('sig', defaultSig)
    var saveButton = document.getElementsByName('saveSig')[0]
    saveButton.addEventListener('click', function () {
      GM_setValue('sig', (userSig.value))
    })
  }
  if (window.location.href.indexOf('#!article') > -1) polling()
  window.addEventListener('hashchange', polling)
})()
<commit_msg>beautify code<commit_after>// ==UserScript==
// @name         byr-signature
// @namespace    https://github.com/flowmemo/byr-signature
// @version      0.1.5
// @description  为北邮人论坛发帖添加个性签名
// @author       flowmemo
// @match        https://bbs.byr.cn/*
// @grant        GM_getValue
// @grant        GM_setValue
// @license      MIT
// ==/UserScript==

// JavaScript Standard Style: https://github.com/feross/standard

;(function () {
  'use strict'

  log('hello')

  var _DEBUG = 0
  function log () {
    if (_DEBUG === 1) console.log.apply(console, arguments)
  }

  var defaultSig = '————\n' +
        '示例签名:\n' +
        '微博 [url=http://weibo.com/flowmemo][color=#0000FF]@flowmemo[/color][/url] , 现在主要写JavaScript. 关注广泛, 欢迎交流.\n' +
        '\n' +
        '[url=https://github.com/flowmemo/byr-signature][color=#0000FF]此签名通过「北邮人签名档」脚本发送[/color][/url]'
  var id // timeout id

  function addPostSig () {
    log('addPostSig')
    var signature = GM_getValue('sig', defaultSig)
    var div = document.getElementById('post_content')
    if (!div) return
    div.value += '\n' + signature
    return true
  }

  function addQuickSig () {
    log('addQuickSig')

    var signature = GM_getValue('sig', defaultSig)
    var page = document.querySelector('.page-select a')
    if (!page) return
    var curPage = page.text
    if (!window.location.href.match(/\?p=(\d+)/)) {
      if (curPage !== '1') return
    } else {
      var hrefPage = window.location.href.match(/\?p=(\d+)/)[1]
      if (hrefPage !== curPage) return
    }

    var div = document.getElementsByName('content')[0]
    if (!div) return
    div.value += '\n' + signature
    log('add value')
    return true
  }

  function addSigPanel () {
    log('sigPanel')

    var div = document.createElement('div')
    var postItems = document.getElementsByClassName('post-list-item')
    var referNode = postItems[postItems.length - 2]
    referNode.appendChild(div)

    div.outerHTML = '

' +
            '
byr-signature自定义签名
' +
            '
' +
            '<textarea class="post-textarea" name="sig-content" placeholder="在此输入你的签名"></textarea>
' +
            '

<input name="saveSig" type="button" value="保存"> 保存后刷新页面生效（注意保存你的发帖内容）

'

    var userSig = document.getElementsByName('sig-content')[0]
    userSig.value = GM_getValue('sig', defaultSig)
    var saveButton = document.getElementsByName('saveSig')[0]
    saveButton.addEventListener('click', function () {
      GM_setValue('sig', (userSig.value))
    })
  }

  function polling () {
    log('polling')

    if (window.location.href.indexOf('#!article') === -1) return
    window.clearTimeout(id) // clear timeout if it already exists

    ;(function cb () {
      if (window.location.href.indexOf('/post') > -1) {
        if (addPostSig()) {
          addSigPanel()
          return
        }
      } else if (addQuickSig()) return
      id = setTimeout(cb, 300)
    })()
  }

  if (window.location.href.indexOf('#!article') > -1) polling()
  window.addEventListener('hashchange', polling)
})()
<|endoftext|>


<commit_before>import { legacyVendorAssetsGlob, sprinkles, sprinklesDir, vendorAssetsDir } from "./util.js";
import { bower as mergeBowerDeps, npm as mergeNpmDeps } from "@userfrosting/merge-package-dependencies";
import { browserifyDependencies } from "@userfrosting/browserify-dependencies";
import { sync as deleteSync } from "del";
import childProcess, { exec as _exec } from "child_process";
import { existsSync } from "fs";
import { promisify } from "util";
import { GulpLogLogger } from "@userfrosting/ts-log-adapter-gulplog";

// Promisify exec
const exec = promisify(_exec);

/**
 * Runs the provided command and captures output.
 * @todo Add support for realtime logging
 * @param {string} source Used to annotate logs.
 * @param {string} cmd Command to execute.
 * @param {childProcess.ExecOptions} options Options to pass to `exec`.
 */
async function runCommand(source, cmd, options) {
    const log = new GulpLogLogger(`${source}> ${cmd}`)
    log.info("Running command");

    try {
        const result = await exec(cmd, options);
        if (result.stdout) result.stdout.split("\n").forEach(msg => log.info(msg));
        if (result.stderr) result.stderr.split("\n").forEach(msg => log.error(msg));
    } catch (e) {
        /** @type {{ stdout: string, stderr: string }} */
        const err = e;
        if (e.stdout) err.stdout.split("\n").forEach(msg => log.info(msg));
        if (e.stderr) err.stderr.split("\n").forEach(msg => log.error(msg));
        log.error("Command has completed with an error");
        throw e;
    }

    log.info("Command has completed successfully");
}

/**
 * Installs vendor assets. Mapped to npm script "uf-assets-install".
 */
export async function assetsInstall() {
    const log = new GulpLogLogger(assetsInstall.name);

    // Clean up any legacy assets
    if (deleteSync(legacyVendorAssetsGlob, { force: true }))
        log.info("Legacy frontend vendor assets were deleted. Frontend vendor assets are now installed to 'app/assets'.");

    // See if there are any npm packages
    // TODO Would be better to read in file here then hand it off since we can avoid redundant `existsSync` calls
    const npmPaths = [];
    for (const sprinkle of sprinkles) {
        const path = sprinklesDir + sprinkle + "/package.json";
        if (existsSync(path)) npmPaths.push(path);
    }

    if (npmPaths.length > 0) {
        // Install npm dependencies
        log.info("Installing vendor assets with NPM...")

        // Remove package.json and package-lock.json (if it happens to exist)
        deleteSync(vendorAssetsDir + "package.json", { force: true });
        deleteSync(vendorAssetsDir + "package-lock.json", { force: true });

        // Generate package.json
        const npmTemplate = {
            // Private makes sure it isn't published, and cuts out a lot of unnecessary fields.
            private: true
        };
        log.info("Collating dependencies...");
        /** @type {{ private: boolean, dependencies?: { [x: string]: string } }} */
        const pkg = mergeNpmDeps(npmTemplate, npmPaths, vendorAssetsDir, true);
        log.info("Dependency collation complete.");

        log.info("Using npm from PATH variable");

        // Remove any existing unneeded dependencies
        log.info("Removing extraneous dependencies");
        await runCommand(assetsInstall.name, "npm prune", { cwd: vendorAssetsDir });

        // Perform installation
        log.info("Installing dependencies");
        await runCommand(assetsInstall.name, "npm install", { cwd: vendorAssetsDir });

        // Conduct audit
        log.info("Running audit");
        try {
            await runCommand(assetsInstall.name, "npm audit", { cwd: vendorAssetsDir });
        }
        catch {
            log.warn("There appear to be some vulnerabilities within your dependencies. Updating is recommended.");
        }

        // Browserify dependencies
        log.info("Compiling compatible node modules into UMD bundles with browserify");
        deleteSync(vendorAssetsDir + "browser_modules/", { force: true });
        await browserifyDependencies({
            dependencies: Object.keys(pkg.dependencies ?? []),
            inputDir: vendorAssetsDir + "node_modules/",
            outputDir: vendorAssetsDir + "browser_modules/",
            silentFailures: true,
        });
    }
    else {
        // Delete npm artefacts
        deleteSync([
            vendorAssetsDir + "package.json",
            vendorAssetsDir + "node_modules/",
            vendorAssetsDir + "package-lock.json",
            vendorAssetsDir + "browser_modules/"
        ], { force: true });
    }

    // See if there are any Bower packages
    // TODO Would be better to read in file here then hand it off since we can avoid redundant `existsSync` calls
    const bowerPaths = [];
    for (const sprinkle of sprinkles) {
        const path = sprinklesDir + sprinkle + "/bower.json";
        if (existsSync(path)) {
            log.warn(`DEPRECATED: Detected bower.json in ${sprinkle} Sprinkle. Support for bower (bower.json) will be removed in the future, please use npm/yarn (package.json) instead. https://learn.userfrosting.com/upgrading/41-to-42#bower-deprecation-new-npm-support`);
            bowerPaths.push(path);
        }
    }

    if (bowerPaths.length > 0) {
        // Install bower dependencies
        log.info("Installing vendor assets with Bower...")

        // TODO I think we might be able to get away with removing this
        deleteSync(vendorAssetsDir + "bower.json", { force: true });

        // Generate package.json
        const bowerTemplate = {
            name: "uf-vendor-assets"
        };
        log.info("Collating dependencies...");
        mergeBowerDeps(bowerTemplate, bowerPaths, vendorAssetsDir, true);
        log.info("Dependency collation complete.");

        // Perform installation
        log.info("Installed dependencies");
        // --allow-root stops bower from complaining about being in 'sudo' in various situations
        await runCommand(assetsInstall.name, "bower install -q --allow-root", { cwd: vendorAssetsDir });

        // Prune any unnecessary dependencies
        log.info("Removing extraneous dependencies");
        // --allow-root stops bower from complaining about being in 'sudo' in various situations
        await runCommand(assetsInstall.name, "bower prune -q --allow-root", { cwd: vendorAssetsDir });
    }
    else {
        // Remove bower artefacts
        deleteSync([
            vendorAssetsDir + "bower.json",
            vendorAssetsDir + "bower_components/"
        ], { force: true });
    }
};
<commit_msg>NodeJS 12 compatibility fix<commit_after>import { legacyVendorAssetsGlob, sprinkles, sprinklesDir, vendorAssetsDir } from "./util.js";
import { bower as mergeBowerDeps, npm as mergeNpmDeps } from "@userfrosting/merge-package-dependencies";
import { browserifyDependencies } from "@userfrosting/browserify-dependencies";
import { sync as deleteSync } from "del";
import childProcess, { exec as _exec } from "child_process";
import { existsSync } from "fs";
import { promisify } from "util";
import { GulpLogLogger } from "@userfrosting/ts-log-adapter-gulplog";

// Promisify exec
const exec = promisify(_exec);

/**
 * Runs the provided command and captures output.
 * @todo Add support for realtime logging
 * @param {string} source Used to annotate logs.
 * @param {string} cmd Command to execute.
 * @param {childProcess.ExecOptions} options Options to pass to `exec`.
 */
async function runCommand(source, cmd, options) {
    const log = new GulpLogLogger(`${source}> ${cmd}`)
    log.info("Running command");

    try {
        const result = await exec(cmd, options);
        if (result.stdout) result.stdout.split("\n").forEach(msg => log.info(msg));
        if (result.stderr) result.stderr.split("\n").forEach(msg => log.error(msg));
    } catch (e) {
        /** @type {{ stdout: string, stderr: string }} */
        const err = e;
        if (e.stdout) err.stdout.split("\n").forEach(msg => log.info(msg));
        if (e.stderr) err.stderr.split("\n").forEach(msg => log.error(msg));
        log.error("Command has completed with an error");
        throw e;
    }

    log.info("Command has completed successfully");
}

/**
 * Installs vendor assets. Mapped to npm script "uf-assets-install".
 */
export async function assetsInstall() {
    const log = new GulpLogLogger(assetsInstall.name);

    // Clean up any legacy assets
    if (deleteSync(legacyVendorAssetsGlob, { force: true }))
        log.info("Legacy frontend vendor assets were deleted. Frontend vendor assets are now installed to 'app/assets'.");

    // See if there are any npm packages
    // TODO Would be better to read in file here then hand it off since we can avoid redundant `existsSync` calls
    const npmPaths = [];
    for (const sprinkle of sprinkles) {
        const path = sprinklesDir + sprinkle + "/package.json";
        if (existsSync(path)) npmPaths.push(path);
    }

    if (npmPaths.length > 0) {
        // Install npm dependencies
        log.info("Installing vendor assets with NPM...")

        // Remove package.json and package-lock.json (if it happens to exist)
        deleteSync(vendorAssetsDir + "package.json", { force: true });
        deleteSync(vendorAssetsDir + "package-lock.json", { force: true });

        // Generate package.json
        const npmTemplate = {
            // Private makes sure it isn't published, and cuts out a lot of unnecessary fields.
            private: true
        };
        log.info("Collating dependencies...");
        /** @type {{ private: boolean, dependencies?: { [x: string]: string } }} */
        const pkg = mergeNpmDeps(npmTemplate, npmPaths, vendorAssetsDir, true);
        log.info("Dependency collation complete.");

        log.info("Using npm from PATH variable");

        // Remove any existing unneeded dependencies
        log.info("Removing extraneous dependencies");
        await runCommand(assetsInstall.name, "npm prune", { cwd: vendorAssetsDir });

        // Perform installation
        log.info("Installing dependencies");
        await runCommand(assetsInstall.name, "npm install", { cwd: vendorAssetsDir });

        // Conduct audit
        log.info("Running audit");
        try {
            await runCommand(assetsInstall.name, "npm audit", { cwd: vendorAssetsDir });
        }
        catch {
            log.warn("There appear to be some vulnerabilities within your dependencies. Updating is recommended.");
        }

        // Browserify dependencies
        log.info("Compiling compatible node modules into UMD bundles with browserify");
        deleteSync(vendorAssetsDir + "browser_modules/", { force: true });
        await browserifyDependencies({
            dependencies: Object.keys(pkg.dependencies || []),
            inputDir: vendorAssetsDir + "node_modules/",
            outputDir: vendorAssetsDir + "browser_modules/",
            silentFailures: true,
        });
    }
    else {
        // Delete npm artefacts
        deleteSync([
            vendorAssetsDir + "package.json",
            vendorAssetsDir + "node_modules/",
            vendorAssetsDir + "package-lock.json",
            vendorAssetsDir + "browser_modules/"
        ], { force: true });
    }

    // See if there are any Bower packages
    // TODO Would be better to read in file here then hand it off since we can avoid redundant `existsSync` calls
    const bowerPaths = [];
    for (const sprinkle of sprinkles) {
        const path = sprinklesDir + sprinkle + "/bower.json";
        if (existsSync(path)) {
            log.warn(`DEPRECATED: Detected bower.json in ${sprinkle} Sprinkle. Support for bower (bower.json) will be removed in the future, please use npm/yarn (package.json) instead. https://learn.userfrosting.com/upgrading/41-to-42#bower-deprecation-new-npm-support`);
            bowerPaths.push(path);
        }
    }

    if (bowerPaths.length > 0) {
        // Install bower dependencies
        log.info("Installing vendor assets with Bower...")

        // TODO I think we might be able to get away with removing this
        deleteSync(vendorAssetsDir + "bower.json", { force: true });

        // Generate package.json
        const bowerTemplate = {
            name: "uf-vendor-assets"
        };
        log.info("Collating dependencies...");
        mergeBowerDeps(bowerTemplate, bowerPaths, vendorAssetsDir, true);
        log.info("Dependency collation complete.");

        // Perform installation
        log.info("Installed dependencies");
        // --allow-root stops bower from complaining about being in 'sudo' in various situations
        await runCommand(assetsInstall.name, "bower install -q --allow-root", { cwd: vendorAssetsDir });

        // Prune any unnecessary dependencies
        log.info("Removing extraneous dependencies");
        // --allow-root stops bower from complaining about being in 'sudo' in various situations
        await runCommand(assetsInstall.name, "bower prune -q --allow-root", { cwd: vendorAssetsDir });
    }
    else {
        // Remove bower artefacts
        deleteSync([
            vendorAssetsDir + "bower.json",
            vendorAssetsDir + "bower_components/"
        ], { force: true });
    }
};
<|endoftext|>


<commit_before>const webpack           = require('webpack'),
      makeConfig        = require('../make-config'),
      projectConfig     = require('../../../config'),
      HtmlWebpackPlugin = require('html-webpack-plugin');

const config = makeConfig({
  name   : 'Client',
  target : 'web',
  entry  : {
    app : [
      projectConfig.inSrc('entry-points/client')
    ],
    vendor : projectConfig.VENDOR_DEPENDENCIES
  },
  output : {
    filename : '[name].[hash].js',
    path     : projectConfig.inDist('client'),
    publicPath : '/'
  }
});

// ------------------------------------
// Client-Specific Plugins
// ------------------------------------
// NOTE: this is a temporary workaround. I don't know how to get Karma
// to include the vendor bundle that webpack creates, so to get around that
// we remove the bundle splitting when webpack is used with Karma.
const commonChunkPlugin = new webpack.optimize.CommonsChunkPlugin(
  'vendor', '[name].[hash].js'
);
commonChunkPlugin.__KARMA_IGNORE__ = true;

config.plugins.push(
  new webpack.DefinePlugin({
    '__CLIENT__' : true,
    '__SERVER__' : false
  }),
  new HtmlWebpackPlugin({
    template : projectConfig.inSrc('index.html'),
    hash     : true,
    filename : 'index.html',
    minify   : projectConfig.__PROD__,
    inject   : 'body'
  }),
  commonChunkPlugin
);

// ------------------------------------
// Client-Specific Loaders
// ------------------------------------
config.module.loaders.push(
  {
    test : /\.scss$/,
    loaders : [
      'style-loader',
      'css-loader',
      'autoprefixer?browsers=last 2 version',
      'sass-loader?includePaths[]=' + projectConfig.inSrc('styles')
    ]
  }
);

module.exports = function makeClientConfig (type) {
  return require('./_' + (type || projectConfig.NODE_ENV))(config);
};
<commit_msg>Fix webpack client config<commit_after>const webpack           = require('webpack'),
      makeConfig        = require('../make-config'),
      projectConfig     = require('../../../config'),
      HtmlWebpackPlugin = require('html-webpack-plugin');

const config = makeConfig({
  name   : 'Client',
  target : 'web',
  entry  : {
    app : [
      projectConfig.inSrc('entry-points/client')
    ],
    vendor : projectConfig.VENDOR_DEPENDENCIES
  },
  output : {
    filename : '[name].[hash].js',
    path     : projectConfig.inDist('client'),
    publicPath : '/'
  }
});

// ------------------------------------
// Client-Specific Plugins
// ------------------------------------
// NOTE: this is a temporary workaround. I don't know how to get Karma
// to include the vendor bundle that webpack creates, so to get around that
// we remove the bundle splitting when webpack is used with Karma.
const commonChunkPlugin = new webpack.optimize.CommonsChunkPlugin(
  'vendor', '[name].[hash].js'
);
commonChunkPlugin.__KARMA_IGNORE__ = true;

config.plugins.push(
  new webpack.DefinePlugin({
    '__CLIENT__' : true,
    '__SERVER__' : false
  }),
  new HtmlWebpackPlugin({
    template : projectConfig.inSrc('index.html'),
    hash     : true,
    filename : 'index.html',
    minify   : projectConfig.__PROD__,
    inject   : 'body'
  }),
  commonChunkPlugin
);

// ------------------------------------
// Client-Specific Loaders
// ------------------------------------
config.module.loaders.push(
  {
    test : /\.scss$/,
    loaders : [
      'style-loader',
      'css-loader',
      'autoprefixer?browsers=last 2 version',
      'sass-loader?includePaths[]=' + projectConfig.inSrc('styles')
    ]
  }
);

module.exports = function makeClientConfig (type) {
  return require('./_' + (type || projectConfig.NODE_ENV).trim())(config);
};
<|endoftext|>


<commit_before>const appLocaleData = require('react-intl/locale-data/zh');

module.exports = {
  locale: 'zh-CN',
  data: appLocaleData,
  messages: {
    'app.header.search': '全文本搜索...',
    'app.header.menu.home': '首页',
    'app.header.menu.practice': '实践',
    'app.header.menu.pattern': '模式',
    'app.header.menu.components': '组件生态',
    'app.header.menu.spec': '设计规范',
    'app.header.menu.resource': '资源',
    'app.header.menu.start': '快速使用',
    'app.header.menu.business': '业务生态',
    'app.header.menu.customize-theme': '主题',
    'app.header.menu.icons': '图标',
    'app.header.lang': 'EN',
    'app.content.edit-page': '在 Github 上编辑此页！',
    'app.content.edit-demo': '在 Github 上编辑此示例！',
    'app.component.examples': '代码演示',
    'app.demo.copy': '复制代码',
    'app.demo.copied': '复制成功',
    'app.home.slogan': '一个 UI 组件库',
    'app.home.introduce': '设计规范',
    'app.home.start': '开发指引',
    'app.home.best-practice': '丰富的基础组件',
    'app.home.experience': '近一年的中后台设计实践，积累了大量的优秀案例。',
    'app.home.design-pattern': '设计模式',
    'app.home.theme': '主题模式',
    'app.home.them-detail': '个性化的主题模板,满足各种姿势的需求。',
    'app.home.pattern': '总结中后台设计中反复出现的问题，并提供相应的解决方案。',
    'app.home.reusable-components': '丰富的基础组件',
    'app.home.components-intro': '丰富、灵活、实用的基础组件，为业务产品提供强有力的设计支持。',
    'app.home.learn-more': '了解更多',
    'app.home.sub-slogan': '微小·确定·幸福',
    'app.home.vision': '这是一套致力于提升『用户』和『设计者』使用体验的中后台设计语言。',
    'app.footer.repo': '源码仓库',
    'app.footer.awesome': 'Awesome Groot',
    'app.footer.customize-theme': '定制主题',
    'app.footer.scaffold': '脚手架',
    'app.footer.scaffolds': '脚手架市场',
    'app.footer.dev-tools': '开发工具',
    'app.footer.dva': '应用框架',
    'app.footer.links': '相关站点',
    'app.footer.mobile': '移动版',
    'app.footer.data-vis': '数据可视化',
    'app.footer.eggjs': '企业级 Node 开发框架',
    'app.footer.encloseio': 'Node.js 编译器',
    'app.footer.motion': '设计动效',
    'app.footer.antd-library': 'Axure 部件库',
    'app.footer.design-platform': '蚂蚁金服设计平台',
    'app.footer.antux': '页面逻辑素材',
    'app.footer.community': '社区',
    'app.footer.change-log': '更新记录',
    'app.footer.faq': '常见问题',
    'app.footer.feedback': '反馈和建议',
    'app.footer.stackoverflow': 'StackOverflow',
    'app.footer.segmentfault': 'SegmentFault',
    'app.footer.discuss-en': '在线讨论 (English)',
    'app.footer.discuss-cn': '在线讨论 (中文)',
    'app.footer.bug-report': '报告 Bug',
    'app.footer.issues': '讨论列表',
    'app.footer.version': '文档版本：',
    'app.footer.author': '蚂蚁金服体验技术部出品',
    'app.publish.title': 'antd@2.0.0 发布！',
    'app.publish.greeting': '你好，',
    'app.publish.intro': ' 已正式发布，欢迎升级。',
    'app.publish.old-version-guide': '如果您还需要使用旧版，请查阅 ',
    'app.publish.old-version-tips': '，也可通过页面右下角的文档版本选择框进行切换。',
  },
};
<commit_msg>fixed: header 标题名称<commit_after>const appLocaleData = require('react-intl/locale-data/zh');

module.exports = {
  locale: 'zh-CN',
  data: appLocaleData,
  messages: {
    'app.header.search': '全文本搜索...',
    'app.header.menu.home': '首页',
    'app.header.menu.practice': '实践',
    'app.header.menu.pattern': '模式',
    'app.header.menu.components': '基础组件',
    'app.header.menu.spec': '设计规范',
    'app.header.menu.resource': '资源',
    'app.header.menu.start': '快速使用',
    'app.header.menu.business': '业务组件',
    'app.header.menu.customize-theme': '主题',
    'app.header.menu.icons': '图标',
    'app.header.lang': 'EN',
    'app.content.edit-page': '在 Github 上编辑此页！',
    'app.content.edit-demo': '在 Github 上编辑此示例！',
    'app.component.examples': '代码演示',
    'app.demo.copy': '复制代码',
    'app.demo.copied': '复制成功',
    'app.home.slogan': '一个 UI 组件库',
    'app.home.introduce': '设计规范',
    'app.home.start': '开发指引',
    'app.home.best-practice': '丰富的基础组件',
    'app.home.experience': '近一年的中后台设计实践，积累了大量的优秀案例。',
    'app.home.design-pattern': '设计模式',
    'app.home.theme': '主题模式',
    'app.home.them-detail': '个性化的主题模板,满足各种姿势的需求。',
    'app.home.pattern': '总结中后台设计中反复出现的问题，并提供相应的解决方案。',
    'app.home.reusable-components': '丰富的基础组件',
    'app.home.components-intro': '丰富、灵活、实用的基础组件，为业务产品提供强有力的设计支持。',
    'app.home.learn-more': '了解更多',
    'app.home.sub-slogan': '微小·确定·幸福',
    'app.home.vision': '这是一套致力于提升『用户』和『设计者』使用体验的中后台设计语言。',
    'app.footer.repo': '源码仓库',
    'app.footer.awesome': 'Awesome Groot',
    'app.footer.customize-theme': '定制主题',
    'app.footer.scaffold': '脚手架',
    'app.footer.scaffolds': '脚手架市场',
    'app.footer.dev-tools': '开发工具',
    'app.footer.dva': '应用框架',
    'app.footer.links': '相关站点',
    'app.footer.mobile': '移动版',
    'app.footer.data-vis': '数据可视化',
    'app.footer.eggjs': '企业级 Node 开发框架',
    'app.footer.encloseio': 'Node.js 编译器',
    'app.footer.motion': '设计动效',
    'app.footer.antd-library': 'Axure 部件库',
    'app.footer.design-platform': '蚂蚁金服设计平台',
    'app.footer.antux': '页面逻辑素材',
    'app.footer.community': '社区',
    'app.footer.change-log': '更新记录',
    'app.footer.faq': '常见问题',
    'app.footer.feedback': '反馈和建议',
    'app.footer.stackoverflow': 'StackOverflow',
    'app.footer.segmentfault': 'SegmentFault',
    'app.footer.discuss-en': '在线讨论 (English)',
    'app.footer.discuss-cn': '在线讨论 (中文)',
    'app.footer.bug-report': '报告 Bug',
    'app.footer.issues': '讨论列表',
    'app.footer.version': '文档版本：',
    'app.footer.author': '蚂蚁金服体验技术部出品',
    'app.publish.title': 'antd@2.0.0 发布！',
    'app.publish.greeting': '你好，',
    'app.publish.intro': ' 已正式发布，欢迎升级。',
    'app.publish.old-version-guide': '如果您还需要使用旧版，请查阅 ',
    'app.publish.old-version-tips': '，也可通过页面右下角的文档版本选择框进行切换。',
  },
};
<|endoftext|>