├── .gitignore ├── LICENSE ├── README.md ├── index.js ├── lib ├── createNew.js ├── exporters.js ├── findAggregation.js ├── findAll.js ├── findOne.js ├── parseData.js ├── parseQuery.js ├── removeAll.js ├── removeOne.js ├── tools.js └── updateOne.js ├── package-lock.json ├── package.json └── test ├── index.js └── resources └── users.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Dependency directory 23 | # Commenting this out is preferred by some people, see 24 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 25 | node_modules 26 | 27 | # Users Environment Variables 28 | .lock-wscript 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 uhray 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Crud-Mongoose 2 | 3 | Crud mongoose is a library of middleware for connecting [crud](https://github.com/uhray/crud) to [mongoose](mongoosejs.com). It make it pretty easy to create simple APIs, with plenty of default and configurable capabilities. 4 | 5 | Simple Example: 6 | 7 | ```js 8 | var crud = require('crud'), 9 | cm = require('crud-mongoose'), 10 | mongoose = require('mongoose'), 11 | Model = mongoose.model( 12 | 'users', 13 | new mongoose.Schema({ 14 | firstName: { type: String, required: true }, 15 | lastName: { type: String, required: true }, 16 | gender: { type: String, required: true, enum: ['M', 'F'] }, 17 | created: { type: Date, default: Date.now }, 18 | }) 19 | ); 20 | 21 | // All Users ------------------------------------------------------------------- 22 | 23 | crud 24 | .entity('/users') 25 | .Create() 26 | .pipe(cm.createNew(Model)); 27 | 28 | crud 29 | .entity('/users') 30 | .Read() 31 | .pipe(cm.findAll(Model)); 32 | 33 | crud 34 | .entity('/users') 35 | .Delete() 36 | .pipe(cm.removeAll(Model)); 37 | 38 | // One User -------------------------------------------------------------------- 39 | 40 | crud 41 | .entity('/users/:_id') 42 | .Read() 43 | .pipe(cm.findOne(Model)); 44 | 45 | crud 46 | .entity('/users/:_id') 47 | .Update() 48 | .pipe(cm.updateOne(Model)); 49 | 50 | crud 51 | .entity('/users/:_id') 52 | .Delete() 53 | .pipe(cm.removeOne(Model)); 54 | ``` 55 | 56 | With this example, you can do simple GET, POST, PUT, and DELETE routes to: 57 | 58 | - Create a user - `POST` on `/api/users` 59 | - Read many users - `GET` on `/api/users` 60 | - Delete many users - `DELETE` on `/api/users` 61 | - Read one user - `GET` on `/api/users/` 62 | - Update one user - `PUT` on `/api/users/` 63 | - Delete one user - `DELETE` on `/api/users/` 64 | 65 | There are also a number of default query-able parameters. For example, the following GET requests could be used to get different subsets of data: 66 | 67 | - `/api/users?gender=F` - get all female users 68 | - `/api/users?lastName=Thompson` - get all users with lastName "Thompson" 69 | - `/api/users?limit=10` - only get 10 users 70 | - `/api/users?skip=50&limit=100&sortBy=created:asc` - Sorts users by created date ascending, skips 50, and then returns max 100 71 | - `/api/users?page=2&perPage=100&sortBy=created` - Sorts users by created date ascending (note it's ascending by default), and then skips the first 200 because it's page 2 (starts at 0) and there are only 100 per page. 72 | - `/api/users?fields=firstName,lastName` - get all users but only return firstName and lastName fields. 73 | 74 | There are other queries you can make, but this gives a pretty good example of why this library makes API creation so easy. BUT, it's not only easy, it's also really configurable so you are not boxed in. And, if you need to configure things this library doesn't allow, you can always drop in your own middleware function into Crud, which is pretty easy anyway. 75 | 76 | ## API 77 | 78 | The API has two types of middleware functions: 79 | 80 | - Middleware that interacts directly with the MongoDB through mongoose - [here](#mongoose-middleware). 81 | - Middleware that modifies the crud _data_ and _query_ objects - [here](#modify-middleware). 82 | 83 | All middleware can be placed into a [Crud Pipe](https://github.com/uhray/crud#method-pipe), like this: 84 | 85 | ```js 86 | crud('/users') 87 | .Read() 88 | .pipe(cm.findAll()); 89 | ``` 90 | 91 | #### Mongoose Middleware 92 | 93 | The following middleware functions all interact with a Mongoose Model, using the crud _data_ and _query_ objects. (See [here](https://github.com/uhray/crud#method-pipe) for info on the meaning of those objects). 94 | 95 | # cm.**createNew**(_Model_) 96 | 97 | This method creates a new record in the _Model_ based on the crud _data_ object. The middleware is basically this: 98 | 99 | ```js 100 | function(data, query, callback) { 101 | new Model(data).save(callback); 102 | } 103 | ``` 104 | 105 | Example, allowing you to `POST` on `/api/users` to create a new user: 106 | 107 | ```js 108 | crud 109 | .entity('/users') 110 | .Create() 111 | .pipe(cm.createNew(Model)); 112 | ``` 113 | 114 | # cm.**findAll**(_Model_, [*fields*]) 115 | 116 | This method does a `find` on the _Model_ using the _query_ object. The _fields_ parameter is an optional array of fields you want to allow in the response. So, if you only want to show certain fields you can provide an array like `['firstName', 'lastName']`. Or, if you wish to restrict a field ALWAYS, you can put a `-` before it (e.g. `['firstname', 'lastname', '-password']` will by default only show first and last names, but will not allow you to ever query for the password fields). 117 | 118 | For starters, you can think of it as something like this: 119 | 120 | ```js 121 | function (data, query, callback) { 122 | Model.find(query).select(fields.join(' ')).lean().exec(callback); 123 | // see here: http://mongoosejs.com/docs/api.html#query_Query-select for mongoose select 124 | } 125 | ``` 126 | 127 | That's where it started, but then we added a lot more functionality. There are special _query_ parameters that are parsed here: 128 | 129 | - _limit_ - limits the number of records responded with 130 | - _skip_ - skips this many records on the response 131 | - _page_ - used for pagination. Indexed starting at zero. 132 | - _perPage_ - number of records per page. Pagination doesn't work well without a value here. 133 | - _sortBy_ - sortable values. This is formatted like this: `firstvalue:asc,secondvalue:desc`, where the comma denotes different fields and the colon is used to show `asc` for ascending or `desc` for descending. Default is ascending. 134 | - _fields_ - a comma separated listed of fields to select. 135 | 136 | An example, allowing you to query all users with a `GET` on `/api/users`: 137 | 138 | ```js 139 | crud 140 | .entity('/users') 141 | .Read() 142 | .pipe(cm.findAll(Model, ['firstName', 'lastName'])); 143 | ``` 144 | 145 | The following are the chainable properties: 146 | 147 | - findAll(Model).**stream**() - sets the mode to streaming. This will not pass things through the crud chain anymore and instead stream responses. This is useful for queries that expect huge responses. 148 | - findAll(Model).**exports**(_object_) - turns on exporting capabilities. This allows you to set export functionality so, for example, query `?export=csv` will export the data as a csv file. The _object_ parameter specifies keys as the export keys like `'csv'` and the values are functions that handle the exporting. An example would be `findAll(Model).exports({ csv: cm.exporters.csv() })`. See [exporter functions](#exporter-functions) for information on how they are defined and the pre-packaged functions in crud-mongoose. 149 | - findAll(Model).**metadata**(allow) - _allow_ is boolean deciding whether or not to send metadata with responses. The default is `false`. Metadata will only be sent when a query has set `page` and `perPage`, because it's not useful otherwise. 150 | 151 | # cm.**findAggregation**(_Model_, [*fields*]) 152 | 153 | This method does an `aggregate` one the _Model_ using the _query_ object as a `$match` pipeline. 154 | 155 | > NOTE: This feature is in beta. We haven't had a ton of time to test, but needed it for some work. 156 | 157 | Everything operates the same as [findAll](#findAll), with these changes: 158 | 159 | **Removed Functionality from findAll**: 160 | 161 | - Export: no exporting functionality 162 | - Metadata: no metadata functionality 163 | 164 | **Adding Functionality from findAll**: 165 | 166 | - findAggregation(Model).**additionalStages**(_spot_='end', _stages_) - adds additional pipeline stages to the aggregation. This runs after the projection and before the sort/limit/skip functionality. The argument _spot_ indicates where to put these stages in the pipeline. The options are `start`, `end`, `preSort`, and `afterLimit` (good for joins that don't affect the \$match but are expensive). The argument _stages_ is an array, where each value is a MongoDB pipeline stage. If the value is an object, it will be put in the pipeline. If the value is a function it will be called with one argument (`getDefaultProjection`), which when called returns an object projecting all standard fields for the Model so you can add new ones. The context of the function call will be the same as the `crud` context. 167 | - findAggregation(Model).**sortPresets**(_presets_) - This allows you to add new fields for sorting. For example, you can add a new field that is the sum of two fields. _presets_ is a key-value lookup that will be added to the [\$project](https://docs.mongodb.com/manual/reference/operator/aggregation/project/) stage. These values will not be added unless the sort key is used in the `sortBy` query parameter to prevent additional work that's unnecessary. 168 | - findAggregation(Model).**cursor**(_fn_) - This allows you to modify the cursor. This function is passed (_cursor_) and must respond with the cursor. An example: `.cursor(cursor => { return cursor.allowDiskUse(true); }` 169 | 170 | Example: 171 | 172 | ```js 173 | .pipe(cm.findAggregation(Model).additionalStages('start', [ 174 | { $lookup: { 175 | from: 'users', 176 | localField: 'user', 177 | foreignField: '_id', 178 | as: 'user' 179 | } } 180 | ]).sortPresets({ 181 | totalLeads: { $sum: [ 182 | '$applicationStats.facebook.qualified', 183 | '$applicationStats.facebook.unqualified', 184 | '$applicationStats.jobBoard.qualified', 185 | '$applicationStats.jobBoard.unqualified', 186 | '$applicationStats.embedded.qualified', 187 | '$applicationStats.embedded.unqualified' 188 | ] } 189 | })) 190 | ``` 191 | 192 | # cm.**findOne**(_Model_, [*fields*]) 193 | 194 | This method does a `findOne` on the _Model_ using the query object. The _fields_ parameter is an optional array of fields you want to allow in the response. So, if you only want to show certain fields you can provide an array like `['firstName', 'lastName']`. Or, if you wish to restrict a field ALWAYS, you can put a `-` before it (e.g. `['firstname', 'lastname', '-password']` will by default only show first and last names, but will not allow you to ever query for the password fields). 195 | 196 | For starters, you can think of it as something like this: 197 | 198 | ```js 199 | function (data, query, callback) { 200 | Model.findOne(query).select(fields.join(' ')) 201 | .lean().exec(callback); 202 | // see here: http://mongoosejs.com/docs/api.html#query_Query-select for mongoose select 203 | } 204 | ``` 205 | 206 | That's where it started, but then we added more functionality. There are special _query_ parameters that are parsed here: (well, there is only one right now). 207 | 208 | - _fields_ - a comma separated listed of fields to select. 209 | 210 | An example, allowing you to query one user with `GET` on `/api/users/`: 211 | 212 | ```js 213 | crud 214 | .entity('/users/:_id') 215 | .Read() 216 | .pipe(cm.findOne(Model, ['firstName', 'lastName'])); 217 | ``` 218 | 219 | Note: It's important for the URL to be formatted like `/users/:_id`, because then the query will have `{ _id: }`, which is needed to find the individual user. 220 | 221 | # cm.**removeAll**(_Model_) 222 | 223 | This method does a `remove` on the _Model_ using the query object. The middleware is basically this: 224 | 225 | ```js 226 | function(data, query, callback) { 227 | Model.remove(query).lean().callback(); 228 | } 229 | ``` 230 | 231 | An example so you can remove all users with a `DELETE` on `/api/users`: 232 | 233 | ```js 234 | crud 235 | .entity('/users') 236 | .Delete() 237 | .pipe(cm.removeAll(Model)); 238 | ``` 239 | 240 | # cm.**removeOne**(_Model_) 241 | 242 | This method does a `findOneAndRemove` on the _Model_ using the query object. The middleware is basically this: 243 | 244 | ```js 245 | function(data, query, callback) { 246 | Model.findOneAndRemove(query).lean().callback(); 247 | } 248 | ``` 249 | 250 | An example so you can remove one user with a `DELETE` on `/api/users/`: 251 | 252 | ```js 253 | crud 254 | .entity('/users/:_id') 255 | .Delete() 256 | .pipe(cm.removeOne(Model)); 257 | ``` 258 | 259 | Note: It's important for the URL to be formatted like `/users/:_id`, because then the query will have `{ _id: }`, which is needed to find the individual user. 260 | 261 | # cm.**updateOne**(_Model_, _Options_) 262 | 263 | This method does a `findOne` and then and `update` on the _Model_ using the _query_ object for querying and the _data_ object for the update. The middleware is basically this: 264 | 265 | ```js 266 | function(data, query, callback) { 267 | Model.findOne(query, function(e, d) { 268 | d.set(data); 269 | d.save(callback); 270 | }); 271 | } 272 | ``` 273 | 274 | > If the `data` value has a value for **v, it is removed: `if ('**v' in data) delete data.\_\_v`. This is because [document versioning](http://mongoosejs.com/docs/guide.html#versionKey) was causing problems. If this removal causes problems, we should revisit this. 275 | 276 | _Options_ 277 | 278 | - `findOneAndUpdate` (Default=`false`) - We used to use `findOneAndUpdate`, but decided to do a `find` then update because updates do not use mongoose validators. Unfortunately, this does not allow you to apply MongoDB updates like $push, $pull, etc. So, if you need to use other update methods, you can set this to true. Just remember this will not obey the Mongoose validation. Additionally, you may need this if you're trying to update a Mixed mongoose object because Mixed objects cannot be updated with the `object.set` function. 279 | 280 | _Example_ - An example so you can update a user with a `PUT` on `/api/users/`: 281 | 282 | ```js 283 | crud 284 | .entity('/users/:_id') 285 | .Update() 286 | .pipe(cm.updateOne(Model)); 287 | ``` 288 | 289 | Note: It's important for the URL to be formatted like `/users/:_id`, because then the query will have `{ _id: }`, which is needed to find the individual user. 290 | 291 | #### Modify Middleware 292 | 293 | These middleware functions modify crud _data_ and _query_ objects. (See [here](https://github.com/uhray/crud#method-pipe) for info on the meaning of those objects). The purpose of these middleware functions are for the instances where you want to set default parameters (e.g. default limit on a read of 100 records), prevent the use of certain parameters (e.g. don't allow querying of users by age), establishing max query limits (e.g. cannot query for more than 100 records), or overriding certain values (e.g. the data on an update always updating the `"updated"` date field). 294 | 295 | # cm.**parseQuery**() 296 | 297 | This method allows you to parse the _query_ object with special options. Calling this function returns a middleware function that has chainable properties so you can configure the function. For example, you could chain configurations like this: 298 | 299 | ```js 300 | crud('/users') 301 | .Read() 302 | .pipe(cm.parseQuery().defaults({ limit: 10 })) 303 | .pipe(cm.findAll()); 304 | ``` 305 | 306 | The following are the chainable properties: 307 | 308 | - parseQuery().**defaults**(_defaults_) - _defaults_ is a key-value object that will set any key-value parameters on the _query_ object if they are not set. For example, if you have `{ limit: 10 }` as the _defaults_, then if you were to query `/api/users` it would be equivalent to `/api/users?limit=10`. Also, if any values in the key-values are functions, then they will be called when set on the query. 309 | 310 | - parseQuery().**removes**(_key1_, [*key2*, *key3* ... ]) - the _keys_, passed as individual arguments, are keys that will be removed from the query so API users cannot query by this field. For example, if you have `.removes('age')`, then you cannot query like this: `/api/users?age=23`, because age will be removed from the query. 311 | 312 | - parseData().**required**(_key1_, [*key2*, *key3* ... ]) - the _keys_, passed as individual arguments, are keys indicate fields that need to be present on the query object. An empty string does not count as "present". 313 | 314 | - parseQuery().**overrides**(_overrides_) - _overrides_ is a key-value object that will force its key-value parameters on the _query_ object. For example, if you have `{ active: true }` as the _overrides_, then if you were to query `/api/users?active=false` it would be equivalent to `/api/users?active=true`. You could use this to prevent API users from querying inactive records. Also, if any values in the key-values are functions, then they will be called when set on the query. 315 | 316 | - parseQuery().**maxes**(_maxes_) - _maxes_ is a key-value object that will force any key-value parameters on the _query_ object that are in the _maxes_ object to be no-more-than the max. For example, if you have `{ limit: 100 }` as the _maxes_, then if you were to query `/api/users?limit=1000`, it would override the `limit` parameter to be `100`, since that is the max. 317 | 318 | # cm.**parseData**() 319 | 320 | This method allows you to parse the _data_ object with special options. Calling this function returns a middleware function that has chainable properties so you can configure the function. For example, you could chain configurations like this: 321 | 322 | ```js 323 | crud('/users/:_id') 324 | .Update() 325 | .pipe(cm.parseData().overrides({ updated: Date.now })) 326 | .pipe(cm.findAll()); 327 | ``` 328 | 329 | The chainable properties are the SAME as those from [parseQuery](#parseQuery), except these important things: 330 | 331 | - They modify the _data_ object (not the _query_ object) 332 | - There is no _maxes_ method, because that is made specifically to handle the query limits, pages, etc. 333 | - On `required`, anything that is not truthy AND is not the boolean `false` is not considered present. This allows you to pass `false` to a required field, but not `null` or `''`. 334 | - You can request which fields should not be flattened by doing `.dontFlatten('fieldNotToFlatten')`, which is useful for Mixed types in Mongoose. 335 | 336 | > Note, if you remove the ability to send certain data, like `parseData().removes('info')`, this does not mean the user cannot updated pass something like `{ 'info.age' : 7 }`, which does update mongoose documents because everything is treated as flat in mongoose. Likewise, if you have an array such as `{ favcolors: ['red', 'blue', 'green'] }`, you could pass `{ 'favcolors.0' : 'black' }`. Because of this, if key in the data object has a period (meaning it's already been flattened somewhat), parseData will remove this key-value from the resultant data object. This is solely for security reasons. 337 | 338 | ## Exporter Functions 339 | 340 | Exporter functions are used to export the api data into something other than JSON, say CSV for example. 341 | 342 | The are passed three arguments: 343 | 344 | - response - express response object. 345 | - cursor - The mongoose cursor with the rest of the query parameters set 346 | - callback - callback to be called when done. The first argument should be the error message if there is one or null. 347 | 348 | Prepackaged in crud-mongoose are the folloowing exporters, which can be accessed by `cm.exporters`. If you're using the csv exporter you could do the following: 349 | 350 | ```js 351 | crud.entity('/users').Read() 352 | .pipe(cm.findAll(Model) 353 | .exports({ csv: cm.exporters.csv() }) 354 | 355 | ``` 356 | 357 | # cm.exporters.**csv**([*headers*]) 358 | 359 | Exports the resulting query as a csv file. The optional _headers_ argument allows you to specify an array of csv headers, which will be the first line and will be used as the accessors for the data values for each row. If not provided, the key's on the first row will be used. 360 | 361 | # cm.exporters.**xlsx**([*headers*]) 362 | 363 | Exports the resulting query as a xlsx file. The optional _headers_ argument allows you to specify an array of xlsx headers, which will be the first line and will be used as the accessors for the data values for each row. If not provided, the key's on the first row will be used. 364 | 365 | ## Debug 366 | 367 | The Crud module has sprinkled some [debug](https://github.com/visionmedia/debug) messages throughout the module. If you wish to turn these on, run your sever with the environment variable `DEBUG=crud-mongoose*` set. Or, you can turn them on for only one method, like `DEBUG=crud-mongoose:findOne`. 368 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = exports = require('require-dir')('./lib'); 3 | -------------------------------------------------------------------------------- /lib/createNew.js: -------------------------------------------------------------------------------- 1 | 2 | var tools = require('./tools'), 3 | debug = require('debug')('crud-mongoose:createNew'); 4 | 5 | module.exports = exports = createNew; 6 | 7 | function createNew(Model) { 8 | 9 | createNew.Model = Model; 10 | 11 | return createNew; 12 | 13 | function createNew(data, query, callback) { 14 | debug('creating new %j', data); 15 | new Model(data).save(function(e, d) { 16 | if (e || !d) return callback(e || 'failed to save'); 17 | d = d.toObject ? d.toObject() : d; 18 | debug('created new %j', d); 19 | callback(null, d); 20 | }); 21 | } 22 | } 23 | 24 | 25 | -------------------------------------------------------------------------------- /lib/exporters.js: -------------------------------------------------------------------------------- 1 | var tools = require('./tools'), 2 | debug = require('debug')('crud-mongoose:exporters'), 3 | xlsx = require('xlsx-stream'); 4 | 5 | module.exports = exports = {}; 6 | 7 | exports.csv = function(k) { 8 | return function(res, cursor, cb) { 9 | var keys = k, 10 | init = false; 11 | 12 | res.set('Content-Type', 'text/csv'); 13 | res.set('Content-Disposition', 'attachment'); 14 | 15 | cursor.on('data', function(doc) { 16 | var arr = [], i, d; 17 | 18 | if (!keys || !init) { 19 | keys = keys || Object.keys(JSON.parse(JSON.stringify(doc))); 20 | res.write(keys.map(function(d) { 21 | return JSON.stringify(String(d)); 22 | }).join(',') + '\n'); 23 | } 24 | 25 | init = true; 26 | doc = tools.flattenObject(doc); 27 | 28 | for (i = 0; i < keys.length; i++) { 29 | d = doc[keys[i]]; 30 | d = d === undefined ? "" : String(d); 31 | arr.push(JSON.stringify(d)); 32 | } 33 | 34 | res.write(arr.join(',') + '\n'); 35 | }) 36 | .on('error', function(e) { 37 | cb(e || 'error in stream'); 38 | }) 39 | .on('close', function() { 40 | res.end(); 41 | cb(); 42 | }); 43 | } 44 | }; 45 | 46 | exports.xlsx = function(k) { 47 | return function(res, cursor, cb) { 48 | var keys = k, 49 | init = false, 50 | x = xlsx(), 51 | type = 'application/vnd.openxmlformats-officedocument' + 52 | '.spreadsheetml.sheet'; 53 | 54 | res.set('Content-Type', type); 55 | res.set('Content-Disposition', 'attachment'); 56 | x.pipe(res); 57 | 58 | cursor.on('data', function(doc) { 59 | var arr = [], i, d; 60 | 61 | if (!keys || !init) { 62 | keys = keys || Object.keys(JSON.parse(JSON.stringify(doc))); 63 | x.write(keys); 64 | } 65 | 66 | init = true; 67 | doc = tools.flattenObject(doc); 68 | 69 | for (i = 0; i < keys.length; i++) { 70 | d = doc[keys[i]]; 71 | d = d === undefined ? "" : String(d); 72 | arr.push(d); 73 | } 74 | 75 | x.write(arr); 76 | }) 77 | .on('error', function(e) { 78 | cb(e || 'error in stream'); 79 | x.end() 80 | }) 81 | .on('close', function() { 82 | x.end() 83 | cb(); 84 | }); 85 | } 86 | }; 87 | -------------------------------------------------------------------------------- /lib/findAggregation.js: -------------------------------------------------------------------------------- 1 | var tools = require('./tools'), 2 | debug = require('debug')('crud-mongoose:findAggregation'), 3 | _ = require('lodash'), 4 | specials = ['limit', 'skip', 'page', 'perPage', 'sortBy', 'fields', 'export'], 5 | mongoose = require('mongoose'); 6 | 7 | module.exports = exports = findAggregation; 8 | 9 | function findAggregation(Model, projection) { 10 | var stream = false, 11 | additionalStages = { 12 | start: [], 13 | preSort: [], 14 | end: [], 15 | }, 16 | onCursor = function (cursor) { 17 | return cursor; 18 | }, 19 | initialStages = [], 20 | sortPresets, 21 | computedDefaultProjection; 22 | 23 | projection = projection || []; 24 | findAggregation.Model = Model; 25 | 26 | findAggregation.sortPresets = function (sp) { 27 | sortPresets = sp; 28 | return findAggregation; 29 | }; 30 | 31 | findAggregation.cursor = function (fn) { 32 | onCursor = fn; 33 | return findAggregation; 34 | }; 35 | 36 | findAggregation.stream = function () { 37 | stream = true; 38 | return findAggregation; 39 | }; 40 | 41 | findAggregation.additionalStages = function (spot, stages) { 42 | if (!stages) { 43 | stages = spot; 44 | spot = 'end'; 45 | } 46 | 47 | additionalStages[spot] = stages; 48 | return findAggregation; 49 | }; 50 | 51 | return findAggregation; 52 | 53 | function convertType(Model, key, value) { 54 | var schema = (Model && Model.schema && Model.schema.obj) || {}, 55 | type = _.get(schema, key), 56 | k; 57 | 58 | if (key === '$or' || key === '$and') { 59 | value = _.each(value, v => { 60 | for (k in v) { 61 | v[k] = convertType(Model, k, v[k]); 62 | } 63 | return v; 64 | }); 65 | 66 | return value; 67 | } 68 | 69 | if (!type) { 70 | return value; 71 | } 72 | if ('type' in type) { 73 | type = type.type; 74 | } 75 | 76 | if (value.hasOwnProperty('$regex')) { 77 | return value; 78 | } 79 | 80 | // handle operators 81 | if ( 82 | value.hasOwnProperty('$lt') || 83 | value.hasOwnProperty('$gt') || 84 | value.hasOwnProperty('$lte') || 85 | value.hasOwnProperty('$gte') || 86 | value.hasOwnProperty('$nin') || 87 | value.hasOwnProperty('$ne') || 88 | value.hasOwnProperty('$in') 89 | ) { 90 | for (k in value) { 91 | value[k] = convertType(Model, key, value[k]); 92 | } 93 | return value; 94 | } 95 | 96 | // handle exists 97 | if (value.hasOwnProperty('$exists')) { 98 | value['$exists'] = 99 | value['$exists'] === 'true' || value['$exists'] === true; 100 | return value; 101 | } 102 | 103 | // handle Arrays 104 | if (value instanceof Array) { 105 | for (k = 0; k < value.length; k++) { 106 | value[k] = convertType(Model, key, value[k]); 107 | } 108 | return value; 109 | } 110 | 111 | // handle types 112 | if (type === Boolean) { 113 | return value === 'true' || value === true; 114 | } else if (type === Number) { 115 | return parseFloat(value); 116 | } else if (type === Date) { 117 | return new Date(value); 118 | } else if (/^[a-f0-9]{24}$/i.test(String(value))) { 119 | return mongoose.Types.ObjectId(value); 120 | } else { 121 | return value; 122 | } 123 | } 124 | 125 | function defaultProjection(Model, fields) { 126 | // No need to recompute 127 | if (computedDefaultProjection) { 128 | return _.clone(computedDefaultProjection); 129 | } 130 | 131 | var p = {}, 132 | schema = (Model && Model.schema && Model.schema.obj) || {}, 133 | outs = {}, 134 | ins = {}, 135 | k, 136 | i, 137 | hasIns; 138 | 139 | // split between included and excluded fields 140 | for (i = 0; i < projection.length; i++) { 141 | if (projection[i][0] == '-') { 142 | outs[projection[i].replace(/^-/, '')] = true; 143 | } else { 144 | ins[projection[i]] = true; 145 | } 146 | } 147 | 148 | if (fields) { 149 | fields = fields.split(','); 150 | ins = {}; 151 | 152 | for (i = 0; i < fields.length; i++) { 153 | if (!outs[fields[i]]) { 154 | ins[fields[i]] = true; 155 | } 156 | } 157 | } 158 | 159 | hasIns = Object.keys(ins).length > 0; 160 | 161 | for (k in schema) { 162 | if (outs[k]) { 163 | continue; 164 | } 165 | if (hasIns && !ins[k]) { 166 | continue; 167 | } 168 | p[k] = '$' + k; 169 | } 170 | 171 | p['_id'] = '$_id'; 172 | 173 | // only store default projection if fields are not used the first time 174 | // through 175 | if (!fields && !_.size(fields)) { 176 | computedDefaultProjection = _.clone(p); 177 | } 178 | return p; 179 | } 180 | 181 | function findAggregation(data, query, callback) { 182 | var keys = {}, 183 | self = this, 184 | pipeline = [], 185 | cursor, 186 | sort, 187 | sort_split, 188 | i, 189 | k, 190 | proj, 191 | streamError; 192 | 193 | debug('original query - %j', query); 194 | 195 | // gather special keys 196 | for (i = 0; i < specials.length; i++) { 197 | if (query.hasOwnProperty(specials[i])) { 198 | if ( 199 | specials[i] !== 'sortBy' && 200 | specials[i] !== 'fields' && 201 | specials[i] !== 'export' 202 | ) 203 | keys[specials[i]] = parseInt(query[specials[i]]); 204 | else keys[specials[i]] = query[specials[i]]; 205 | delete query[specials[i]]; 206 | } 207 | } 208 | 209 | debug('special query params %j', keys); 210 | debug('going to find %j', query); 211 | 212 | for (k in query) { 213 | query[k] = convertType(Model, k, query[k]); 214 | } 215 | 216 | appendStages(pipeline, additionalStages.start, self); 217 | 218 | pipeline.push({ 219 | $match: query, 220 | }); 221 | 222 | if (keys.hasOwnProperty('page')) { 223 | keys.skip = keys.skip || 0; 224 | keys.perPage = keys.perPage; 225 | keys.skip += keys.page * keys.perPage; 226 | keys.limit = Math.min(keys.limit || Infinity, keys.perPage); 227 | debug('page - %s\tperPage - %s', keys.page, keys.perPage); 228 | } 229 | 230 | appendStages(pipeline, additionalStages.preSort, self); 231 | 232 | if (keys.hasOwnProperty('sortBy')) { 233 | sort = {}; 234 | sort_split = keys.sortBy.split(','); 235 | for (i = 0; i < sort_split.length; i++) { 236 | k = sort_split[i].split(':')[0]; 237 | sort[k] = sort_split[i].split(':')[1] || 'asc'; 238 | if (sort[k] === 'desc') { 239 | sort[k] = -1; 240 | } else { 241 | sort[k] = 1; 242 | } 243 | if (sortPresets && sortPresets[k]) { 244 | proj = proj || defaultProjection(Model, keys.fields); 245 | proj[k] = sortPresets[k]; 246 | } 247 | } 248 | debug('sorting: %j', sort); 249 | pipeline.push({ $sort: sort }); 250 | } 251 | 252 | if (keys.fields || _.size(projection)) { 253 | proj = proj || defaultProjection(Model, keys.fields); 254 | } 255 | 256 | appendStages(pipeline, additionalStages.end, self); 257 | 258 | if (proj) { 259 | // Needs to be right after the $match query 260 | pipeline.splice(1, 0, { $project: proj }); 261 | } 262 | 263 | if (keys.hasOwnProperty('skip')) { 264 | debug('skipping %d records', keys.skip); 265 | pipeline.push({ $skip: keys.skip }); 266 | } 267 | 268 | if (keys.hasOwnProperty('limit')) { 269 | debug('limiting to %d records', keys.limit); 270 | pipeline.push({ $limit: keys.limit }); 271 | } 272 | 273 | appendStages(pipeline, additionalStages.afterLimit, self); 274 | 275 | debug('%j', pipeline); 276 | cursor = onCursor(Model.aggregate(pipeline)); 277 | 278 | if (stream) { 279 | i = 0; 280 | try { 281 | if (streamOpen(self.response)) { 282 | self.response.set('Content-Type', 'application/json'); 283 | } 284 | } catch (e) {} 285 | cursor 286 | .cursor() 287 | .exec() 288 | .on('data', function (doc) { 289 | var d = i++ ? ',' : '{ "error" : null, "data": ['; 290 | if (streamError) { 291 | return; 292 | } 293 | try { 294 | if (streamOpen(self.response)) { 295 | self.response.write(d + JSON.stringify(doc)); 296 | } 297 | } catch (e) { 298 | console.error('error on write'); 299 | } 300 | }) 301 | .on('error', function (e) { 302 | streamError = e || 'error in stream'; 303 | callback(streamError); 304 | }) 305 | .on('end', function () { 306 | if (streamError) { 307 | return; 308 | } 309 | try { 310 | if (streamOpen(self.response)) { 311 | if (!i) self.response.write('{ "error": null, "data": ['); 312 | self.response.write(']}'); 313 | self.response.end(); 314 | self.close(); 315 | } 316 | } catch (e) {} 317 | }); 318 | } else { 319 | cursor.exec(callback); 320 | } 321 | 322 | function appendStages(pipeline, stages, context) { 323 | var i, v; 324 | if (stages) { 325 | for (i = 0; i < stages.length; i++) { 326 | if (typeof stages[i] === 'function') { 327 | v = stages[i].call(context, function () { 328 | return defaultProjection(Model, keys.fields); 329 | }); 330 | 331 | if (v) { 332 | pipeline.push(v); 333 | } 334 | } else { 335 | pipeline.push(stages[i]); 336 | } 337 | } 338 | } 339 | } 340 | 341 | function streamOpen(res) { 342 | return !res.ended && !res.finished; 343 | } 344 | } 345 | } 346 | -------------------------------------------------------------------------------- /lib/findAll.js: -------------------------------------------------------------------------------- 1 | 2 | var tools = require('./tools'), 3 | debug = require('debug')('crud-mongoose:findAll'), 4 | specials = [ 'limit', 'skip', 'page', 'perPage', 'sortBy', 'fields', 5 | 'export' ]; 6 | 7 | module.exports = exports = findAll; 8 | 9 | function findAll(Model, projection) { 10 | var stream = false, 11 | sendMetadata = false, 12 | allowedExports = {}; 13 | 14 | findAll.Model = Model; 15 | 16 | findAll.stream = function() { 17 | stream = true; 18 | return findAll; 19 | } 20 | 21 | findAll.exports = function(obj) { 22 | allowedExports = obj; 23 | return findAll; 24 | } 25 | 26 | findAll.metadata = function(d) { 27 | sendMetadata = d; 28 | return findAll; 29 | }; 30 | 31 | return findAll; 32 | 33 | function findAll(data, query, callback) { 34 | var keys = {}, 35 | self = this, 36 | cursor, sort, sort_split, i; 37 | 38 | debug('original query - %j', query); 39 | 40 | // gather special keys 41 | for (i = 0; i < specials.length; i++) { 42 | if (query.hasOwnProperty(specials[i])) { 43 | if (specials[i] !== 'sortBy' && specials[i] !== 'fields' && 44 | specials[i] !== 'export') 45 | keys[specials[i]] = parseInt(query[specials[i]]); 46 | else keys[specials[i]] = query[specials[i]]; 47 | delete query[specials[i]]; 48 | } 49 | } 50 | 51 | debug('special query params %j', keys); 52 | debug('going to find %j', query); 53 | 54 | cursor = Model.find(query); 55 | 56 | // modifying cursor for provided keys 57 | if (keys.hasOwnProperty('page')) { 58 | keys.skip = keys.skip || 0; 59 | keys.perPage = keys.perPage; 60 | keys.skip += keys.page * keys.perPage; 61 | keys.limit = Math.min(keys.limit || Infinity, keys.perPage); 62 | debug('page - %s\tperPage - %s', keys.page, keys.perPage); 63 | } 64 | 65 | if (keys.hasOwnProperty('limit')) { 66 | debug('limiting to %d records', keys.limit); 67 | cursor.limit(keys.limit); 68 | } 69 | 70 | if (keys.hasOwnProperty('skip')) { 71 | debug('skipping %d records', keys.skip); 72 | cursor.skip(keys.skip); 73 | } 74 | 75 | if (keys.hasOwnProperty('sortBy')) { 76 | sort = {}; 77 | sort_split = keys.sortBy.split(','); 78 | for (i = 0; i < sort_split.length; i++) { 79 | sort[sort_split[i].split(':')[0]] = 80 | sort_split[i].split(':')[1] || 'asc'; 81 | } 82 | debug('sorting: %j', sort); 83 | cursor.sort(sort); 84 | } 85 | 86 | cursor.select(tools.select(projection, keys.fields)); 87 | 88 | if (keys.hasOwnProperty('export') && allowedExports[keys.export]) { 89 | debug('Will export type %s', keys.export); 90 | allowedExports[keys.export](self.response, cursor.lean(), function(e) { 91 | if (e) callback(e); 92 | else self.close(); 93 | }); 94 | } else if (stream) { 95 | i = 0; 96 | self.response.set('Content-Type', 'application/json'); 97 | cursor.lean().cursor() 98 | .on('data', function(doc) { 99 | var d = (i++) ? ',' : '{ "error" : null, "data": ['; 100 | self.response.write(d + JSON.stringify(doc)); 101 | }) 102 | .on('error', function(e) { 103 | callback(e || 'error in stream'); 104 | }) 105 | .on('end', function() { 106 | if (!i) self.response.write('{ "error": null, "data": ['); 107 | self.response.write(']}'); 108 | self.response.end(); 109 | self.close(); 110 | }); 111 | } else if (sendMetadata && keys.hasOwnProperty('page') && 112 | keys.hasOwnProperty('perPage')) { 113 | Model.count(query, function(e, count) { 114 | var metadata = {}; 115 | if (e) return callback(e); 116 | 117 | metadata.records = count; 118 | metadata.page = keys.page; 119 | metadata.totalPages = Math.ceil(count / keys.perPage); 120 | metadata.perPage = keys.perPage; 121 | cursor.lean().exec(function(e, d) { 122 | callback(null, d, query, metadata); 123 | }); 124 | }); 125 | } else cursor.lean().exec(callback); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /lib/findOne.js: -------------------------------------------------------------------------------- 1 | 2 | var tools = require('./tools'), 3 | debug = require('debug')('crud-mongoose:findOne'); 4 | 5 | module.exports = exports = findOne; 6 | 7 | function findOne(Model, projection) { 8 | 9 | findOne.Model = Model; 10 | findOne.projection = projection; 11 | 12 | return findOne; 13 | 14 | function findOne(data, query, callback) { 15 | var cursor, fields; 16 | 17 | if (query.hasOwnProperty('fields')) { 18 | fields = query.fields; 19 | delete query.fields; 20 | } 21 | 22 | debug('going to find %j', query); 23 | cursor = Model.findOne(query); 24 | 25 | cursor.select(tools.select(projection, fields)); 26 | 27 | cursor.lean().exec(callback); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/parseData.js: -------------------------------------------------------------------------------- 1 | 2 | var tools = require('./tools'), 3 | debug = require('debug')('crud-mongoose:parseData'), 4 | gets = require('gets'); 5 | 6 | module.exports = exports = parseData; 7 | 8 | function parseData() { 9 | var removes = [], 10 | required = [], 11 | dontFlatten = [], 12 | defaults = {}, 13 | overrides = {}; 14 | 15 | parseDataFn.removes = function() { 16 | removes.push.apply(removes, arguments); 17 | return parseDataFn; 18 | } 19 | 20 | parseDataFn.required = function() { 21 | required.push.apply(required, arguments); 22 | return parseDataFn; 23 | } 24 | 25 | parseDataFn.defaults = function(defs) { 26 | tools.merge(defaults, defs); 27 | return parseDataFn; 28 | } 29 | 30 | parseDataFn.overrides = function(os) { 31 | tools.merge(overrides, os); 32 | return parseDataFn; 33 | } 34 | 35 | parseDataFn.dontFlatten = function(d) { 36 | dontFlatten.push.apply(dontFlatten, arguments); 37 | return parseDataFn; 38 | } 39 | 40 | return parseDataFn.removes('_id'); 41 | 42 | function parseDataFn(original, query, callback) { 43 | var i, f, d, data; 44 | 45 | data = tools.flattenObject(original, removes, null, dontFlatten); 46 | 47 | debug('parsing data:\n data-> %j ' + 48 | '\n remove-> %j' + 49 | '\n require-> %j' + 50 | '\n flattened/removed-> %j' + 51 | '\n default-> %j' + 52 | '\n override-> %j', 53 | original, removes, required, data, defaults, overrides); 54 | 55 | // check required fields 56 | for (i = 0; i < required.length; i++) { 57 | if (!data.hasOwnProperty(required[i]) || 58 | (!data[required[i]] && typeof (data[required[i]]) != 'boolean')) { 59 | return callback('required data field: ' + required[i]); 60 | } 61 | } 62 | 63 | // set defaults 64 | tools.mergeDefaults(data, defaults); 65 | 66 | // override fields 67 | tools.mergeOverrides(data, overrides); 68 | 69 | // done 70 | debug('parsed data - %j', data); 71 | callback(null, data); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /lib/parseQuery.js: -------------------------------------------------------------------------------- 1 | 2 | var tools = require('./tools'), 3 | debug = require('debug')('crud-mongoose:parseQuery'); 4 | 5 | module.exports = exports = parseQuery; 6 | 7 | function parseQuery() { 8 | var removes = [], 9 | required = [], 10 | defaults = {}, 11 | overrides = {}, 12 | maxes = {}; 13 | 14 | parseQueryFn.removes = function(p) { 15 | for (var i = 0; i < arguments.length; i++) removes.push(arguments[i]); 16 | return parseQueryFn; 17 | } 18 | 19 | parseQueryFn.required = function(p) { 20 | for (var i = 0; i < arguments.length; i++) required.push(arguments[i]); 21 | return parseQueryFn; 22 | } 23 | 24 | parseQueryFn.defaults = function(defs) { 25 | tools.merge(defaults, defs); 26 | return parseQueryFn; 27 | } 28 | 29 | parseQueryFn.overrides = function(os) { 30 | tools.merge(overrides, os); 31 | return parseQueryFn; 32 | } 33 | 34 | parseQueryFn.maxes = function(m) { 35 | tools.merge(maxes, m); 36 | return parseQueryFn; 37 | } 38 | 39 | return parseQueryFn; 40 | 41 | function parseQueryFn(data, query, callback) { 42 | var i; 43 | 44 | debug('parsing query:\n query -> %j ' + 45 | '\n remove-> %j' + 46 | '\n require-> %j' + 47 | '\n default-> %j' + 48 | '\n override-> %j' + 49 | '\n maxes-> %j', 50 | query, removes, required, defaults, overrides, maxes); 51 | 52 | // check required fields 53 | for (i = 0; i < required.length; i++) { 54 | if (!query.hasOwnProperty(required[i]) || 55 | query[required[i]] == '') { 56 | return callback('required query field: ' + required[i]); 57 | } 58 | } 59 | 60 | // remove specified fields 61 | for (i = 0; i < removes.length; i++) { 62 | if (query.hasOwnProperty(removes[i])) { 63 | debug('removing %s', removes[i]); 64 | delete query[removes[i]]; 65 | } 66 | } 67 | 68 | // set defaults 69 | tools.mergeDefaults(query, defaults); 70 | 71 | // ensure max 72 | for (i in maxes) { 73 | if (query.hasOwnProperty(i)) { 74 | query[i] = Math.min(parseFloat(query[i]), maxes[i]); 75 | } 76 | } 77 | 78 | // override fields 79 | tools.mergeOverrides(query, overrides); 80 | 81 | // done 82 | debug('parsed query - %j', query); 83 | callback(null, data, query); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /lib/removeAll.js: -------------------------------------------------------------------------------- 1 | 2 | var tools = require('./tools'), 3 | debug = require('debug')('crud-mongoose:removeAll'); 4 | 5 | module.exports = exports = removeAll; 6 | 7 | function removeAll(Model) { 8 | return function(data, query, callback) { 9 | debug('going to remove - %j', query); 10 | Model.remove(query).lean().exec(callback); 11 | } 12 | } 13 | 14 | -------------------------------------------------------------------------------- /lib/removeOne.js: -------------------------------------------------------------------------------- 1 | 2 | var tools = require('./tools'), 3 | debug = require('debug')('crud-mongoose:removeOne'); 4 | 5 | module.exports = exports = removeOne; 6 | 7 | function removeOne(Model) { 8 | 9 | removeOne.Model = Model; 10 | return removeOne; 11 | 12 | function removeOne(data, query, callback) { 13 | debug('going to remove - %j', query); 14 | Model.findOneAndRemove(query).lean().exec(callback); 15 | } 16 | } 17 | 18 | 19 | -------------------------------------------------------------------------------- /lib/tools.js: -------------------------------------------------------------------------------- 1 | 2 | var debug = require('debug')('crud-mongoose:tools'), 3 | tools = module.exports = {}; 4 | 5 | tools.argArray = function(args) { 6 | return Array.prototype.slice.call(args, 0); 7 | } 8 | 9 | tools.merge = function(a, b) { 10 | a = a || {}; 11 | b = b || {}; 12 | for (var k in b) a[k] = b[k]; 13 | return a; 14 | } 15 | 16 | tools.mergeOverrides = function(obj, overrides) { 17 | var i; 18 | 19 | obj = obj || {}; 20 | overrides = overrides || {}; 21 | 22 | for (i in overrides) { 23 | obj[i] = overrides[i]; 24 | if ('function' == typeof obj[i]) obj[i] = obj[i](); 25 | } 26 | } 27 | 28 | tools.mergeDefaults = function(obj, defaults) { 29 | var i; 30 | 31 | obj = obj || {}; 32 | defaults = defaults || {}; 33 | 34 | for (i in defaults) { 35 | if (!obj.hasOwnProperty(i)) { 36 | obj[i] = defaults[i]; 37 | if ('function' == typeof obj[i]) obj[i] = obj[i](); 38 | } 39 | } 40 | } 41 | 42 | tools.select = function(projection, fields) { 43 | var outs = [], 44 | ins = [], 45 | r = '', 46 | pass, split, i, j; 47 | 48 | projection = projection || []; 49 | 50 | // split between included and excluded fields 51 | for (i = 0; i < projection.length; i++) { 52 | if (projection[i][0] == '-') { 53 | outs.push(projection[i].replace(/^-/, '')); 54 | } else r += projection[i] + ' '; 55 | } 56 | 57 | // no extra fields 58 | if (!fields) { 59 | debug('selecting %s', r || projection); 60 | return r || projection.join(' '); 61 | } 62 | 63 | debug('outs: ', outs); 64 | debug('fields: ', fields); 65 | fields = fields.split(','); 66 | r = ''; 67 | 68 | // include only allowed fields 69 | for (i = 0; i < fields.length; i++) { 70 | split = fields[i].split('.'); 71 | pass = true; 72 | for (j = 0; j < split.length; j++) { 73 | if (~outs.indexOf(split.slice(0, j + 1).join('.'))) { 74 | debug('disallowing querying: %s', fields[i]); 75 | pass = false; 76 | break; 77 | } 78 | } 79 | if (pass) r += fields[i] + ' '; 80 | } 81 | 82 | // prevent something where you disallow 'user.name' and they try to select 83 | // 'user'. This would give them access to the 'name'. The only want to 84 | // prevent this is to disallow the projection here. 85 | if (r.length) { 86 | for (i = 0; i < outs.length; i++) { 87 | split = outs[i].split('.'); 88 | pass = true; 89 | for (j = 0; j < split.length; j++) { 90 | if (~r.indexOf(split.slice(0, j + 1).join('.'))) { 91 | debug('disallowing projection because disallowed split: %s', split); 92 | pass = false; 93 | break; 94 | } 95 | } 96 | if (!pass) { 97 | r = []; 98 | break; 99 | } 100 | } 101 | } 102 | 103 | // remove disallowed fields if none were allowed 104 | if (!r.length) r = '-' + outs.join(' -'); 105 | 106 | debug('selecting', r); 107 | 108 | return r; 109 | } 110 | 111 | tools.flattenObject = function(ob, removes, prev, dontFlatten) { 112 | var toReturn = {}, 113 | removes = removes || [], 114 | prev = prev || '', 115 | dontFlatten = dontFlatten || [], 116 | i, flatObject, x; 117 | 118 | debug('Flattening:\n Object-> %j\n Removing-> %j\n Prev-> %j', 119 | ob, removes, prev); 120 | 121 | for (i in ob) { 122 | if (!ob.hasOwnProperty(i)) continue; 123 | if (dontFlatten.indexOf(prev + i) != -1) { 124 | toReturn[i] = ob[i]; 125 | continue; 126 | } 127 | if (removes.length && ~removes.indexOf(prev + i)) { 128 | debug(' Removing %s', prev + i); 129 | continue; 130 | } 131 | if (/\./.test(i)) { 132 | debug(' "%s": the should not be flat already.', i); 133 | continue; 134 | } 135 | if ((typeof ob[i]) == 'object' && !(ob[i] instanceof Array) && 136 | !(ob[i] instanceof Date) && !(ob[i] === null)) { 137 | flatObject = tools.flattenObject( 138 | ob[i], 139 | removes, 140 | prev + i + '.', 141 | dontFlatten 142 | ); 143 | for (x in flatObject) { 144 | if (!flatObject.hasOwnProperty(x)) continue; 145 | toReturn[i + '.' + x] = flatObject[x]; 146 | } 147 | } else { 148 | toReturn[i] = ob[i]; 149 | } 150 | } 151 | 152 | return toReturn; 153 | } 154 | -------------------------------------------------------------------------------- /lib/updateOne.js: -------------------------------------------------------------------------------- 1 | 2 | var tools = require('./tools'), 3 | debug = require('debug')('crud-mongoose:updateOne'); 4 | 5 | module.exports = exports = updateOne; 6 | 7 | function updateOne(Model, options) { 8 | options = tools.merge({ /* defaults */ }, options); 9 | 10 | updateOne.Model = Model; 11 | updateOne.options = options; 12 | 13 | return updateOne; 14 | 15 | function updateOne(data, query, callback) { 16 | debug('going to update - \n query -> %j \n data -> %j', query, data); 17 | 18 | if ('__v' in data) delete data.__v; 19 | 20 | if (options.findOneAndUpdate) { 21 | Model.findOneAndUpdate(query, data, { 'new': true }) 22 | .lean().exec(callback); 23 | } else { 24 | Model.findOne(query, function(e, d) { 25 | if (e || !(d && d.set)) return callback(e || 'no value found'); 26 | d.set(data); 27 | d.save(function(e, d) { 28 | callback(e, d && d.toObject && d.toObject()); 29 | }); 30 | }); 31 | } 32 | } 33 | } 34 | 35 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "crud-mongoose", 3 | "version": "1.0.46", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "crud-mongoose", 9 | "version": "1.0.46", 10 | "license": "MIT", 11 | "dependencies": { 12 | "debug": "^2.1.0", 13 | "gets": "^1.0.0", 14 | "lodash": "^4.17.4", 15 | "mongoose": "^4.1.7", 16 | "require-dir": "^0.3.2", 17 | "xlsx-stream": "^0.1.2" 18 | }, 19 | "devDependencies": { 20 | "body-parser": "^1.9.0", 21 | "express": "^4.9.7", 22 | "node-crud": "^2.0.1" 23 | } 24 | }, 25 | "node_modules/accepts": { 26 | "version": "1.3.8", 27 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", 28 | "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", 29 | "dev": true, 30 | "dependencies": { 31 | "mime-types": "~2.1.34", 32 | "negotiator": "0.6.3" 33 | }, 34 | "engines": { 35 | "node": ">= 0.6" 36 | } 37 | }, 38 | "node_modules/archiver": { 39 | "version": "0.4.10", 40 | "resolved": "https://registry.npmjs.org/archiver/-/archiver-0.4.10.tgz", 41 | "integrity": "sha512-kZfIZkWWptGDLj1IHt8HgOLHe4oBGPmHSoAJ142HVt7adzSO657//wLbrkyxyOT+IuGJCA1wMBB4U90MAEiUjQ==", 42 | "dependencies": { 43 | "iconv-lite": "~0.2.11", 44 | "readable-stream": "~1.0.2" 45 | }, 46 | "engines": { 47 | "node": ">= 0.6.3" 48 | } 49 | }, 50 | "node_modules/archiver/node_modules/iconv-lite": { 51 | "version": "0.2.11", 52 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz", 53 | "integrity": "sha512-KhmFWgaQZY83Cbhi+ADInoUQ8Etn6BG5fikM9syeOjQltvR45h7cRKJ/9uvQEuD61I3Uju77yYce0/LhKVClQw==", 54 | "engines": { 55 | "node": ">=0.4.0" 56 | } 57 | }, 58 | "node_modules/archiver/node_modules/isarray": { 59 | "version": "0.0.1", 60 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", 61 | "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" 62 | }, 63 | "node_modules/archiver/node_modules/readable-stream": { 64 | "version": "1.0.34", 65 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", 66 | "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", 67 | "dependencies": { 68 | "core-util-is": "~1.0.0", 69 | "inherits": "~2.0.1", 70 | "isarray": "0.0.1", 71 | "string_decoder": "~0.10.x" 72 | } 73 | }, 74 | "node_modules/archiver/node_modules/string_decoder": { 75 | "version": "0.10.31", 76 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", 77 | "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" 78 | }, 79 | "node_modules/array-flatten": { 80 | "version": "1.1.1", 81 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 82 | "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", 83 | "dev": true 84 | }, 85 | "node_modules/async": { 86 | "version": "2.6.0", 87 | "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz", 88 | "integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==", 89 | "dependencies": { 90 | "lodash": "^4.14.0" 91 | } 92 | }, 93 | "node_modules/bluebird": { 94 | "version": "3.5.0", 95 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz", 96 | "integrity": "sha512-3LE8m8bqjGdoxfvf71yhFNrUcwy3NLy00SAo+b6MfJ8l+Bc2DzQ7mUHwX6pjK2AxfgV+YfsjCeVW3T5HLQTBsQ==" 97 | }, 98 | "node_modules/body-parser": { 99 | "version": "1.20.3", 100 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", 101 | "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", 102 | "dev": true, 103 | "dependencies": { 104 | "bytes": "3.1.2", 105 | "content-type": "~1.0.5", 106 | "debug": "2.6.9", 107 | "depd": "2.0.0", 108 | "destroy": "1.2.0", 109 | "http-errors": "2.0.0", 110 | "iconv-lite": "0.4.24", 111 | "on-finished": "2.4.1", 112 | "qs": "6.13.0", 113 | "raw-body": "2.5.2", 114 | "type-is": "~1.6.18", 115 | "unpipe": "1.0.0" 116 | }, 117 | "engines": { 118 | "node": ">= 0.8", 119 | "npm": "1.2.8000 || >= 1.4.16" 120 | } 121 | }, 122 | "node_modules/bson": { 123 | "version": "1.0.9", 124 | "resolved": "https://registry.npmjs.org/bson/-/bson-1.0.9.tgz", 125 | "integrity": "sha512-IQX9/h7WdMBIW/q/++tGd+emQr0XMdeZ6icnT/74Xk9fnabWn+gZgpE+9V+gujL3hhJOoNrnDVY7tWdzc7NUTg==", 126 | "deprecated": "Fixed a critical issue with BSON serialization documented in CVE-2019-2391, see https://bit.ly/2KcpXdo for more details", 127 | "engines": { 128 | "node": ">=0.6.19" 129 | } 130 | }, 131 | "node_modules/buffer-shims": { 132 | "version": "1.0.0", 133 | "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", 134 | "integrity": "sha512-Zy8ZXMyxIT6RMTeY7OP/bDndfj6bwCan7SS98CEndS6deHwWPpseeHlwarNcBim+etXnF9HBc1non5JgDaJU1g==" 135 | }, 136 | "node_modules/bytes": { 137 | "version": "3.1.2", 138 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", 139 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", 140 | "dev": true, 141 | "engines": { 142 | "node": ">= 0.8" 143 | } 144 | }, 145 | "node_modules/call-bind": { 146 | "version": "1.0.7", 147 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", 148 | "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", 149 | "dev": true, 150 | "dependencies": { 151 | "es-define-property": "^1.0.0", 152 | "es-errors": "^1.3.0", 153 | "function-bind": "^1.1.2", 154 | "get-intrinsic": "^1.2.4", 155 | "set-function-length": "^1.2.1" 156 | }, 157 | "engines": { 158 | "node": ">= 0.4" 159 | }, 160 | "funding": { 161 | "url": "https://github.com/sponsors/ljharb" 162 | } 163 | }, 164 | "node_modules/connect-timeout": { 165 | "version": "1.9.0", 166 | "resolved": "https://registry.npmjs.org/connect-timeout/-/connect-timeout-1.9.0.tgz", 167 | "integrity": "sha512-q4bsBIPd+eSGtnh/u6EBOKfuG+4YvwsN0idlOsg6KAw71Qpi0DCf2eCc/Va63QU9qdOeYC8katxoC+rHMNygZg==", 168 | "dev": true, 169 | "dependencies": { 170 | "http-errors": "~1.6.1", 171 | "ms": "2.0.0", 172 | "on-finished": "~2.3.0", 173 | "on-headers": "~1.0.1" 174 | }, 175 | "engines": { 176 | "node": ">= 0.8" 177 | } 178 | }, 179 | "node_modules/connect-timeout/node_modules/depd": { 180 | "version": "1.1.2", 181 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 182 | "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", 183 | "dev": true, 184 | "engines": { 185 | "node": ">= 0.6" 186 | } 187 | }, 188 | "node_modules/connect-timeout/node_modules/http-errors": { 189 | "version": "1.6.3", 190 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", 191 | "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", 192 | "dev": true, 193 | "dependencies": { 194 | "depd": "~1.1.2", 195 | "inherits": "2.0.3", 196 | "setprototypeof": "1.1.0", 197 | "statuses": ">= 1.4.0 < 2" 198 | }, 199 | "engines": { 200 | "node": ">= 0.6" 201 | } 202 | }, 203 | "node_modules/connect-timeout/node_modules/inherits": { 204 | "version": "2.0.3", 205 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 206 | "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", 207 | "dev": true 208 | }, 209 | "node_modules/connect-timeout/node_modules/on-finished": { 210 | "version": "2.3.0", 211 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 212 | "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", 213 | "dev": true, 214 | "dependencies": { 215 | "ee-first": "1.1.1" 216 | }, 217 | "engines": { 218 | "node": ">= 0.8" 219 | } 220 | }, 221 | "node_modules/connect-timeout/node_modules/setprototypeof": { 222 | "version": "1.1.0", 223 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", 224 | "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", 225 | "dev": true 226 | }, 227 | "node_modules/connect-timeout/node_modules/statuses": { 228 | "version": "1.5.0", 229 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 230 | "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", 231 | "dev": true, 232 | "engines": { 233 | "node": ">= 0.6" 234 | } 235 | }, 236 | "node_modules/content-disposition": { 237 | "version": "0.5.4", 238 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", 239 | "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", 240 | "dev": true, 241 | "dependencies": { 242 | "safe-buffer": "5.2.1" 243 | }, 244 | "engines": { 245 | "node": ">= 0.6" 246 | } 247 | }, 248 | "node_modules/content-type": { 249 | "version": "1.0.5", 250 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", 251 | "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", 252 | "dev": true, 253 | "engines": { 254 | "node": ">= 0.6" 255 | } 256 | }, 257 | "node_modules/cookie": { 258 | "version": "0.7.1", 259 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", 260 | "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", 261 | "dev": true, 262 | "engines": { 263 | "node": ">= 0.6" 264 | } 265 | }, 266 | "node_modules/cookie-signature": { 267 | "version": "1.0.6", 268 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 269 | "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", 270 | "dev": true 271 | }, 272 | "node_modules/core-util-is": { 273 | "version": "1.0.3", 274 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", 275 | "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" 276 | }, 277 | "node_modules/cors": { 278 | "version": "2.8.5", 279 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", 280 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", 281 | "dev": true, 282 | "dependencies": { 283 | "object-assign": "^4", 284 | "vary": "^1" 285 | }, 286 | "engines": { 287 | "node": ">= 0.10" 288 | } 289 | }, 290 | "node_modules/debug": { 291 | "version": "2.6.9", 292 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 293 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 294 | "dependencies": { 295 | "ms": "2.0.0" 296 | } 297 | }, 298 | "node_modules/define-data-property": { 299 | "version": "1.1.4", 300 | "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", 301 | "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", 302 | "dev": true, 303 | "dependencies": { 304 | "es-define-property": "^1.0.0", 305 | "es-errors": "^1.3.0", 306 | "gopd": "^1.0.1" 307 | }, 308 | "engines": { 309 | "node": ">= 0.4" 310 | }, 311 | "funding": { 312 | "url": "https://github.com/sponsors/ljharb" 313 | } 314 | }, 315 | "node_modules/depd": { 316 | "version": "2.0.0", 317 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", 318 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", 319 | "dev": true, 320 | "engines": { 321 | "node": ">= 0.8" 322 | } 323 | }, 324 | "node_modules/destroy": { 325 | "version": "1.2.0", 326 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", 327 | "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", 328 | "dev": true, 329 | "engines": { 330 | "node": ">= 0.8", 331 | "npm": "1.2.8000 || >= 1.4.16" 332 | } 333 | }, 334 | "node_modules/duplexer": { 335 | "version": "0.1.2", 336 | "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", 337 | "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" 338 | }, 339 | "node_modules/ee-first": { 340 | "version": "1.1.1", 341 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 342 | "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", 343 | "dev": true 344 | }, 345 | "node_modules/encodeurl": { 346 | "version": "2.0.0", 347 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", 348 | "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", 349 | "dev": true, 350 | "engines": { 351 | "node": ">= 0.8" 352 | } 353 | }, 354 | "node_modules/es-define-property": { 355 | "version": "1.0.0", 356 | "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", 357 | "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", 358 | "dev": true, 359 | "dependencies": { 360 | "get-intrinsic": "^1.2.4" 361 | }, 362 | "engines": { 363 | "node": ">= 0.4" 364 | } 365 | }, 366 | "node_modules/es-errors": { 367 | "version": "1.3.0", 368 | "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", 369 | "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", 370 | "dev": true, 371 | "engines": { 372 | "node": ">= 0.4" 373 | } 374 | }, 375 | "node_modules/es6-promise": { 376 | "version": "3.2.1", 377 | "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz", 378 | "integrity": "sha512-oj4jOSXvWglTsc3wrw86iom3LDPOx1nbipQk+jaG3dy+sMRM6ReSgVr/VlmBuF6lXUrflN9DCcQHeSbAwGUl4g==" 379 | }, 380 | "node_modules/escape-html": { 381 | "version": "1.0.3", 382 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 383 | "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", 384 | "dev": true 385 | }, 386 | "node_modules/etag": { 387 | "version": "1.8.1", 388 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 389 | "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", 390 | "dev": true, 391 | "engines": { 392 | "node": ">= 0.6" 393 | } 394 | }, 395 | "node_modules/express": { 396 | "version": "4.21.1", 397 | "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", 398 | "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", 399 | "dev": true, 400 | "dependencies": { 401 | "accepts": "~1.3.8", 402 | "array-flatten": "1.1.1", 403 | "body-parser": "1.20.3", 404 | "content-disposition": "0.5.4", 405 | "content-type": "~1.0.4", 406 | "cookie": "0.7.1", 407 | "cookie-signature": "1.0.6", 408 | "debug": "2.6.9", 409 | "depd": "2.0.0", 410 | "encodeurl": "~2.0.0", 411 | "escape-html": "~1.0.3", 412 | "etag": "~1.8.1", 413 | "finalhandler": "1.3.1", 414 | "fresh": "0.5.2", 415 | "http-errors": "2.0.0", 416 | "merge-descriptors": "1.0.3", 417 | "methods": "~1.1.2", 418 | "on-finished": "2.4.1", 419 | "parseurl": "~1.3.3", 420 | "path-to-regexp": "0.1.10", 421 | "proxy-addr": "~2.0.7", 422 | "qs": "6.13.0", 423 | "range-parser": "~1.2.1", 424 | "safe-buffer": "5.2.1", 425 | "send": "0.19.0", 426 | "serve-static": "1.16.2", 427 | "setprototypeof": "1.2.0", 428 | "statuses": "2.0.1", 429 | "type-is": "~1.6.18", 430 | "utils-merge": "1.0.1", 431 | "vary": "~1.1.2" 432 | }, 433 | "engines": { 434 | "node": ">= 0.10.0" 435 | } 436 | }, 437 | "node_modules/finalhandler": { 438 | "version": "1.3.1", 439 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", 440 | "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", 441 | "dev": true, 442 | "dependencies": { 443 | "debug": "2.6.9", 444 | "encodeurl": "~2.0.0", 445 | "escape-html": "~1.0.3", 446 | "on-finished": "2.4.1", 447 | "parseurl": "~1.3.3", 448 | "statuses": "2.0.1", 449 | "unpipe": "~1.0.0" 450 | }, 451 | "engines": { 452 | "node": ">= 0.8" 453 | } 454 | }, 455 | "node_modules/forwarded": { 456 | "version": "0.2.0", 457 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", 458 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", 459 | "dev": true, 460 | "engines": { 461 | "node": ">= 0.6" 462 | } 463 | }, 464 | "node_modules/fresh": { 465 | "version": "0.5.2", 466 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 467 | "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", 468 | "dev": true, 469 | "engines": { 470 | "node": ">= 0.6" 471 | } 472 | }, 473 | "node_modules/function-bind": { 474 | "version": "1.1.2", 475 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", 476 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", 477 | "dev": true, 478 | "funding": { 479 | "url": "https://github.com/sponsors/ljharb" 480 | } 481 | }, 482 | "node_modules/get-intrinsic": { 483 | "version": "1.2.4", 484 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", 485 | "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", 486 | "dev": true, 487 | "dependencies": { 488 | "es-errors": "^1.3.0", 489 | "function-bind": "^1.1.2", 490 | "has-proto": "^1.0.1", 491 | "has-symbols": "^1.0.3", 492 | "hasown": "^2.0.0" 493 | }, 494 | "engines": { 495 | "node": ">= 0.4" 496 | }, 497 | "funding": { 498 | "url": "https://github.com/sponsors/ljharb" 499 | } 500 | }, 501 | "node_modules/gets": { 502 | "version": "1.0.1", 503 | "resolved": "https://registry.npmjs.org/gets/-/gets-1.0.1.tgz", 504 | "integrity": "sha512-7DRjxMIQjEPd/z9Ny4tfSY9wsytjrDmf4AMpyeAurmqyUeFQWA6AbiuwGUx3Mjca9y3ZcB5oAmJK5ogoE9xrpw==" 505 | }, 506 | "node_modules/gopd": { 507 | "version": "1.0.1", 508 | "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", 509 | "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", 510 | "dev": true, 511 | "dependencies": { 512 | "get-intrinsic": "^1.1.3" 513 | }, 514 | "funding": { 515 | "url": "https://github.com/sponsors/ljharb" 516 | } 517 | }, 518 | "node_modules/has-property-descriptors": { 519 | "version": "1.0.2", 520 | "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", 521 | "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", 522 | "dev": true, 523 | "dependencies": { 524 | "es-define-property": "^1.0.0" 525 | }, 526 | "funding": { 527 | "url": "https://github.com/sponsors/ljharb" 528 | } 529 | }, 530 | "node_modules/has-proto": { 531 | "version": "1.0.3", 532 | "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", 533 | "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", 534 | "dev": true, 535 | "engines": { 536 | "node": ">= 0.4" 537 | }, 538 | "funding": { 539 | "url": "https://github.com/sponsors/ljharb" 540 | } 541 | }, 542 | "node_modules/has-symbols": { 543 | "version": "1.0.3", 544 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", 545 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", 546 | "dev": true, 547 | "engines": { 548 | "node": ">= 0.4" 549 | }, 550 | "funding": { 551 | "url": "https://github.com/sponsors/ljharb" 552 | } 553 | }, 554 | "node_modules/hasown": { 555 | "version": "2.0.2", 556 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", 557 | "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", 558 | "dev": true, 559 | "dependencies": { 560 | "function-bind": "^1.1.2" 561 | }, 562 | "engines": { 563 | "node": ">= 0.4" 564 | } 565 | }, 566 | "node_modules/hooks-fixed": { 567 | "version": "2.0.2", 568 | "resolved": "https://registry.npmjs.org/hooks-fixed/-/hooks-fixed-2.0.2.tgz", 569 | "integrity": "sha512-YurCM4gQSetcrhwEtpQHhQ4M7Zo7poNGqY4kQGeBS6eZtOcT3tnNs01ThFa0jYBByAiYt1MjMjP/YApG0EnAvQ==", 570 | "engines": { 571 | "node": ">=0.4.0" 572 | } 573 | }, 574 | "node_modules/http-errors": { 575 | "version": "2.0.0", 576 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", 577 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", 578 | "dev": true, 579 | "dependencies": { 580 | "depd": "2.0.0", 581 | "inherits": "2.0.4", 582 | "setprototypeof": "1.2.0", 583 | "statuses": "2.0.1", 584 | "toidentifier": "1.0.1" 585 | }, 586 | "engines": { 587 | "node": ">= 0.8" 588 | } 589 | }, 590 | "node_modules/iconv-lite": { 591 | "version": "0.4.24", 592 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 593 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 594 | "dev": true, 595 | "dependencies": { 596 | "safer-buffer": ">= 2.1.2 < 3" 597 | }, 598 | "engines": { 599 | "node": ">=0.10.0" 600 | } 601 | }, 602 | "node_modules/inherits": { 603 | "version": "2.0.4", 604 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 605 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 606 | }, 607 | "node_modules/ipaddr.js": { 608 | "version": "1.9.1", 609 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 610 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", 611 | "dev": true, 612 | "engines": { 613 | "node": ">= 0.10" 614 | } 615 | }, 616 | "node_modules/isarray": { 617 | "version": "1.0.0", 618 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 619 | "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" 620 | }, 621 | "node_modules/kareem": { 622 | "version": "1.5.0", 623 | "resolved": "https://registry.npmjs.org/kareem/-/kareem-1.5.0.tgz", 624 | "integrity": "sha512-DFYc05y1WSs6Ar++MHYRYu7/5r5356WDaKk8tQ8m6rlXD3VLpyG6Np81U78/wWJ4b5hjFXS7HkJNYrs85VypQA==" 625 | }, 626 | "node_modules/lodash": { 627 | "version": "4.17.21", 628 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 629 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" 630 | }, 631 | "node_modules/lodash.get": { 632 | "version": "4.4.2", 633 | "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", 634 | "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" 635 | }, 636 | "node_modules/media-typer": { 637 | "version": "0.3.0", 638 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 639 | "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", 640 | "dev": true, 641 | "engines": { 642 | "node": ">= 0.6" 643 | } 644 | }, 645 | "node_modules/merge-descriptors": { 646 | "version": "1.0.3", 647 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", 648 | "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", 649 | "dev": true, 650 | "funding": { 651 | "url": "https://github.com/sponsors/sindresorhus" 652 | } 653 | }, 654 | "node_modules/methods": { 655 | "version": "1.1.2", 656 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 657 | "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", 658 | "dev": true, 659 | "engines": { 660 | "node": ">= 0.6" 661 | } 662 | }, 663 | "node_modules/mime": { 664 | "version": "1.6.0", 665 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 666 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", 667 | "dev": true, 668 | "bin": { 669 | "mime": "cli.js" 670 | }, 671 | "engines": { 672 | "node": ">=4" 673 | } 674 | }, 675 | "node_modules/mime-db": { 676 | "version": "1.52.0", 677 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 678 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 679 | "dev": true, 680 | "engines": { 681 | "node": ">= 0.6" 682 | } 683 | }, 684 | "node_modules/mime-types": { 685 | "version": "2.1.35", 686 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 687 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 688 | "dev": true, 689 | "dependencies": { 690 | "mime-db": "1.52.0" 691 | }, 692 | "engines": { 693 | "node": ">= 0.6" 694 | } 695 | }, 696 | "node_modules/mongodb": { 697 | "version": "2.2.34", 698 | "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-2.2.34.tgz", 699 | "integrity": "sha512-JNW3PxPSMaG5Qu1yWb/BwVmAd5/d6sdX0Fy8Av3pAF3KbwDRqormEGIJMoDjiV/JtOR2zRSPk4xHgZxwI/bAnQ==", 700 | "dependencies": { 701 | "es6-promise": "3.2.1", 702 | "mongodb-core": "2.1.18", 703 | "readable-stream": "2.2.7" 704 | }, 705 | "engines": { 706 | "node": ">=0.10.3" 707 | } 708 | }, 709 | "node_modules/mongodb-core": { 710 | "version": "2.1.18", 711 | "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-2.1.18.tgz", 712 | "integrity": "sha512-i+PcaD5a5xF6gc2ujgvQDORV2iSRfoJ+IeBWUZrq37KRMJp+3G79/gPRXVkj/3CEGCPIDDYoTkaRgNpxJDrKBA==", 713 | "dependencies": { 714 | "bson": "~1.0.4", 715 | "require_optional": "~1.0.0" 716 | } 717 | }, 718 | "node_modules/mongoose": { 719 | "version": "4.13.21", 720 | "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-4.13.21.tgz", 721 | "integrity": "sha512-0VZtQu1rSUPwUtbb7zh6CymI0nNkVInOIDbtWNlna070qnUO14On8PpSVSwlx3gwmkKL2OkP4ioCj5YHC6trMg==", 722 | "dependencies": { 723 | "async": "2.6.0", 724 | "bson": "~1.0.4", 725 | "hooks-fixed": "2.0.2", 726 | "kareem": "1.5.0", 727 | "lodash.get": "4.4.2", 728 | "mongodb": "2.2.34", 729 | "mpath": "0.5.1", 730 | "mpromise": "0.5.5", 731 | "mquery": "2.3.3", 732 | "ms": "2.0.0", 733 | "muri": "1.3.0", 734 | "regexp-clone": "0.0.1", 735 | "sliced": "1.0.1" 736 | }, 737 | "engines": { 738 | "node": ">=0.6.19" 739 | } 740 | }, 741 | "node_modules/mpath": { 742 | "version": "0.5.1", 743 | "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.5.1.tgz", 744 | "integrity": "sha512-H8OVQ+QEz82sch4wbODFOz+3YQ61FYz/z3eJ5pIdbMEaUzDqA268Wd+Vt4Paw9TJfvDgVKaayC0gBzMIw2jhsg==", 745 | "engines": { 746 | "node": ">=4.0.0" 747 | } 748 | }, 749 | "node_modules/mpromise": { 750 | "version": "0.5.5", 751 | "resolved": "https://registry.npmjs.org/mpromise/-/mpromise-0.5.5.tgz", 752 | "integrity": "sha512-b/IJDqWlRXIW3ZouxIkUYLZFrr4qK/oUEgfVAywuvm77nTdDmY6y57lHxA8kfLnOSM+SbAUN/VvU1RxsGBLkQw==" 753 | }, 754 | "node_modules/mquery": { 755 | "version": "2.3.3", 756 | "resolved": "https://registry.npmjs.org/mquery/-/mquery-2.3.3.tgz", 757 | "integrity": "sha512-NC8L14kn+qxJbbJ1gbcEMDxF0sC3sv+1cbRReXXwVvowcwY1y9KoVZFq0ebwARibsadu8lx8nWGvm3V0Pf0ZWQ==", 758 | "dependencies": { 759 | "bluebird": "3.5.0", 760 | "debug": "2.6.9", 761 | "regexp-clone": "0.0.1", 762 | "sliced": "0.0.5" 763 | } 764 | }, 765 | "node_modules/mquery/node_modules/sliced": { 766 | "version": "0.0.5", 767 | "resolved": "https://registry.npmjs.org/sliced/-/sliced-0.0.5.tgz", 768 | "integrity": "sha512-9bYT917D6H3+q8GlQBJmLVz3bc4OeVGfZ2BB12wvLnluTGfG6/8UdOUbKJDW1EEx9SZMDbjnatkau5/XcUeyOw==" 769 | }, 770 | "node_modules/ms": { 771 | "version": "2.0.0", 772 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 773 | "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" 774 | }, 775 | "node_modules/muri": { 776 | "version": "1.3.0", 777 | "resolved": "https://registry.npmjs.org/muri/-/muri-1.3.0.tgz", 778 | "integrity": "sha512-FiaFwKl864onHFFUV/a2szAl7X0fxVlSKNdhTf+BM8i8goEgYut8u5P9MqQqIYwvaMxjzVESsoEm/2kfkFH1rg==" 779 | }, 780 | "node_modules/negotiator": { 781 | "version": "0.6.3", 782 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", 783 | "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", 784 | "dev": true, 785 | "engines": { 786 | "node": ">= 0.6" 787 | } 788 | }, 789 | "node_modules/node-crud": { 790 | "version": "2.1.7", 791 | "resolved": "https://registry.npmjs.org/node-crud/-/node-crud-2.1.7.tgz", 792 | "integrity": "sha512-iW57JyL3FAfDFf+Bx121lZDjftaRCSyEUyad7J1OS6WyORwgYzuOYDiHUKUK3FvtTv+ztKDNON9lNKk/yNr/ug==", 793 | "dev": true, 794 | "dependencies": { 795 | "connect-timeout": "^1.2.1", 796 | "cors": "^2.4.2", 797 | "debug": "^1.0.2", 798 | "require-dir": "^0.3.2" 799 | } 800 | }, 801 | "node_modules/node-crud/node_modules/debug": { 802 | "version": "1.0.5", 803 | "resolved": "https://registry.npmjs.org/debug/-/debug-1.0.5.tgz", 804 | "integrity": "sha512-SIKSrp4+XqcUaNWhwaPJbLFnvSXPsZ4xBdH2WRK0Xo++UzMC4eepYghGAVhVhOwmfq3kqowqJ5w45R3pmYZnuA==", 805 | "dev": true, 806 | "dependencies": { 807 | "ms": "2.0.0" 808 | } 809 | }, 810 | "node_modules/object-assign": { 811 | "version": "4.1.1", 812 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 813 | "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", 814 | "dev": true, 815 | "engines": { 816 | "node": ">=0.10.0" 817 | } 818 | }, 819 | "node_modules/object-inspect": { 820 | "version": "1.13.3", 821 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", 822 | "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", 823 | "dev": true, 824 | "engines": { 825 | "node": ">= 0.4" 826 | }, 827 | "funding": { 828 | "url": "https://github.com/sponsors/ljharb" 829 | } 830 | }, 831 | "node_modules/on-finished": { 832 | "version": "2.4.1", 833 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", 834 | "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", 835 | "dev": true, 836 | "dependencies": { 837 | "ee-first": "1.1.1" 838 | }, 839 | "engines": { 840 | "node": ">= 0.8" 841 | } 842 | }, 843 | "node_modules/on-headers": { 844 | "version": "1.0.2", 845 | "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", 846 | "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", 847 | "dev": true, 848 | "engines": { 849 | "node": ">= 0.8" 850 | } 851 | }, 852 | "node_modules/parseurl": { 853 | "version": "1.3.3", 854 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 855 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", 856 | "dev": true, 857 | "engines": { 858 | "node": ">= 0.8" 859 | } 860 | }, 861 | "node_modules/path-to-regexp": { 862 | "version": "0.1.10", 863 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", 864 | "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", 865 | "dev": true 866 | }, 867 | "node_modules/process-nextick-args": { 868 | "version": "1.0.7", 869 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", 870 | "integrity": "sha512-yN0WQmuCX63LP/TMvAg31nvT6m4vDqJEiiv2CAZqWOGNWutc9DfDk1NPYYmKUFmaVM2UwDowH4u5AHWYP/jxKw==" 871 | }, 872 | "node_modules/proxy-addr": { 873 | "version": "2.0.7", 874 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", 875 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", 876 | "dev": true, 877 | "dependencies": { 878 | "forwarded": "0.2.0", 879 | "ipaddr.js": "1.9.1" 880 | }, 881 | "engines": { 882 | "node": ">= 0.10" 883 | } 884 | }, 885 | "node_modules/qs": { 886 | "version": "6.13.0", 887 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", 888 | "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", 889 | "dev": true, 890 | "dependencies": { 891 | "side-channel": "^1.0.6" 892 | }, 893 | "engines": { 894 | "node": ">=0.6" 895 | }, 896 | "funding": { 897 | "url": "https://github.com/sponsors/ljharb" 898 | } 899 | }, 900 | "node_modules/range-parser": { 901 | "version": "1.2.1", 902 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 903 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", 904 | "dev": true, 905 | "engines": { 906 | "node": ">= 0.6" 907 | } 908 | }, 909 | "node_modules/raw-body": { 910 | "version": "2.5.2", 911 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", 912 | "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", 913 | "dev": true, 914 | "dependencies": { 915 | "bytes": "3.1.2", 916 | "http-errors": "2.0.0", 917 | "iconv-lite": "0.4.24", 918 | "unpipe": "1.0.0" 919 | }, 920 | "engines": { 921 | "node": ">= 0.8" 922 | } 923 | }, 924 | "node_modules/readable-stream": { 925 | "version": "2.2.7", 926 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.7.tgz", 927 | "integrity": "sha512-a6ibcfWFhgihuTw/chl+u3fB5ykBZFmnvpyZHebY0MCQE4vvYcsCLpCeaQ1BkH7HdJYavNSqF0WDLeo4IPHQaQ==", 928 | "dependencies": { 929 | "buffer-shims": "~1.0.0", 930 | "core-util-is": "~1.0.0", 931 | "inherits": "~2.0.1", 932 | "isarray": "~1.0.0", 933 | "process-nextick-args": "~1.0.6", 934 | "string_decoder": "~1.0.0", 935 | "util-deprecate": "~1.0.1" 936 | } 937 | }, 938 | "node_modules/regexp-clone": { 939 | "version": "0.0.1", 940 | "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-0.0.1.tgz", 941 | "integrity": "sha512-tfYXF0HXEYh3AtgdjqNLQ8+tmZSAKIS7KtOjmB1laJgfbsi+Lf2RVNwLZVOE3U27yBXikzQuIXglLlakvb8Thw==" 942 | }, 943 | "node_modules/require_optional": { 944 | "version": "1.0.1", 945 | "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", 946 | "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", 947 | "dependencies": { 948 | "resolve-from": "^2.0.0", 949 | "semver": "^5.1.0" 950 | } 951 | }, 952 | "node_modules/require-dir": { 953 | "version": "0.3.2", 954 | "resolved": "https://registry.npmjs.org/require-dir/-/require-dir-0.3.2.tgz", 955 | "integrity": "sha512-Onohhl/mv/fdIf51dpIiiDQXzLHBPXINN6XLzhW9D/hYrW6ANsYrXVkVE1I5X4Ly5r/hCZXGwDfaD+qsiqcroA==", 956 | "engines": { 957 | "node": "*" 958 | } 959 | }, 960 | "node_modules/resolve-from": { 961 | "version": "2.0.0", 962 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", 963 | "integrity": "sha512-qpFcKaXsq8+oRoLilkwyc7zHGF5i9Q2/25NIgLQQ/+VVv9rU4qvr6nXVAw1DsnXJyQkZsR4Ytfbtg5ehfcUssQ==", 964 | "engines": { 965 | "node": ">=0.10.0" 966 | } 967 | }, 968 | "node_modules/safe-buffer": { 969 | "version": "5.2.1", 970 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 971 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 972 | "dev": true, 973 | "funding": [ 974 | { 975 | "type": "github", 976 | "url": "https://github.com/sponsors/feross" 977 | }, 978 | { 979 | "type": "patreon", 980 | "url": "https://www.patreon.com/feross" 981 | }, 982 | { 983 | "type": "consulting", 984 | "url": "https://feross.org/support" 985 | } 986 | ] 987 | }, 988 | "node_modules/safer-buffer": { 989 | "version": "2.1.2", 990 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 991 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", 992 | "dev": true 993 | }, 994 | "node_modules/semver": { 995 | "version": "5.7.2", 996 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", 997 | "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", 998 | "bin": { 999 | "semver": "bin/semver" 1000 | } 1001 | }, 1002 | "node_modules/send": { 1003 | "version": "0.19.0", 1004 | "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", 1005 | "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", 1006 | "dev": true, 1007 | "dependencies": { 1008 | "debug": "2.6.9", 1009 | "depd": "2.0.0", 1010 | "destroy": "1.2.0", 1011 | "encodeurl": "~1.0.2", 1012 | "escape-html": "~1.0.3", 1013 | "etag": "~1.8.1", 1014 | "fresh": "0.5.2", 1015 | "http-errors": "2.0.0", 1016 | "mime": "1.6.0", 1017 | "ms": "2.1.3", 1018 | "on-finished": "2.4.1", 1019 | "range-parser": "~1.2.1", 1020 | "statuses": "2.0.1" 1021 | }, 1022 | "engines": { 1023 | "node": ">= 0.8.0" 1024 | } 1025 | }, 1026 | "node_modules/send/node_modules/encodeurl": { 1027 | "version": "1.0.2", 1028 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 1029 | "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", 1030 | "dev": true, 1031 | "engines": { 1032 | "node": ">= 0.8" 1033 | } 1034 | }, 1035 | "node_modules/send/node_modules/ms": { 1036 | "version": "2.1.3", 1037 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 1038 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 1039 | "dev": true 1040 | }, 1041 | "node_modules/serve-static": { 1042 | "version": "1.16.2", 1043 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", 1044 | "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", 1045 | "dev": true, 1046 | "dependencies": { 1047 | "encodeurl": "~2.0.0", 1048 | "escape-html": "~1.0.3", 1049 | "parseurl": "~1.3.3", 1050 | "send": "0.19.0" 1051 | }, 1052 | "engines": { 1053 | "node": ">= 0.8.0" 1054 | } 1055 | }, 1056 | "node_modules/set-function-length": { 1057 | "version": "1.2.2", 1058 | "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", 1059 | "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", 1060 | "dev": true, 1061 | "dependencies": { 1062 | "define-data-property": "^1.1.4", 1063 | "es-errors": "^1.3.0", 1064 | "function-bind": "^1.1.2", 1065 | "get-intrinsic": "^1.2.4", 1066 | "gopd": "^1.0.1", 1067 | "has-property-descriptors": "^1.0.2" 1068 | }, 1069 | "engines": { 1070 | "node": ">= 0.4" 1071 | } 1072 | }, 1073 | "node_modules/setprototypeof": { 1074 | "version": "1.2.0", 1075 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", 1076 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", 1077 | "dev": true 1078 | }, 1079 | "node_modules/side-channel": { 1080 | "version": "1.0.6", 1081 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", 1082 | "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", 1083 | "dev": true, 1084 | "dependencies": { 1085 | "call-bind": "^1.0.7", 1086 | "es-errors": "^1.3.0", 1087 | "get-intrinsic": "^1.2.4", 1088 | "object-inspect": "^1.13.1" 1089 | }, 1090 | "engines": { 1091 | "node": ">= 0.4" 1092 | }, 1093 | "funding": { 1094 | "url": "https://github.com/sponsors/ljharb" 1095 | } 1096 | }, 1097 | "node_modules/sliced": { 1098 | "version": "1.0.1", 1099 | "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", 1100 | "integrity": "sha512-VZBmZP8WU3sMOZm1bdgTadsQbcscK0UM8oKxKVBs4XAhUo2Xxzm/OFMGBkPusxw9xL3Uy8LrzEqGqJhclsr0yA==" 1101 | }, 1102 | "node_modules/statuses": { 1103 | "version": "2.0.1", 1104 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", 1105 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", 1106 | "dev": true, 1107 | "engines": { 1108 | "node": ">= 0.8" 1109 | } 1110 | }, 1111 | "node_modules/string_decoder": { 1112 | "version": "1.0.3", 1113 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", 1114 | "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", 1115 | "dependencies": { 1116 | "safe-buffer": "~5.1.0" 1117 | } 1118 | }, 1119 | "node_modules/string_decoder/node_modules/safe-buffer": { 1120 | "version": "5.1.2", 1121 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 1122 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 1123 | }, 1124 | "node_modules/through": { 1125 | "version": "2.3.8", 1126 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", 1127 | "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" 1128 | }, 1129 | "node_modules/toidentifier": { 1130 | "version": "1.0.1", 1131 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", 1132 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", 1133 | "dev": true, 1134 | "engines": { 1135 | "node": ">=0.6" 1136 | } 1137 | }, 1138 | "node_modules/type-is": { 1139 | "version": "1.6.18", 1140 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 1141 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 1142 | "dev": true, 1143 | "dependencies": { 1144 | "media-typer": "0.3.0", 1145 | "mime-types": "~2.1.24" 1146 | }, 1147 | "engines": { 1148 | "node": ">= 0.6" 1149 | } 1150 | }, 1151 | "node_modules/unpipe": { 1152 | "version": "1.0.0", 1153 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 1154 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", 1155 | "dev": true, 1156 | "engines": { 1157 | "node": ">= 0.8" 1158 | } 1159 | }, 1160 | "node_modules/util-deprecate": { 1161 | "version": "1.0.2", 1162 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1163 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" 1164 | }, 1165 | "node_modules/utils-merge": { 1166 | "version": "1.0.1", 1167 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 1168 | "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", 1169 | "dev": true, 1170 | "engines": { 1171 | "node": ">= 0.4.0" 1172 | } 1173 | }, 1174 | "node_modules/vary": { 1175 | "version": "1.1.2", 1176 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 1177 | "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", 1178 | "dev": true, 1179 | "engines": { 1180 | "node": ">= 0.8" 1181 | } 1182 | }, 1183 | "node_modules/xlsx-stream": { 1184 | "version": "0.1.2", 1185 | "resolved": "https://registry.npmjs.org/xlsx-stream/-/xlsx-stream-0.1.2.tgz", 1186 | "integrity": "sha512-oMGhRA4eD5zynd+pgjiuf+f29IJLwOUAumie37qXxmdnymUqm6Aj2w5F1DmiU3YG9TnivDxjnTqopk629f8Hng==", 1187 | "dependencies": { 1188 | "archiver": "~0.4.10", 1189 | "duplexer": "~ 0.1.1", 1190 | "lodash": "~ 1.3.0", 1191 | "through": "~ 2.3.4" 1192 | }, 1193 | "engines": { 1194 | "node": "~ 0.8.0" 1195 | } 1196 | }, 1197 | "node_modules/xlsx-stream/node_modules/lodash": { 1198 | "version": "1.3.1", 1199 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.3.1.tgz", 1200 | "integrity": "sha512-F7AB8u+6d00CCgnbjWzq9fFLpzOMCgq6mPjOW4+8+dYbrnc0obRrC+IHctzfZ1KKTQxX0xo/punrlpOWcf4gpw==", 1201 | "engines": [ 1202 | "node", 1203 | "rhino" 1204 | ] 1205 | } 1206 | } 1207 | } 1208 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "crud-mongoose", 3 | "version": "2.0.0", 4 | "description": "crud middleware specifically for mongoose", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git@github.com:uhray/crud-mongoose.git" 12 | }, 13 | "author": "Bobby Thompson ", 14 | "license": "MIT", 15 | "bugs": { 16 | "url": "https://github.com/uhray/crud-mongoose/issues" 17 | }, 18 | "homepage": "https://github.com/uhray/crud-mongoose", 19 | "dependencies": { 20 | "debug": "^2.1.0", 21 | "gets": "^1.0.0", 22 | "lodash": "^4.17.4", 23 | "mongoose": "^5.13.22", 24 | "require-dir": "^0.3.2", 25 | "xlsx-stream": "^0.1.2" 26 | }, 27 | "devDependencies": { 28 | "express": "^4.9.7", 29 | "body-parser": "^1.9.0", 30 | "node-crud": "^2.0.1" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | 2 | var crud = require('node-crud'), 3 | express = require('express'), 4 | cm = require('../'), 5 | mongoose = require('mongoose'), 6 | resources = require('require-dir')('./resources'), 7 | app = express(); 8 | 9 | 10 | // Connect to db --------------------------------------------------------------- 11 | mongoose.connect('mongodb://127.0.0.1/test'); 12 | mongoose.connection.on('error',function (e) { 13 | console.error('Mongoose default connection error: ' + e); 14 | }); 15 | mongoose.connection.once('connected', function() { 16 | console.log('connected to db.'); 17 | }); 18 | 19 | // set up express middleware --------------------------------------------------- 20 | app.use(require('body-parser').urlencoded({ extended: true })); 21 | app.use(require('body-parser').json()); 22 | 23 | // launch app ------------------------------------------------------------------ 24 | crud.launch(app); 25 | app.listen(3000); 26 | console.log('app listening on 3000'); 27 | -------------------------------------------------------------------------------- /test/resources/users.js: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // USER RESOURCE // 3 | // ============================================================================= 4 | 5 | // Load Modules ---------------------------------------------------------------- 6 | 7 | var crud = require('node-crud'), 8 | cm = require('../../'), 9 | mongoose = require('mongoose'), 10 | model, schema; 11 | 12 | // Create a Schema & Model ----------------------------------------------------- 13 | 14 | Schema = exports.Schema = new mongoose.Schema({ 15 | firstName: { type: String, required: true }, 16 | lastName: { type: String, required: true }, 17 | info: { 18 | gender: String, 19 | age: Number 20 | }, 21 | active: { type: Boolean, default: true }, 22 | created: { type: Date, default: Date.now }, 23 | updated: { type: Date, default: Date.now }, 24 | turnkey: { type: String } 25 | }); 26 | 27 | Model = exports.Model = mongoose.model('users', Schema); 28 | 29 | // All Users ------------------------------------------------------------------- 30 | 31 | crud.entity('/users').Read() 32 | .pipe(cm.parseQuery() 33 | .removes('info.age', 'auth') // can't query by age or auth 34 | //.overrides({ active: true }) // can only query active people 35 | //.defaults({ 'info.gender': 'M' }) // default only males 36 | .maxes({ limit: 8 })) // max limit is 100 37 | .pipe(cm.findAll(Model, ['-turnkey']).stream() 38 | .exports({ csv: cm.exporters.csv() })) 39 | 40 | crud.entity('/users').Create() 41 | .pipe(cm.createNew(Model)); 42 | 43 | crud.entity('/users').Delete() 44 | .pipe(cm.removeAll(Model)); 45 | 46 | crud.entity('/users').on('error', function(method, e) { 47 | console.log('%s error: %j', method, e); 48 | }); 49 | 50 | // One User -------------------------------------------------------------------- 51 | 52 | crud.entity('/users/:_id').Read() 53 | .pipe(cm.findOne(Model, [ 'firstName', 'lastName', 'info', '-turnkey' ])) 54 | 55 | crud.entity('/users/:_id').Update() 56 | .pipe(cm.parseData() 57 | .removes('auth') // can't set auth data 58 | .overrides({ updated: Date.now }) // override updated date 59 | .defaults({ 'info.gender': 'M' })) // default only males 60 | .pipe(cm.updateOne(Model)); 61 | 62 | crud.entity('/users/:_id').Delete() 63 | .pipe(cm.removeOne(Model)); 64 | 65 | crud.entity('/users/:_id').on('error', function(method, e) { 66 | debug('one | %s error: %j', method, e); 67 | }); 68 | --------------------------------------------------------------------------------