Line | Hits | Source |
---|---|---|
1 | 1 | var _ = require('lodash'); |
2 | 1 | var r = require('rethinkdb'); |
3 | 1 | var op = require('objectpath'); |
4 | 1 | var qs = require('qs'); |
5 | 1 | var li = require('li'); |
6 | ||
7 | 1 | var filters = ['gt', 'lt', 'gte', 'le', 'ge', 'eq', 'not', 'contains']; |
8 | ||
9 | 1 | module.exports = { |
10 | sort: function(req, query) { | |
11 | 10 | if(typeof req.query.sort !== 'string') |
12 | 10 | return query |
13 | ||
14 | 0 | var pointer = r.row; |
15 | 0 | op.parse(req.query.sort).forEach(function(key){ |
16 | 0 | pointer = pointer(key); |
17 | }); | |
18 | 0 | return req.query.direction === 'desc' ? query.orderBy(r.desc(pointer)) : query.orderBy(pointer); |
19 | }, | |
20 | filter: function(req, query) { | |
21 | 10 | if(!req.query.filter) |
22 | 9 | return query; |
23 | ||
24 | 1 | _.each(req.query.filter, function(expr, path) { |
25 | ||
26 | // make sure a method is specified | |
27 | 1 | if(typeof expr !== 'object') |
28 | 0 | return; |
29 | ||
30 | 1 | _.each(expr, function(value, method){ |
31 | // ignore non-whitelisted methods | |
32 | 1 | if(filters.indexOf(method) === -1) |
33 | 0 | return; |
34 | ||
35 | // try to parse the value as json | |
36 | 2 | try { value = JSON.parse(value); } catch(e){} |
37 | ||
38 | // apply the filter methods | |
39 | 1 | var pointer = r.row; |
40 | 1 | op.parse(path).forEach(function(key){ |
41 | 1 | pointer = pointer(key); |
42 | }); | |
43 | ||
44 | // apply the filter | |
45 | 1 | query = query.filter(pointer[method](value)); |
46 | }); | |
47 | }); | |
48 | ||
49 | 1 | return query; |
50 | }, | |
51 | paginate: function(req, res, page, per_page, total) { | |
52 | 10 | var pages = { |
53 | first: 1, | |
54 | last: Math.ceil(total / per_page) | |
55 | }; | |
56 | ||
57 | 10 | if(page > 1) |
58 | 1 | pages.prev = page - 1; |
59 | ||
60 | 10 | if(page < pages.last) |
61 | 1 | pages.next = page + 1; |
62 | ||
63 | 10 | res.set('Pages', JSON.stringify(pages)); |
64 | 10 | res.set('Link', li.stringify(_.mapValues(pages, function(value){ |
65 | 22 | return req.path + '?' + qs.stringify(_.extend({}, req.query, {page: value, per_page: per_page})); |
66 | }))); | |
67 | } | |
68 | } |
Line | Hits | Source |
---|---|---|
1 | 1 | 'use strict'; |
2 | ||
3 | 1 | var _ = require('lodash'); |
4 | 1 | var r = require('rethinkdb'); |
5 | ||
6 | 1 | var controller = require('../controller.js'); |
7 | ||
8 | 1 | function prepare(o){ |
9 | 7 | if(_.isArray(o)) |
10 | 1 | return _.map(o, prepare); |
11 | ||
12 | 6 | return _.assign(o, {href: '/api/cycles/' + o.id}); |
13 | } | |
14 | ||
15 | 1 | function sanitize(o){ |
16 | 4 | delete o.href; |
17 | 4 | delete o.id; |
18 | } | |
19 | ||
20 | 1 | module.exports = function(config, resources) { |
21 | 1 | return { |
22 | create: function(req, res, next) { | |
23 | 3 | if(!req.user) |
24 | 0 | return next(401); |
25 | ||
26 | // only admin can create a cycle | |
27 | 3 | if(!req.user.admin) |
28 | 1 | return next(403); |
29 | ||
30 | // sanitize the input | |
31 | 2 | sanitize(req.body); |
32 | ||
33 | // validate request against schema | |
34 | 2 | var err = resources.validator.validate('cycle', req.body, {useDefault: true}); |
35 | 2 | if(err) |
36 | 1 | return next({code: 400, message: err}); |
37 | ||
38 | // set timestamps | |
39 | 1 | req.body.created = req.body.updated = r.now(); |
40 | ||
41 | // transform dates | |
42 | 1 | if(typeof req.body.open === 'string') |
43 | 0 | req.body.open = new Date(req.body.open); |
44 | ||
45 | 1 | if(typeof req.body.close === 'string') |
46 | 0 | req.body.close = new Date(req.body.close); |
47 | ||
48 | 1 | resources.db.acquire(function(err, conn) { |
49 | 1 | if(err) |
50 | 0 | return next(err); |
51 | ||
52 | // insert the cycle | |
53 | 1 | r.table('cycles').insert(req.body, {returnChanges: true}).run(conn, function(err, result){ |
54 | 1 | resources.db.release(conn); |
55 | ||
56 | 1 | if(err) |
57 | 0 | return next(err); |
58 | ||
59 | 1 | var cycle = result.changes[0].new_val; |
60 | ||
61 | 1 | return res.status(201).send(cycle); |
62 | }); | |
63 | }); | |
64 | }, | |
65 | list: function(req, res, next) { | |
66 | 1 | if(!req.user) |
67 | 0 | return next(401); |
68 | ||
69 | // get the cycles from the DB | |
70 | 1 | var query = r.table('cycles'); |
71 | ||
72 | // hide drafts fron non-admin users | |
73 | 1 | if(!req.user.admin) |
74 | 0 | query = query.filter(r.row('status').eq('draft').not()); |
75 | ||
76 | // only include open cycles | |
77 | 1 | if(req.query.open && req.query.open !== 'false') |
78 | 0 | query = query.filter(r.row('open').eq(true).or(r.row('open').ne(false).and(r.row('open').lt(r.now()))).and(r.row('close').eq(false).or(r.row('open').gt(r.now())))); |
79 | ||
80 | // filter | |
81 | 1 | query = controller.filter(req, query); |
82 | ||
83 | // search | |
84 | 1 | if(req.query.search && req.query.search !== '') |
85 | 0 | query = query.filter(r.row('title').downcase().match(req.query.search.toLowerCase())); |
86 | ||
87 | // sort | |
88 | 1 | query = controller.sort(req, query); |
89 | ||
90 | 1 | resources.db.acquire(function(err, conn) { |
91 | 1 | if(err) |
92 | 0 | return next(err); |
93 | ||
94 | 1 | var per_page = parseInt(req.query.per_page, 10) || 50; |
95 | 1 | var page = parseInt(req.query.page, 10) || 1; |
96 | ||
97 | 1 | r.expr({ |
98 | // get the total results count | |
99 | total: query.count(), | |
100 | // get the results for this page | |
101 | cycles: query.skip(per_page * (page - 1)).limit(per_page).coerceTo('array') | |
102 | }).run(conn, function(err, results){ | |
103 | 1 | resources.db.release(conn); |
104 | ||
105 | 1 | if(err) |
106 | 0 | return next(err); |
107 | ||
108 | 1 | var cycles = results.cycles; |
109 | ||
110 | // set pagination headers | |
111 | 1 | controller.paginate(req, res, page, per_page, results.total); |
112 | ||
113 | 1 | res.send(prepare(cycles)); |
114 | }); | |
115 | }); | |
116 | }, | |
117 | show: function(req, res, next) { | |
118 | 1 | if(!req.user) |
119 | 0 | return next(401); |
120 | ||
121 | 1 | var id; |
122 | ||
123 | 1 | if(req.params.cycle) // get by ID |
124 | 1 | id = req.params.cycle; |
125 | 0 | else if(req.params.project) // get by project |
126 | 0 | id = r.table('projects').get(req.params.project)('cycle_id'); |
127 | else | |
128 | 0 | return next(400); |
129 | ||
130 | 1 | return resources.db.acquire(function(err, conn) { |
131 | 1 | if(err) |
132 | 0 | return next(err); |
133 | ||
134 | // get cycles from the DB | |
135 | 1 | r.table('cycles').get(id).run(conn, function(err, cycle){ |
136 | 1 | resources.db.release(conn); |
137 | ||
138 | 1 | if(err) |
139 | 0 | return next(err); |
140 | ||
141 | // hide draft cycles from non-admin users | |
142 | 1 | if(!cycle || (!req.user.admin && cycle.status === 'draft')) |
143 | 0 | return next(404); |
144 | ||
145 | 1 | return res.send(prepare(cycle)); |
146 | }); | |
147 | }); | |
148 | }, | |
149 | update: function(req, res, next) { | |
150 | 2 | if(!req.user) |
151 | 0 | return next(401); |
152 | ||
153 | 2 | if(!req.user.admin) |
154 | 1 | return next(403, 'Only administrators may update a cycle.'); |
155 | ||
156 | // sanitize the input | |
157 | 1 | sanitize(req.body); |
158 | ||
159 | // validate request against schema | |
160 | 1 | var err = resources.validator.validate('cycle', req.body, {checkRequired: false}); |
161 | 1 | if(err) |
162 | 0 | return next({code: 400, message: err}); |
163 | ||
164 | // set timestamps | |
165 | 1 | delete req.body.created; |
166 | 1 | req.body.updated = r.now(); |
167 | ||
168 | // transform dates | |
169 | 1 | if(typeof req.body.open === 'string') |
170 | 0 | req.body.open = new Date(req.body.open); |
171 | ||
172 | 1 | if(typeof req.body.close === 'string') |
173 | 0 | req.body.close = new Date(req.body.close); |
174 | ||
175 | // remove indexed objects with null values | |
176 | 1 | var without = {}; |
177 | 1 | _.each(['events', 'flow', 'roles', 'statuses', 'users'], function(index){ |
178 | 5 | _.each(req.body[index], function(value, id){ |
179 | 0 | if(value !== null) |
180 | 0 | return; |
181 | ||
182 | 0 | without[index] = without[index] || {}; |
183 | 0 | without[index][id] = true; |
184 | 0 | delete req.body[index][id]; |
185 | }); | |
186 | }); | |
187 | ||
188 | 1 | resources.db.acquire(function(err, conn) { |
189 | 1 | if(err) |
190 | 0 | return next(err); |
191 | ||
192 | // get cycles from the DB | |
193 | 1 | r.table('cycles').get(req.params.cycle).replace(r.row.without(without).merge(req.body), {returnChanges: true}).run(conn, function(err, result){ |
194 | 1 | resources.db.release(conn); |
195 | ||
196 | 1 | if(err) |
197 | 0 | return next(err); |
198 | ||
199 | 1 | var cycle = result.changes[0].new_val; |
200 | ||
201 | 1 | return res.send(prepare(cycle)); |
202 | }); | |
203 | }); | |
204 | }, | |
205 | replace: function(req, res, next) { | |
206 | 2 | if(!req.user) |
207 | 0 | return next(401); |
208 | ||
209 | 2 | if(!req.user.admin) |
210 | 1 | return next(403, 'Only administrators may replace a cycle.'); |
211 | ||
212 | // sanitize the input | |
213 | 1 | sanitize(req.body); |
214 | ||
215 | // validate request against schema | |
216 | 1 | var err = resources.validator.validate('cycle', req.body, {useDefault: true}); |
217 | 1 | if(err) |
218 | 0 | return next({code: 400, message: err}); |
219 | ||
220 | // inject ID | |
221 | 1 | req.body.id = req.params.cycle; |
222 | ||
223 | // set timestamps | |
224 | 1 | req.body.created = r.row('created'); |
225 | 1 | req.body.updated = r.now(); |
226 | ||
227 | // transform dates | |
228 | 1 | if(typeof req.body.open === 'string') |
229 | 0 | req.body.open = new Date(req.body.open); |
230 | ||
231 | 1 | if(typeof req.body.close === 'string') |
232 | 0 | req.body.close = new Date(req.body.close); |
233 | ||
234 | 1 | resources.db.acquire(function(err, conn) { |
235 | 1 | if(err) |
236 | 0 | return next(err); |
237 | ||
238 | // get cycles from the DB | |
239 | 1 | r.table('cycles').get(req.params.cycle).replace(req.body, {returnChanges: true}).run(conn, function(err, result){ |
240 | 1 | resources.db.release(conn); |
241 | ||
242 | 1 | if(err) |
243 | 0 | return next(err); |
244 | ||
245 | 1 | var cycle = result.changes[0].new_val; |
246 | ||
247 | 1 | return res.send(prepare(cycle)); |
248 | }); | |
249 | }); | |
250 | }, | |
251 | destroy: function(req, res, next) { | |
252 | 3 | if(!req.user) |
253 | 0 | return next(401); |
254 | ||
255 | 3 | if(!req.user.admin) |
256 | 1 | return next(403, 'Only administrators may delete a cycle.'); |
257 | ||
258 | 2 | resources.db.acquire(function(err, conn) { |
259 | 2 | if(err) |
260 | 0 | return next(err); |
261 | ||
262 | 2 | return r.branch(r.table('projects').filter({cycle_id: req.params.cycle}).limit(1).count().eq(0), |
263 | r.table('cycles').get(req.params.cycle).delete({returnChanges: true}), | |
264 | null | |
265 | ).run(conn, function(err, result){ | |
266 | 2 | resources.db.release(conn); |
267 | ||
268 | 2 | if(err) |
269 | 0 | return next(err); |
270 | ||
271 | 2 | if(result === null) |
272 | 1 | return next(400, 'The cycle cannot be destroyed because projects depend on it.'); |
273 | ||
274 | 1 | var cycle = result.changes[0].old_val; |
275 | ||
276 | 1 | return res.send(prepare(cycle)); |
277 | }); | |
278 | }); | |
279 | } | |
280 | }; | |
281 | } | |
282 |
Line | Hits | Source |
---|---|---|
1 | 1 | 'use strict'; |
2 | ||
3 | 1 | var url = require('url'); |
4 | 1 | var _ = require('lodash'); |
5 | 1 | var r = require('rethinkdb'); |
6 | 1 | var async = require('async'); |
7 | 1 | var fs = require('fs'); |
8 | ||
9 | 1 | var controller = require('../controller.js'); |
10 | ||
11 | 1 | function sanitize(o){ |
12 | 3 | delete o.href; |
13 | 3 | delete o.id; |
14 | } | |
15 | ||
16 | 1 | module.exports = function(config, resources) { |
17 | 1 | return { |
18 | create: function(req, res, next) { | |
19 | 1 | if(!req.user) |
20 | 0 | return next(401); |
21 | ||
22 | // sanitize the input | |
23 | 1 | sanitize(req.body); |
24 | ||
25 | // make sure we have files to upload | |
26 | 1 | if(!req.files || Object.keys(req.files).length === 0) |
27 | 0 | return next(400); |
28 | ||
29 | 1 | resources.db.acquire(function(err, conn) { |
30 | 1 | if(err) |
31 | 0 | return next(err); |
32 | ||
33 | // insert the file records into the db | |
34 | 1 | async.map(_.map(req.files), |
35 | function(file, callback){ | |
36 | // read the file from the uploads directory | |
37 | 1 | fs.readFile(file.path, function(err, data) { |
38 | 1 | if(err) |
39 | 0 | return callback(err); |
40 | ||
41 | 1 | return callback(null, { |
42 | user_id: req.user.id, | |
43 | name: file.originalname, | |
44 | encoding: file.encoding, | |
45 | mimetype: file.mimetype, | |
46 | data: data, | |
47 | extension: file.extension, | |
48 | size: file.size, | |
49 | lock: false, | |
50 | created: r.now(), | |
51 | updated: r.now() | |
52 | }); | |
53 | }); | |
54 | }, | |
55 | function(err, inserts){ | |
56 | 1 | if(err) |
57 | 0 | return next(err); |
58 | ||
59 | 1 | r.table('files').insert(inserts, {returnChanges: true})('changes').map(function(row){ |
60 | 1 | return row('new_val').without('data'); |
61 | }).run(conn, function(err, results){ | |
62 | 1 | resources.db.release(conn); |
63 | ||
64 | 1 | if(err) |
65 | 0 | return next(err); |
66 | ||
67 | // TODO: unlink the uploaded files | |
68 | ||
69 | 1 | res.status(201).send(results); |
70 | }); | |
71 | } | |
72 | ); | |
73 | }); | |
74 | }, | |
75 | list: function(req, res, next) { | |
76 | 3 | if(!req.user) |
77 | 0 | return next(401); |
78 | ||
79 | // get the file records from the DB | |
80 | 3 | var query = r.table('files').without('data'); |
81 | ||
82 | // hide others' files from non-admin users | |
83 | 3 | if(!req.user.admin) |
84 | 2 | query = query.filter({user_id: req.user.id}); |
85 | ||
86 | // filter by user | |
87 | 3 | if(req.params.user) |
88 | 0 | query = query.filter({user_id: req.params.user}); |
89 | ||
90 | // filter | |
91 | 3 | query = controller.filter(req, query); |
92 | ||
93 | // search | |
94 | 3 | if(req.query.search && req.query.search !== '') |
95 | 0 | query = query.filter(r.row('title').downcase().match(req.query.search.toLowerCase())); |
96 | ||
97 | // sort | |
98 | 3 | query = controller.sort(req, query); |
99 | ||
100 | 3 | resources.db.acquire(function(err, conn) { |
101 | 3 | if(err) |
102 | 0 | return next(err); |
103 | ||
104 | 3 | var per_page = parseInt(req.query.per_page, 10) || 50; |
105 | 3 | var page = parseInt(req.query.page, 10) || 1; |
106 | ||
107 | 3 | r.expr({ |
108 | // get the total results count | |
109 | total: query.count(), | |
110 | // get the results for this page | |
111 | files: query.skip(per_page * (page - 1)).limit(per_page).coerceTo('array') | |
112 | }).run(conn, function(err, results){ | |
113 | 3 | resources.db.release(conn); |
114 | ||
115 | 3 | if(err) |
116 | 0 | return next(err); |
117 | ||
118 | 3 | var files = results.files; |
119 | ||
120 | // set pagination headers | |
121 | 3 | controller.paginate(req, res, page, per_page, results.total); |
122 | ||
123 | 3 | res.send(files); |
124 | }); | |
125 | }); | |
126 | }, | |
127 | show: function(req, res, next) { | |
128 | // if(!req.user) | |
129 | // return next(401); | |
130 | ||
131 | 1 | resources.db.acquire(function(err, conn) { |
132 | 1 | if(err) |
133 | 0 | return next(err); |
134 | ||
135 | // get files from the DB | |
136 | 1 | r.table('files').get(req.params.file).run(conn, function(err, file){ |
137 | 1 | resources.db.release(conn); |
138 | ||
139 | 1 | if(err) |
140 | 0 | return next(err); |
141 | ||
142 | 1 | if(!file) |
143 | 0 | return next(404); |
144 | ||
145 | // hide others' files from non-admin users | |
146 | // if(!file || (!req.user.admin && files.user_id !== req.user.id)) | |
147 | // return next(404); | |
148 | ||
149 | 1 | res.set('Content-Type', file.mimetype); |
150 | 1 | res.set('Content-Length', file.size); |
151 | 1 | res.set('Content-Disposition', 'attachment; filename="' + file.name + '"'); |
152 | 1 | return res.send(file.data); |
153 | }); | |
154 | }); | |
155 | }, | |
156 | update: function(req, res, next) { | |
157 | 1 | if(!req.user) |
158 | 0 | return next(401); |
159 | ||
160 | // sanitize the input | |
161 | 1 | sanitize(req.body); |
162 | ||
163 | // validate request against schema | |
164 | 1 | var err = resources.validator.validate('file', req.body, {checkRequired: false}); |
165 | 1 | if(err) |
166 | 0 | return next({code: 400, message: err}); |
167 | ||
168 | // make sure data is not replaced | |
169 | 1 | delete req.body.data; |
170 | ||
171 | // set timestamps | |
172 | 1 | delete req.body.created; |
173 | 1 | req.body.updated = r.now(); |
174 | ||
175 | 1 | resources.db.acquire(function(err, conn) { |
176 | 1 | if(err) |
177 | 0 | return next(err); |
178 | ||
179 | // get files from the DB | |
180 | 1 | r.table('files').get(req.params.file).update(req.body, {returnChanges: true})('changes').nth(0)('new_val').without('data').run(conn, function(err, file){ |
181 | 1 | resources.db.release(conn); |
182 | ||
183 | 1 | if(err) |
184 | 0 | return next(err); |
185 | ||
186 | 1 | return res.send(file); |
187 | }); | |
188 | }); | |
189 | }, | |
190 | replace: function(req, res, next) { | |
191 | 2 | if(!req.user) |
192 | 0 | return next(401); |
193 | ||
194 | 2 | if(!req.user.admin) |
195 | 1 | return next(403, 'Only administrators may replace a file.'); |
196 | ||
197 | // sanitize the input | |
198 | 1 | sanitize(req.body); |
199 | ||
200 | // validate request against schema | |
201 | 1 | var err = resources.validator.validate('file', req.body, {useDefault: true}); |
202 | 1 | if(err) |
203 | 0 | return next({code: 400, message: err}); |
204 | ||
205 | // inject ID | |
206 | 1 | req.body.id = req.params.file; |
207 | ||
208 | // make sure data is not replaced | |
209 | 1 | req.body.data = r.row('data'); |
210 | ||
211 | // set timestamps | |
212 | 1 | req.body.created = r.row('created'); |
213 | 1 | req.body.updated = r.now(); |
214 | ||
215 | // transform dates | |
216 | 1 | if(typeof req.body.open === 'string') |
217 | 0 | req.body.open = new Date(req.body.open); |
218 | ||
219 | 1 | if(typeof req.body.close === 'string') |
220 | 0 | req.body.close = new Date(req.body.close); |
221 | ||
222 | 1 | resources.db.acquire(function(err, conn) { |
223 | 1 | if(err) |
224 | 0 | return next(err); |
225 | ||
226 | // get files from the DB | |
227 | 1 | r.table('files').get(req.params.file).replace(req.body, {returnChanges: true})('changes').nth(0)('new_val').without('data').run(conn, function(err, file){ |
228 | 1 | resources.db.release(conn); |
229 | ||
230 | 1 | if(err) |
231 | 0 | return next(err); |
232 | ||
233 | 1 | return res.send(file); |
234 | }); | |
235 | }); | |
236 | }, | |
237 | destroy: function(req, res, next) { | |
238 | 2 | if(!req.user) |
239 | 0 | return next(401); |
240 | ||
241 | // if(!req.user.admin) | |
242 | // return next(403, 'Only administrators may delete a file.'); | |
243 | ||
244 | // TODO: check that there are no projects that depend on this file | |
245 | ||
246 | 2 | resources.db.acquire(function(err, conn) { |
247 | 2 | if(err) |
248 | 0 | return next(err); |
249 | ||
250 | // get files from the DB | |
251 | 2 | r.table('files').get(req.params.file).without('data').run(conn, function(err, file){ |
252 | 2 | if(err) { |
253 | 0 | resources.db.release(conn); |
254 | 0 | return next(err); |
255 | } | |
256 | ||
257 | 2 | if(!file) { |
258 | 0 | resources.db.release(conn); |
259 | 0 | return next(404); |
260 | } | |
261 | ||
262 | 2 | if(file.user_id === req.user.id && !req.user.admin) { |
263 | 1 | resources.db.release(conn); |
264 | 1 | return next(403); |
265 | } | |
266 | ||
267 | 1 | if(file.lock) { |
268 | 0 | resources.db.release(conn); |
269 | 0 | return next(423); |
270 | } | |
271 | ||
272 | // destroy the db entry | |
273 | 1 | r.table('files').get(req.params.file).delete().run(conn, function(err, result){ |
274 | 1 | resources.db.release(conn); |
275 | ||
276 | 1 | if(err) |
277 | 0 | return next(err); |
278 | ||
279 | 1 | res.set('Content-Type', file.mimetype); |
280 | 1 | res.set('Content-Length', file.size); |
281 | 1 | res.set('Content-Disposition', 'attachment; filename="' + file.name + '"'); |
282 | 1 | return res.send(file); |
283 | }); | |
284 | }); | |
285 | }); | |
286 | } | |
287 | }; | |
288 | } | |
289 |
Line | Hits | Source |
---|---|---|
1 | 1 | 'use strict'; |
2 | ||
3 | 1 | var url = require('url'); |
4 | 1 | var _ = require('lodash'); |
5 | 1 | var r = require('rethinkdb'); |
6 | ||
7 | 1 | var controller = require('../controller.js'); |
8 | ||
9 | 1 | module.exports = function(config, resources) { |
10 | ||
11 | 1 | function sanitize(o) { |
12 | // strip locks | |
13 | 14 | _.each(o.flow, function(stage){ delete stage.lock; }) |
14 | 3 | delete o.href; |
15 | 3 | delete o.id; |
16 | } | |
17 | ||
18 | 1 | function getRole(user, project, cycle) { |
19 | 4 | if(project.users[user.id] && project.users[user.id].role && cycle.roles[project.users[user.id].role]) |
20 | 1 | return cycle.roles[project.users[user.id].role]; |
21 | ||
22 | 3 | if(cycle.users[user.id] && cycle.users[user.id].role && cycle.roles[cycle.users[user.id].role]) |
23 | 3 | return cycle.roles[cycle.users[user.id].role]; |
24 | ||
25 | 0 | return null; |
26 | } | |
27 | ||
28 | 1 | function buildEvents(project, cycle) { |
29 | 3 | var events = {}; |
30 | ||
31 | 3 | _.each(cycle.events, function(event, id){ |
32 | 27 | var value = event.conditions.some(function(conditions){ |
33 | 27 | return conditions.every(function(condition){ |
34 | 30 | var tester = resources.testers[condition.name] |
35 | ||
36 | 30 | if(!tester) |
37 | 0 | return false; |
38 | ||
39 | 30 | return tester(condition.options, project); |
40 | }); | |
41 | }); | |
42 | ||
43 | // nothing's changed | |
44 | 27 | if(project.events[id] && project.events[id][0] && project.events[id][0].value === value) |
45 | 18 | return; |
46 | ||
47 | // TODO: process listeners | |
48 | ||
49 | // prepend the event to an existing list | |
50 | 9 | if(project.events[id]) |
51 | 0 | return events[id] = r.row('events')(id).prepend({ |
52 | value: value, | |
53 | date: r.now() | |
54 | }); | |
55 | ||
56 | // add the first event | |
57 | 9 | events[id] = [{ |
58 | value: value, | |
59 | date: r.now() | |
60 | }]; | |
61 | }); | |
62 | ||
63 | 3 | return events; |
64 | } | |
65 | ||
66 | 1 | function applyLocks(project, cycle) { |
67 | 4 | var locks = {}; |
68 | 4 | _.each(cycle.flow, function(stage, id){ |
69 | ||
70 | // open | |
71 | 44 | if(stage.lock.open.some(function(event){ |
72 | 48 | if(!project.events[event] || !project.events[event][0] || !project.events[event][0].value) |
73 | 31 | return locks[id] = cycle.events[event] ? cycle.events[event].messages[0] : 'The event "'+event+'" has not yet opened this stage.'; |
74 | 31 | })) return; |
75 | ||
76 | // close | |
77 | 13 | if(stage.lock.close.some(function(event){ |
78 | 39 | if(project.events[event] && project.events[event][0] && project.events[event][0].value) |
79 | 13 | return locks[id] = cycle.events[event] ? cycle.events[event].messages[1] : 'The event "'+event+'" has closed this stage.'; |
80 | 13 | })) return; |
81 | ||
82 | 0 | locks[id] = null; |
83 | }); | |
84 | ||
85 | 4 | return locks; |
86 | } | |
87 | ||
88 | 1 | function Project(role, project, cycle) { |
89 | 4 | if(!project.flow) |
90 | 2 | project.flow = {}; |
91 | ||
92 | // apply locks | |
93 | 4 | _.each(applyLocks(project, cycle), function(lock, id) { |
94 | 44 | project.flow[id] = project.flow[id] || {id: id, data: {}, status: 'none'}; |
95 | 44 | project.flow[id].lock = lock; |
96 | }); | |
97 | ||
98 | // TODO: apply role restrictions | |
99 | ||
100 | // hide users | |
101 | ||
102 | // hide flow stages | |
103 | ||
104 | // hide invitations | |
105 | ||
106 | 4 | return project; |
107 | } | |
108 | ||
109 | 1 | return { |
110 | create: function(req, res, next) { | |
111 | 1 | if(!req.user) |
112 | 0 | return next(401); |
113 | ||
114 | // sanitize request | |
115 | 1 | sanitize(req.body); |
116 | ||
117 | // validate request against schema | |
118 | 1 | var err = resources.validator.validate('project', req.body, {useDefault: true}); |
119 | 1 | if(err) |
120 | 0 | return next({code: 400, message: err}); |
121 | ||
122 | // set timestamps | |
123 | 1 | req.body.created = req.body.updated = r.now(); |
124 | ||
125 | // transform dates | |
126 | 1 | if(typeof req.body.open === 'string') |
127 | 0 | req.body.open = new Date(req.body.open); |
128 | ||
129 | 1 | if(typeof req.body.close === 'string') |
130 | 0 | req.body.close = new Date(req.body.close); |
131 | ||
132 | 1 | resources.db.acquire(function(err, conn) { |
133 | 1 | if(err) |
134 | 0 | return next(err); |
135 | ||
136 | 1 | r.table('cycles').get(req.body.cycle_id).run(conn, function(err, cycle){ |
137 | 1 | if(err){ |
138 | 0 | resources.db.release(conn); |
139 | 0 | return next(err); |
140 | } | |
141 | ||
142 | 1 | if(!cycle){ |
143 | 0 | resources.db.release(conn); |
144 | 0 | return next(400, 'No such cycle.'); |
145 | } | |
146 | ||
147 | // restrictions for non-admin users | |
148 | 1 | if(!req.user.admin){ |
149 | ||
150 | // TODO: verify that project is open | |
151 | ||
152 | // add current user with default role | |
153 | 1 | req.body.users[req.user.id] = { |
154 | id: req.user.id, | |
155 | role: cycle.defaults.role | |
156 | } | |
157 | ||
158 | // TODO: verify that any other users are of allowable roles | |
159 | ||
160 | // apply default status | |
161 | 1 | req.body.status = cycle.defaults.status; |
162 | } | |
163 | ||
164 | // process the events | |
165 | 1 | req.body.events = buildEvents(req.body, cycle) |
166 | ||
167 | // insert the project | |
168 | 1 | r.table('projects').insert(req.body, {returnChanges: true}).run(conn, function(err, result){ |
169 | 1 | resources.db.release(conn); |
170 | ||
171 | 1 | if(err) |
172 | 0 | return next(err); |
173 | ||
174 | 1 | var project = result.changes[0].new_val; |
175 | ||
176 | 1 | return res.status(201).send(project); |
177 | }); | |
178 | }); | |
179 | }); | |
180 | }, | |
181 | list: function(req, res, next) { | |
182 | 0 | if(!req.user) |
183 | 0 | return next(401); |
184 | ||
185 | 0 | resources.db.acquire(function(err, conn) { |
186 | 0 | if(err) |
187 | 0 | return next(err); |
188 | ||
189 | 0 | var query = r.table('projects'); |
190 | 0 | var per_page = parseInt(req.query.per_page, 10) || 50; |
191 | 0 | var page = parseInt(req.query.page, 10) || 1; |
192 | ||
193 | // restrict to user | |
194 | // TODO: make sure to only return users visible to the current user | |
195 | 0 | if(req.params.user) |
196 | 0 | return r.table('cycles').hasFields({users: req.params.user})('id').run(conn, function(err, cursor){ |
197 | 0 | cursor.toArray(function(err, cycleIds){ |
198 | 0 | query = query.filter(r.expr(cycleIds).contains(r.row('cycle_id')).or(r.row('users').hasFields(req.params.user))); |
199 | 0 | next(); |
200 | }); | |
201 | }); | |
202 | ||
203 | // all projects | |
204 | 0 | return next(); |
205 | ||
206 | 0 | function next(){ |
207 | ||
208 | // filter | |
209 | 0 | query = controller.filter(req, query); |
210 | ||
211 | // search | |
212 | 0 | if(req.query.search && req.query.search !== '') |
213 | 0 | query = query.filter(r.row('title').downcase().match(req.query.search.toLowerCase())); |
214 | ||
215 | // sort | |
216 | 0 | query = controller.sort(req, query); |
217 | ||
218 | 0 | r.expr({ |
219 | // get the total results count | |
220 | total: query.count(), | |
221 | // get the results for this page | |
222 | projects: query.skip(per_page * (page - 1)).limit(per_page).coerceTo('array') | |
223 | }).run(conn, function(err, results){ | |
224 | 0 | resources.db.release(conn); |
225 | ||
226 | 0 | if(err) |
227 | 0 | return next(err); |
228 | ||
229 | 0 | var projects = results.projects; |
230 | ||
231 | // set pagination headers | |
232 | 0 | controller.paginate(req, res, page, per_page, results.total); |
233 | ||
234 | 0 | res.send(projects); |
235 | }); | |
236 | } | |
237 | }); | |
238 | }, | |
239 | show: function(req, res, next) { | |
240 | 2 | if(!req.user) |
241 | 0 | return next(401); |
242 | ||
243 | // get the project & cycle | |
244 | 2 | resources.db.acquire(function(err, conn) { |
245 | 2 | if(err) |
246 | 0 | return next(err); |
247 | ||
248 | // get cycles from the DB | |
249 | 2 | var query = r.table('projects').get(req.params.project); |
250 | 2 | r.expr({ |
251 | project: query, | |
252 | cycle: r.branch(query, r.table('cycles').get(query('cycle_id')), null) | |
253 | }).run(conn, function(err, result){ | |
254 | 2 | resources.db.release(conn); |
255 | ||
256 | 2 | if(err) |
257 | 0 | return next(err); |
258 | ||
259 | 2 | var project = result.project; |
260 | 2 | var cycle = result.cycle; |
261 | ||
262 | 2 | if(!project || !cycle) |
263 | 0 | return next(404); |
264 | ||
265 | // restrict to admin and assigned users | |
266 | 2 | var role = getRole(req.user, project, cycle); |
267 | 2 | if(!role && !req.user.admin) |
268 | 0 | return next(403); |
269 | ||
270 | 2 | return res.send(Project(role, project, cycle)); |
271 | }); | |
272 | }); | |
273 | ||
274 | }, | |
275 | update: function(req, res, next) { | |
276 | 1 | if(!req.user) |
277 | 0 | return next(401); |
278 | ||
279 | // sanitize request | |
280 | 1 | sanitize(req.body); |
281 | ||
282 | // validate request against schema | |
283 | 1 | var err = resources.validator.validate('project', req.body, {checkRequired: false}); |
284 | 1 | if(err) |
285 | 0 | return next({code: 400, message: err}); |
286 | ||
287 | 1 | resources.db.acquire(function(err, conn) { |
288 | 1 | if(err) |
289 | 0 | return next(err); |
290 | ||
291 | 1 | var query = r.table('projects').get(req.params.project); |
292 | 1 | r.expr({ |
293 | project: query, | |
294 | cycle: r.branch(query, r.table('cycles').get(query('cycle_id')), null) | |
295 | }).run(conn, function(err, result){ | |
296 | 1 | if(err){ |
297 | 0 | resources.db.release(conn); |
298 | 0 | return next(err); |
299 | } | |
300 | ||
301 | 1 | var project = result.project; |
302 | 1 | var cycle = result.cycle; |
303 | ||
304 | 1 | if(!project || !cycle) |
305 | 0 | return next(404); |
306 | ||
307 | // restrict to admin and assigned users | |
308 | 1 | var role = getRole(req.user, project, cycle); |
309 | 1 | if(!role && !req.user.admin) |
310 | 0 | return next(403); |
311 | ||
312 | // set timestamps | |
313 | 1 | delete req.body.created; |
314 | 1 | req.body.updated = r.now(); |
315 | ||
316 | // remove indexed objects with null values | |
317 | 1 | var without = {}; |
318 | 1 | _.each(['users'], function(index){ |
319 | 1 | _.each(req.body[index], function(value, id){ |
320 | 0 | if(value !== null) |
321 | 0 | return; |
322 | ||
323 | 0 | without[index] = without[index] || {}; |
324 | 0 | without[index][id] = true; |
325 | 0 | delete req.body[index][id]; |
326 | }); | |
327 | }); | |
328 | ||
329 | // TODO: process components individually | |
330 | ||
331 | // TODO: prevent from updating certain fields | |
332 | ||
333 | // update the record | |
334 | 1 | r.table('projects').get(req.params.project).replace(r.row.without(without).merge(req.body), {returnChanges: true}).run(conn, function(err, result){ |
335 | 1 | if(err){ |
336 | 0 | resources.db.release(conn); |
337 | 0 | return next(err); |
338 | } | |
339 | ||
340 | 1 | var project = result.changes[0].new_val; |
341 | ||
342 | // process the events | |
343 | 1 | var events = buildEvents(project, cycle); |
344 | ||
345 | // no events to update | |
346 | 1 | if(!Object.keys(events).length){ |
347 | 1 | resources.db.release(conn); |
348 | 1 | return res.send(Project(role, project, cycle)); |
349 | } | |
350 | ||
351 | // update the events | |
352 | 0 | r.table('projects').get(req.params.project).update({events: events}, {returnChanges: true}).run(conn, function(err, result){ |
353 | 0 | resources.db.release(conn); |
354 | ||
355 | 0 | if(err) |
356 | 0 | return next(err); |
357 | ||
358 | 0 | var project = result.new_val; |
359 | ||
360 | 0 | return res.send(Project(role, project, cycle)); |
361 | }); | |
362 | }); | |
363 | }); | |
364 | }); | |
365 | ||
366 | // // TODO: validate against schema | |
367 | ||
368 | // // TODO: get and process the current cycle and project | |
369 | // var cycle = {}; | |
370 | // var project = {}; | |
371 | ||
372 | // // restrict to admin and assigned users | |
373 | // var role = getRole(req.user, project, cycle); | |
374 | // if(!role && !req.user.admin) | |
375 | // return next(403); | |
376 | ||
377 | // // if we aren't processing a component | |
378 | // if(!req.body.flow) | |
379 | // return next(); | |
380 | ||
381 | // // build the list of component processes to run | |
382 | // var tasks = {}; | |
383 | // _.each(req.body.flow, function(data, id){ | |
384 | // if(!resources.components[cycle.flow[id].component.name]) | |
385 | // throw new Error('no such component!'); | |
386 | ||
387 | // tasks[id] = async.apply( | |
388 | // resources.components[cycle.flow[id].component.name].update, | |
389 | // data, | |
390 | // role, | |
391 | // cycle.flow[id].component.options, | |
392 | // project | |
393 | // ) | |
394 | // }); | |
395 | ||
396 | // // components are in charge of making | |
397 | // // the db calls to update their stages, | |
398 | // // so we're done with the flow | |
399 | // delete req.body.flow; | |
400 | ||
401 | ||
402 | // async.parallel(tasks, function(err, flow){ | |
403 | // if(err) | |
404 | // return next(err); | |
405 | ||
406 | // // TODO: what to do w/ result? | |
407 | ||
408 | // return next() | |
409 | // }); | |
410 | ||
411 | ||
412 | // function next(){ | |
413 | // if(!req.user.admin) | |
414 | // return next(); | |
415 | ||
416 | // r.table('projects').get(req.params.project).update(req.body) | |
417 | ||
418 | // function next(){ | |
419 | ||
420 | // // get and build the project | |
421 | ||
422 | // res.status(200).send(Project(role, project, cycle)); | |
423 | // } | |
424 | // } | |
425 | }, | |
426 | replace: function(req, res, next) { | |
427 | 2 | if(!req.user) |
428 | 0 | return next(401); |
429 | ||
430 | 2 | if(!req.user.admin) |
431 | 1 | return next(403, 'Only administrators may replace a project.'); |
432 | ||
433 | // sanitize request | |
434 | 1 | sanitize(req.body); |
435 | ||
436 | // validate request against schema | |
437 | 1 | var err = resources.validator.validate('project', req.body, {useDefault: true}); |
438 | 1 | if(err) |
439 | 0 | return next({code: 400, message: err}); |
440 | ||
441 | // inject ID | |
442 | 1 | req.body.id = req.params.project; |
443 | ||
444 | // set timestamps | |
445 | 1 | req.body.created = r.row('created'); |
446 | 1 | req.body.updated = r.now(); |
447 | ||
448 | 1 | resources.db.acquire(function(err, conn) { |
449 | 1 | if(err) |
450 | 0 | return next(err); |
451 | ||
452 | 1 | r.table('cycles').get(req.body.cycle_id).run(conn, function(err, cycle){ |
453 | 1 | if(err){ |
454 | 0 | resources.db.release(conn); |
455 | 0 | return next(err); |
456 | } | |
457 | ||
458 | 1 | if(!cycle){ |
459 | 0 | resources.db.release(conn); |
460 | 0 | return next(400, 'No such cycle.'); |
461 | } | |
462 | ||
463 | // replace the record in the db | |
464 | 1 | r.table('projects').get(req.params.project).replace(req.body, {returnChanges: true}).run(conn, function(err, result){ |
465 | 1 | if(err){ |
466 | 0 | resources.db.release(conn); |
467 | 0 | return next(err); |
468 | } | |
469 | ||
470 | 1 | var project = result.changes[0].new_val; |
471 | 1 | var role = getRole(req.user, project, cycle); |
472 | ||
473 | // process the events | |
474 | 1 | var events = buildEvents(project, cycle); |
475 | ||
476 | // no events to update | |
477 | 1 | if(!Object.keys(events).length){ |
478 | 1 | resources.db.release(conn); |
479 | 1 | return res.send(Project(role, project, cycle)); |
480 | } | |
481 | ||
482 | // update the events | |
483 | 0 | r.table('projects').get(req.params.project).update({events: events}, {returnChanges: true}).run(conn, function(err, result){ |
484 | 0 | resources.db.release(conn); |
485 | ||
486 | 0 | if(err) |
487 | 0 | return next(err); |
488 | ||
489 | 0 | var project = result.changes[0].new_val; |
490 | ||
491 | 0 | return res.send(Project(role, project, cycle)); |
492 | }); | |
493 | }); | |
494 | }); | |
495 | }); | |
496 | }, | |
497 | destroy: function(req, res, next) { | |
498 | 2 | if(!req.user) |
499 | 0 | return next(401); |
500 | ||
501 | 2 | if(!req.user.admin) |
502 | 1 | return next(403, 'Only administrators may delete a project.'); |
503 | ||
504 | 1 | resources.db.acquire(function(err, conn) { |
505 | 1 | if(err) |
506 | 0 | return next(err); |
507 | ||
508 | 1 | return r.table('projects').get(req.params.project).delete({returnChanges: true}).run(conn, function(err, result){ |
509 | 1 | resources.db.release(conn); |
510 | ||
511 | 1 | if(err) |
512 | 0 | return next(err); |
513 | ||
514 | 1 | var project = result.old_val; |
515 | ||
516 | 1 | return res.send(project); |
517 | }); | |
518 | }); | |
519 | } | |
520 | }; | |
521 | } | |
522 |
Line | Hits | Source |
---|---|---|
1 | 1 | 'use strict'; |
2 | ||
3 | 1 | var _ = require('lodash'); |
4 | 1 | var r = require('rethinkdb'); |
5 | 1 | var crypto = require('crypto'); |
6 | 1 | var passport = require('passport'); |
7 | 1 | var scrypt = require('scrypt'); |
8 | 1 | scrypt.hash.config.keyEncoding = scrypt.verify.config.keyEncoding = 'utf8'; |
9 | 1 | scrypt.hash.config.outputEncoding = scrypt.verify.config.outputEncoding = 'base64'; |
10 | ||
11 | 1 | var controller = require('../controller.js'); |
12 | ||
13 | 1 | var blacklist = ['password','recovery_token']; |
14 | 1 | var whitelist = ['id', 'email', 'name', 'href', 'admin', 'created','updated']; |
15 | ||
16 | 1 | function prepare(o, p){ |
17 | 67 | if(_.isArray(o)) |
18 | 56 | return _.map(o, function(o){return prepare(o, p);}); |
19 | ||
20 | 61 | return _.assign( |
21 | (p === true || p === o.id) ? _.omit(o, blacklist) : _.pick(o, whitelist), | |
22 | {href: '/api/users/' + o.id} | |
23 | ); | |
24 | } | |
25 | ||
26 | 1 | function sanitize(o){ |
27 | 9 | delete o.href; |
28 | 9 | delete o.id; |
29 | } | |
30 | ||
31 | 1 | module.exports = function(config, resources) { |
32 | 1 | return { |
33 | create: function(req, res, next){ | |
34 | 6 | var email = 'welcome'; |
35 | ||
36 | // generate random password if not set | |
37 | 6 | req.body.password = req.body.password || crypto.randomBytes(256).toString('base64'); |
38 | ||
39 | // validate request against schema | |
40 | 6 | var err = resources.validator.validate('user', req.body, {useDefault: true}); |
41 | 6 | if(err) |
42 | 1 | return next({code: 400, message: err}); |
43 | ||
44 | 5 | var privilige = true; |
45 | ||
46 | // sanitize the input | |
47 | 5 | sanitize(req.body); |
48 | ||
49 | 5 | passport.authenticate('bearer', { session: false }, function(err, user) { |
50 | ||
51 | // only allow admins to create a new admin user | |
52 | 5 | if (req.body.admin && (err || !user || !user.admin)) |
53 | 2 | return next({code: 403, message: 'You are not authorized to create admin accounts.'}); |
54 | ||
55 | // add timestamps | |
56 | 3 | req.body.created = req.body.updated = r.now(); |
57 | ||
58 | // encrypt the password | |
59 | 3 | req.body.password = scrypt.hash(req.body.password, scrypt.params(0.1)); |
60 | ||
61 | 3 | resources.db.acquire(function(err, conn) { |
62 | 3 | if(err) |
63 | 0 | return next(err); |
64 | ||
65 | // make the email case insensitive | |
66 | 3 | req.body.email = req.body.email.toLowerCase(); |
67 | ||
68 | // insert the user | |
69 | 3 | r.branch(r.table('users').filter({email: req.body.email}).limit(1).count().eq(1), |
70 | {'$$ERROR$$': {'code': 409, 'message': 'An account already exists with this email'}}, | |
71 | r.table('users').insert(req.body, {returnChanges: true}) | |
72 | ).run(conn, function(err, result){ | |
73 | 3 | resources.db.release(conn); |
74 | ||
75 | 3 | if(err) |
76 | 0 | return next(err); |
77 | ||
78 | 3 | if(result['$$ERROR$$']) |
79 | 1 | return next(result['$$ERROR$$']); |
80 | ||
81 | 2 | var user = result.changes[0].new_val; |
82 | ||
83 | 2 | return res.status(201).send(prepare(user, privilige)); |
84 | }); | |
85 | }); | |
86 | })(req, res); | |
87 | }, | |
88 | list: function(req, res, next){ | |
89 | 6 | if(!req.user) |
90 | 0 | return next(401); |
91 | ||
92 | 6 | var privilige = req.user.admin || req.user.id; |
93 | ||
94 | 6 | resources.db.acquire(function(err, conn) { |
95 | 6 | if(err) |
96 | 0 | return next(err); |
97 | ||
98 | 6 | function getUsers(ids){ |
99 | ||
100 | 6 | var per_page = parseInt(req.query.per_page, 10) || 50; |
101 | 6 | var page = parseInt(req.query.page, 10) || 1; |
102 | ||
103 | // if we aren't getting any results | |
104 | 6 | if(ids && ids.length === 0) |
105 | 0 | return res.send([]); |
106 | ||
107 | // get users from the DB | |
108 | 6 | var query = r.table('users'); |
109 | ||
110 | // restrict to ids | |
111 | 6 | if(ids) |
112 | 0 | query = query.getAll.apply(query, ids); |
113 | ||
114 | // filter | |
115 | 6 | query = controller.filter(req, query); |
116 | ||
117 | // search | |
118 | 6 | if(req.query.search && req.query.search !== '') |
119 | 0 | query = query.filter( |
120 | r.row('email').downcase().match(req.query.search.toLowerCase()) | |
121 | .or(r.row('name').downcase().match(req.query.search.toLowerCase())) | |
122 | ); | |
123 | ||
124 | // sort | |
125 | 6 | query = controller.sort(req, query); |
126 | ||
127 | 6 | r.expr({ |
128 | total: query.count(), // get the total results count | |
129 | users: query.skip(per_page * (page - 1)).limit(per_page).coerceTo('array') // get the results for this page | |
130 | }).run(conn, function(err, results){ | |
131 | 6 | resources.db.release(conn); |
132 | ||
133 | 6 | if(err) |
134 | 0 | return next(err); |
135 | ||
136 | 6 | var users = results.users; |
137 | ||
138 | // set pagination headers | |
139 | 6 | controller.paginate(req, res, page, per_page, results.total); |
140 | ||
141 | 6 | res.send(prepare(users)); |
142 | }); | |
143 | } | |
144 | ||
145 | // get users by cycle | |
146 | 6 | if(req.params.cycle){ |
147 | 0 | return r.table('cycles').get(req.params.cycle).run(conn, function(err, cycle){ |
148 | 0 | if(err){ |
149 | 0 | resources.db.release(conn); |
150 | 0 | return next(err); |
151 | } | |
152 | ||
153 | 0 | if(!cycle){ |
154 | 0 | resources.db.release(conn); |
155 | 0 | return next(404); |
156 | } | |
157 | ||
158 | 0 | var role, ids = []; |
159 | ||
160 | // (admin) fetch all users | |
161 | 0 | if(req.user.admin){ |
162 | 0 | Object.keys(cycle.users).forEach(function(id){ |
163 | 0 | ids.push(id); |
164 | }); | |
165 | ||
166 | 0 | return getUsers(ids); |
167 | } | |
168 | ||
169 | // (role) fetch all users visible to the current user's role on the cycle | |
170 | 0 | if(cycle.users[req.user.id] && (role = cycle.users[req.user.id].role) && cycle.roles[role]){ |
171 | 0 | _.each(cycle.users, function(data, id){ |
172 | 0 | if(_.indexOf(cycle.roles[role].visible, data.role) !== -1) |
173 | 0 | ids.push(id); |
174 | }); | |
175 | ||
176 | 0 | return getUsers(ids); |
177 | } | |
178 | ||
179 | // empty array for this user | |
180 | 0 | return res.send([]); |
181 | }); | |
182 | } | |
183 | ||
184 | // get users by project | |
185 | 6 | if(req.params.project){ |
186 | 0 | return r.table('projects').get(req.params.project).run(conn, function(err, project){ |
187 | 0 | if(err){ |
188 | 0 | resources.db.release(conn); |
189 | 0 | return next(err); |
190 | } | |
191 | ||
192 | 0 | if(!project){ |
193 | 0 | resources.db.release(conn); |
194 | 0 | return next(404); |
195 | } | |
196 | ||
197 | 0 | return r.table('cycles').get(project.cycle_id).run(conn, function(err, cycle){ |
198 | 0 | if(err){ |
199 | 0 | resources.db.release(conn); |
200 | 0 | return next(err); |
201 | } | |
202 | ||
203 | 0 | if(!cycle){ |
204 | 0 | resources.db.release(conn); |
205 | 0 | return next(404); |
206 | } | |
207 | ||
208 | 0 | var role, ids = []; |
209 | ||
210 | // (admin) fetch all users | |
211 | 0 | if(req.user.admin){ |
212 | 0 | Object.keys(cycle.users).forEach(function(id){ |
213 | 0 | ids.push(id); |
214 | }); | |
215 | ||
216 | 0 | Object.keys(project.users).forEach(function(id){ |
217 | 0 | ids.push(id); |
218 | }); | |
219 | ||
220 | 0 | return getUsers(_.unique(ids)); |
221 | } | |
222 | ||
223 | // (role) fetch all users visible to the current user's role on the project or cycle | |
224 | 0 | if( |
225 | (project.users[req.user.id] && (role = project.users[req.user.id].role) && cycle.roles[role]) // role on project | |
226 | || (cycle.users[req.user.id] && (role = cycle.users[req.user.id].role) && cycle.roles[role]) // role on cycle | |
227 | ){ | |
228 | 0 | _.each(cycle.users, function(data, id){ |
229 | 0 | if(_.indexOf(cycle.roles[role].visible, data.role) !== -1) |
230 | 0 | ids.push(id); |
231 | }); | |
232 | ||
233 | 0 | return getUsers(ids); |
234 | } | |
235 | ||
236 | // empty array for this user | |
237 | 0 | return res.send([]); |
238 | }); | |
239 | }); | |
240 | } | |
241 | ||
242 | 6 | return getUsers(); |
243 | }); | |
244 | }, | |
245 | show: function(req, res, next){ | |
246 | 5 | if(!req.user) |
247 | 0 | return next(401); |
248 | ||
249 | 5 | var id; |
250 | ||
251 | 5 | if(req.params.user) // get by ID |
252 | 5 | id = req.params.user; |
253 | 0 | else if(req.params.file) // get by file |
254 | 0 | id = r.table('files').get(req.params.file)('user_id'); |
255 | else | |
256 | 0 | return next(400); |
257 | ||
258 | 5 | resources.db.acquire(function(err, conn) { |
259 | 5 | if(err) |
260 | 0 | return next(err); |
261 | ||
262 | // get users from the DB | |
263 | 5 | r.table('users').get(id).run(conn, function(err, user){ |
264 | 5 | resources.db.release(conn); |
265 | ||
266 | 5 | if(err) |
267 | 0 | return next(err); |
268 | ||
269 | 5 | if(!user) |
270 | 0 | return next(404); |
271 | ||
272 | 5 | var privilige = req.user.admin || req.user.id === user.id; |
273 | ||
274 | 5 | return res.send(prepare(user, privilige)); |
275 | }); | |
276 | }); | |
277 | }, | |
278 | update: function(req, res, next){ | |
279 | 5 | if(!req.user) |
280 | 0 | return next(401); |
281 | ||
282 | 5 | if(!req.user.admin && req.user.id !== req.params.user) |
283 | 1 | return next({code: 403, message: 'Non-admin users may only update themselves.'}); |
284 | ||
285 | 4 | if(!req.user.admin && req.body.admin) |
286 | 1 | return next({code: 403, message: 'You are not authorized to give admin status.'}); |
287 | ||
288 | 3 | var privilige = true; |
289 | ||
290 | // sanitize the input | |
291 | 3 | sanitize(req.body); |
292 | ||
293 | // validate request against schema | |
294 | 3 | var err = resources.validator.validate('user', req.body, {checkRequired: false}); |
295 | 3 | if(err) |
296 | 1 | return next({code: 400, message: err}); |
297 | ||
298 | // add timestamps | |
299 | 2 | req.body.updated = r.now(); |
300 | ||
301 | // encrypt the password | |
302 | 2 | if(req.body.password) |
303 | 0 | req.body.password = scrypt.hash(req.body.password, scrypt.params(0.1)); |
304 | ||
305 | 2 | resources.db.acquire(function(err, conn) { |
306 | 2 | if(err) |
307 | 0 | return next(err); |
308 | ||
309 | // build the query | |
310 | 2 | var query = r.table('users').get(req.params.user).update(req.body, {returnChanges: true}); |
311 | ||
312 | // verify email is not already taken by a different user | |
313 | 2 | if(typeof req.body.email !== 'undefined') |
314 | 0 | query = r.do(r.table('users').filter({email: req.body.email}).limit(1).nth(0).default(null), function(conflict){ |
315 | 0 | return r.branch(conflict.eq(null).not().and(conflict('id').eq(req.params.user).not()), |
316 | {'$$ERROR$$': {'code': 409, 'message': 'An account already exists with this email'}}, | |
317 | query | |
318 | ); | |
319 | }); | |
320 | ||
321 | // update the user | |
322 | 2 | query.run(conn, function(err, result){ |
323 | 2 | resources.db.release(conn); |
324 | ||
325 | 2 | if(err) |
326 | 0 | return next(err); |
327 | ||
328 | 2 | if(result['$$ERROR$$']) |
329 | 0 | return next(result['$$ERROR$$']); |
330 | ||
331 | 2 | var user = result.changes[0].new_val; |
332 | ||
333 | 2 | return res.send(prepare(user, privilige)); |
334 | }); | |
335 | ||
336 | }); | |
337 | }, | |
338 | replace: function(req, res, next){ | |
339 | 2 | if(!req.user) |
340 | 0 | return next(401); |
341 | ||
342 | 2 | if(!req.user.admin) |
343 | 1 | return next({code: 403, message: 'Only admins can replace a user record.'}); |
344 | ||
345 | 1 | var privilige = true; |
346 | ||
347 | // sanitize the input | |
348 | 1 | sanitize(req.body); |
349 | ||
350 | // validate request against schema | |
351 | 1 | var err = resources.validator.validate('user', req.body, {useDefault: true}); |
352 | 1 | if(err) |
353 | 0 | return next({code: 400, message: err}); |
354 | ||
355 | 1 | resources.db.acquire(function(err, conn) { |
356 | 1 | if(err) |
357 | 0 | return next(err); |
358 | ||
359 | // build the query | |
360 | 1 | var query = r.table('users').get(req.params.user).replace(function(row){ |
361 | ||
362 | // inject ID | |
363 | 1 | req.body.id = req.params.user; |
364 | ||
365 | // add timestamps | |
366 | 1 | req.body.created = row('created'); |
367 | 1 | req.body.updated = r.now(); |
368 | ||
369 | // encrypt the password or use old one | |
370 | 1 | req.body.password = req.body.password ? scrypt.hash(req.body.password, scrypt.params(0.1)) : row('password'); |
371 | ||
372 | 1 | return req.body; |
373 | }, {returnChanges: true}); | |
374 | ||
375 | // verify email is not already taken by a different user | |
376 | 1 | if(typeof req.body.email !== 'undefined') |
377 | 1 | query = r.do(r.table('users').filter({email: req.body.email}).limit(1).nth(0).default(null), function(conflict){ |
378 | 1 | return r.branch(conflict.eq(null).not().and(conflict('id').eq(req.params.user).not()), |
379 | {'$$ERROR$$': {'code': 409, 'message': 'An account already exists with this email'}}, | |
380 | query | |
381 | ); | |
382 | }); | |
383 | ||
384 | // replace the user | |
385 | 1 | query.run(conn, function(err, result){ |
386 | 1 | resources.db.release(conn); |
387 | ||
388 | 1 | if(err) |
389 | 0 | return next(err); |
390 | ||
391 | 1 | if(result['$$ERROR$$']) |
392 | 0 | return next(result['$$ERROR$$']); |
393 | ||
394 | 1 | var user = result.changes[0].new_val; |
395 | ||
396 | 1 | return res.send(prepare(user, privilige)); |
397 | }); | |
398 | }); | |
399 | }, | |
400 | destroy: function(req, res, next){ | |
401 | ||
402 | 3 | if(!req.user || !req.user.admin) |
403 | 1 | return next(403, 'Only administrators may delete a user.'); |
404 | ||
405 | 2 | if(req.user.id == req.params.user) |
406 | 1 | return next(400, 'You cannot delete your own admin account.'); |
407 | ||
408 | 1 | var privilige = true; |
409 | ||
410 | 1 | resources.db.acquire(function(err, conn) { |
411 | 1 | if(err) |
412 | 0 | return next(err); |
413 | ||
414 | // get users from the DB | |
415 | 1 | r.table('users').get(req.params.user).delete({returnChanges: true}).run(conn, function(err, result){ |
416 | 1 | resources.db.release(conn); |
417 | ||
418 | 1 | if(err) |
419 | 0 | return next(err); |
420 | ||
421 | 1 | var user = result.changes[0].old_val; |
422 | ||
423 | 1 | return res.send(prepare(user, privilige)); |
424 | }); | |
425 | }); | |
426 | } | |
427 | }; | |
428 | }; | |
429 |
Line | Hits | Source |
---|---|---|
1 | 1 | 'use strict'; |
2 | ||
3 | 1 | var passport = require('passport'); |
4 | ||
5 | 1 | module.exports = function(config, router, resources){ |
6 | ||
7 | 1 | var controller = require('../controllers/cycles')(config, resources); |
8 | ||
9 | // create | |
10 | // ------ | |
11 | 1 | router.post( |
12 | '/api/cycles', | |
13 | passport.authenticate('bearer', { session: false }), | |
14 | controller.create | |
15 | ); | |
16 | ||
17 | // list | |
18 | // ---- | |
19 | 1 | router.get( |
20 | '/api/cycles', | |
21 | passport.authenticate('bearer', { session: false }), | |
22 | controller.list | |
23 | ); | |
24 | ||
25 | // show | |
26 | // ---- | |
27 | 1 | router.get( |
28 | '/api/cycles/:cycle', | |
29 | passport.authenticate('bearer', { session: false }), | |
30 | controller.show | |
31 | ); | |
32 | ||
33 | // show (by project) | |
34 | // ----------------- | |
35 | 1 | router.get( |
36 | '/api/projects/:project/cycle', | |
37 | passport.authenticate('bearer', { session: false }), | |
38 | controller.show | |
39 | ); | |
40 | ||
41 | // update | |
42 | // ------ | |
43 | 1 | router.patch( |
44 | '/api/cycles/:cycle', | |
45 | passport.authenticate('bearer', { session: false }), | |
46 | controller.update | |
47 | ); | |
48 | ||
49 | // replace | |
50 | // ------ | |
51 | 1 | router.put( |
52 | '/api/cycles/:cycle', | |
53 | passport.authenticate('bearer', { session: false }), | |
54 | controller.replace | |
55 | ); | |
56 | ||
57 | // destroy | |
58 | // ------- | |
59 | 1 | router.delete( |
60 | '/api/cycles/:cycle', | |
61 | passport.authenticate('bearer', { session: false }), | |
62 | controller.destroy | |
63 | ); | |
64 | }; | |
65 |
Line | Hits | Source |
---|---|---|
1 | 1 | 'use strict'; |
2 | ||
3 | 1 | var multer = require('multer'); |
4 | 1 | var passport = require('passport'); |
5 | ||
6 | 1 | module.exports = function(config, router, resources){ |
7 | ||
8 | 1 | var controller = require('../controllers/files')(config, resources); |
9 | ||
10 | // create | |
11 | // ------ | |
12 | 1 | router.post( |
13 | '/api/files', | |
14 | passport.authenticate('bearer', { session: false }), | |
15 | multer({ dest: config.files.directory }), | |
16 | controller.create | |
17 | ); | |
18 | ||
19 | // list | |
20 | // ---- | |
21 | 1 | router.get( |
22 | '/api/files', | |
23 | passport.authenticate('bearer', { session: false }), | |
24 | controller.list | |
25 | ); | |
26 | ||
27 | // list (by user) | |
28 | // -------------- | |
29 | 1 | router.get( |
30 | '/api/users/:user/files', | |
31 | passport.authenticate('bearer', { session: false }), | |
32 | controller.list | |
33 | ); | |
34 | ||
35 | // show | |
36 | // ---- | |
37 | 1 | router.get( |
38 | '/api/files/:file', | |
39 | // passport.authenticate('bearer', { session: false }), | |
40 | controller.show | |
41 | ); | |
42 | ||
43 | // update | |
44 | // ------ | |
45 | 1 | router.patch( |
46 | '/api/files/:file', | |
47 | passport.authenticate('bearer', { session: false }), | |
48 | controller.update | |
49 | ); | |
50 | ||
51 | // replace | |
52 | // ------ | |
53 | 1 | router.put( |
54 | '/api/files/:file', | |
55 | passport.authenticate('bearer', { session: false }), | |
56 | controller.replace | |
57 | ); | |
58 | ||
59 | // destroy | |
60 | // ------- | |
61 | 1 | router.delete( |
62 | '/api/files/:file', | |
63 | passport.authenticate('bearer', { session: false }), | |
64 | controller.destroy | |
65 | ); | |
66 | }; | |
67 |
Line | Hits | Source |
---|---|---|
1 | 1 | 'use strict'; |
2 | ||
3 | 1 | var passport = require('passport'); |
4 | ||
5 | 1 | module.exports = function(config, router, resources){ |
6 | ||
7 | 1 | var controller = require('../controllers/projects')(config, resources); |
8 | ||
9 | // create | |
10 | // ------ | |
11 | 1 | router.post( |
12 | '/api/projects', | |
13 | passport.authenticate('bearer', { session: false }), | |
14 | controller.create | |
15 | ); | |
16 | ||
17 | // list | |
18 | // ---- | |
19 | 1 | router.get( |
20 | '/api/projects', | |
21 | passport.authenticate('bearer', { session: false }), | |
22 | controller.list | |
23 | ); | |
24 | ||
25 | // list (by cycle) | |
26 | // --------------- | |
27 | 1 | router.get( |
28 | '/api/cycles/:cycle/projects', | |
29 | passport.authenticate('bearer', { session: false }), | |
30 | controller.list | |
31 | ); | |
32 | ||
33 | // list (by user) | |
34 | // ----------------- | |
35 | 1 | router.get( |
36 | '/api/users/:user/projects', | |
37 | passport.authenticate('bearer', { session: false }), | |
38 | controller.list | |
39 | ); | |
40 | ||
41 | // show | |
42 | // ---- | |
43 | 1 | router.get( |
44 | '/api/projects/:project', | |
45 | passport.authenticate('bearer', { session: false }), | |
46 | controller.show | |
47 | ); | |
48 | ||
49 | // update | |
50 | // ------ | |
51 | 1 | router.patch( |
52 | '/api/projects/:project', | |
53 | passport.authenticate('bearer', { session: false }), | |
54 | controller.update | |
55 | ); | |
56 | ||
57 | // replace | |
58 | // ------ | |
59 | 1 | router.put( |
60 | '/api/projects/:project', | |
61 | passport.authenticate('bearer', { session: false }), | |
62 | controller.replace | |
63 | ); | |
64 | ||
65 | // destroy | |
66 | // ------- | |
67 | 1 | router.delete( |
68 | '/api/projects/:project', | |
69 | passport.authenticate('bearer', { session: false }), | |
70 | controller.destroy | |
71 | ); | |
72 | }; | |
73 |
Line | Hits | Source |
---|---|---|
1 | 1 | 'use strict'; |
2 | ||
3 | 1 | var _ = require('lodash'); |
4 | 1 | var r = require('rethinkdb'); |
5 | 1 | var jwt = require('jsonwebtoken'); |
6 | 1 | var crypto = require('crypto'); |
7 | 1 | var scrypt = require('scrypt'); |
8 | 1 | scrypt.hash.config.keyEncoding = scrypt.verify.config.keyEncoding = 'utf8'; |
9 | 1 | scrypt.hash.config.outputEncoding = scrypt.verify.config.outputEncoding = 'base64'; |
10 | ||
11 | 1 | module.exports = function(config, router, resources){ |
12 | 1 | router.post('/api/tokens', function(req, res, next){ |
13 | ||
14 | // validate request | |
15 | 29 | if (!req.body.email) |
16 | 1 | return res.error(400, 'Email is required to generate a token'); |
17 | ||
18 | // make the email case insensitive | |
19 | 28 | req.body.email = req.body.email.toLowerCase(); |
20 | ||
21 | // grab a db conn | |
22 | 28 | resources.db.acquire(function(err, conn) { |
23 | 28 | if(err) |
24 | 0 | return res.error(err); |
25 | ||
26 | // look for a user by email | |
27 | 28 | r.table('users').filter({email: req.body.email}).limit(1).run(conn, function(err, cursor){ |
28 | 28 | if(err){ |
29 | 0 | resources.db.release(conn); |
30 | 0 | return res.error(err); |
31 | } | |
32 | ||
33 | 28 | cursor.toArray(function(err, users){ |
34 | 28 | resources.db.release(conn); |
35 | ||
36 | 28 | if(err) |
37 | 0 | return res.error(err); |
38 | ||
39 | 28 | if(!users.length) |
40 | 3 | return res.error(404, 'User not found.'); |
41 | ||
42 | 25 | var user = users[0]; |
43 | ||
44 | // exchange authentication token | |
45 | 25 | if(typeof req.body.token !== 'undefined'){ |
46 | ||
47 | 4 | if(typeof req.body.token !== 'string') |
48 | 0 | return res.error(401, 'Invalid token.'); |
49 | ||
50 | 4 | return jwt.verify(req.body.token, config.auth.secret, function(err, decoded) { |
51 | ||
52 | 4 | if(err) |
53 | 1 | return res.error(401, 'Invalid token.'); |
54 | ||
55 | // exchange for same user or an admin | |
56 | 3 | if(user.id !== decoded.sub && !decoded.admin) |
57 | 1 | return res.error(403); |
58 | ||
59 | // send the user a token | |
60 | 2 | return res.status(201).send({ |
61 | token: jwt.sign({admin: user.admin}, config.auth.secret, { expiresInMinutes: 24*60, subject: user.id }) | |
62 | }); | |
63 | }); | |
64 | } | |
65 | ||
66 | // authenticate by password | |
67 | 21 | if(typeof req.body.password != 'undefined'){ |
68 | ||
69 | 16 | if(typeof req.body.password !== 'string' || req.body.password == '') |
70 | 0 | return res.error(401, 'Invalid password.'); |
71 | ||
72 | 16 | return scrypt.verify(new Buffer(user.password, 'base64'), req.body.password, function(err){ |
73 | 16 | if(err) |
74 | 1 | return res.error(401, 'Incorrect password.'); |
75 | ||
76 | // send the user a token | |
77 | 15 | return res.status(201).send({ |
78 | token: jwt.sign({admin: user.admin}, config.auth.secret, { expiresInMinutes: 24*60, subject: user.id }) | |
79 | }); | |
80 | }); | |
81 | } | |
82 | ||
83 | // authenticate by recovery token | |
84 | 5 | if(typeof req.body.recovery_token != 'undefined'){ |
85 | 5 | if(typeof user.recovery_token != 'string') |
86 | 1 | return res.error(401, 'No recovery token set.'); |
87 | ||
88 | 4 | return scrypt.verify(new Buffer(user.recovery_token, 'base64'), req.body.recovery_token, function(err){ |
89 | 4 | if(err) |
90 | 2 | return res.error(401, 'Invalid recovery token.'); |
91 | ||
92 | 2 | var recovery_token = JSON.parse(new Buffer(req.body.recovery_token, 'base64').toString('utf8')); |
93 | ||
94 | 2 | if(!recovery_token.expiration || recovery_token.expiration < Date.now()) |
95 | 1 | return res.error(401, 'Expired recovery token.'); |
96 | ||
97 | // authenticate by email (recovery token) | |
98 | 1 | return resources.db.acquire(function(err, conn) { |
99 | 1 | if(err) |
100 | 0 | return res.error(err); |
101 | ||
102 | // update the user | |
103 | 1 | r.table('users').get(user.id).update({recovery_token: null}).run(conn, function(err, results){ |
104 | 1 | resources.db.release(conn); |
105 | ||
106 | 1 | if(err) |
107 | 0 | return res.error(err); |
108 | ||
109 | 1 | return res.status(201).send({ |
110 | token: jwt.sign({admin: user.admin}, config.auth.secret, { expiresInMinutes: 24*60, subject: user.id }) | |
111 | }); | |
112 | }); | |
113 | }); | |
114 | }); | |
115 | } | |
116 | ||
117 | // authenticate by email (recovery token) | |
118 | 0 | resources.db.acquire(function(err, conn) { |
119 | 0 | if(err) |
120 | 0 | return res.error(err); |
121 | ||
122 | // generate the token | |
123 | 0 | var recovery_token = new Buffer(JSON.stringify({ |
124 | email: user.email, | |
125 | expiration: Date.now() + 3600000, // 1 hour from now | |
126 | secret: crypto.randomBytes(256).toString('base64') | |
127 | })).toString('base64'); | |
128 | ||
129 | // update the user | |
130 | 0 | r.table('users').get(user.id).update({ |
131 | recovery_token: scrypt.hash(recovery_token, scrypt.params(0.1)) | |
132 | }, {returnChanges: true}).run(conn, function(err, results){ | |
133 | 0 | resources.db.release(conn); |
134 | ||
135 | 0 | if(err) |
136 | 0 | return res.error(err); |
137 | ||
138 | 0 | if(!results.new_val || !results.new_val.recovery_token) |
139 | 0 | return res.error(500, 'Failed to update user with recovery token'); |
140 | ||
141 | // email recovery token to user | |
142 | 0 | resources.mail({ |
143 | to: user.email, | |
144 | subject: 'Your Recovery Token', | |
145 | html: resources.emailTemplates.recovery({ | |
146 | user: results.new_val, | |
147 | link: req.protocol + '://' + (req.headers.host || req.hostname) + '/#/recovery?recovery_token=' + recovery_token | |
148 | }), | |
149 | }, function(err, info){ | |
150 | 0 | if(err) |
151 | 0 | return res.error(err); |
152 | ||
153 | 0 | return res.status(201).send(); |
154 | }); | |
155 | }); | |
156 | }); | |
157 | }); | |
158 | }); | |
159 | }); | |
160 | }); | |
161 | }; | |
162 |
Line | Hits | Source |
---|---|---|
1 | 1 | 'use strict'; |
2 | ||
3 | 1 | var passport = require('passport'); |
4 | ||
5 | 1 | module.exports = function(config, router, resources){ |
6 | ||
7 | 1 | var controller = require('../controllers/users')(config, resources); |
8 | ||
9 | // create | |
10 | // ------ | |
11 | 1 | router.post( |
12 | '/api/users', | |
13 | controller.create | |
14 | ); | |
15 | ||
16 | // list | |
17 | // ---- | |
18 | 1 | router.get( |
19 | '/api/users', | |
20 | passport.authenticate('bearer', { session: false }), | |
21 | controller.list | |
22 | ); | |
23 | ||
24 | // list (by cycle) | |
25 | // --------------- | |
26 | 1 | router.get( |
27 | '/api/cycles/:cycle/users', | |
28 | passport.authenticate('bearer', { session: false }), | |
29 | controller.list | |
30 | ); | |
31 | ||
32 | // list (by project) | |
33 | // ----------------- | |
34 | 1 | router.get( |
35 | '/api/projects/:project/users', | |
36 | passport.authenticate('bearer', { session: false }), | |
37 | controller.list | |
38 | ); | |
39 | ||
40 | // show | |
41 | // ---- | |
42 | 1 | router.get( |
43 | '/api/users/:user', | |
44 | passport.authenticate('bearer', { session: false }), | |
45 | controller.show | |
46 | ); | |
47 | ||
48 | // show (by file) | |
49 | // -------------- | |
50 | 1 | router.get( |
51 | '/api/files/:file/user', | |
52 | passport.authenticate('bearer', { session: false }), | |
53 | controller.show | |
54 | ); | |
55 | ||
56 | // update | |
57 | // ------ | |
58 | 1 | router.patch( |
59 | '/api/users/:user', | |
60 | passport.authenticate('bearer', { session: false }), | |
61 | controller.update | |
62 | ); | |
63 | ||
64 | // replace | |
65 | // ------ | |
66 | 1 | router.put( |
67 | '/api/users/:user', | |
68 | passport.authenticate('bearer', { session: false }), | |
69 | controller.replace | |
70 | ); | |
71 | ||
72 | // destroy | |
73 | // ------- | |
74 | 1 | router.delete( |
75 | '/api/users/:user', | |
76 | passport.authenticate('bearer', { session: false }), | |
77 | controller.destroy | |
78 | ); | |
79 | }; | |
80 |
Line | Hits | Source |
---|---|---|
1 | 1 | 'use strict'; |
2 | ||
3 | 1 | var r = require('rethinkdb'); |
4 | 1 | var jwt = require('jsonwebtoken'); |
5 | 1 | var passport = require('passport'); |
6 | 1 | var BearerStrategy = require('passport-http-bearer'); |
7 | ||
8 | ||
9 | 1 | module.exports = function(config, resources){ |
10 | ||
11 | // configure passport | |
12 | 1 | passport.use(new BearerStrategy(function(token, done) { |
13 | 52 | jwt.verify(token, config.secret, function(err, decoded) { |
14 | ||
15 | 52 | if(err) |
16 | 0 | return done(err); |
17 | ||
18 | 52 | if(!decoded.sub) |
19 | 0 | return done('No subject encoded in token.'); |
20 | ||
21 | 52 | resources.db.acquire(function(err, conn) { |
22 | 52 | if(err) |
23 | 0 | return done(err); |
24 | ||
25 | // get users from the DB | |
26 | 52 | r.table('users').get(decoded.sub).run(conn, function(err, user){ |
27 | 52 | resources.db.release(conn); |
28 | ||
29 | 52 | if(err) |
30 | 0 | return done(err); |
31 | ||
32 | 52 | if(!user) |
33 | 0 | return done('Invalid subject encoded in token.'); |
34 | ||
35 | 52 | return done(null, user); |
36 | }); | |
37 | }); | |
38 | }); | |
39 | })); | |
40 | ||
41 | // return the initialized middleware | |
42 | 1 | return passport.initialize(); |
43 | }; | |
44 |
Line | Hits | Source |
---|---|---|
1 | 1 | 'use strict'; |
2 | ||
3 | 1 | var Pool = require('generic-pool').Pool; |
4 | 1 | var r = require('rethinkdb'); |
5 | ||
6 | 1 | module.exports = function(options, max, min, idleTimeoutMillis) { |
7 | 1 | return Pool({ |
8 | name: 'rethinkdb', | |
9 | create: function(callback) { | |
10 | 2 | return r.connect(options, callback); |
11 | }, | |
12 | destroy: function(connection) { | |
13 | 0 | return connection.close(); |
14 | }, | |
15 | log: false, | |
16 | min: min || 2, | |
17 | max: max || 10, | |
18 | idleTimeoutMillis: idleTimeoutMillis || 30000 | |
19 | }); | |
20 | }; |
Line | Hits | Source |
---|---|---|
1 | 1 | 'use strict'; |
2 | ||
3 | 1 | var fs = require('fs'); |
4 | 1 | var _ = require('lodash'); |
5 | 1 | var express = require('express'); |
6 | 1 | var bodyParser = require('body-parser'); |
7 | ||
8 | 1 | module.exports = function(config, app){ |
9 | 1 | var router = express.Router(); |
10 | 1 | var transporter = require('nodemailer').createTransport(config.mail.transport); |
11 | ||
12 | /************* | |
13 | * Resources * | |
14 | *************/ | |
15 | ||
16 | // add resources | |
17 | 1 | var resources = { |
18 | db: require('./api/utils/db.js')(config.db), | |
19 | mail: function(data, callback) { | |
20 | 0 | _.defaults(data, config.mail.defaults); |
21 | 0 | return transporter.sendMail(data, callback); |
22 | }, | |
23 | validator: require('jjv')(), | |
24 | testers: {}, | |
25 | listeners: {}, | |
26 | components: {}, | |
27 | emailTemplates: {} | |
28 | }; | |
29 | ||
30 | // customize validator | |
31 | 1 | resources.validator.addType('date', function (v) { |
32 | 6 | return !isNaN(Date.parse(v)); |
33 | }); | |
34 | ||
35 | 1 | resources.validator.addTypeCoercion('date', function (v) { |
36 | 0 | return new Date(v); |
37 | }); | |
38 | ||
39 | // add schemas | |
40 | 1 | fs.readdirSync(__dirname + '/api/schemas/').forEach(function(file){ |
41 | 4 | if(file.indexOf('.json') !== (file.length - 5)) |
42 | 0 | return; |
43 | ||
44 | 4 | var schema = require(__dirname + '/api/schemas/' + file); |
45 | 4 | resources.validator.addSchema(schema.id, schema); |
46 | }); | |
47 | ||
48 | /************************* | |
49 | * Dependency Management * | |
50 | *************************/ | |
51 | ||
52 | // make sure bower directory exists | |
53 | 2 | try { fs.mkdirSync(__dirname + '/app/assets/bower'); } catch(e) {} |
54 | ||
55 | // add gandhi modules | |
56 | 1 | var bowerJson = require('../bower.json'); |
57 | 1 | config.modules.forEach(function(directory){ |
58 | ||
59 | // add client side | |
60 | 11 | if(fs.existsSync(directory + '/bower.json')){ |
61 | 7 | var json = require(directory + '/bower.json'); |
62 | ||
63 | // symlink into bower | |
64 | 14 | try { fs.unlinkSync(__dirname + '/app/assets/bower/' + json.name); } catch(e) {} |
65 | 7 | fs.symlinkSync(directory, __dirname + '/app/assets/bower/' + json.name, 'dir'); |
66 | ||
67 | 7 | bowerJson.dependencies[json.name] = __dirname + '/app/assets/bower/' + json.name; |
68 | ||
69 | // add the angular module | |
70 | 7 | if(typeof json.module == 'string') |
71 | 7 | bowerJson.modules.push(json.module); |
72 | } | |
73 | ||
74 | // add server side | |
75 | 11 | if(fs.existsSync(directory + '/package.json')){ |
76 | 6 | require(directory)(router, resources); |
77 | } | |
78 | }); | |
79 | ||
80 | // install dependencies | |
81 | 1 | require('bower').commands.install(undefined, undefined, { cwd: __dirname + '/../' }); |
82 | ||
83 | // get all the bower deps | |
84 | 1 | var deps = require('wiredep')({ |
85 | bowerJson: bowerJson, | |
86 | cwd: __dirname + '/../' | |
87 | }); | |
88 | 1 | var main = ''; |
89 | ||
90 | // require all the js files | |
91 | 1 | main += deps.js.map(function(file){ |
92 | 24 | return fs.readFileSync(file); |
93 | }).join('\n') + '\n'; | |
94 | ||
95 | // build the gandhi module | |
96 | 1 | main += 'angular.module("gandhi", ["' + bowerJson.modules.join('","') + '"]);' + '\n'; |
97 | ||
98 | // require source files | |
99 | 1 | main += bowerJson.main.map(function(file){ |
100 | 9 | return fs.readFileSync(__dirname + '/app/' + file); |
101 | }).join('\n') + '\n'; | |
102 | ||
103 | // require all the css files | |
104 | 1 | var styles = deps.css.map(function(file){ |
105 | 4 | return 'document.write(\'<link rel="stylesheet" type="text/css" href="' + file.replace(new RegExp('^'+__dirname+'/app/'), '') + '" />\');'; |
106 | }).join('\n') + '\n'; | |
107 | ||
108 | ||
109 | /*********** | |
110 | * Routing * | |
111 | ***********/ | |
112 | ||
113 | // add middleware | |
114 | 1 | router.use('/api', bodyParser.urlencoded({extended: true})); |
115 | 1 | router.use('/api', bodyParser.json()); |
116 | 1 | router.use('/api', require('res-error')); |
117 | 1 | router.use('/api', require('./api/middleware/auth.js')(config.auth, resources)); |
118 | ||
119 | // add the endpoints | |
120 | 1 | fs.readdirSync(__dirname + '/api/endpoints/').forEach(function(file){ |
121 | 5 | if(file.indexOf('.js') === (file.length - 3)) |
122 | 5 | require(__dirname + '/api/endpoints/' + file)(config, router, resources); |
123 | }); | |
124 | ||
125 | // serve main.js | |
126 | 1 | router.get('/main.js', function(req, res){ |
127 | 0 | res.set('Content-Type','text/javascript'); |
128 | 0 | res.send(main); |
129 | }); | |
130 | ||
131 | // serve styles.js | |
132 | 1 | router.get('/styles.js', function(req, res){ |
133 | 0 | res.set('Content-Type','text/javascript'); |
134 | 0 | res.send(styles); |
135 | }); | |
136 | ||
137 | // serve static files | |
138 | 1 | router.use('/modules', express.static(__dirname + '/modules')); |
139 | 1 | router.use(express.static(__dirname + '/app')); |
140 | ||
141 | // apply the router | |
142 | 1 | app.use(config.root, router); |
143 | ||
144 | // error handling | |
145 | 1 | app.use(function(err, req, res, next){ |
146 | 20 | var e = res.error(err); |
147 | ||
148 | 20 | if(e.code === 500) |
149 | 0 | console.error(e.code, e.message, e.err); |
150 | }); | |
151 | }; | |
152 |
Line | Hits | Source |
---|---|---|
1 | 1 | 'use strict'; |
2 | ||
3 | 1 | var _ = require('lodash'); |
4 | ||
5 | 1 | module.exports = function(router, resources){ |
6 | 1 | resources.components['form'] = { |
7 | create: function() { | |
8 | ||
9 | }, | |
10 | read: function() { | |
11 | ||
12 | }, | |
13 | update: function() { | |
14 | ||
15 | }, | |
16 | destroy: function() { | |
17 | ||
18 | } | |
19 | } | |
20 | } | |
21 |
Line | Hits | Source |
---|---|---|
1 | 1 | 'use strict'; |
2 | ||
3 | 1 | var _ = require('lodash'); |
4 | ||
5 | 1 | module.exports = function(router, resources){ |
6 | 1 | resources.components['message'] = { |
7 | create: function(data, cycle) { | |
8 | ||
9 | }, | |
10 | read: function() { | |
11 | ||
12 | }, | |
13 | update: function() { | |
14 | ||
15 | }, | |
16 | destroy: function() { | |
17 | ||
18 | } | |
19 | } | |
20 | } | |
21 |
Line | Hits | Source |
---|---|---|
1 | 1 | 'use strict'; |
2 | ||
3 | 1 | var _ = require('lodash'); |
4 | ||
5 | 1 | module.exports = function(router, resources){ |
6 | 1 | resources.components['start'] = { |
7 | create: function() { | |
8 | ||
9 | }, | |
10 | read: function() { | |
11 | ||
12 | }, | |
13 | update: function() { | |
14 | ||
15 | }, | |
16 | destroy: function() { | |
17 | ||
18 | } | |
19 | } | |
20 | } | |
21 |
Line | Hits | Source |
---|---|---|
1 | 1 | 'use strict'; |
2 | ||
3 | 1 | var fs = require('fs'); |
4 | 1 | var handlebars = require('handlebars'); |
5 | ||
6 | 1 | module.exports = function(router, resources){ |
7 | 1 | resources.emailTemplates.recovery = handlebars.compile(fs.readFileSync(__dirname + '/template.html').toString('utf8')); |
8 | } | |
9 |
Line | Hits | Source |
---|---|---|
1 | 1 | 'use strict'; |
2 | ||
3 | 1 | var pointer = require('json-pointer'); |
4 | ||
5 | 1 | module.exports = function(router, resources){ |
6 | 1 | resources.testers.date = function(options, project){ |
7 | 9 | var res = new Date() > new Date(options.date); |
8 | 9 | return options.mode === 'before' ? !res : !!res; |
9 | } | |
10 | } | |
11 |
Line | Hits | Source |
---|---|---|
1 | 1 | 'use strict'; |
2 | ||
3 | 1 | var pointer = require('json-pointer'); |
4 | ||
5 | 1 | module.exports = function(router, resources){ |
6 | 1 | resources.testers.regex = function(options, project){ |
7 | 21 | try { |
8 | 21 | var data = pointer.get(project, options.pointer); |
9 | 9 | var res = typeof data === 'string' ? data.match(new RegExp(options.regex)) : false; |
10 | 9 | return options.invert ? !res : !!res; |
11 | } catch(e){ | |
12 | 12 | return !!options.invert; |
13 | } | |
14 | } | |
15 | } | |
16 |