Files
habitica/website/server/controllers/api-v3/pushNotifications.js
Keith Holliday 6aa204c3f5 Fixed concurrency issues with push devices (#10598)
* Fixed concurrency issues with push devices

* Fixed push notificaiton response and model adding
2018-08-17 07:01:41 -05:00

110 lines
2.9 KiB
JavaScript

import { authWithHeaders } from '../../middlewares/auth';
import {
NotAuthorized,
NotFound,
} from '../../libs/errors';
import { model as PushDevice } from '../../models/pushDevice';
let api = {};
/**
* @apiIgnore
* @api {post} /api/v3/user/push-devices Add a push device to a user
* @apiName UserAddPushDevice
* @apiGroup User
*
* @apiParam (Body) {String} regId The id of the push device
* @apiParam (Body) {String} type The type of push device
*
* @apiSuccess {Object} data List of push devices
* @apiSuccess {String} message Success message
*/
api.addPushDevice = {
method: 'POST',
url: '/user/push-devices',
middlewares: [authWithHeaders({
userFieldsToExclude: ['inbox'],
})],
async handler (req, res) {
const user = res.locals.user;
req.checkBody('regId', res.t('regIdRequired')).notEmpty();
req.checkBody('type', res.t('typeRequired')).notEmpty().isIn(['ios', 'android']);
const validationErrors = req.validationErrors();
if (validationErrors) throw validationErrors;
const pushDevices = user.pushDevices;
const item = {
regId: req.body.regId,
type: req.body.type,
};
if (pushDevices.find(device => device.regId === item.regId)) {
throw new NotAuthorized(res.t('pushDeviceAlreadyAdded'));
}
// Concurrency safe update
const pushDevice = (new PushDevice(item)).toJSON(); // Create a mongo doc
await user.update({
$push: { pushDevices: pushDevice },
}).exec();
// Update the response
user.pushDevices.push(pushDevice);
res.respond(200, user.pushDevices, res.t('pushDeviceAdded'));
},
};
/**
* @apiIgnore
* @api {delete} /api/v3/user/push-devices/:regId remove a push device from a user
* @apiName UserRemovePushDevice
* @apiGroup User
*
* @apiParam (Path) {String} regId The id of the push device
*
* @apiSuccess {Object} data List of push devices
* @apiSuccess {String} message Success message
*/
api.removePushDevice = {
method: 'DELETE',
url: '/user/push-devices/:regId',
middlewares: [authWithHeaders({
userFieldsToExclude: ['inbox'],
})],
async handler (req, res) {
const user = res.locals.user;
req.checkParams('regId', res.t('regIdRequired')).notEmpty();
const validationErrors = req.validationErrors();
if (validationErrors) throw validationErrors;
const regId = req.params.regId;
const pushDevices = user.pushDevices;
const indexOfPushDevice = pushDevices.findIndex((element) => {
return element.regId === regId;
});
if (indexOfPushDevice === -1) {
throw new NotFound(res.t('pushDeviceNotFound'));
}
// Concurrency safe update
const pullQuery = { $pull: { pushDevices: { $elemMatch: { regId } } } };
await user.update(pullQuery).exec();
// Update the response
pushDevices.splice(indexOfPushDevice, 1);
res.respond(200, user.pushDevices, res.t('pushDeviceRemoved'));
},
};
module.exports = api;