Index: Dev/trunk/src/server/app.js
===================================================================
--- Dev/trunk/src/server/app.js	(revision 464)
+++ Dev/trunk/src/server/app.js	(revision 466)
@@ -4,5 +4,5 @@
 var fs = require("fs");
 var path = require("path");
-var proxy = require("./simple-http-proxy");
+var proxy = require("./util/simple-http-proxy");
 var _ = require("underscore");
 
@@ -31,4 +31,10 @@
         }
     }));
+    passport.serializeUser(function(user, done) {
+        done(null, user.username);
+    });
+    passport.deserializeUser(function(id, done) {
+        done(null, {username: id});
+    });
 
     var app = express();
@@ -56,10 +62,48 @@
     });
 
+    
     // url to login (might work on others as well?)
     // you should then have a session to work with
-    app.post('/api/login');
+    // should return a user info object
+    function returnUser(req,res) {
+        res.send(200, req.user);
+    }
+    app.post(
+        '/api/login',
+        passport.authenticate('local'),
+        returnUser);
 
+    // ensure we're authenticated on API calls
+    app.use('/api', function(req,res,next){
+        if (!req.user) {
+            return res.send(401,{error:"Login before accessing API."});
+        } else {
+            return next();
+        }
+    });
+    
+    app.get(
+        '/api/login',
+        returnUser);
+
+    app.post(
+        '/api/logout', function(req,res){
+            req.logout();
+            res.send(200,{});
+        });
+
+    app.get('/api/surveyRun/:id/response/csv',
+            function(req, res) {
+                var id = req.params.id;
+                // query CouchDB and build the CSV file
+                res.set({
+                    'Content-Type': 'text/csv',
+                    'Content-Disposition': 'attachment; filename=responses-'+id+'.csv'
+                });
+                res.send(200, "Response for surveyRun "+id);
+            });
+    
     // forward to couch
-    app.use('/data/couch', proxy(settings.couchDbURL));
+    app.use('/api/data', proxy(settings.couchDbURL));
 
     return app;
Index: Dev/trunk/src/server/config/config-couchdb.js
===================================================================
--- Dev/trunk/src/server/config/config-couchdb.js	(revision 464)
+++ Dev/trunk/src/server/config/config-couchdb.js	(revision 466)
@@ -1,5 +1,4 @@
 var q = require('q');
-var HTTP = require('q-io/http');
-var URL = require('url');
+var request = require('../util/request');
 var _ = require('underscore');
 var util = require('util');
@@ -27,9 +26,7 @@
     }
 
-    function request(method,path,content) {
+    function dbRequest(method,path,content) {
         var url = couchDbURL+path;
-        var parsedURL = URL.parse(url);
         var options = {
-            url: url,
             method: method,
             headers: {
@@ -37,32 +34,12 @@
                 'accept': 'application/json'
             },
-            body: {
-                forEach: function(callback) {
-                    callback(JSON.stringify(content || {}));
-                }
-            }
+            body: content
         };
-        // because q-io doesn't support auth properly, we have to
-        // build the f*ing wheel again.
-        if ( parsedURL.auth ) {
-            var auth = new Buffer(parsedURL.auth).toString("base64");
-            options.headers.authorization = 'Basic '+auth;
-        }
-        return HTTP.request(options)
-        .then(function(res){
-            return res.body.read().then(function(content){
-                return JSON.parse(content.toString() || "{}");
-            });
-        },function(res){
-            return res.body.read().then(function(error){
-                console.warn(error); // q.all doesn't do errors, so let's show them here
-                return JSON.parse(error.toString() || "{}");
-            });
-        });
+        return request(url,options);
     }
 
     console.log("Configuring CouchDB for QED");
     console.log("Checking CouchDB version");
-    return request('GET','')
+    return dbRequest('GET','')
     .then(function(res){
         if (res.version !== "1.2.0" ) {
@@ -73,10 +50,10 @@
     }).then(function(res){
         console.log("Checking database 'qed'");
-        return request('GET','qed')
+        return dbRequest('GET','qed')
         .then(function(res){
             console.log("Database 'qed' found.");
         },function(err){
             console.log("Creating database 'qed'");
-            return request('PUT','qed');
+            return dbRequest('PUT','qed');
         });
     }).then(function(){
@@ -92,10 +69,10 @@
                 case "update":
                     console.log(docUrl+" updating.");
-                    return request('GET',docUrl)
+                    return dbRequest('GET',docUrl)
                     .then(function(oldDoc){
                         _.extend(oldDoc,doc);
-                        return request('PUT',docUrl,oldDoc);
+                        return dbRequest('PUT',docUrl,oldDoc);
                     },function(){
-                        return request('PUT',docUrl,doc);
+                        return dbRequest('PUT',docUrl,doc);
                     });
                 break;
@@ -103,10 +80,10 @@
                 default:
                     console.log(docUrl+" replacing.");
-                    return request('GET',docUrl)
+                    return dbRequest('GET',docUrl)
                     .then(function(oldDoc){
                         _.extend(doc,_.pick(oldDoc,'_id','_rev'));
-                        return request('PUT',docUrl,doc);
+                        return dbRequest('PUT',docUrl,doc);
                     },function(){
-                        return request('PUT',docUrl,doc);
+                        return dbRequest('PUT',docUrl,doc);
                     });
                 break;
Index: Dev/trunk/src/server/simple-http-proxy.js
===================================================================
--- Dev/trunk/src/server/simple-http-proxy.js	(revision 464)
+++ 	(revision )
@@ -1,106 +1,0 @@
-
-/**
- * Module dependencies
- */
-var url = require('url')
-    , join = require('path').join
-    , protocols = {
-        http: require('http'),
-        https: require('https')
-    };
-
-module.exports = function(endpoint, opts) {
-    opts = opts || {};
-
-    var parsedUrl = url.parse(endpoint);
-
-    return function simpleHttpProxy(req, res, next) {
-        // Get our forwarding info
-        var hostInfo = req.headers.host.split(":");
-
-        // Remove the host header
-        delete req.headers.host;
-
-        // Optionally delete cookie
-        if (opts.cookies === false) {
-            delete req.headers.cookie;
-        }
-
-        // Should we keep the trailing slash?
-        var trailingSlash = req.originalUrl[req.originalUrl.length-1] === "/";
-
-        // Setup the options
-        var options = {
-            hostname: parsedUrl.hostname,
-            auth: parsedUrl.auth,
-            port: parsedUrl.port,
-            headers: req.headers,
-            path: join(parsedUrl.pathname, trailingSlash ? req.url : req.url.substring(1)),
-            method: req.method
-        };
-
-        // Enable forwarding headers
-        if(opts.xforward) {
-            // Get the path at which the middleware is mounted
-            var resPath = req.originalUrl.replace(req.url, "");
-
-            // We'll need to add a / if it's not on there
-            if (resPath.indexOf("/") !== 0) {
-                resPath = join("/", resPath);
-            }
-
-            // Pass along our headers
-            options.headers[opts.xforward.proto || "x-forwarded-proto"] = req.connection.encrypted ? "https" : "http";
-            options.headers[opts.xforward.host || "x-forwarded-host"] = hostInfo[0];
-            options.headers[opts.xforward.path || "x-forwarded-path"] = resPath;
-
-            if (hostInfo[1]) {
-                options.headers[opts.xforward.port || "x-forwarded-port"] = hostInfo[1];
-            }
-        }
-
-        /**
-         * Hack for nginx backends where node, by default, sends no 'content-length'
-         * header if no data is sent with the request and sets the 'transfer-encoding'
-         * to 'chunked'. Nginx will then send a 411 because the body contains a '0'
-         * which is the length of the chunk
-         *
-         *     GET /proxy
-         *     transfer-encoding: chunked
-         *     
-         *     0
-         *
-         */
-        if( ["POST", "DELETE"].indexOf(req.method) !== -1 && options.headers['transfer-encoding'] !== 'chunked') {
-            options.headers['content-length'] = options.headers['content-length'] || '0';
-        }
-
-        // Make the request with the correct protocol
-        var request = protocols[(parsedUrl.protocol || 'http').replace(":", "")].request(options, function(response) {
-
-            // Send down the statusCode and headers
-            res.writeHead(response.statusCode, response.headers);
-
-            // Pipe the response
-            response.pipe(res);
-        });
-
-        // Handle any timeouts that occur
-        request.setTimeout(opts.timeout || 10000, function() {
-            // Clean up the socket
-            // TODO is there a better way to do this? There's a 'socket hang up' error being emitted...
-            request.setSocketKeepAlive(false);
-            request.socket.destroy();
-
-            // Pass down the error
-            var err = new Error("Proxy to '"+endpoint+"' timed out");
-            request.emit("error", err);
-        });
-
-        // Pipe the client request upstream
-        req.pipe(request);
-
-        // Pass on our errors
-        request.on('error', next);
-    };
-};
Index: Dev/trunk/src/server/util/request.js
===================================================================
--- Dev/trunk/src/server/util/request.js	(revision 466)
+++ Dev/trunk/src/server/util/request.js	(revision 466)
@@ -0,0 +1,39 @@
+var http = require('q-io/http'),
+    url = require('url'),
+    _ = require('underscore');
+
+module.exports = function(urlOrObject, options) {
+
+    options = options
+        ? _.clone(options)
+        : {};
+    options.url = _.isString(urlOrObject)
+        ? url.parse(urlOrObject)
+        : _.clone(urlOrObject);
+
+    // wrap content into q-io thingy
+    if ( options.body ) {
+        options.body = {
+            forEach: function(callback) {
+                callback(JSON.stringify(options.body));
+            }
+        };
+    };
+    
+    // add auth header since q-io doesn't support this
+    if ( options.url.auth ) {
+        options.headers.authorization = 'Basic '+(new Buffer(options.url.auth).toString("base64"));
+    }
+    
+    // make request and collect results
+    return http.request(options)
+    .then(function(res){
+        return res.body.read().then(function(content){
+            return content.length > 0 ? JSON.parse(content) : null;
+        });
+    },function(res){
+        return res.body.read().then(function(error){
+            return error.length > 0 ? JSON.parse(error) : null;
+        });
+    });
+};
Index: Dev/trunk/src/server/util/simple-http-proxy.js
===================================================================
--- Dev/trunk/src/server/util/simple-http-proxy.js	(revision 466)
+++ Dev/trunk/src/server/util/simple-http-proxy.js	(revision 466)
@@ -0,0 +1,106 @@
+
+/**
+ * Module dependencies
+ */
+var url = require('url')
+    , join = require('path').join
+    , protocols = {
+        http: require('http'),
+        https: require('https')
+    };
+
+module.exports = function(endpoint, opts) {
+    opts = opts || {};
+
+    var parsedUrl = url.parse(endpoint);
+
+    return function simpleHttpProxy(req, res, next) {
+        // Get our forwarding info
+        var hostInfo = req.headers.host.split(":");
+
+        // Remove the host header
+        delete req.headers.host;
+
+        // Optionally delete cookie
+        if (opts.cookies === false) {
+            delete req.headers.cookie;
+        }
+
+        // Should we keep the trailing slash?
+        var trailingSlash = req.originalUrl[req.originalUrl.length-1] === "/";
+
+        // Setup the options
+        var options = {
+            hostname: parsedUrl.hostname,
+            auth: parsedUrl.auth,
+            port: parsedUrl.port,
+            headers: req.headers,
+            path: join(parsedUrl.pathname, trailingSlash ? req.url : req.url.substring(1)),
+            method: req.method
+        };
+
+        // Enable forwarding headers
+        if(opts.xforward) {
+            // Get the path at which the middleware is mounted
+            var resPath = req.originalUrl.replace(req.url, "");
+
+            // We'll need to add a / if it's not on there
+            if (resPath.indexOf("/") !== 0) {
+                resPath = join("/", resPath);
+            }
+
+            // Pass along our headers
+            options.headers[opts.xforward.proto || "x-forwarded-proto"] = req.connection.encrypted ? "https" : "http";
+            options.headers[opts.xforward.host || "x-forwarded-host"] = hostInfo[0];
+            options.headers[opts.xforward.path || "x-forwarded-path"] = resPath;
+
+            if (hostInfo[1]) {
+                options.headers[opts.xforward.port || "x-forwarded-port"] = hostInfo[1];
+            }
+        }
+
+        /**
+         * Hack for nginx backends where node, by default, sends no 'content-length'
+         * header if no data is sent with the request and sets the 'transfer-encoding'
+         * to 'chunked'. Nginx will then send a 411 because the body contains a '0'
+         * which is the length of the chunk
+         *
+         *     GET /proxy
+         *     transfer-encoding: chunked
+         *     
+         *     0
+         *
+         */
+        if( ["POST", "DELETE"].indexOf(req.method) !== -1 && options.headers['transfer-encoding'] !== 'chunked') {
+            options.headers['content-length'] = options.headers['content-length'] || '0';
+        }
+
+        // Make the request with the correct protocol
+        var request = protocols[(parsedUrl.protocol || 'http').replace(":", "")].request(options, function(response) {
+
+            // Send down the statusCode and headers
+            res.writeHead(response.statusCode, response.headers);
+
+            // Pipe the response
+            response.pipe(res);
+        });
+
+        // Handle any timeouts that occur
+        request.setTimeout(opts.timeout || 10000, function() {
+            // Clean up the socket
+            // TODO is there a better way to do this? There's a 'socket hang up' error being emitted...
+            request.setSocketKeepAlive(false);
+            request.socket.destroy();
+
+            // Pass down the error
+            var err = new Error("Proxy to '"+endpoint+"' timed out");
+            request.emit("error", err);
+        });
+
+        // Pipe the client request upstream
+        req.pipe(request);
+
+        // Pass on our errors
+        request.on('error', next);
+    };
+};
