<commit_before>/**
 * Module dependencies.
 */

var _ = require('lodash'),
	util = require('util'),
	rc = require('rc');

/**
 * Captains Log
 *
 * @param {Object} overrides
 *
 * @option {Object} custom			[overrides Winston if specified]
 * @option {Array} transports		[array of already-configured&instantiated Winston transports-- overrides the defaults!!!]
 * @option {Object} logLevels		[optional - log levels object for winston.setLevels, defaults to npm conventions]
 * @option {String} level			[silly, verbose, info, debug, warn, error, or silent]
 * @option {Boolean} inspect		[defaults to true-- whether to make the log output more readable (combines all args into one string)]
 *
 */

var DEFAULT_OPTIONS = {
	level: 'info',
	inspect: true,
	logLevels: {
		silly: 0,
		verbose: 1,
		info: 2,
		debug: 3,
		warn: 4,
		error: 5,
		// crit: 6,
		// emerg: 6,
		// fail: 6,
		silent: 7
	},

	// By default, look for options in:
	//   + `.captainlogrc` files
	//   + `CAPTAINSLOG-*` env variables
	//
	// More on `rc` conventions:
	// https://github.com/dominictarr/rc#standards
	appName: 'captainslog'
};

/**
 *
 * Default logger = Winston
 * ====================================
 *
 * By default, this module encapsulates Winston, a popular logger
 * by @indexzero and the gals/guys over at Nodejitsu.
 *
 * More info/docs on Winston:
 * https://github.com/flatiron/winston
 *
 */

/**
 *
 * Using your own custom logger
 * ====================================
 *
 * To use a library other than Winstone, `overrides.custom` must be
 * passed in with, at minimum, an n-ary `log` method, e.g.:
 *
 * var log = new CaptainsLog({}, someLogger);
 * log.log()
 * log.log('blah')
 * log.log('blah', 'foo')
 * log.log('blah', 'foo', {bar: 'baz'})
 * log.log('blah', 'foo', {bar: 'baz'}, ['a', 3], 2, false);
 * // etc.
 */

module.exports = function CaptainsLog ( overrides ) {
	var self = this;

	// Grab winston inside of the constructor just to be safe.
	// (since it seems to maintain some global state)
	var winston = require('winston');

	// Options passed in programmatically are highest priority.
	if (typeof overrides !== 'object') { overrides = {}; }
	var options = _.cloneDeep(overrides);
	// Then `rc` configuration conventions.
	// (https://github.com/dominictarr/rc#standards)
	var rconf = rc(overrides.appName);
	rconf.level = rconf.level ||  // Accept command-line shortcuts:
	rconf.verbose ? 'verbose' :   // --verbose
	rconf.silent ? 'silent' :     // --silent
	rconf.silly ? 'silly' :       // --silly
	undefined;
	// Then the implicit defaults. (DEFAULT_OPTIONS above)
	_.defaults(options, rconf, DEFAULT_OPTIONS);

	// Tranports
	//
	// NOTE:
	// Custom transports may be registed by passing them in
	// the `options.transports` config.  However, this approach
	// will replace the built-in transports, so use w/ care.
	if (	options.transports &&
			!_.isArray(options.transports)
		) {
		throw new Error('Invalid `options.transports`.\n\n' +
			'Should be an array of instantiated Winston transports, e.g.:\n' +
			'new (winston.transports.Console)({' + '\n' +
		    '  level: "silly",' + '\n' +
		    '  colorize: "true",' + '\n' +
		    '  label: "foo"' + '\n' +
		    '}),' + '\n' +
		    'new (winston.transports.File)({' + '\n' +
		    '  filename: "/path/to/some/file"' + '\n' +
		    '})'
		);
	}

	// Default transports
	if ( !options.transports ) {

		var consoleOptions = _.extend({
			json: false,
			colorize: true,
			timestamp: false
		}, options);

		// Build the transports array
		options.transports = [
			new (winston.transports.Console)(consoleOptions)
		];

		// TODO:
		// Consider adding default file log in the future.
		// (would need to figure out where to stream them)
		//
		// file: {
		// 	json: true,
		// 	colorize: false,
		// 	timestamp: true,
		// 	maxSize: 10000000,
		// 	maxFiles: 10
		// }
	}

	var logger;

	// If a logger override was specified, use it.
	if ( options.custom ) {
		logger = options.custom;

		// Make sure all supported log methods exist

		// We assume that at least something called
		// `logger.log` exists.
		if (!logger.log) {
			throw new Error(
				'Unsupported logger override!\n' +
				'(has no `.log()` method.)'
			);
		}

		// Fill in the gaps for the log methods
		// used by Sails core in case they're missing.
		logger.debug = logger.debug || logger.log;
		logger.info = logger.info || logger.log;
		logger.warn = logger.warn || logger.error || logger.log;
		logger.error = logger.error || logger.log;
		logger.verbose = logger.verbose || logger.log;
		logger.silly = logger.silly || logger.log;

		// Return logger
		return _buildLogger(logger);
	}

	// No override was specified, so we'll use winston.

	// Instantiate logger
	logger = new (winston.Logger)({
		levels: options.logLevels,
		transports: options.transports
	});

	// Return callable version of core logger
	return _buildLogger(logger);

	/**
	 * Return a special version of `logger` which may
	 * be called directly as a function (implicitly calls
	 * `logger.debug` behind the scenes)
	 *
	 * @param  {Object} logger [original logger]
	 * @return {Function}      [callable logger]
	 * @api private
	 */
	function _buildLogger( logger ) {

		// Make base logger callable (debug)
		var _logger = _inspect(logger.debug);

		// Mix-in log methods, but run `_inspect`
		// on their arguments to improve the readability
		// of log output.
		_logger.error = _inspect(logger.error);
		_logger.warn = _inspect(logger.warn);
		_logger.debug = _inspect(logger.debug);
		_logger.info = _inspect(logger.info);
		_logger.verbose = _inspect(logger.verbose);
		_logger.silly = _inspect(logger.silly);

		return _logger;
	}

	/**
	 * Build a log function which combines arguments into a string,
	 * enhancing them for readability.
	 *
	 * TODO: make this behavior configurable in `options`
	 * in the future (possibly on a per-transport basis)
	 *
	 * @return {Function} [log fn]
	 * @api private
	 */
	function _inspect( logFn ) {
		return function () {
			var args = Array.prototype.slice.call(arguments);

			// If options.inspect is disabled, just call the log fn normally
			if ( ! options.inspect ) {
				return logFn.apply(logFn, args);
			}

			// Compose `str` of all the arguments
			var pieces = [];
			var str = '';
			_.each(arguments, function(arg) {
				if (typeof arg === 'object') {
					if (arg instanceof Error) {
						pieces.push(arg.stack);
						return;
					}
					pieces.push(util.inspect(arg));
					return;
				}

				if (typeof arg === 'function') {
					pieces.push(arg.valueOf());
					return;
				}

				pieces.push(arg);
			});
			str = pieces.join(' ');

			// Call log fn
			return logFn.apply(logFn, [str]);
		};
	}

};

	// Hmm.. this approach doesn't seem to work:
	//
	// Register available transports, adding a category called 'sails'.
	// winston.loggers.add('sails', options.transports);
	// Instantiate a logger.  We'll call it the `sails` logger.
	// logger = winston.loggers.get('sails');

	// Ensure levels on the winston logger
	// are configured to use npm conventions.

	// Here are the log levels, for reference:
	/*
	{
		silly: 0,
		verbose: 1,
		info: 2,
		debug: 3,
		warn: 4,
		error: 5
	}
	*/

	// TODO: wtf winston??? what is going on here??
	// (the built-in `npm` levels don't seem to match npm's
	// documentation)
	//
	// logger.setLevels(options.logLevels || winston.config.npm.levels);
	//

	// new (winston.Logger)({
	// 	transports: transports
	// });

	// Now generate the configured logging methods:

	// Supported log methods
	// var log = _generateLogFn('debug');
	// log.log = _generateLogFn('debug');
	// log.error = _generateLogFn('error');
	// log.warn = _generateLogFn('warn');
	// log.debug = _generateLogFn('debug');
	// log.info = _generateLogFn('info');
	// log.verbose = _generateLogFn('verbose');
	// log.silly = _generateLogFn('silly');

	// Export logger object
	// return log;

	/**
	 * @returns a configured-level-aware log function
	 *          at the specified log level
	 */

	// function _generateLogFn(level) {
	// 	return function() {

	// 		var fn = logger[level];
	// 		fn(str);
	// 	};
	// }
	//
	//
	//
	//
	// If filePath option is set, ALSO write log output to a flat file
	// if (!_.isUndefined(options.filePath)) {
	// 	transports.push(
	// 		new(winston.transports.File)({
	// 			filename: options.filePath,
	// 			maxsize: options.maxSize,
	// 			maxFiles: options.maxFiles,
	// 			level: options.level,
	// 			json: options.json,
	// 			colorize: false,
	// 			stripColors: true
	// 		}));
	// }

	// // Winston's log levels
	// var winstonLogLevels = {
	//     silly: 0,
	//     verbose: 1,
	//     info: 2,
	//     notice: 2,
	//     debug: 3,
	//     alert: 4,
	//     warn: 4,
	//     warning: 4,
	//     error: 5,
	//     crit: 6,
	//     emerg: 6,
	//     fail: 6,
	//     silent: 7
	// };

	// // Colors to use for various log levels
	// var logColors = {
	//     silly: 'cyan',
	//     verbose: 'cyan',
	//     info: 'green',
	//     notice: 'green',
	//     debug: 'blue',
	//     alert: 'yellow',
	//     warn: 'yellow',
	//     warning: 'yellow',
	//     error: 'red',
	//     crit: 'red',
	//     emerg: 'red',
	//     fail: 'red',
	//     silent: 'white'
	// };

	// if adapter option is set, ALSO write log output to an adapter
	// if (!_.isUndefined(options.adapters)) {
	// 	_.each(options.adapters, function(val, transport) {
	// 		if (typeof val.module !== 'undefined') {

	// 			// The winston module MUST be exported to a variable of same name
	// 			var module = require(val.module)[transport];

	// 			// Make sure it was exported correctly
	// 			if (module) {
	// 				transports.push(
	// 					new(module)(val)
	// 				);
	// 			}
	// 		}
	// 	});
	// }

<commit_msg>fix rc name<commit_after>/**
 * Module dependencies.
 */

var _ = require('lodash'),
	util = require('util'),
	rc = require('rc');

/**
 * Captains Log
 *
 * @param {Object} overrides
 *
 * @option {Object} custom			[overrides Winston if specified]
 * @option {Array} transports		[array of already-configured&instantiated Winston transports-- overrides the defaults!!!]
 * @option {Object} logLevels		[optional - log levels object for winston.setLevels, defaults to npm conventions]
 * @option {String} level			[silly, verbose, info, debug, warn, error, or silent]
 * @option {Boolean} inspect		[defaults to true-- whether to make the log output more readable (combines all args into one string)]
 *
 */

var DEFAULT_OPTIONS = {
	level: 'info',
	inspect: true,
	logLevels: {
		silly: 0,
		verbose: 1,
		info: 2,
		debug: 3,
		warn: 4,
		error: 5,
		// crit: 6,
		// emerg: 6,
		// fail: 6,
		silent: 7
	}
};

// By default, look for options in:
//   + `.captainlogrc` files
//   + `CAPTAINSLOG-*` env variables
//
// More on `rc` conventions:
// https://github.com/dominictarr/rc#standards
var DEFAULT_RC_NAME = 'captainslog';

/**
 *
 * Default logger = Winston
 * ====================================
 *
 * By default, this module encapsulates Winston, a popular logger
 * by @indexzero and the gals/guys over at Nodejitsu.
 *
 * More info/docs on Winston:
 * https://github.com/flatiron/winston
 *
 */

/**
 *
 * Using your own custom logger
 * ====================================
 *
 * To use a library other than Winstone, `overrides.custom` must be
 * passed in with, at minimum, an n-ary `log` method, e.g.:
 *
 * var log = new CaptainsLog({}, someLogger);
 * log.log()
 * log.log('blah')
 * log.log('blah', 'foo')
 * log.log('blah', 'foo', {bar: 'baz'})
 * log.log('blah', 'foo', {bar: 'baz'}, ['a', 3], 2, false);
 * // etc.
 */

module.exports = function CaptainsLog ( overrides ) {
	var self = this;

	// Grab winston inside of the constructor just to be safe.
	// (since it seems to maintain some global state)
	var winston = require('winston');

	// Options passed in programmatically are highest priority.
	if (typeof overrides !== 'object') { overrides = {}; }
	var options = _.cloneDeep(overrides);

	// Then `rc` configuration conventions.
	// (https://github.com/dominictarr/rc#standards)
	var appName = overrides.appName || DEFAULT_RC_NAME;
	var rconf = rc(appName);
	rconf.level = rconf.level ||  // Accept command-line shortcuts:
	rconf.verbose ? 'verbose' :   // --verbose
	rconf.silent ? 'silent' :     // --silent
	rconf.silly ? 'silly' :       // --silly
	undefined;
	// Then the implicit defaults. (DEFAULT_OPTIONS above)
	_.defaults(options, rconf, DEFAULT_OPTIONS);

	// Tranports
	//
	// NOTE:
	// Custom transports may be registed by passing them in
	// the `options.transports` config.  However, this approach
	// will replace the built-in transports, so use w/ care.
	if (	options.transports &&
			!_.isArray(options.transports)
		) {
		throw new Error('Invalid `options.transports`.\n\n' +
			'Should be an array of instantiated Winston transports, e.g.:\n' +
			'new (winston.transports.Console)({' + '\n' +
		    '  level: "silly",' + '\n' +
		    '  colorize: "true",' + '\n' +
		    '  label: "foo"' + '\n' +
		    '}),' + '\n' +
		    'new (winston.transports.File)({' + '\n' +
		    '  filename: "/path/to/some/file"' + '\n' +
		    '})'
		);
	}

	// Default transports
	if ( !options.transports ) {

		var consoleOptions = _.extend({
			json: false,
			colorize: true,
			timestamp: false
		}, options);

		// Build the transports array
		options.transports = [
			new (winston.transports.Console)(consoleOptions)
		];

		// TODO:
		// Consider adding default file log in the future.
		// (would need to figure out where to stream them)
		//
		// file: {
		// 	json: true,
		// 	colorize: false,
		// 	timestamp: true,
		// 	maxSize: 10000000,
		// 	maxFiles: 10
		// }
	}

	var logger;

	// If a logger override was specified, use it.
	if ( options.custom ) {
		logger = options.custom;

		// Make sure all supported log methods exist

		// We assume that at least something called
		// `logger.log` exists.
		if (!logger.log) {
			throw new Error(
				'Unsupported logger override!\n' +
				'(has no `.log()` method.)'
			);
		}

		// Fill in the gaps for the log methods
		// used by Sails core in case they're missing.
		logger.debug = logger.debug || logger.log;
		logger.info = logger.info || logger.log;
		logger.warn = logger.warn || logger.error || logger.log;
		logger.error = logger.error || logger.log;
		logger.verbose = logger.verbose || logger.log;
		logger.silly = logger.silly || logger.log;

		// Return logger
		return _buildLogger(logger);
	}

	// No override was specified, so we'll use winston.

	// Instantiate logger
	logger = new (winston.Logger)({
		levels: options.logLevels,
		transports: options.transports
	});

	// Return callable version of core logger
	return _buildLogger(logger);

	/**
	 * Return a special version of `logger` which may
	 * be called directly as a function (implicitly calls
	 * `logger.debug` behind the scenes)
	 *
	 * @param  {Object} logger [original logger]
	 * @return {Function}      [callable logger]
	 * @api private
	 */
	function _buildLogger( logger ) {

		// Make base logger callable (debug)
		var _logger = _inspect(logger.debug);

		// Mix-in log methods, but run `_inspect`
		// on their arguments to improve the readability
		// of log output.
		_logger.error = _inspect(logger.error);
		_logger.warn = _inspect(logger.warn);
		_logger.debug = _inspect(logger.debug);
		_logger.info = _inspect(logger.info);
		_logger.verbose = _inspect(logger.verbose);
		_logger.silly = _inspect(logger.silly);

		return _logger;
	}

	/**
	 * Build a log function which combines arguments into a string,
	 * enhancing them for readability.
	 *
	 * TODO: make this behavior configurable in `options`
	 * in the future (possibly on a per-transport basis)
	 *
	 * @return {Function} [log fn]
	 * @api private
	 */
	function _inspect( logFn ) {
		return function () {
			var args = Array.prototype.slice.call(arguments);

			// If options.inspect is disabled, just call the log fn normally
			if ( ! options.inspect ) {
				return logFn.apply(logFn, args);
			}

			// Compose `str` of all the arguments
			var pieces = [];
			var str = '';
			_.each(arguments, function(arg) {
				if (typeof arg === 'object') {
					if (arg instanceof Error) {
						pieces.push(arg.stack);
						return;
					}
					pieces.push(util.inspect(arg));
					return;
				}

				if (typeof arg === 'function') {
					pieces.push(arg.valueOf());
					return;
				}

				pieces.push(arg);
			});
			str = pieces.join(' ');

			// Call log fn
			return logFn.apply(logFn, [str]);
		};
	}

};

	// Hmm.. this approach doesn't seem to work:
	//
	// Register available transports, adding a category called 'sails'.
	// winston.loggers.add('sails', options.transports);
	// Instantiate a logger.  We'll call it the `sails` logger.
	// logger = winston.loggers.get('sails');

	// Ensure levels on the winston logger
	// are configured to use npm conventions.

	// Here are the log levels, for reference:
	/*
	{
		silly: 0,
		verbose: 1,
		info: 2,
		debug: 3,
		warn: 4,
		error: 5
	}
	*/

	// TODO: wtf winston??? what is going on here??
	// (the built-in `npm` levels don't seem to match npm's
	// documentation)
	//
	// logger.setLevels(options.logLevels || winston.config.npm.levels);
	//

	// new (winston.Logger)({
	// 	transports: transports
	// });

	// Now generate the configured logging methods:

	// Supported log methods
	// var log = _generateLogFn('debug');
	// log.log = _generateLogFn('debug');
	// log.error = _generateLogFn('error');
	// log.warn = _generateLogFn('warn');
	// log.debug = _generateLogFn('debug');
	// log.info = _generateLogFn('info');
	// log.verbose = _generateLogFn('verbose');
	// log.silly = _generateLogFn('silly');

	// Export logger object
	// return log;

	/**
	 * @returns a configured-level-aware log function
	 *          at the specified log level
	 */

	// function _generateLogFn(level) {
	// 	return function() {

	// 		var fn = logger[level];
	// 		fn(str);
	// 	};
	// }
	//
	//
	//
	//
	// If filePath option is set, ALSO write log output to a flat file
	// if (!_.isUndefined(options.filePath)) {
	// 	transports.push(
	// 		new(winston.transports.File)({
	// 			filename: options.filePath,
	// 			maxsize: options.maxSize,
	// 			maxFiles: options.maxFiles,
	// 			level: options.level,
	// 			json: options.json,
	// 			colorize: false,
	// 			stripColors: true
	// 		}));
	// }

	// // Winston's log levels
	// var winstonLogLevels = {
	//     silly: 0,
	//     verbose: 1,
	//     info: 2,
	//     notice: 2,
	//     debug: 3,
	//     alert: 4,
	//     warn: 4,
	//     warning: 4,
	//     error: 5,
	//     crit: 6,
	//     emerg: 6,
	//     fail: 6,
	//     silent: 7
	// };

	// // Colors to use for various log levels
	// var logColors = {
	//     silly: 'cyan',
	//     verbose: 'cyan',
	//     info: 'green',
	//     notice: 'green',
	//     debug: 'blue',
	//     alert: 'yellow',
	//     warn: 'yellow',
	//     warning: 'yellow',
	//     error: 'red',
	//     crit: 'red',
	//     emerg: 'red',
	//     fail: 'red',
	//     silent: 'white'
	// };

	// if adapter option is set, ALSO write log output to an adapter
	// if (!_.isUndefined(options.adapters)) {
	// 	_.each(options.adapters, function(val, transport) {
	// 		if (typeof val.module !== 'undefined') {

	// 			// The winston module MUST be exported to a variable of same name
	// 			var module = require(val.module)[transport];

	// 			// Make sure it was exported correctly
	// 			if (module) {
	// 				transports.push(
	// 					new(module)(val)
	// 				);
	// 			}
	// 		}
	// 	});
	// }

<|endoftext|>


<commit_before>#!/usr/bin/env node

'use strict';

var argv = require('optimist').argv;
if (argv.h || argv.help) {
  console.log('\nusage:');
  console.log('-h, --help        : show help');
  console.log('-p, --port        : port (default 5080)');
  console.log('-P, --pouch-port  : pouchdb-server port (default 16984)');
  console.log('-l, --log         : pouchdb-server log level (ev|short|tiny|combined|off)');
  console.log();
  return process.exit(0);
}

var port = argv.p || argv.port || 5080;
var pouchPort = argv.P || argv['pouch-port'] || 16984;

console.log('\nWelcome!');
console.log('To start using local-npm, just run: ');
console.log('\n  $ npm set registry http://127.0.0.1:' + port + '/fullfatdb');

var NUM_PARALLEL_TASKS = 10;

var SKIM_REMOTE = 'https://skimdb.npmjs.com/registry';
var SKIM_LOCAL = 'http://localhost:' + pouchPort + '/skimdb';
var FAT_LOCAL = 'http://localhost:' + pouchPort + '/fullfatdb';
var FAT_REMOTE = 'http://registry.npmjs.org';

var request = require('request');
var Promise = require('bluebird');
var fs = require('fs');
var Fullfat = require('npm-fullfat-registry');
var pouchdbServerLite = require('./pouchdb-server-lite');
var app = pouchdbServerLite.app;
var PouchDB = pouchdbServerLite.PouchDB;

var skimRemote = new PouchDB(SKIM_REMOTE);
var skimPouch = new PouchDB('skimdb');
var fatPouch = new PouchDB('fullfatdb');
// I tend to use more than the default 10 listeners
skimPouch.setMaxListeners(NUM_PARALLEL_TASKS * 50);
fatPouch.setMaxListeners(NUM_PARALLEL_TASKS * 50);

// replicate from central skimdb
var upToDate;
var startingTimeout = 1000;
var backoff = 1.1;
var sync;

function replicateSkim() {
  skimRemote.info().then(function (info) {
    sync = skimPouch.replicate.from(skimRemote, {
      live: true,
      batch_size: 1000
    }).on('change', function (change) {
      var percent = Math.min(100,
        (Math.floor(change.last_seq / info.update_seq * 10000) / 100).toFixed(2));
      console.log('Replicating skimdb, last_seq is: ' + change.last_seq + ' (' + percent + '%)');
    }).on('uptodate', function () {
      console.log('local skimdb is up to date');
      fs.writeFile('uptodate.txt', '1');
      upToDate = true;
    }).on('error', function (err) {
      console.error('error during replication with skimdb');
      console.error(err);
      restartReplication();
    });
  }).catch(function (err) {
    console.error('error doing info() on ' + SKIM_REMOTE);
    console.error(err);
    restartReplication();
  });
}
function restartReplication() {
  // just keep going
  startingTimeout *= backoff;
  setTimeout(replicateSkim, Math.round(startingTimeout));
}
replicateSkim();

var fullFat;

// start doing exciting shit
Promise.resolve().then(function () {
  return Promise.promisify(fs.readFile)('uptodate.txt').catch(function (err) {
    return '0'; // default
  });
}).then(function (upToDateFile) {
  upToDate = upToDateFile === '1';
  if (upToDate) {
    console.log('local skimdb is up to date');
  }
}).then(function () {
  // trick Fullfat into never doing automatic syncing; we don't use it for that
  return Promise.promisify(fs.writeFile)('registry.seq', '999999999999');
}).then(function () {
  fullFat = new Fullfat({
    skim: SKIM_LOCAL,
    fat: FAT_LOCAL,
    seq_file: 'registry.seq',
    missing_log: 'missing.log'
  })
  .on('start', function () {
    // we want the changes feed to only be local,
    // but all other operations should be remote
    // if we're not done replicating yet
    process.nextTick(function () {
      fullFat.skim = upToDate ? SKIM_LOCAL : SKIM_REMOTE;
    });
  })
  .on('error', function (err) {
    console.error("fullfat hit an error");
    console.error(err);
  });
}).then(function () {
  // start user-facing proxy server

  // only execute n fullFat.js operations in parallel
  var queues = [];
  for (var i = 0; i < NUM_PARALLEL_TASKS; i++) {
    queues.push(Promise.resolve());
  }

  function processWithFullFat(docId) {
    console.log(docId + ': got request...');
    // recover by fetching with fullFat
    // wait for changes to let us know it was fetched
    var queueIdx = docId.charCodeAt(0) % queues.length;
    queues[queueIdx] = queues[queueIdx].then(function () {
      console.log(docId + ': looking up in pouch');
      return new Promise(function (resolve, reject) {
        var changes = fatPouch.changes({
          since: 'latest',
          live: true
        }).on('change', function onChange(change) {
          console.log(change.id + ': got change');
          if (change.id === docId) {
            console.log('successfully wrote to local fullfatdb: ' + docId);
            changes.cancel();
            resolve();
          }
        }).on('error', function (err) {
          console.error('change hit error');
          reject(err);
        });
        console.log(docId + ': fetching in the background');
        // this seq is only used by fullFat to determine the file name to write tgz's to
        fullFat.getDoc({id: docId, seq: Math.round(Math.random() * 1000000)});
      });
    });
    return queues[queueIdx];
  }

  function updateAfterIncomingChange() {
    // keep a log of what the last seq we checked was
    fs.readFile('skim-seq.txt', function (err, data) {
      var seq = data ? parseInt(data) : 0;
      console.log('reading skimPouch changes since ' + seq);
      skimPouch.changes({since: seq, live: true}).on('change', function (change) {
        fs.writeFile('skim-seq.txt', change.seq.toString());
        fatPouch.allDocs({keys: [change.id]}).then(function (res) {
          if (res[0] && res[0].rev !== change.changes[0].rev) {
            console.log('new change came in for ' + change.id + ', updating...');
            return processWithFullFat(change.id);
          }
        }).catch(function (err) {
          console.error('unhandled skimPouch allDocs err');
          console.error(err);
        });
      }).on('error', function (err) {
        console.error('unhandled skimPouch changes err');
        console.error(err);
      });
    });
  }
  if (upToDate) {
    updateAfterIncomingChange();
  } else {
    skimPouch.on('uptodate', function () {
      fullFat.skim = SKIM_LOCAL; // internal API, probably shouldn't do this
      skimPouch.info().then(function (info) {
        return Promise.promisify(fs.writeFile)('skim-seq.txt',
            info.update_seq.toString()).then(function () {
          updateAfterIncomingChange();
        });
      }).catch(function (err) {
        console.error('unhandled writeFile err');
        console.error(err);
      });
    });
  }

  var server = require('http').createServer(function (req, res) {
    var docId;
    Promise.resolve().then(function () {
      if (req.method.toLowerCase() === 'get' &&
          /^\/fullfatdb\/([^\/]+)/.test(req.url)) {
        // simple doc or attachment request (get)
        var url = require('url').parse(req.url);
        docId = decodeURIComponent(url.pathname.split('/')[2]);
        console.log(docId + ': simple doc request');
        // check if it exists first
        return fatPouch.get(docId).catch(function (err) {
          if (err.status === 404) {
            console.log('not found locally: ' + docId);
            process.nextTick(function () {
              processWithFullFat(docId); // exec in background
            });
          }
          throw err;
        });
      } else {
        throw new Error('bad_local_request');
      }
    }).then(function (doc) {
      console.log(docId + ': doc exists locally');
      request.get('http://127.0.0.1:' + pouchPort + req.url).pipe(res);
    }).catch(function (err) {
      if (err.name === 'bad_local_request') {
        console.error('got a bad request: ' + req.url);
        res.status(400).send({error: 'this proxy only accepts GETs to /fullfatdb'});
      } else {
        console.error(docId + ': no local doc, need to proxy to remote');
        // we got an error, so let's proxy to the remote fat
        // so we can return immediately
        var url = req.url.replace(/^\/fullfatdb/, '');
        request.get(FAT_REMOTE + url).pipe(res);
      }
    });
  });
  server.listen(5080, function (err) {
    if (err) {
      console.error(err);
    } else {
      console.log('Proxy server started at http://127.0.0.1:' + port);
    }
  });
}).catch(function (err) {
  console.error(err);
  throw err;
});

process.on('SIGINT', function () {
  // close gracefully
  sync.cancel();
  Promise.all([fatPouch, skimPouch].map(function (pouch) {
    return pouch.close();
  })).then(function () {
    process.exit(0);
  });
});
<commit_msg>use the right registry, better intro output<commit_after>#!/usr/bin/env node

'use strict';

var argv = require('optimist').argv;
if (argv.h || argv.help) {
  console.log('\nusage:');
  console.log('-h, --help        : show help');
  console.log('-p, --port        : port (default 5080)');
  console.log('-P, --pouch-port  : pouchdb-server port (default 16984)');
  console.log('-l, --log         : pouchdb-server log level (ev|short|tiny|combined|off)');
  console.log();
  return process.exit(0);
}

var port = argv.p || argv.port || 5080;
var pouchPort = argv.P || argv['pouch-port'] || 16984;

var NUM_PARALLEL_TASKS = 10;

var SKIM_REMOTE = 'https://skimdb.npmjs.com/registry';
var SKIM_LOCAL = 'http://localhost:' + pouchPort + '/skimdb';
var FAT_LOCAL = 'http://localhost:' + pouchPort + '/fullfatdb';
var FAT_REMOTE = 'https://registry.npmjs.org';

console.log('\nWelcome!');
console.log('To start using local-npm, just run: ');
console.log('\n  $ npm set registry http://127.0.0.1:' + port + '/fullfatdb');
console.log('\nTo switch back, you can run: ');
console.log('\n  $ npm set registry ' + FAT_REMOTE);

var request = require('request');
var Promise = require('bluebird');
var fs = require('fs');
var Fullfat = require('npm-fullfat-registry');
var pouchdbServerLite = require('./pouchdb-server-lite');
var app = pouchdbServerLite.app;
var PouchDB = pouchdbServerLite.PouchDB;

var skimRemote = new PouchDB(SKIM_REMOTE);
var skimPouch = new PouchDB('skimdb');
var fatPouch = new PouchDB('fullfatdb');
// I tend to use more than the default 10 listeners
skimPouch.setMaxListeners(NUM_PARALLEL_TASKS * 50);
fatPouch.setMaxListeners(NUM_PARALLEL_TASKS * 50);

// replicate from central skimdb
var upToDate;
var startingTimeout = 1000;
var backoff = 1.1;
var sync;

function replicateSkim() {
  skimRemote.info().then(function (info) {
    sync = skimPouch.replicate.from(skimRemote, {
      live: true,
      batch_size: 1000
    }).on('change', function (change) {
      var percent = Math.min(100,
        (Math.floor(change.last_seq / info.update_seq * 10000) / 100).toFixed(2));
      console.log('Replicating skimdb, last_seq is: ' + change.last_seq + ' (' + percent + '%)');
    }).on('uptodate', function () {
      console.log('local skimdb is up to date');
      fs.writeFile('uptodate.txt', '1');
      upToDate = true;
    }).on('error', function (err) {
      console.error('error during replication with skimdb');
      console.error(err);
      restartReplication();
    });
  }).catch(function (err) {
    console.error('error doing info() on ' + SKIM_REMOTE);
    console.error(err);
    restartReplication();
  });
}
function restartReplication() {
  // just keep going
  startingTimeout *= backoff;
  setTimeout(replicateSkim, Math.round(startingTimeout));
}
replicateSkim();

var fullFat;

// start doing exciting shit
Promise.resolve().then(function () {
  return Promise.promisify(fs.readFile)('uptodate.txt').catch(function (err) {
    return '0'; // default
  });
}).then(function (upToDateFile) {
  upToDate = upToDateFile === '1';
  if (upToDate) {
    console.log('local skimdb is up to date');
  }
}).then(function () {
  // trick Fullfat into never doing automatic syncing; we don't use it for that
  return Promise.promisify(fs.writeFile)('registry.seq', '999999999999');
}).then(function () {
  fullFat = new Fullfat({
    skim: SKIM_LOCAL,
    fat: FAT_LOCAL,
    seq_file: 'registry.seq',
    missing_log: 'missing.log'
  })
  .on('start', function () {
    // we want the changes feed to only be local,
    // but all other operations should be remote
    // if we're not done replicating yet
    process.nextTick(function () {
      fullFat.skim = upToDate ? SKIM_LOCAL : SKIM_REMOTE;
    });
  })
  .on('error', function (err) {
    console.error("fullfat hit an error");
    console.error(err);
  });
}).then(function () {
  // start user-facing proxy server

  // only execute n fullFat.js operations in parallel
  var queues = [];
  for (var i = 0; i < NUM_PARALLEL_TASKS; i++) {
    queues.push(Promise.resolve());
  }

  function processWithFullFat(docId) {
    console.log(docId + ': got request...');
    // recover by fetching with fullFat
    // wait for changes to let us know it was fetched
    var queueIdx = docId.charCodeAt(0) % queues.length;
    queues[queueIdx] = queues[queueIdx].then(function () {
      console.log(docId + ': looking up in pouch');
      return new Promise(function (resolve, reject) {
        var changes = fatPouch.changes({
          since: 'latest',
          live: true
        }).on('change', function onChange(change) {
          console.log(change.id + ': got change');
          if (change.id === docId) {
            console.log('successfully wrote to local fullfatdb: ' + docId);
            changes.cancel();
            resolve();
          }
        }).on('error', function (err) {
          console.error('change hit error');
          reject(err);
        });
        console.log(docId + ': fetching in the background');
        // this seq is only used by fullFat to determine the file name to write tgz's to
        fullFat.getDoc({id: docId, seq: Math.round(Math.random() * 1000000)});
      });
    });
    return queues[queueIdx];
  }

  function updateAfterIncomingChange() {
    // keep a log of what the last seq we checked was
    fs.readFile('skim-seq.txt', function (err, data) {
      var seq = data ? parseInt(data) : 0;
      console.log('reading skimPouch changes since ' + seq);
      skimPouch.changes({since: seq, live: true}).on('change', function (change) {
        fs.writeFile('skim-seq.txt', change.seq.toString());
        fatPouch.allDocs({keys: [change.id]}).then(function (res) {
          if (res[0] && res[0].rev !== change.changes[0].rev) {
            console.log('new change came in for ' + change.id + ', updating...');
            return processWithFullFat(change.id);
          }
        }).catch(function (err) {
          console.error('unhandled skimPouch allDocs err');
          console.error(err);
        });
      }).on('error', function (err) {
        console.error('unhandled skimPouch changes err');
        console.error(err);
      });
    });
  }
  if (upToDate) {
    updateAfterIncomingChange();
  } else {
    skimPouch.on('uptodate', function () {
      fullFat.skim = SKIM_LOCAL; // internal API, probably shouldn't do this
      skimPouch.info().then(function (info) {
        return Promise.promisify(fs.writeFile)('skim-seq.txt',
            info.update_seq.toString()).then(function () {
          updateAfterIncomingChange();
        });
      }).catch(function (err) {
        console.error('unhandled writeFile err');
        console.error(err);
      });
    });
  }

  var server = require('http').createServer(function (req, res) {
    var docId;
    Promise.resolve().then(function () {
      if (req.method.toLowerCase() === 'get' &&
          /^\/fullfatdb\/([^\/]+)/.test(req.url)) {
        // simple doc or attachment request (get)
        var url = require('url').parse(req.url);
        docId = decodeURIComponent(url.pathname.split('/')[2]);
        console.log(docId + ': simple doc request');
        // check if it exists first
        return fatPouch.get(docId).catch(function (err) {
          if (err.status === 404) {
            console.log('not found locally: ' + docId);
            process.nextTick(function () {
              processWithFullFat(docId); // exec in background
            });
          }
          throw err;
        });
      } else {
        throw new Error('bad_local_request');
      }
    }).then(function (doc) {
      console.log(docId + ': doc exists locally');
      request.get('http://127.0.0.1:' + pouchPort + req.url).pipe(res);
    }).catch(function (err) {
      if (err.name === 'bad_local_request') {
        console.error('got a bad request: ' + req.url);
        res.status(400).send({error: 'this proxy only accepts GETs to /fullfatdb'});
      } else {
        console.error(docId + ': no local doc, need to proxy to remote');
        // we got an error, so let's proxy to the remote fat
        // so we can return immediately
        var url = req.url.replace(/^\/fullfatdb/, '');
        request.get(FAT_REMOTE + url).pipe(res);
      }
    });
  });
  server.listen(5080, function (err) {
    if (err) {
      console.error(err);
    } else {
      console.log('Proxy server started at http://127.0.0.1:' + port);
    }
  });
}).catch(function (err) {
  console.error(err);
  throw err;
});

process.on('SIGINT', function () {
  // close gracefully
  sync.cancel();
  Promise.all([fatPouch, skimPouch].map(function (pouch) {
    return pouch.close();
  })).then(function () {
    process.exit(0);
  });
});
<|endoftext|>


<commit_before>/* jshint node: true */
'use strict';

module.exports = {
  name: 'ember-lodash-computed-decorators' /**,

  isDevelopingAddon: function(app) {
    return true;
  }
  **/
};
<commit_msg>Spacing<commit_after>/* jshint node: true */
'use strict';

module.exports = {
  name: 'ember-lodash-computed-decorators'/**,

  isDevelopingAddon: function(app) {
    return true;
  }
  **/
};
<|endoftext|>


<commit_before>var path = require('path');
var spawn = require('child_process').spawn;
var debug = require('debug')('electron-squirrel-startup');
var app = require('app');

var run = function(args, done) {
  var updateExe = path.resolve(path.dirname(process.execPath), '..', 'Update.exe');
  debug('Spawning `%s` with args `%s`', updateExe, args);
  spawn(updateExe, args, {
    detached: true
  }).on('close', done);
};

var check = function() {
  if (process.platform === 'win32') {
    var cmd = process.argv[1];
    debug('processing squirrel command `%s`', cmd);
    var target = path.basename(process.execPath);
    if (cmd === '--squirrel-install' || cmd === '--squirrel-updated') {
      run(['--createShortcut', target], app.quit);
      return true;
    }
    if (cmd === '--squirrel-uninstall') {
      run(['--removeShortcut', target], app.quit);
      return true;
    }
    if (cmd === '--squirrel-obsolete') {
      app.quit();
      return true;
    }
  }
  return false;
};

module.exports = check();
<commit_msg>fix(windows) shortcut path must be quoted<commit_after>var path = require('path');
var spawn = require('child_process').spawn;
var debug = require('debug')('electron-squirrel-startup');
var app = require('app');

var run = function(args, done) {
  var updateExe = path.resolve(path.dirname(process.execPath), '..', 'Update.exe');
  debug('Spawning `%s` with args `%s`', updateExe, args);
  spawn(updateExe, args, {
    detached: true
  }).on('close', done);
};

var check = function() {
  if (process.platform === 'win32') {
    var cmd = process.argv[1];
    debug('processing squirrel command `%s`', cmd);

    var target = '"' + path.basename(process.execPath) +'"';

    if (cmd === '--squirrel-install' || cmd === '--squirrel-updated') {
      run(['--createShortcut', target], app.quit);
      return true;
    }
    if (cmd === '--squirrel-uninstall') {
      run(['--removeShortcut', target], app.quit);
      return true;
    }
    if (cmd === '--squirrel-obsolete') {
      app.quit();
      return true;
    }
  }
  return false;
};

module.exports = check();
<|endoftext|>


<commit_before>#!/usr/bin/env node

var pkg =               require('./package.json');
var config =            require('./config.js');
var log =               require('yalm');
log.setLevel(config.verbosity);

log(pkg.name + ' ' + pkg.version + ' starting');

var fs =                require('fs');
var Mqtt =              require('mqtt');

var mqttStatus = {};        // Holds the payloads of the last-received message, keys are the topics.
var mqttCallbacks = {};     // Holds arrays of subscription callbacks, keys are the topics.
var mqttConnected;

var bridgeListening;

log.info('mqtt trying to connect', config.url);
var mqtt = Mqtt.connect(config.url, {will: {topic: config.name + '/connected', payload: '0', retain: true}});

mqtt.on('connect', function () {
    mqttConnected = true;
    log.info('mqtt connected ' + config.url);
    if (!bridgeListening) mqtt.publish(config.name + '/connected', '1', {retain: true});
});

mqtt.on('reconnect', function () {
    log.info('mqtt reconnect');
});

mqtt.on('offline', function () {
    log.info('mqtt offline');
});

mqtt.on('close', function () {
    if (mqttConnected) {
        mqttConnected = false;
        log.info('mqtt closed ' + config.url);
    }
});

mqtt.on('error', function (err) {
    log.error('mqtt error ' + err);
});

mqtt.on('message', function (topic, payload) {
    payload = payload.toString();
    var state;
    try {
        // todo - check for json objects in a less nasty way ;)
        if (payload.indexOf('{') === -1) throw 'not an object'; // We have no use for arrays here.
        // We got an Object - let's hope it follows mqtt-smarthome architecture and has an attribute "val"
        // see https://github.com/mqtt-smarthome/mqtt-smarthome/blob/master/Architecture.md
        state = JSON.parse(payload).val;
    } catch (e) {
        // Nasty type guessing.
        // Do we really need to cast the strings "true" and "false" to bool?
        if (payload === 'true') {
            state = true;
        } else if (payload === 'false') {
            state = false;
        } else if (!isNaN(payload)) {
            state = parseFloat(payload);
        } else {
            state = payload;
        }
    }
    log.debug('< mqtt', topic, state);
    mqttStatus[topic] = state;
    if (mqttCallbacks[topic]) {
        mqttCallbacks[topic].forEach(function (cb) {
            cb(state);
        });
    }
});

// MQTT subscribe function that provides a callback on incoming messages.
// Not meant to be used with wildcards!
function mqttSub(topic, callback) {
    if (typeof callback === 'function') {
        if (!mqttCallbacks[topic]) {
            mqttCallbacks[topic] = [callback];
            log.debug('mqtt subscribe', topic);
            mqtt.subscribe(topic);
        } else {
            mqttCallbacks[topic].push(callback);
        }
    } else {
        log.debug('mqtt subscribe', topic);
        mqtt.subscribe(topic);
    }
}

// MQTT publish function, checks for valid topic and converts payload to string in a meaningful manner.
function mqttPub(topic, payload, options) {
    if (!topic || (typeof topic !== 'string')) {
        log.error('mqttPub invalid topic', topic);
    } else {
        if (typeof payload === 'object') {
            payload = JSON.stringify(payload);
        } else if (typeof payload !== 'string') {
            payload = '' + payload;
        }
        mqtt.publish(topic, payload, options, function (err) {
            if (err) log.error('mqtt publish error ' + err);
        });
    }
}

var pkgHap =            require('./node_modules/hap-nodejs/package.json');
log.info('using hap-nodejs version', pkgHap.version);

var HAP =               require('hap-nodejs');
var uuid =              HAP.uuid;
var Bridge =            HAP.Bridge;
var Accessory =         HAP.Accessory;
var Service =           HAP.Service;
var Characteristic =    HAP.Characteristic;

if (config.storagedir) {
    log.info('using directory ' + config.storagedir + ' for persistent storage');
}
// If storagedir is not set it uses HAP-Nodejs default
// (usually /usr/local/lib/node_modules/homekit2mqtt/node_modules/node-persist/persist)
HAP.init(config.storagedir || undefined);

// Create Bridge which will host all Accessories
var bridge = new Bridge(config.bridgename, uuid.generate(config.bridgename));

// Listen for Bridge identification event
bridge.on('identify', function (paired, callback) {
    log('< hap bridge identify', paired ? '(paired)' : '(unpaired)');
    callback();
});

// Handler for Accessory identification events
function identify(settings, paired, callback) {
    log.debug('< hap identify', settings.name, paired ? '(paired)' : '(unpaired)');
    if (settings.topic.identify) {
        log.debug('> mqtt', settings.topic.identify, settings.payload.identify);
        mqttPub(settings.topic.identify, settings.payload.identify);
    }
    callback();
}

function newAccessory(settings) {
    var acc = new Accessory(settings.name, uuid.generate(settings.id));
    if (settings.manufacturer || settings.model || settings.serial) {
        acc.getService(Service.AccessoryInformation)
            .setCharacteristic(Characteristic.Manufacturer, settings.manufacturer || "-")
            .setCharacteristic(Characteristic.Model, settings.model || "-" )
            .setCharacteristic(Characteristic.SerialNumber, settings.serial || "-");
    }
    acc.on('identify', function (paired, callback) {
        identify(settings, paired, callback);
    });
    return acc;
}

var createAccessory = {};
// import createAccessory functions
fs.readdirSync(__dirname + '/accessories').forEach(function (file) {
    var acc = file.replace(/\.js$/, '');
    createAccessory[acc] = require(__dirname + '/accessories/' + file)({mqttPub, mqttSub, mqttStatus, log, newAccessory, Service, Characteristic});
});

// Load and create all accessories
log.info('loading HomeKit to MQTT mapping file ' + config.mapfile);
var mapping = require(config.mapfile);
var accCount = 0;
Object.keys(mapping).forEach(function (id) {
    var a = mapping[id];
    a.id = id;
    if (createAccessory[a.service]) {
        log.debug('addBridgedAccessory ' + a.service + ' ' + a.name);
        bridge.addBridgedAccessory(createAccessory[a.service](a));
        accCount++;
    } else {
        log.err('unknown service', a.service, id);
    }
});
log.info('hap created', accCount, 'Accessories');

log('hap publishing bridge "' + config.bridgename + '" username=' + config.username, 'port=' + config.port, 'pincode=' + config.c);
bridge.publish({
    username: config.username,
    port: config.port,
    pincode: config.c,
    category: Accessory.Categories.OTHER
});

bridge._server.on('listening', function () {
    bridgeListening = true;
    mqtt.publish(config.name + '/connected', '2', {retain: true});
    log('hap Bridge listening on port', config.port);
});

bridge._server.on('pair', function (username, publickey) {
    log('hap paired', username);
});

bridge._server.on('unpair', function (username) {
    log('hap unpaired', username);
});

bridge._server.on('verify', function () {
    log('hap verify');
});
<commit_msg>creates empty payload config if undefined<commit_after>#!/usr/bin/env node

var pkg =               require('./package.json');
var config =            require('./config.js');
var log =               require('yalm');
log.setLevel(config.verbosity);

log(pkg.name + ' ' + pkg.version + ' starting');

var fs =                require('fs');
var Mqtt =              require('mqtt');

var mqttStatus = {};        // Holds the payloads of the last-received message, keys are the topics.
var mqttCallbacks = {};     // Holds arrays of subscription callbacks, keys are the topics.
var mqttConnected;

var bridgeListening;

log.info('mqtt trying to connect', config.url);
var mqtt = Mqtt.connect(config.url, {will: {topic: config.name + '/connected', payload: '0', retain: true}});

mqtt.on('connect', function () {
    mqttConnected = true;
    log.info('mqtt connected ' + config.url);
    if (!bridgeListening) mqtt.publish(config.name + '/connected', '1', {retain: true});
});

mqtt.on('reconnect', function () {
    log.info('mqtt reconnect');
});

mqtt.on('offline', function () {
    log.info('mqtt offline');
});

mqtt.on('close', function () {
    if (mqttConnected) {
        mqttConnected = false;
        log.info('mqtt closed ' + config.url);
    }
});

mqtt.on('error', function (err) {
    log.error('mqtt error ' + err);
});

mqtt.on('message', function (topic, payload) {
    payload = payload.toString();
    var state;
    try {
        // todo - check for json objects in a less nasty way ;)
        if (payload.indexOf('{') === -1) throw 'not an object'; // We have no use for arrays here.
        // We got an Object - let's hope it follows mqtt-smarthome architecture and has an attribute "val"
        // see https://github.com/mqtt-smarthome/mqtt-smarthome/blob/master/Architecture.md
        state = JSON.parse(payload).val;
    } catch (e) {
        // Nasty type guessing.
        // Do we really need to cast the strings "true" and "false" to bool?
        if (payload === 'true') {
            state = true;
        } else if (payload === 'false') {
            state = false;
        } else if (!isNaN(payload)) {
            state = parseFloat(payload);
        } else {
            state = payload;
        }
    }
    log.debug('< mqtt', topic, state);
    mqttStatus[topic] = state;
    if (mqttCallbacks[topic]) {
        mqttCallbacks[topic].forEach(function (cb) {
            cb(state);
        });
    }
});

// MQTT subscribe function that provides a callback on incoming messages.
// Not meant to be used with wildcards!
function mqttSub(topic, callback) {
    if (typeof callback === 'function') {
        if (!mqttCallbacks[topic]) {
            mqttCallbacks[topic] = [callback];
            log.debug('mqtt subscribe', topic);
            mqtt.subscribe(topic);
        } else {
            mqttCallbacks[topic].push(callback);
        }
    } else {
        log.debug('mqtt subscribe', topic);
        mqtt.subscribe(topic);
    }
}

// MQTT publish function, checks for valid topic and converts payload to string in a meaningful manner.
function mqttPub(topic, payload, options) {
    if (!topic || (typeof topic !== 'string')) {
        log.error('mqttPub invalid topic', topic);
    } else {
        if (typeof payload === 'object') {
            payload = JSON.stringify(payload);
        } else if (typeof payload !== 'string') {
            payload = '' + payload;
        }
        mqtt.publish(topic, payload, options, function (err) {
            if (err) log.error('mqtt publish error ' + err);
        });
    }
}

var pkgHap =            require('./node_modules/hap-nodejs/package.json');
log.info('using hap-nodejs version', pkgHap.version);

var HAP =               require('hap-nodejs');
var uuid =              HAP.uuid;
var Bridge =            HAP.Bridge;
var Accessory =         HAP.Accessory;
var Service =           HAP.Service;
var Characteristic =    HAP.Characteristic;

if (config.storagedir) {
    log.info('using directory ' + config.storagedir + ' for persistent storage');
}
// If storagedir is not set it uses HAP-Nodejs default
// (usually /usr/local/lib/node_modules/homekit2mqtt/node_modules/node-persist/persist)
HAP.init(config.storagedir || undefined);

// Create Bridge which will host all Accessories
var bridge = new Bridge(config.bridgename, uuid.generate(config.bridgename));

// Listen for Bridge identification event
bridge.on('identify', function (paired, callback) {
    log('< hap bridge identify', paired ? '(paired)' : '(unpaired)');
    callback();
});

// Handler for Accessory identification events
function identify(settings, paired, callback) {
    log.debug('< hap identify', settings.name, paired ? '(paired)' : '(unpaired)');
    if (settings.topic.identify) {
        log.debug('> mqtt', settings.topic.identify, settings.payload.identify);
        mqttPub(settings.topic.identify, settings.payload.identify);
    }
    callback();
}

function newAccessory(settings) {
    var acc = new Accessory(settings.name, uuid.generate(settings.id));
    if (settings.manufacturer || settings.model || settings.serial) {
        acc.getService(Service.AccessoryInformation)
            .setCharacteristic(Characteristic.Manufacturer, settings.manufacturer || "-")
            .setCharacteristic(Characteristic.Model, settings.model || "-" )
            .setCharacteristic(Characteristic.SerialNumber, settings.serial || "-");
    }
    if (!settings.payload) settings.payload = {};
    acc.on('identify', function (paired, callback) {
        identify(settings, paired, callback);
    });
    return acc;
}

var createAccessory = {};
// import createAccessory functions
fs.readdirSync(__dirname + '/accessories').forEach(function (file) {
    var acc = file.replace(/\.js$/, '');
    createAccessory[acc] = require(__dirname + '/accessories/' + file)({mqttPub, mqttSub, mqttStatus, log, newAccessory, Service, Characteristic});
});

// Load and create all accessories
log.info('loading HomeKit to MQTT mapping file ' + config.mapfile);
var mapping = require(config.mapfile);
var accCount = 0;
Object.keys(mapping).forEach(function (id) {
    var a = mapping[id];
    a.id = id;
    if (createAccessory[a.service]) {
        log.debug('addBridgedAccessory ' + a.service + ' ' + a.name);
        bridge.addBridgedAccessory(createAccessory[a.service](a));
        accCount++;
    } else {
        log.err('unknown service', a.service, id);
    }
});
log.info('hap created', accCount, 'Accessories');

log('hap publishing bridge "' + config.bridgename + '" username=' + config.username, 'port=' + config.port, 'pincode=' + config.c);
bridge.publish({
    username: config.username,
    port: config.port,
    pincode: config.c,
    category: Accessory.Categories.OTHER
});

bridge._server.on('listening', function () {
    bridgeListening = true;
    mqtt.publish(config.name + '/connected', '2', {retain: true});
    log('hap Bridge listening on port', config.port);
});

bridge._server.on('pair', function (username, publickey) {
    log('hap paired', username);
});

bridge._server.on('unpair', function (username) {
    log('hap unpaired', username);
});

bridge._server.on('verify', function () {
    log('hap verify');
});
<|endoftext|>


<commit_before>var util = require('util');
var request = require('request');
var express  = require('express');
var cookieParser  = require('cookie-parser');
var bodyParser  = require('body-parser');
var session = require('express-session');
var gcal = require('google-calendar');
var passport = require('passport');
var argv = require('yargs').argv;
var mongojs = require('mongojs');

var MongoStore = require('connect-mongo')(session);
var GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;

var PRODUCTION = (process.env.NODE_ENV == 'production');
var MONGO_URL = argv.MONGO_URL ||
    process.env.MONGO_URL ||
    process.env.MONGOHQ_URL ||
    'mongodb://127.0.0.1:27017';
var MONGO_DB = 'smartplanner';
var GOOGLE_CLIENT_ID = argv.GOOGLE_CLIENT_ID || process.env.GOOGLE_CLIENT_ID;
var GOOGLE_CLIENT_SECRET = argv.GOOGLE_CLIENT_SECRET || process.env.GOOGLE_CLIENT_SECRET;
var PORT = argv.PORT || process.env.PORT || 8082;
var CALLBACK_URL = PRODUCTION ?
    'https://smartplanner.herokuapp.com/auth/callback' :
    'http://localhost:' + PORT + '/auth/callback';

if (!GOOGLE_CLIENT_ID) {
  throw new Error('No GOOGLE_CLIENT_ID configured. Provide a GOOGLE_CLIENT_ID via command line arguments or an environment variable');
}
if (!GOOGLE_CLIENT_SECRET) {
  throw new Error('No GOOGLE_CLIENT_SECRET configured. Provide a GOOGLE_CLIENT_SECRET via command line arguments or an environment variable');
}

console.log('MONGO_URL (or MONGOHQ_URL):', MONGO_URL);
console.log('Authentication callback url:', CALLBACK_URL);

// create an express app
var app = express();
app.use(cookieParser());
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
app.use(session({
  //store: new MongoStore({url: MONGO_URL + '/' + MONGO_DB + '/sessions', ssl: PRODUCTION}), // TODO: use SSL
  store: new MongoStore({url: MONGO_URL + '/' + MONGO_DB + '/sessions'}),
  secret: 'youre not going to guess this one',
  resave: true,
  saveUninitialized: true
}));
app.use(passport.initialize());

// create a connection to mongodb
var db = mongojs(MONGO_URL + '/' + MONGO_DB, ['users', 'sessions']);

// serve static content
app.use('/', express.static(__dirname + '/client'));
app.use('/node_modules/', express.static(__dirname + '/node_modules'));

app.listen(PORT);
console.log('Server listening at http://localhost:' + PORT);

// Setup passportjs server for authentication
passport.use(new GoogleStrategy({
      clientID: GOOGLE_CLIENT_ID,
      clientSecret: GOOGLE_CLIENT_SECRET,
      callbackURL: CALLBACK_URL,
      scope: ['profile', 'email', 'https://www.googleapis.com/auth/calendar'],
      accessType: 'offline',
      approvalPrompt: 'force'
    },
    function(accessToken, refreshToken, params, profile, done) {
      // FIXME: refreshToken is undefined
      profile.auth = {
        accessToken: accessToken,
        refreshToken: refreshToken,
        params: params
      };
      //console.log('auth', profile.auth);
      return done(null, profile);
    }
));

app.get('/auth',
    passport.authenticate('google', { session: false}));
// TODO: if auth token was not valid, logout

app.get('/auth/callback',
    passport.authenticate('google', { session: false, failureRedirect: '/' }),
    function(req, res) {
      // copy required fields to the session object
      var expires_in = req.user.auth.params.expires_in * 1000; // ms
      req.session.cookie.expires = new Date(Date.now() + expires_in);
      req.session.cookie.maxAge = expires_in;
      req.session.email = req.user._json.email;
      req.session.accessToken = req.user.auth.accessToken;
      req.session.refreshToken = req.user.auth.refreshToken;

      var redirectTo = req.session.redirectTo || '/';
      res.redirect(redirectTo);
    });

app.get('/user/signin', function(req, res, next) {
  req.session.redirectTo = '/';
  return res.redirect('/auth');
});

app.get('/user/signout', function(req, res, next) {
  req.session.destroy(function(err) {
    res.redirect('/');
  })
});

function auth(req, res, next) {
  if(!req.session.accessToken) {
    req.session.redirectTo = req.url;
    return res.redirect('/auth');
  }
  else {
    return next();
  }
}

// retrieve user settings
// note: this call does not authorize the session, returns {loggedIn: false} when not logged in
app.get('/user', function(req, res, next) {
  var loggedIn = req.session.accessToken != null;
  if (loggedIn) {
    var email = req.session.email;
    db.users.findOne({email: email}, function (err, docs) {
      if(err) return res.status(500).send(err);
      if (docs == null) {
        getUserInfo(req.session.accessToken, function (err, user) {
          if(err) return res.status(500).send(err);

          // store user in the database
          updateUser(user, function (err, user) {
            if(err) return res.status(500).send(err);
            return res.json(user);
          })
        });
      }
      else {
        docs.loggedIn = true;
        return res.json(docs);
      }
    });
  }
  else {
    // not logged in
    return res.json({loggedIn: false});
  }
});

// store (update) user settings
app.put('/user', auth);
app.put('/user', function(req, res, next) {
  var user = req.body;
  var email = req.session.email;
  if (email && (user.email == email || user.email == null)) { // only allow saving the users own profile
    user.email = email;

    updateUser(user, function (err, user) {
      if(err) return res.status(500).send(err);
      return res.json(user);
    });
  }
  else {
    return res.status(403).send('Not logged in');
  }
});

function updateUser(user, callback) {
  user.updated = new Date().toISOString();

  db.users.findAndModify({
    query: {email: user.email},
    update: {$set: user, $inc: {seq: 1}},
    upsert: true,   // create a new document when not existing
    new: true       // return the newly created or the updated document
  }, function (err, updatedUser, lastErrorObject) {
    callback(err, updatedUser);
  });
}

app.all('/calendar*', auth);

app.get('/calendar', function(req, res){
  var accessToken = req.session.accessToken;

  gcal(accessToken).calendarList.list(function(err, data) {
    if(err) return res.status(500).send(err);
    return res.json(data);
  });
});

app.get('/calendar/:calendarId', function(req, res){
  var accessToken     = req.session.accessToken;
  var calendarId      = req.params.calendarId;
  var now = new Date();
  var options = {
    singleEvents: true, // expand recurring events
    orderBy: 'startTime',
    timeMin: new Date(now.getFullYear(), now.getMonth(), now.getDate()).toISOString(),
    timeMax: new Date(now.getFullYear(), now.getMonth(), now.getDate() + 7).toISOString()
  };

  gcal(accessToken).events.list(calendarId, options, function(err, data) {
    if(err) return res.status(500).send(err);
    return res.json(data);
  });
});

app.get('/calendar/:calendarId/add', function(req, res){
  var accessToken     = req.session.accessToken;
  var calendarId      = req.params.calendarId;
  var text            = req.query.text || 'Hello World';

  gcal(accessToken).events.quickAdd(calendarId, text, function(err, data) {
    if(err) return res.status(500).send(err);
    return res.redirect('/calendar/'+calendarId);
  });
});

app.delete('/calendar/:calendarId/:eventId/remove', function(req, res){
  var accessToken     = req.session.accessToken;
  var calendarId      = req.params.calendarId;
  var eventId         = req.params.eventId;

  gcal(accessToken).events.delete(calendarId, eventId, function(err, data) {
    if(err) return res.status(500).send(err);
    return res.redirect('/calendar/'+calendarId);
  });
});

app.get('/freeBusy/:calendarId', function(req, res) {
  // TODO: get freeBusy for a list with calendars
  var accessToken     = req.session.accessToken;
  var calendarId      = req.params.calendarId;
  var now = new Date();
  var query = {
    timeMin: new Date(now.getFullYear(), now.getMonth(), now.getDate()).toISOString(),
    timeMax: new Date(now.getFullYear(), now.getMonth(), now.getDate() + 7).toISOString(),
    items: [
      {id: calendarId}
    ]
  };

  gcal(accessToken).freebusy.query(query, function(err, data) {
    if(err) return res.status(500).send(err);
    return res.json(data);
  });
});

/**
 * Retrieve user information from google
 * @param {string} accessToken
 * @param {function} callback   called as `callback(err, user)`
 */
function getUserInfo (accessToken, callback) {
  var url = 'https://www.googleapis.com/oauth2/v1/userinfo?access_token=' + accessToken;
  request(url, function (error, response, body) {
    try {
      if (!error && body.length > 0) {
        var data = JSON.parse(body);
        callback(null, {
          loggedIn: true,
          name: data.name || null,
          email: data.email || null,
          picture: data.picture || null
        });
      }
    }
    catch (err) {
      callback(err, null);
    }
  });
}
<commit_msg>Default calendar is selected when a new user is created<commit_after>var util = require('util');
var request = require('request');
var express  = require('express');
var cookieParser  = require('cookie-parser');
var bodyParser  = require('body-parser');
var session = require('express-session');
var gcal = require('google-calendar');
var passport = require('passport');
var argv = require('yargs').argv;
var mongojs = require('mongojs');

var MongoStore = require('connect-mongo')(session);
var GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;

var PRODUCTION = (process.env.NODE_ENV == 'production');
var MONGO_URL = argv.MONGO_URL ||
    process.env.MONGO_URL ||
    process.env.MONGOHQ_URL ||
    'mongodb://127.0.0.1:27017';
var MONGO_DB = 'smartplanner';
var GOOGLE_CLIENT_ID = argv.GOOGLE_CLIENT_ID || process.env.GOOGLE_CLIENT_ID;
var GOOGLE_CLIENT_SECRET = argv.GOOGLE_CLIENT_SECRET || process.env.GOOGLE_CLIENT_SECRET;
var PORT = argv.PORT || process.env.PORT || 8082;
var CALLBACK_URL = PRODUCTION ?
    'https://smartplanner.herokuapp.com/auth/callback' :
    'http://localhost:' + PORT + '/auth/callback';

if (!GOOGLE_CLIENT_ID) {
  throw new Error('No GOOGLE_CLIENT_ID configured. Provide a GOOGLE_CLIENT_ID via command line arguments or an environment variable');
}
if (!GOOGLE_CLIENT_SECRET) {
  throw new Error('No GOOGLE_CLIENT_SECRET configured. Provide a GOOGLE_CLIENT_SECRET via command line arguments or an environment variable');
}

console.log('MONGO_URL (or MONGOHQ_URL):', MONGO_URL);
console.log('Authentication callback url:', CALLBACK_URL);

// create an express app
var app = express();
app.use(cookieParser());
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
app.use(session({
  //store: new MongoStore({url: MONGO_URL + '/' + MONGO_DB + '/sessions', ssl: PRODUCTION}), // TODO: use SSL
  store: new MongoStore({url: MONGO_URL + '/' + MONGO_DB + '/sessions'}),
  secret: 'youre not going to guess this one',
  resave: true,
  saveUninitialized: true
}));
app.use(passport.initialize());

// create a connection to mongodb
var db = mongojs(MONGO_URL + '/' + MONGO_DB, ['users', 'sessions']);

// serve static content
app.use('/', express.static(__dirname + '/client'));
app.use('/node_modules/', express.static(__dirname + '/node_modules'));

app.listen(PORT);
console.log('Server listening at http://localhost:' + PORT);

// Setup passportjs server for authentication
passport.use(new GoogleStrategy({
      clientID: GOOGLE_CLIENT_ID,
      clientSecret: GOOGLE_CLIENT_SECRET,
      callbackURL: CALLBACK_URL,
      scope: ['profile', 'email', 'https://www.googleapis.com/auth/calendar'],
      accessType: 'offline',
      approvalPrompt: 'force'
    },
    function(accessToken, refreshToken, params, profile, done) {
      // FIXME: refreshToken is undefined
      profile.auth = {
        accessToken: accessToken,
        refreshToken: refreshToken,
        params: params
      };
      //console.log('auth', profile.auth);
      return done(null, profile);
    }
));

app.get('/auth',
    passport.authenticate('google', { session: false}));
// TODO: if auth token was not valid, logout

app.get('/auth/callback',
    passport.authenticate('google', { session: false, failureRedirect: '/' }),
    function(req, res) {
      // copy required fields to the session object
      var expires_in = req.user.auth.params.expires_in * 1000; // ms
      req.session.cookie.expires = new Date(Date.now() + expires_in);
      req.session.cookie.maxAge = expires_in;
      req.session.email = req.user._json.email;
      req.session.accessToken = req.user.auth.accessToken;
      req.session.refreshToken = req.user.auth.refreshToken;

      var redirectTo = req.session.redirectTo || '/';
      res.redirect(redirectTo);
    });

app.get('/user/signin', function(req, res, next) {
  req.session.redirectTo = '/';
  return res.redirect('/auth');
});

app.get('/user/signout', function(req, res, next) {
  req.session.destroy(function(err) {
    res.redirect('/');
  })
});

function auth(req, res, next) {
  if(!req.session.accessToken) {
    req.session.redirectTo = req.url;
    return res.redirect('/auth');
  }
  else {
    return next();
  }
}

// retrieve user settings
// note: this call does not authorize the session, returns {loggedIn: false} when not logged in
app.get('/user', function(req, res, next) {
  var loggedIn = req.session.accessToken != null;
  if (loggedIn) {
    var email = req.session.email;
    db.users.findOne({email: email}, function (err, docs) {
      if(err) return res.status(500).send(err);
      if (docs == null) {
        getUserInfo(req.session.accessToken, function (err, user) {
          if(err) return res.status(500).send(err);

          // store user in the database
          updateUser(user, function (err, user) {
            if(err) return res.status(500).send(err);
            return res.json(user);
          })
        });
      }
      else {
        docs.loggedIn = true;
        return res.json(docs);
      }
    });
  }
  else {
    // not logged in
    return res.json({loggedIn: false});
  }
});

// store (update) user settings
app.put('/user', auth);
app.put('/user', function(req, res, next) {
  var user = req.body;
  var email = req.session.email;
  if (email && (user.email == email || user.email == null)) { // only allow saving the users own profile
    user.email = email;

    updateUser(user, function (err, user) {
      if(err) return res.status(500).send(err);
      return res.json(user);
    });
  }
  else {
    return res.status(403).send('Not logged in');
  }
});

function updateUser(user, callback) {
  user.updated = new Date().toISOString();

  db.users.findAndModify({
    query: {email: user.email},
    update: {
      $set: user,
      $setOnInsert: { calendars: [user.email] }, // requires Mongo v2.4
      $inc: {seq: 1}
    },
    upsert: true,   // create a new document when not existing
    new: true       // return the newly created or the updated document
  }, function (err, updatedUser, lastErrorObject) {
    callback(err, updatedUser);
  });
}

app.all('/calendar*', auth);

app.get('/calendar', function(req, res){
  var accessToken = req.session.accessToken;

  gcal(accessToken).calendarList.list(function(err, data) {
    if(err) return res.status(500).send(err);
    return res.json(data);
  });
});

app.get('/calendar/:calendarId', function(req, res){
  var accessToken     = req.session.accessToken;
  var calendarId      = req.params.calendarId;
  var now = new Date();
  var options = {
    singleEvents: true, // expand recurring events
    orderBy: 'startTime',
    timeMin: new Date(now.getFullYear(), now.getMonth(), now.getDate()).toISOString(),
    timeMax: new Date(now.getFullYear(), now.getMonth(), now.getDate() + 7).toISOString()
  };

  gcal(accessToken).events.list(calendarId, options, function(err, data) {
    if(err) return res.status(500).send(err);
    return res.json(data);
  });
});

app.get('/calendar/:calendarId/add', function(req, res){
  var accessToken     = req.session.accessToken;
  var calendarId      = req.params.calendarId;
  var text            = req.query.text || 'Hello World';

  gcal(accessToken).events.quickAdd(calendarId, text, function(err, data) {
    if(err) return res.status(500).send(err);
    return res.redirect('/calendar/'+calendarId);
  });
});

app.delete('/calendar/:calendarId/:eventId/remove', function(req, res){
  var accessToken     = req.session.accessToken;
  var calendarId      = req.params.calendarId;
  var eventId         = req.params.eventId;

  gcal(accessToken).events.delete(calendarId, eventId, function(err, data) {
    if(err) return res.status(500).send(err);
    return res.redirect('/calendar/'+calendarId);
  });
});

app.get('/freeBusy/:calendarId?', function(req, res) {
  var calendarId  = req.params.calendarId; // TODO: retrieve for requested calendarId
  var accessToken = req.session.accessToken;
  var email       = req.session.email;
  db.users.findOne({email: email}, function (err, user) {
    var items;
    if (user && user.calendars) {
      items = user.calendars.map(function (calendarId) {
        return {id: calendarId};
      })
    }
    else {
      // default: just use the users own calendar
      items = [{id: email}];
    }

    var now = new Date();
    var query = {
      timeMin: new Date(now.getFullYear(), now.getMonth(), now.getDate()).toISOString(),
      timeMax: new Date(now.getFullYear(), now.getMonth(), now.getDate() + 7).toISOString(),
      items: items
    };

    gcal(accessToken).freebusy.query(query, function(err, data) {
      if(err) return res.status(500).send(err);

      // TODO: merge the freeBusy intervals of all selected calendars

      return res.json(data);
    });
  });
});

// TODO: remove this function
app.get('/freeBusy/:calendarId', function(req, res) {
  var accessToken     = req.session.accessToken;
  var calendarId      = req.params.calendarId;
  var now = new Date();
  var query = {
    timeMin: new Date(now.getFullYear(), now.getMonth(), now.getDate()).toISOString(),
    timeMax: new Date(now.getFullYear(), now.getMonth(), now.getDate() + 7).toISOString(),
    items: [
      {id: calendarId}
    ]
  };

  gcal(accessToken).freebusy.query(query, function(err, data) {
    if(err) return res.status(500).send(err);
    return res.json(data);
  });
});

/**
 * Retrieve user information from google
 * @param {string} accessToken
 * @param {function} callback   called as `callback(err, user)`
 */
function getUserInfo (accessToken, callback) {
  var url = 'https://www.googleapis.com/oauth2/v1/userinfo?access_token=' + accessToken;
  request(url, function (error, response, body) {
    try {
      if (!error && body.length > 0) {
        var data = JSON.parse(body);
        callback(null, {
          loggedIn: true,
          name: data.name || null,
          email: data.email || null,
          picture: data.picture || null
        });
      }
    }
    catch (err) {
      callback(err, null);
    }
  });
}
<|endoftext|>


<commit_before>(function(vc,vm){

    vc.extends({
        data:{
            propertyPayInfo:{
                cycles:'',
                receivableAmount:'0.00',
                receivedAmount:'0.00',
                remark:'',
                feeId:'',
                builtUpArea:'',
                squarePrice:'',
                additionalAmount:'',
                communityId:''
            }
        },
        watch:{
            "propertyPayInfo.cycles":{//深度监听，可监听到对象、数组的变化
                handler(val, oldVal){
                    vc.component.propertyPayInfo.receivableAmount = vc.component.propertyPayInfo.builtUpArea
                                                                    * vc.component.propertyPayInfo.squarePrice
                                                                    + vc.component.propertyPayInfo.additionalAmount;
                    vc.component.propertyPayInfo.receivedAmount = vc.component.propertyPayInfo.builtUpArea
                                                                  * vc.component.propertyPayInfo.squarePrice
                                                                  + vc.component.propertyPayInfo.additionalAmount;
                },
                deep:true
            }
         },
         _initMethod:function(){

         },
         _initEvent:function(){
             vc.on('propertyPay','openPayModel',function(_params){
                vc.component.refreshPropertyPayInfo();

                $('#propertyPayModel').modal('show');
                vc.component.propertyPayInfo.feeId = _params.feeId;
                vc.component.propertyPayInfo.builtUpArea = _params.builtUpArea;
                vc.component.propertyPayInfo.communityId = vc.getCurrentCommunity().communityId;
            });
        },
        methods:{

            payFeeValidate:function(){
                        return vc.validate.validate({
                            propertyPayInfo:vc.component.propertyPayInfo
                        },{
                            'propertyPayInfo.feeId':[
                                {
                                    limit:"required",
                                    param:"",
                                    errInfo:"费用ID不能为空"
                                }
                            ],
                            'propertyPayInfo.cycles':[
                                {
                                    limit:"required",
                                    param:"",
                                    errInfo:"缴费周期不能为空"
                                }
                            ],
                            'propertyPayInfo.receivableAmount':[
                                {
                                    limit:"required",
                                    param:"",
                                    errInfo:"应收金额不能为空"
                                },
                                {
                                    limit:"money",
                                    param:"",
                                    errInfo:"应收金额不是有效的金额"
                                }
                            ],
                            'propertyPayInfo.receivedAmount':[
                                {
                                    limit:"required",
                                    param:"",
                                    errInfo:"实收金额不能为空"
                                },
                                {
                                    limit:"money",
                                    param:"",
                                    errInfo:"实收金额不是有效的金额"
                                }
                            ],

                            'propertyPayInfo.remark':[
                                {
                                    limit:"maxLength",
                                    param:"200",
                                    errInfo:"备注长度不能超过200位"
                                },
                            ]

                        });
             },
            payFee:function(){
                if(!vc.component.payFeeValidate()){
                    vc.message(vc.validate.errInfo);
                    return ;
                }

                vc.http.post(
                    'propertyPay',
                    'payFee',
                    JSON.stringify(vc.component.propertyPayInfo),
                    {
                        emulateJSON:true
                     },
                     function(json,res){
                        //vm.menus = vm.refreshMenuActive(JSON.parse(json),0);
                        if(res.status == 200){
                            //关闭model
                            $('#propertyPayModel').modal('hide');
                            vc.emit('propertyFee','listFeeDetail',propertyPayInfo);
                            return ;
                        }
                        vc.message(json);
                     },
                     function(errInfo,error){
                        console.log('请求失败处理');

                        vc.message(errInfo);
                     });
            },
            refreshPropertyPayInfo:function(){
                vc.component.propertyPayInfo={
                                             cycles:'',
                                             receivableAmount:'0.00',
                                             receivedAmount:'0.00',
                                             remark:'',
                                             builtUpArea:'',
                                             feeId:'',
                                             squarePrice:'',
                                             additionalAmount:'',
                                             communityId:''
                                         };
                vc.component.loadPropertyConfigFee();

            },
            //加载配置数据
            loadPropertyConfigFee:function(){
                var param = {
                    params:{
                        communityId:vc.getCurrentCommunity().communityId,
                        configId:''
                    }
                };
                vc.http.get(
                    'propertyPay',
                    'loadPropertyConfigData',
                     param,
                     function(json,res){
                        //vm.menus = vm.refreshMenuActive(JSON.parse(json),0);
                        if(res.status == 200){
                            //关闭model
                            vc.copyObject(JSON.parse(json), vc.component.propertyPayInfo);
                            return ;
                        }
                        vc.message(json);
                     },
                     function(errInfo,error){
                        console.log('请求失败处理');

                        vc.message(errInfo);
                     });
                }
        }
    });

})(window.vc,window.vc.component);<commit_msg>修改物业收费js<commit_after>(function(vc,vm){

    vc.extends({
        data:{
            propertyPayInfo:{
                cycles:'',
                receivableAmount:'0.00',
                receivedAmount:'0.00',
                remark:'',
                feeId:'',
                builtUpArea:'',
                squarePrice:'',
                additionalAmount:'',
                communityId:''
            }
        },
        watch:{
            "propertyPayInfo.cycles":{//深度监听，可监听到对象、数组的变化
                handler(val, oldVal){
                    vc.component.propertyPayInfo.receivableAmount = (parseFloat(vc.component.propertyPayInfo.builtUpArea)
                                                                    * parseFloat(vc.component.propertyPayInfo.squarePrice)
                                                                    + parseFloat(vc.component.propertyPayInfo.additionalAmount)) * parseFloat(val);
                    vc.component.propertyPayInfo.receivedAmount = vc.component.propertyPayInfo.receivableAmount;
                },
                deep:true
            }
         },
         _initMethod:function(){

         },
         _initEvent:function(){
             vc.on('propertyPay','openPayModel',function(_params){
                vc.component.refreshPropertyPayInfo();

                $('#propertyPayModel').modal('show');
                vc.component.propertyPayInfo.feeId = _params.feeId;
                vc.component.propertyPayInfo.builtUpArea = _params.builtUpArea;
                vc.component.propertyPayInfo.communityId = vc.getCurrentCommunity().communityId;
            });
        },
        methods:{

            payFeeValidate:function(){
                        return vc.validate.validate({
                            propertyPayInfo:vc.component.propertyPayInfo
                        },{
                            'propertyPayInfo.feeId':[
                                {
                                    limit:"required",
                                    param:"",
                                    errInfo:"费用ID不能为空"
                                }
                            ],
                            'propertyPayInfo.cycles':[
                                {
                                    limit:"required",
                                    param:"",
                                    errInfo:"缴费周期不能为空"
                                }
                            ],
                            'propertyPayInfo.receivableAmount':[
                                {
                                    limit:"required",
                                    param:"",
                                    errInfo:"应收金额不能为空"
                                },
                                {
                                    limit:"money",
                                    param:"",
                                    errInfo:"应收金额不是有效的金额"
                                }
                            ],
                            'propertyPayInfo.receivedAmount':[
                                {
                                    limit:"required",
                                    param:"",
                                    errInfo:"实收金额不能为空"
                                },
                                {
                                    limit:"money",
                                    param:"",
                                    errInfo:"实收金额不是有效的金额"
                                }
                            ],

                            'propertyPayInfo.remark':[
                                {
                                    limit:"maxLength",
                                    param:"200",
                                    errInfo:"备注长度不能超过200位"
                                },
                            ]

                        });
             },
            payFee:function(){
                if(!vc.component.payFeeValidate()){
                    vc.message(vc.validate.errInfo);
                    return ;
                }

                vc.http.post(
                    'propertyPay',
                    'payFee',
                    JSON.stringify(vc.component.propertyPayInfo),
                    {
                        emulateJSON:true
                     },
                     function(json,res){
                        //vm.menus = vm.refreshMenuActive(JSON.parse(json),0);
                        if(res.status == 200){
                            //关闭model
                            $('#propertyPayModel').modal('hide');
                            vc.emit('propertyFee','listFeeDetail',propertyPayInfo);
                            return ;
                        }
                        vc.message(json);
                     },
                     function(errInfo,error){
                        console.log('请求失败处理');

                        vc.message(errInfo);
                     });
            },
            refreshPropertyPayInfo:function(){
                vc.component.propertyPayInfo={
                                             cycles:'',
                                             receivableAmount:'0.00',
                                             receivedAmount:'0.00',
                                             remark:'',
                                             builtUpArea:'',
                                             feeId:'',
                                             squarePrice:'',
                                             additionalAmount:'',
                                             communityId:''
                                         };
                vc.component.loadPropertyConfigFee();

            },
            //加载配置数据
            loadPropertyConfigFee:function(){
                var param = {
                    params:{
                        communityId:vc.getCurrentCommunity().communityId,
                        configId:''
                    }
                };
                vc.http.get(
                    'propertyPay',
                    'loadPropertyConfigData',
                     param,
                     function(json,res){
                        //vm.menus = vm.refreshMenuActive(JSON.parse(json),0);
                        if(res.status == 200){
                            //关闭model
                            vc.copyObject(JSON.parse(json), vc.component.propertyPayInfo);
                            return ;
                        }
                        vc.message(json);
                     },
                     function(errInfo,error){
                        console.log('请求失败处理');

                        vc.message(errInfo);
                     });
                }
        }
    });

})(window.vc,window.vc.component);<|endoftext|>