jsbin/lib/models/bin.js
Remy Sharp f4d825ddd2 User flags
Flagged users have their IP attached to their account. That IP can view the bin, but others cannot. The IP gets updated with the signed in flagged user
2014-07-24 22:04:11 +01:00

192 lines
5.4 KiB
JavaScript

'use strict';
var Observable = require('../utils').Observable,
metrics = require('../metrics'),
metricPrefix = 'timer.bin.',
moment = require('moment'),
crypto = require('crypto'),
dropbox = require('../dropbox'),
processors = require('../processors'),
blacklist = require('../blacklist'),
createFileFromBin = require('bin-to-file');
var binPanels = ['html', 'css', 'javascript'];
var model = {
constructor: function BinModel(store) {
Observable.call(this);
this.store = store;
},
load: function (params, fn) {
this.store.getBin(params, this.binLoadCallback(params, fn));
},
isVisible: function (bin, username) {
if (!username) {
username = '';
}
if (typeof username !== 'string') {
throw new Error('isVisible requires name to be string');
}
// This will only occur if it isn't a real bin, it has been created
// on from default files and does not exist in any database. By this
// logic the bin must be public, it has no owner and no record.
if (!bin || !bin.metadata) {
return true;
}
// this should only let users see the latest
// "active", and visible bin to that user
if (bin && bin.active === 'y') {
if (bin.metadata.visibility === 'public') {
return true;
}
if (username && bin.metadata.name === username) {
return true;
}
}
return false;
},
latest: function (params, fn) {
this.store.getLatestBin(params, this.binLoadCallback(params, fn));
},
binLoadCallback: function (params, fn) {
return function (err, bin) {
if (err || !bin) {
return fn(err, null);
}
if (blacklist.validate(bin) === false) {
return fn(410);
}
this.getBinMetadata(bin, function loadedGetMetadata(err, metadata) {
bin.metadata = metadata;
if (this.isVisible(bin, params.username)) {
fn(null, bin);
} else {
// don't give the bin back
fn(401);
}
}.bind(this));
}.bind(this);
},
// Create a new bin.
create: function (data, fn) {
this.store.generateBinId(data.length, 0, function generateBinId(err, id) {
if (err) {
return fn(err);
}
data.url = id;
data.revision = 1;
data.streamingKey = this.createStreamingKey(id, data.revision);
this.store.setBin(data, function (err, id) {
data.id = id;
fn(err || null, err ? undefined : data);
});
}.bind(this));
},
// Create a new revision.
createRevision: function (data, fn) {
data.streamingKey = this.createStreamingKey(data.url, data.revision);
this.store.setBin(data, function (err, id) {
data.id = id;
fn(err || null, err ? undefined : data);
});
},
saveToDropbox: function (bin, user) {
//if (user.dropbox_token &&
if(this.dropboxBin(bin)) { // jshint ignore:line
dropbox.saveBin(this.fileFromBin(bin), this.fileNameFromBin(bin), user);
}
},
dropboxBin: function (bin) {
// should return true/false based on whether the bin should be saved to dropbox
// e.g look for a dropboxEnabled value, or a metadata.pro val
return !!bin;
},
fileNameFromBin: function(bin) {
return bin.url + '.html';
},
fileFromBin: function (bin) {
// console.log(bin);
// PrePro is short for preprocessors
var binPrePro = JSON.parse(bin.settings || '{}').processors || {};
var binObject = {
revision: bin.revision,
url: bin.url,
html: bin.html,
css: bin.css,
javascript: bin.javascript,
source: {
html: bin.original_html || bin.html,
css: bin.original_css || bin.css,
javascript: bin.original_javascript || bin.javascript
},
processors: binPrePro
};
return createFileFromBin(binObject);
},
updatePanel: function (panel, data, fn) {
this.store.setBinPanel(panel, data, fn);
},
archive: function (bin, fn) {
this.store.archiveBin(bin, fn);
},
createStreamingKey: function (id, rev) {
var key = '' + id + rev + Math.random();
return crypto.createHash('md5').update(key).digest('hex');
},
report: function (params, fn) {
this.store.reportBin(params, fn);
},
getBinMetadata: function(bin, fn) {
this.store.getBinMetadata(bin, fn);
},
setBinVisibility: function(bin, name, value, fn) {
this.store.setBinVisibility(bin, name, value, fn);
},
updateBinData: function (bin, params, fn) {
this.store.updateBinData([bin.url, bin.revision], params, fn);
},
updateOwnersData: function (bin, params, fn) {
this.store.updateOwnersData([bin.url, bin.revision], params, fn);
},
isStreaming: function (bin) {
// if it was changed in the last 20 minutes, then it's streaming
return bin.streaming_key ? moment(bin.created).isAfter(moment().subtract(20, 'm')) : false;
}
};
// hook up timers
Object.keys(model).forEach(function (key) {
var method = model[key];
// check the signature of the function
if (!method.toString().match(/function.*fn\)/)) {
return;
}
model[key] = function metricsWrapper() { // assume callback is last
var timer = metrics.createTimer(metricPrefix + key),
args = [].slice.apply(arguments),
original = args.pop();
var fn = function metricsEndWrapper() {
timer.stop();
original.apply(this, arguments);
};
args.push(fn);
return method.apply(this, args);
};
});
module.exports = Observable.extend(model);