source: Dev/trunk/src/server/app.js @ 477

Last change on this file since 477 was 477, checked in by hendrikvanantwerpen, 12 years ago

Quick hack to allow responding despite not being authenticated. Something like tokes need to be added.

File size: 7.4 KB
Line 
1var express = require("express")
2  , passport = require("passport")
3  , passportLocal = require("passport-local")
4  , fs = require("fs")
5  , path = require("path")
6  , proxy = require("./util/simple-http-proxy")
7  , CSV = require("ya-csv")
8  , CouchDB = require('./util/couch').CouchDB
9  , _ = require("underscore")
10  ;
11
12function assertSetting(name, settings, validate) {
13    if ( typeof settings[name] === 'undefined' ) {
14        throw new Error("Required setting '"+name+"' undefined.");
15    }
16    if ( _.isFunction(validate) && !validate(settings[name]) ) {
17        throw new Error("Setting '"+name+"' with value '"+settings[name]+"' is invalid.");
18    }
19}
20
21exports.App = function(settings) {
22
23    assertSetting("couchDbURL", settings, _.isString);
24    var couch = new CouchDB(settings.couchDbURL);
25   
26    function clientPath(relativePath) {
27        return path.resolve(__dirname+'/../client/'+relativePath);
28    }
29
30    passport.use(new passportLocal.Strategy(function(username, password, done){
31        if ( username === "igor" && password === "mayer" ) {
32            done(null,{ username: "igor" });
33        } else {
34            done(null,false,{ message: 'Invalid credentials.' });
35        }
36    }));
37    passport.serializeUser(function(user, done) {
38        done(null, user.username);
39    });
40    passport.deserializeUser(function(id, done) {
41        done(null, {username: id});
42    });
43
44    var app = express();
45    app.use(express.logger());
46    app.use(express.compress());
47    app.use(express.favicon());
48
49    // cookies and session
50    app.use('/api',express.bodyParser());
51    app.use(express.cookieParser());
52    app.use(express.session({ secret: "quasi experimental design" }));
53
54    // passport
55    app.use(passport.initialize());
56    app.use(passport.session());
57    function ensureAuthenticated(req,res,next){
58        if (!req.user) {
59            return res.send(401,{error:"Login before accessing API."});
60        } else {
61            return next();
62        }
63    }
64    function returnUser(req,res) {
65        res.send(200, req.user);
66    }
67
68    // static resources
69    app.get('/', function(request, response){
70        response.sendfile(clientPath('index.html'));
71    });
72    app.get('/*.html', function(request, response) {
73        response.sendfile(clientPath(request.path));
74    });
75    _.each(['/dojo', '/dijit', '/dojox', '/qed', '/qed-client'], function(dir){
76        app.use(dir, express.static(clientPath(dir)));
77    });
78
79
80    // post to this url to login
81    app.post(
82        '/api/login',
83        passport.authenticate('local'),
84        returnUser);
85
86    // return the info for the current logged in user
87    app.get(
88        '/api/login',
89        ensureAuthenticated,
90        returnUser);
91
92    // explicitly logout this user
93    app.post(
94        '/api/logout',
95        ensureAuthenticated,
96        function(req,res){
97            req.logout();
98            res.send(200,{});
99        });
100
101    // expose everything needed to fill in responses
102    // FIXME : this needs more security, now you can guess ids
103    //         and get actual survey and response data!
104    app.get(
105        '/live',
106        function(req,res){
107            res.redirect('/response.html#!/55704d6a84194c2d7ba9bec93c000e75');
108        });
109    app.get(
110        '/api/surveyRuns/:id',
111        function(req,res){
112            var id = req.params.id;
113            couch.get(id).then(function(result){
114                if ( result.type === 'SurveyRun') {
115                    res.send(200, result);
116                } else {
117                    res.send(404, {error: "Cannot find survey run "+id});
118                }
119            }, function(error){
120                res.send(404, {error: "Cannot find survey run "+id});
121            });
122        });
123    app.get(
124        '/api/responses/:id',
125        function(req,res){
126            var id = req.params.id;
127            couch.get(id).then(function(result){
128                if ( result.type === 'Response') {
129                    res.send(200, result);
130                } else {
131                    res.send(404, {error: "Cannot find response "+id});
132                }
133            }, function(error){
134                res.send(404, {error: "Cannot find response "+id});
135            });
136        });
137    app.post(
138        '/api/responses',
139        function(req,res){
140            console.log(req.body);
141            couch.post(req.body).then(function(result){
142                res.send(200, result);
143            }, function(error){
144                res.send(500, error);
145            });
146        });
147    app.put(
148        '/api/responses/:id',
149        function(req,res){
150            var id = req.params.id;
151            couch.put(id,req.body).then(function(result){
152                res.send(200, result);
153            }, function(error){
154                res.send(500, error);
155            });
156        });
157   
158    // data is proxied to couch
159    app.use('/api/data', ensureAuthenticated);
160    app.use('/api/data', proxy(settings.couchDbURL));
161
162    // generate CSV download of responses
163    app.get(
164        '/api/surveyRuns/:id/responses.csv',
165        ensureAuthenticated,
166        function(req, res) {
167            var id = req.params.id;
168            var url = '_design/responses/_view/by_surveyrun?key='+JSON.stringify(id);
169            couch.get(url).then(function(result){
170                var responses = _.map(result.rows, function(item) { return item.value; });
171                var flatResponses = responsesToVariables(responses);
172                res.set({
173                    'Content-Type': 'text/csv',
174                    'Content-Disposition': 'attachment; filename=surveyRun-'+id+'-responses.csv'
175                });
176                res.status(200);
177                writeObjectsToCSVStream(flatResponses, res);
178                res.end();
179            }, function(error){
180                res.send(404, {error: "Cannot find responses for survey run "+id});
181            });
182        });
183   
184    return app;
185
186};
187
188function responsesToVariables(responses) {
189    return _.map(responses, responseToVariables);
190}
191
192function responseToVariables(response) {
193    var result = flattenObject(response.answers);
194    return result;
195}
196
197function flattenObject(value) {
198    var result = {};
199    (function agg(val,key,res){
200        if ( _.isObject(val) ) {
201            var keys = _.keys(val);
202            // FIXME : dirty hack for questions with only one input
203            if ( keys.length === 1 ) {
204                agg(val[keys[0]],key,res);
205            } else {
206                _.forEach(val, function(v,k){
207                    agg(v,(key ? key+'.' : '')+k,res);
208                });
209            }
210        } else if ( _.isArray(val) ) {
211            // FIXME : dirty hack for questions with only one input
212            if ( val.length === 1 ) {
213                agg(val[0],key,res);
214            } else {
215                _.forEach(val, function(v,i){
216                    agg(v,(key ? key+'.' : '')+i,res);
217                });
218            }
219        } else {
220            res[key] = val;
221        }
222    })(value,null,result);
223    return result;
224}
225
226function writeObjectsToCSVStream(objects, stream) {
227    var keys = _.chain(objects)
228                .map(_.keys)
229                .flatten()
230                .uniq()
231                .value();
232    var idxs = {};
233    _.forEach(keys, function(key,idx){
234        idxs[key] = idx;
235    });
236    var writer = new CSV.CsvWriter(stream);
237    writer.writeRecord(keys);
238    _.forEach(objects, function(obj){
239        var row = [];
240        _.forEach(obj, function(val,key){
241            row[idxs[key]] = val;
242        });
243        writer.writeRecord(row);
244    });
245}
Note: See TracBrowser for help on using the repository browser.