diff --git a/package-lock.json b/package-lock.json index a869a561c8b0589e28f3ee22b4c90c868d2ad412..605431e20bbfdbea8383d90ca99c3712c8bb99c7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -136,7 +136,7 @@ }, "@types/events": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", "integrity": "sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==" }, "@types/express": { @@ -182,9 +182,9 @@ "integrity": "sha512-A2TAGbTFdBw9azHbpVd+/FkdW2T6msN1uct1O9bH3vTerEHKZhTXJUQXy+hNq1B0RagfU8U+KBdqiZpxjhOUQA==" }, "@types/node": { - "version": "10.11.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.11.6.tgz", - "integrity": "sha512-fnA7yvqg3oKQDb3skBif9w5RRKVKAaeKeNuLzZL37XcSiWL4IoSXQnnbchR3UnBu2EMLHBip7ZVEkqoIVBP8QQ==" + "version": "10.12.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.0.tgz", + "integrity": "sha512-3TUHC3jsBAB7qVRGxT6lWyYo2v96BMmD2PTcl47H25Lu7UXtFH/2qqmKiVrnel6Ne//0TFYf6uvNX+HW2FRkLQ==" }, "@types/range-parser": { "version": "1.2.2", @@ -1058,7 +1058,7 @@ }, "enabled": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz", + "resolved": "http://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz", "integrity": "sha1-ll9lE9LC0cX0ZStkouM5ZGf8L5M=", "requires": { "env-variable": "0.0.x" @@ -1085,7 +1085,7 @@ }, "es6-promise": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz", + "resolved": "http://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz", "integrity": "sha1-7FYjOGgDKQkgcXDDlEjiREndH8Q=" }, "escape-string-regexp": { @@ -1344,7 +1344,7 @@ "dependencies": { "async": { "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" } } @@ -1636,7 +1636,7 @@ }, "http-errors": { "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "requires": { "depd": "~1.1.2", @@ -2230,9 +2230,9 @@ } }, "mbjs-generic-api": { - "version": "0.0.16", - "resolved": "https://registry.npmjs.org/mbjs-generic-api/-/mbjs-generic-api-0.0.16.tgz", - "integrity": "sha512-dwA1tRE8eDN+HhY+BNZuC72lZYF1gEHL5VEhTK/NXnpymbVqUxBlKJ020gb+OLm6d3AReWiqFn62VC5Z7XxnoQ==", + "version": "0.0.19", + "resolved": "https://registry.npmjs.org/mbjs-generic-api/-/mbjs-generic-api-0.0.19.tgz", + "integrity": "sha512-3Gqg8adzgzRANAeItgXSrl9PXPh+fV3H0zcXOiDaHbtEPPkNX0mAcguZA3oUME1FnRvTz4HMNnQ5KF6kp9an0A==", "requires": { "@polka/send-type": "^0.4.0", "acl": "^0.4.11", diff --git a/package.json b/package.json index 33dc41272cb7281cc4cb76e79f90757e62dbd742..ad162713868a6d1f514b4814f0ed0125c5a65a2c 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "config": "^2.0.1", "luxon": "^1.3.3", "mbjs-data-models": "0.0.10", - "mbjs-generic-api": "0.0.16", + "mbjs-generic-api": "0.0.19", "mbjs-persistence": "^0.2.0", "mbjs-utils": "0.0.6", "minio": "^7.0.1", diff --git a/src/lib/archives.js b/src/lib/archives.js index 0d975938fd038384fc3e854688a469e0eb226524..af4e083f83c9fd8441a6ddda7ce0dbffad441a00 100644 --- a/src/lib/archives.js +++ b/src/lib/archives.js @@ -38,14 +38,6 @@ module.exports.setupArchives = (api, mapService, annotationService) => { }) }) }) - api.app.get('/archives/maps/:id', async (req, res) => { - const filename = `map_archive_${req.params.id}.zip` - const filePath = path.join(os.tmpdir(), filename) - const file = fs.createReadStream(filePath) - res.setHeader('Content-Type', 'application/zip') - res.setHeader('Content-Disposition', `attachment; filename="${filename}"`) - file.pipe(res) - }) api.app.post('/archives/maps/upload', async function (req, res) { upload.single('file')(req, res, async () => { const results = await exports.readArchive(req.file.path) @@ -126,21 +118,24 @@ module.exports.setupArchives = (api, mapService, annotationService) => { }) } -module.exports.createArchive = async (data) => { +module.exports.createArchive = async data => { Assert.isType(data.map, 'object', 'data.map must be object') Assert.ok(Array.isArray(data.annotations), 'data.annotations must be array') - const - dir = path.join(os.tmpdir(), `archive_${ObjectUtil.slug(data.map.title)}_${data.map.uuid}`), - archive = new yazl.ZipFile() + const dir = path.join(os.tmpdir(), `archive_${ObjectUtil.slug(data.map.title)}_${data.map.uuid}`) await new Promise((resolve, reject) => { rimraf(dir, err => { - if (err) return reject(err) + if (err) { + api.captureException(err) + return reject(err) + } resolve() }) }) + const archive = new yazl.ZipFile() + await fs.mkdir(dir) await fs.mkdir(path.join(dir, 'maps')) await fs.mkdir(path.join(dir, 'annotations')) diff --git a/src/lib/profiles.js b/src/lib/profiles.js index 3f613b208364848c6f8580a5acc3b75146274a9b..193617d64db98992735eb9160e8242f96453d289 100644 --- a/src/lib/profiles.js +++ b/src/lib/profiles.js @@ -13,12 +13,20 @@ class Profiles extends TinyEmitter { this._client = new MongoDB(ObjectUtil.merge({ name: 'profiles', logger: console }, config.get('profiles.mongodb')), 'uuid') const _this = this + this._api = api api.app.get('/profiles/:id', (req, res) => _this.getHandler(req, res)) api.app.post('/profiles', async (req, res) => { req.body.uuid = ObjectUtil.uuid4() - const result = await this.client.create(req.body) + let result + try { + result = await this.client.create(req.body) + } + catch (err) { + api.captureException(err) + return _this._errorResponse(res, 500) + } if (result) { return _this._response(req, res, result) } @@ -30,7 +38,14 @@ class Profiles extends TinyEmitter { req.body.uuid = undefined req.body.user = undefined const data = req.body - let results = await this.client.find({ user: req.params.id }, req.params) + let results + try { + results = await this.client.find({ user: req.params.id }, req.params) + } + catch (err) { + api.captureException(err) + return _this._errorResponse(res, 500) + } if (results.length) { data.uuid = results[0].uuid results = await this.client.update(data.uuid, data, req.params) @@ -40,7 +55,14 @@ class Profiles extends TinyEmitter { }) api.app.patch('/profiles/:id', async (req, res) => { - let results = await this.client.find({ user: req.params.id }, req.params) + let results + try { + results = await this.client.find({user: req.params.id}, req.params) + } + catch (err) { + api.captureException(err) + return _this._errorResponse(res, 500) + } req.body._id = undefined req.body.uuid = undefined req.body.user = undefined @@ -50,16 +72,35 @@ class Profiles extends TinyEmitter { }) if (results.length) { results = ObjectUtil.merge(results[0], copy) - await this.client.update(results.uuid, results, req.params) + try { + await this.client.update(results.uuid, results, req.params) + } + catch (err) { + api.captureException(err) + return _this._errorResponse(res, 500) + } return _this._response(req, res, results) } send(res, 404) }) api.app.delete('/profiles/:id', async (req, res) => { - let results = await this.client.find({ user: req.params.id }, req.params) + let results + try { + results = await this.client.find({user: req.params.id}, req.params) + } + catch (err) { + api.captureException(err) + return _this._errorResponse(res, 500) + } if (results.length) { - results = await this.client.remove(results[0].uuid, req.params) + try { + results = await this.client.remove(results[0].uuid, req.params) + } + catch (err) { + api.captureException(err) + return _this._errorResponse(res, 500) + } if (results) { return _this._response(req, res, results) } @@ -69,7 +110,14 @@ class Profiles extends TinyEmitter { } async getHandler (req, res) { - const results = await this._client.find({ user: req.params.id }, req.params) + let results + try { + results = await this._client.find({user: req.params.id}, req.params) + } + catch (err) { + this._api.captureException(err) + return this._errorResponse(res, 500) + } if (results.length) { return this._response(req, res, results[0]) } diff --git a/src/lib/service.js b/src/lib/service.js index f26708fbd582c3d78a509a45dd16379f5c201680..8945a11f02e83af64a19cb4b4c9d2e50c3cebe60 100644 --- a/src/lib/service.js +++ b/src/lib/service.js @@ -12,6 +12,7 @@ class Service extends TinyEmitter { const _this = this this._name = name + this._captureException = api.captureException this._acl = api.acl this._logger = api.logger this._Model = model @@ -27,7 +28,14 @@ class Service extends TinyEmitter { } async findHandler (req, res) { - let results = await this._client.find(JSON.parse(req.query.query || '{}'), req.params) + let results + try { + results = await this._client.find(JSON.parse(req.query.query || '{}'), req.params) + } + catch (err) { + this._captureException(err) + return this._errorResponse(res, 500) + } const userId = req.user ? req.user.uuid : 'anon' const roles = req.user ? req.user.profile.roles : ['public'] const items = [] @@ -39,7 +47,7 @@ class Service extends TinyEmitter { allowed = await this._acl.areAnyRolesAllowed(roles, entry.uuid, ['get']) } catch (err) { - this._logger.error(`ACL error: ${err.message}`) + this._captureException(err) } } if (allowed) items.push(entry) @@ -48,7 +56,14 @@ class Service extends TinyEmitter { } async getHandler (req, res) { - const result = await this.client.get(req.params.id, req.params) + let result + try { + result = await this.client.get(req.params.id, req.params) + } + catch (err) { + this._captureException(err) + return this._errorResponse(res, 500) + } const roles = req.user ? req.user.profile.roles : ['public'] if (result) { let allowed = false @@ -58,7 +73,7 @@ class Service extends TinyEmitter { allowed = await this._acl.areAnyRolesAllowed(['public'].concat(roles), result.uuid, ['get']) } catch (err) { - this._logger.error(`ACL error: ${err.message}`) + this._captureException(err) } } if (allowed) { @@ -75,52 +90,104 @@ class Service extends TinyEmitter { ctx = this, data = req.body if (Array.isArray(data)) { - const results = await Promise.all(data.map(entry => { - return ctx.create(entry, req.params) - })) - return this._response(req, res, results) + try { + const results = await Promise.all(data.map(entry => { + return ctx.create(entry, req.params) + })) + return this._response(req, res, results) + } + catch (err) { + this._captureException(err) + return this._errorResponse(res, 500) + } } // TODO: allow for full array inserts instead just single requests - const instance = new this.ModelConstructor(data), - result = await this.client.create(instance, req.params) - instance.populate(result) - return this._response(req, res, instance) + // TODO: throw bad request error / validate input + try { + const instance = new this.ModelConstructor(data), + result = await this.client.create(instance, req.params) + instance.populate(result) + this._response(req, res, instance) + } + catch (err) { + this._captureException(err) + this._errorResponse(res, 500) + } } async putHandler (req, res) { const data = req.body - let result = await this.client.get(req.params.id) + let result + try { + result = await this.client.get(req.params.id) + } + catch (err) { + this._captureException(err) + return this._errorResponse(res, 500) + } if (result) { // TODO: transactions anyone?! if (req.user.uuid !== result.author.id) return this._errorResponse(res, 403) data.uuid = req.params.id - let instance = new this.ModelConstructor(data, req.params.id) - await this.client.update(req.params.id, instance, req.params) - return this._response(req, res, instance) + try { + let instance = new this.ModelConstructor(data, req.params.id) + await this.client.update(req.params.id, instance, req.params) + return this._response(req, res, instance) + } + catch (err) { + this._captureException(err) + this._errorResponse(res, 500) + } } else return this._errorResponse(res, 404) } async patchHandler (req, res) { const data = req.body - let existing = await this.client.get(req.params.id) + let existing + try { + existing = await this.client.get(req.params.id) + } + catch (err) { + this._captureException(err) + return this._errorResponse(res, 500) + } if (existing) { if (req.user.uuid !== existing.author.id) return this._errorResponse(res, 403) - let instance = new this.ModelConstructor(existing, req.params.id) - instance.populate(ObjectUtil.merge(instance.toObject(), data)) - await this.client.update(req.params.id, instance, req.params) - return this._response(req, res, instance) + try { + let instance = new this.ModelConstructor(existing, req.params.id) + instance.populate(ObjectUtil.merge(instance.toObject(), data)) + await this.client.update(req.params.id, instance, req.params) + return this._response(req, res, instance) + } + catch (err) { + this._captureException(err) + this._errorResponse(res, 500) + } } else return this._errorResponse(res, 404) } async deleteHandler (req, res) { - let existing = await this.client.get(req.params.id) + let existing + try { + existing = await this.client.get(req.params.id) + } + catch (err) { + this._captureException(err) + return this._errorResponse(res, 500) + } if (existing) { if (req.user.uuid !== existing.author.id) return this._errorResponse(res, 403) - const result = await this.client.remove(req.params.id, req.params) - if (result) { - return this._response(req, res, existing) + try { + const result = await this.client.remove(req.params.id, req.params) + if (result) { + return this._response(req, res, existing) + } + } + catch (err) { + this._captureException(err) + this._errorResponse(res, 500) } } else return this._errorResponse(res, 404) diff --git a/src/middleware/author.js b/src/middleware/author.js index 2b18494dedd3d2e6399a632e6dbedba7b9054d54..5d130537b288699989778d7b3385b0f03cb475cf 100644 --- a/src/middleware/author.js +++ b/src/middleware/author.js @@ -9,7 +9,13 @@ const setup = async function (api, profileService) { }, user: req.user } - const result = await profileService.getHandler(r) + let result + try { + result = await profileService.getHandler(r) + } + catch (err) { + api.captureException(err) + } req.user.profile = ObjectUtil.merge({}, req.user.profile, result ? result.data : undefined) if (req.method.toLowerCase() === 'post') { req.body.author = {