Skip to content

Commit

Permalink
Initial Commit
Browse files Browse the repository at this point in the history
  • Loading branch information
daleharvey committed Nov 21, 2014
0 parents commit 4077a8f
Show file tree
Hide file tree
Showing 6 changed files with 510 additions and 0 deletions.
50 changes: 50 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
'use strict';

var app = require('express')();

var routes = {
'db': require('./lib/routes/db.js'),
'document': require('./lib/routes/document.js'),
'attachments': require('./lib/routes/attachments.js')
};

module.exports = function(PouchDB) {

app.use(function (req, res, next) {
for (var prop in req.query) {
try {
req.query[prop] = JSON.parse(req.query[prop]);
} catch (e) {}
}
next();
});

app.get('/', function (req, res) {
res.status(200).send({'pouchdb-express-router': 'Welcome!'});
});

app.get('/_session', function (req, res) {
res.status(200).send({
'ok': true,
'userCtx':{"name":null,"roles":["_admin"]}
});
});

routes.db(app, PouchDB);
routes.attachments(app, PouchDB);
routes.document(app, PouchDB);

app.use(function (req, res) {
res.status(404).send( {
error: "not_found",
reason: "missing"
});
});

return app;
};

// Used for testing porpoises
var PouchDB = require('pouchdb');
module.exports(PouchDB);
app.listen(5985);
114 changes: 114 additions & 0 deletions lib/routes/attachments.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
'use strict';

var rawBody = require('raw-body');

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

function parseRawBody(req, res, next) {
if (req.params.id === '_design' || req.params.id === '_local') {
return next();
}
rawBody(req, {
length: req.headers['content-length']
}, function (err, string) {
if (err) {
return next(err)
}
req.rawBody = string
next()
});
}

function putAttachment(db, name, req, res) {
var attachment = req.params.attachment
var rev = req.query.rev;
var type = req.get('Content-Type') || 'application/octet-stream';
var body = new Buffer(req.rawBody || '', 'binary');
var opts = req.query;

req.db.putAttachment(name, attachment, rev, body,
type, opts, function (err, response) {
if (err) return utils.sendError(res, err);
utils.sendJSON(res, 201, response);
});
}

// Retrieve a document attachment
function getAttachment(name, req, res) {
var attachment = req.params.attachment;
var opts = req.query;

req.db.get(name, opts, function (err, info) {
if (err) return utils.sendError(res, err);

if (!info._attachments || !info._attachments[attachment]) {
return utils.sendJSON(res, 404, {
error:'not_found',
reason:'missing'
});
};

var type = info._attachments[attachment].content_type;
var md5 = info._attachments[attachment].digest.slice(4);

req.db.getAttachment(name, attachment, function (err, response) {
if (err) return utils.sendError(res, err);
res.setHeader('Content-Type', type);
res.status(200).send(response);
});
});
}

// Delete a document attachment
function deleteAttachment(name, req, res) {
var name = req.params.id;
var attachment = req.params.attachment;
var rev = req.query.rev;
var opts = req.query;

req.db.removeAttachment(name, attachment, rev, function (err, response) {
if (err) return utils.sendError(res, err);
utils.sendJSON(res, 200, response);
});
}


module.exports = function(app, PouchDB) {

app.put('/:db/_design/:id/:attachment(*)', parseRawBody, function (req, res, next) {
putAttachment(req.params.db, '_design/' + req.params.id, req, res);
});

app.put('/:db/:id/:attachment(*)', parseRawBody, function (req, res, next) {
// Be careful not to catch normal design docs or local docs
if (req.params.id === '_design' || req.params.id === '_local') {
return next();
}
putAttachment(req.params.db, req.params.id, req, res);
});

app.get('/:db/_design/:id/:attachment(*)', function (req, res, next) {
getAttachment('_design/' + req.params.id, req, res);
});

app.get('/:db/:id/:attachment(*)', function (req, res, next) {
// Be careful not to catch normal design docs or local docs
if (req.params.id === '_design' || req.params.id === '_local') {
return next();
}
getAttachment(req.params.id, req, res);
});

app.delete('/:db/_design/:id/:attachment(*)', function (req, res, next) {
deleteAttachment('_design/' + req.params.id, req, res);
});

app.delete('/:db/:id/:attachment(*)', function (req, res, next) {
// Be careful not to catch normal design docs or local docs
if (req.params.id === '_design' || req.params.id === '_local') {
return next();
}
deleteAttachment(req.params.id, req, res);
});
};

200 changes: 200 additions & 0 deletions lib/routes/db.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
'use strict';

var jsonParser = require('body-parser').json({limit: '1mb'});
var extend = require('extend');

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

module.exports = function(app, PouchDB) {

// Create a database
app.put('/:db', jsonParser, function (req, res, next) {
var name = encodeURIComponent(req.params.db);
new PouchDB(name, function (err, db) {
if (err) return res.status(412).send(err);
res.status(201).send({ok: true});
});
});

// Delete a database
app.delete('/:db', function (req, res, next) {
var name = encodeURIComponent(req.params.db);
PouchDB.destroy(name, function (err, info) {
if (err) return res.status(500).send(err);
res.status(200).send({ok: true });
});
});

['/:db/*','/:db'].forEach(function (route) {
app.all(route, function (req, res, next) {
var name = encodeURIComponent(req.params.db);
new PouchDB(name, function (err, db) {
if (err) return res.status(412).send(err);
req.db = db;
next();
});
});
});

// Get database information
app.get('/:db', function (req, res, next) {
req.db.info(function (err, info) {
if (err) return res.status(500).send(err);
res.status(200).send(info);
});
});

// Bulk docs operations
app.post('/:db/_bulk_docs', jsonParser, function (req, res, next) {
var opts = 'new_edits' in req.body
? { new_edits: req.body.new_edits } : {};

if (Array.isArray(req.body)) {
return res.status(400).send({
error: "bad_request",
reason: "Request body must be a JSON object"
});
}

req.db.bulkDocs(req.body, opts, function (err, response) {
if (err) return utils.sendError(res, err);
res.status(201).send(response);
});
});

// All docs operations
app.all('/:db/_all_docs', jsonParser, function (req, res, next) {
if (req.method !== 'GET' && req.method !== 'POST') return next();

// Check that the request body, if present, is an object.
if (!!req.body && (typeof req.body !== 'object' || Array.isArray(req.body))) {
return res.status(400).send({
reason: "Something wrong with the request",
error: 'bad_request'
});
}

var opts = extend({}, req.body, req.query);
req.db.allDocs(opts, function (err, response) {
if (err) return res.status(500).send(err);
res.status(200).send(response);
});
});

// Monitor database changes
app.get('/:db/_changes', function (req, res, next) {
req.query.query_params = JSON.parse(JSON.stringify(req.query));

if (req.query.feed === 'continuous' || req.query.feed === 'longpoll') {
var heartbeatInterval;
var heartbeat = (typeof req.query.heartbeat === 'number')
? req.query.heartbeat : 6000;
var written = false;
heartbeatInterval = setInterval(function () {
written = true;
res.write('\n');
}, heartbeat);

var cleanup = function () {
if (heartbeatInterval) {
clearInterval(heartbeatInterval);
}
};

if (req.query.feed === 'continuous') {
req.query.live = req.query.continuous = true;
req.db.changes(req.query).on('change', function (change) {
written = true;
res.write(JSON.stringify(change) + '\n');
}).on('error', function (err) {
if (!written) {
utils.sendError(res, err);
} else {
res.end();
}
cleanup();
});
} else { // longpoll
// first check if there are >0. if so, return them immediately
req.query.live = req.query.continuous = false;
req.db.changes(req.query).on('complete', function (complete) {
if (!complete.results) {
// canceled, ignore
cleanup();
} else if (complete.results.length) {
written = true;
res.write(JSON.stringify(complete) + '\n');
res.end();
cleanup();
} else { // do the longpolling
req.query.live = req.query.continuous = true;
var changes = req.db.changes(req.query).on('change', function (change) {
written = true;
res.write(JSON.stringify({
results: [change],
last_seq: change.seq
}) + '\n');
res.end();
changes.cancel();
cleanup();
}).on('error', function (err) {
if (!written) {
utils.sendError(res, err);
}
cleanup();
});
}
}).on('error', function (err) {
if (!written) {
utils.sendError(res, err);
}
cleanup();
});
}
} else { // straight shot, not continuous
req.query.complete = function (err, response) {
if (err) return utils.sendError(res, err);
res.status(200).send(response);
};
req.db.changes(req.query);
}
});

// DB Compaction
app.post('/:db/_compact', jsonParser, function (req, res, next) {
req.db.compact(req.query, function (err, response) {
if (err) return utils.sendError(res, err);
res.status(200).send({ok: true});
});
});

// Revs Diff
app.post('/:db/_revs_diff', jsonParser, function (req, res, next) {
req.db.revsDiff(req.body || {}, req.query, function (err, diffs) {
if (err) return utils.sendError(res, err);
res.status(200).send(diffs);
});
});


// Query a document view
app.get('/:db/_design/:id/_view/:view', function (req, res, next) {
var query = req.params.id + '/' + req.params.view;
req.db.query(query, req.query, function (err, response) {
if (err) return res.status(500).send(err);
res.status(200).send(response);
});
});

// Temp Views
app.post('/:db/_temp_view', jsonParser, function (req, res, next) {
if (req.body.map) req.body.map = (new Function('return ' + req.body.map))();
req.query.conflicts = true;
req.db.query(req.body, req.query, function (err, response) {
if (err) return utils.sendError(res, err);
utils.sendJSON(res, 200, response);
});
});


};

0 comments on commit 4077a8f

Please sign in to comment.