simple-breakpad-server
Version:
Simple breakpad crash reports collecting server
151 lines (134 loc) • 4.18 kB
JavaScript
(function() {
var COMPOSITE_INDEX, Sequelize, Symfile, cache, config, fs, options, path, schema, sequelize, streamToArray, symbolsPath;
config = require('../config');
cache = require('./cache');
Sequelize = require('sequelize');
sequelize = require('./db');
fs = require('fs-promise');
path = require('path');
streamToArray = require('stream-to-array');
symbolsPath = config.getSymbolsPath();
COMPOSITE_INDEX = 'compositeIndex';
schema = {
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
primaryKey: true
},
os: {
type: Sequelize.STRING,
unique: COMPOSITE_INDEX
},
name: {
type: Sequelize.STRING,
unique: COMPOSITE_INDEX
},
code: {
type: Sequelize.STRING,
unique: COMPOSITE_INDEX
},
arch: {
type: Sequelize.STRING,
unique: COMPOSITE_INDEX
},
contents: Sequelize.TEXT
};
options = {
indexes: [
{
fields: ['created_at']
}
]
};
Symfile = sequelize.define('symfiles', schema, options);
Symfile.saveToDisk = function(symfile) {
var symfileDir;
symfileDir = path.join(symbolsPath, symfile.name, symfile.code);
return fs.mkdirs(symfileDir).then(function() {
var filePath, symbol_name;
symbol_name = symfile.name;
if (path.extname(symbol_name).toLowerCase() === '.pdb') {
symbol_name = symbol_name.slice(0, -4);
}
symbol_name += '.sym';
filePath = path.join(symfileDir, symbol_name);
return fs.writeFile(filePath, symfile.contents);
});
};
Symfile.createFromRequest = function(req, callback) {
var props, streamOps;
props = {};
streamOps = [];
req.busboy.on('file', function(fieldname, file, filename, encoding, mimetype) {
return streamOps.push(streamToArray(file).then(function(parts) {
var buffers, i, j, part, ref;
buffers = [];
for (i = j = 0, ref = parts.length - 1; 0 <= ref ? j <= ref : j >= ref; i = 0 <= ref ? ++j : --j) {
part = parts[i];
buffers.push(part instanceof Buffer ? part : new Buffer(part));
}
return Buffer.concat(buffers);
}).then(function(buffer) {
if (fieldname === 'symfile') {
return props[fieldname] = buffer.toString();
}
}));
});
req.busboy.on('finish', function() {
return Promise.all(streamOps).then(function() {
var arch, code, contents, dec, header, msg, name, os;
if (!'symfile' in props) {
res.status(400);
throw new Error('Form must include a "symfile" field');
}
contents = props.symfile;
header = contents.split('\n')[0].split(/\s+/);
dec = header[0], os = header[1], arch = header[2], code = header[3], name = header[4];
if (dec !== 'MODULE') {
msg = 'Could not parse header (expecting MODULE as first line)';
throw new Error(msg);
}
props = {
os: os,
arch: arch,
code: code,
name: name,
contents: contents
};
return sequelize.transaction(function(t) {
var whereDuplicated;
whereDuplicated = {
where: {
os: os,
arch: arch,
code: code,
name: name
}
};
return Symfile.findOne(whereDuplicated, {
transaction: t
}).then(function(duplicate) {
var p;
p = duplicate != null ? duplicate.destroy({
transaction: t
}) : Promise.resolve();
return p.then(function() {
return Symfile.create(props, {
transaction: t
}).then(function(symfile) {
return Symfile.saveToDisk(symfile).then(function() {
cache.clear();
return callback(null, symfile);
});
});
});
});
});
})["catch"](function(err) {
return callback(err);
});
});
return req.pipe(req.busboy);
};
module.exports = Symfile;
}).call(this);