wildduck/lib/api/mailboxes.js

686 lines
24 KiB
JavaScript
Raw Normal View History

2017-07-26 16:52:55 +08:00
'use strict';
const Joi = require('joi');
const ObjectId = require('mongodb').ObjectId;
2017-07-26 16:52:55 +08:00
const imapTools = require('../../imap-core/lib/imap-tools');
const tools = require('../tools');
2018-08-30 17:24:21 +08:00
const roles = require('../roles');
const util = require('util');
2020-07-20 01:51:06 +08:00
const { sessSchema, sessIPSchema, booleanSchema } = require('../schemas');
feat(apidocs): Autogenerate OpenAPI docs ZMS-100 (#552) * api.js added endpoint for generating openapi docs. added new info to one route in mailboxes.js and messages.js files so that the api docs generation can be done at all * try to first generate json representation of the api docs * add initial Joi Object parsing * api.js make generation dynamic. messages.js add schemas from separate file. messages-schemas.js used for messages endpoint schemas * add additions to schemas. Add new schemas to messages.js and also add response object there. Add response object parsing functionality to api.js * add initial openapi doc yml file generation * remove manual yaml parsing with js-yaml JSON -> YAML parsing * fix replaceWithRefs and parseComponentsDecoupled functions, refactor, remove unnecessary comments and logs * add support for another endpoint * move big code from api.js to tools * fix array type representation, fix response objects, add necessary data and changes to endpoints * redo include logic into exclude login * fix api generation in tools.js to accomodate new naming of objects * fix messages.js, add structuredClone check in tools.js * fix structured clone definition * add one endpoint in messages.js to the api generation * messages.js add one more endpoint to API generation * add response to prev commit. Add new endpoint to API generation. Archive message and archive messages * finish with post endpoints in messages.js * added general request and response schemas. Also added req and res schemas for messages * add multiple GET endpoints to API generation and changed them to new design. Use general schemas made earlier * fix incorrect import of successRes * fix mailboxes.js * refactor general-schemas.js. Fix searchSchema in messages.js. Mailboxes.js fix response * tools.js rename methodObj in API generation to operationObj * tools.js api generation remove string fallbacks * messages.js finish with GET endpoints, addition to API doc generation * for openApi doc generation use JSON now instead of YAML
2023-11-10 23:55:16 +08:00
const { userId, mailboxId } = require('../schemas/request/general-schemas');
const { successRes } = require('../schemas/response/general-schemas');
const { GetMailboxesResult } = require('../schemas/response/mailboxes-schemas');
2017-07-26 16:52:55 +08:00
module.exports = (db, server, mailboxHandler) => {
2018-08-30 17:24:21 +08:00
const getMailboxCounter = util.promisify(tools.getMailboxCounter);
2018-08-30 17:47:31 +08:00
const updateMailbox = util.promisify(mailboxHandler.update.bind(mailboxHandler));
const deleteMailbox = util.promisify(mailboxHandler.del.bind(mailboxHandler));
const createMailbox = mailboxHandler.createAsync.bind(mailboxHandler);
2018-08-30 17:42:06 +08:00
2018-08-30 17:24:21 +08:00
server.get(
{
path: '/users/:user/mailboxes',
tags: ['Mailboxes'],
summary: 'List Mailboxes for a User',
validationObjs: {
requestBody: {},
pathParams: {
user: userId
},
queryParams: {
specialUse: booleanSchema.default(false).description('Should the response include only folders with specialUse flag set.'),
showHidden: booleanSchema.default(false).description('Hidden folders are not included in the listing by default.'),
counters: booleanSchema
.default(false)
.description('Should the response include counters (total + unseen). Counters come with some overhead.'),
sizes: booleanSchema
.default(false)
.description(
'Should the response include mailbox size in bytes. Size numbers come with a lot of overhead as an aggregated query is ran.'
),
sess: sessSchema,
ip: sessIPSchema
},
response: {
200: {
description: 'Success',
model: Joi.object({
success: successRes,
results: Joi.array().items(GetMailboxesResult).description('List of user mailboxes').required()
})
}
}
}
},
tools.responseWrapper(async (req, res) => {
2018-08-30 17:24:21 +08:00
res.charSet('utf-8');
const { pathParams, requestBody, queryParams } = req.route.spec.validationObjs;
const schema = Joi.object({
...pathParams,
...requestBody,
...queryParams
2017-07-26 16:52:55 +08:00
});
2020-07-20 01:51:06 +08:00
const result = schema.validate(req.params, {
2018-08-30 17:24:21 +08:00
abortEarly: false,
convert: true
});
if (result.error) {
res.status(400);
return res.json({
2018-08-30 17:24:21 +08:00
error: result.error.message,
code: 'InputValidationError',
details: tools.validationErrors(result)
2018-08-30 17:24:21 +08:00
});
}
// permissions check
if (req.user && req.user === result.value.user) {
req.validate(roles.can(req.role).readOwn('mailboxes'));
} else {
req.validate(roles.can(req.role).readAny('mailboxes'));
}
let user = new ObjectId(result.value.user);
2018-08-30 17:24:21 +08:00
let counters = result.value.counters;
let sizes = result.value.sizes;
let sizeValues = false;
2018-08-30 17:24:21 +08:00
let userData;
try {
userData = await db.users.collection('users').findOne(
{
_id: user
},
{
projection: {
address: true
}
}
);
} catch (err) {
2021-05-20 19:47:20 +08:00
res.status(500);
return res.json({
2018-08-30 17:24:21 +08:00
error: 'MongoDB Error: ' + err.message,
code: 'InternalDatabaseError'
});
}
if (!userData) {
2021-05-20 19:47:20 +08:00
res.status(404);
return res.json({
2018-08-30 17:24:21 +08:00
error: 'This user does not exist',
code: 'UserNotFound'
});
}
if (sizes) {
try {
sizeValues = await db.database
.collection('messages')
.aggregate([
{
$match: {
user
}
},
{
$project: {
mailbox: '$mailbox',
size: '$size'
}
},
{
$group: {
_id: '$mailbox',
mailboxSize: {
$sum: '$size'
}
}
}
])
.toArray();
} catch (err) {
// ignore
}
}
2018-08-30 17:24:21 +08:00
let mailboxes;
try {
mailboxes = await db.database
2017-11-28 17:51:45 +08:00
.collection('mailboxes')
.find({
user
})
2018-08-30 17:24:21 +08:00
.toArray();
} catch (err) {
2021-05-20 19:47:20 +08:00
res.status(500);
return res.json({
2018-08-30 17:24:21 +08:00
error: 'MongoDB Error: ' + err.message,
code: 'InternalDatabaseError'
});
}
2017-07-26 16:52:55 +08:00
2018-08-30 17:24:21 +08:00
if (!mailboxes) {
mailboxes = [];
}
if (result.value.specialUse) {
mailboxes = mailboxes.filter(mailboxData => mailboxData.path === 'INBOX' || mailboxData.specialUse);
}
2018-08-30 17:24:21 +08:00
2020-07-02 18:08:16 +08:00
if (!result.value.showHidden) {
mailboxes = mailboxes.filter(mailboxData => !mailboxData.hidden);
}
2019-07-31 16:26:38 +08:00
mailboxes = mailboxes
.map(mailboxData => mailboxData)
.sort((a, b) => {
if (a.path === 'INBOX') {
return -1;
}
if (b.path === 'INBOX') {
return 1;
}
if (a.path.indexOf('INBOX/') === 0 && b.path.indexOf('INBOX/') !== 0) {
return -1;
}
if (a.path.indexOf('INBOX/') !== 0 && b.path.indexOf('INBOX/') === 0) {
return 1;
}
if (a.subscribed !== b.subscribed) {
return (a.subscribed ? 0 : 1) - (b.subscribed ? 0 : 1);
}
return a.path.localeCompare(b.path);
});
2018-08-30 17:24:21 +08:00
let responses = [];
2019-07-31 16:26:38 +08:00
let counterOps = [];
2018-08-30 17:24:21 +08:00
for (let mailboxData of mailboxes) {
let path = mailboxData.path.split('/');
let name = path.pop();
let response = {
2021-01-07 15:41:48 +08:00
id: mailboxData._id.toString(),
2018-08-30 17:24:21 +08:00
name,
path: mailboxData.path,
specialUse: mailboxData.specialUse,
modifyIndex: mailboxData.modifyIndex,
2020-07-02 18:08:16 +08:00
subscribed: mailboxData.subscribed,
hidden: !!mailboxData.hidden
2018-08-30 17:24:21 +08:00
};
if (mailboxData.retention) {
response.retention = mailboxData.retention;
}
if (sizeValues) {
for (let sizeValue of sizeValues) {
if (mailboxData._id.equals(sizeValue._id)) {
response.size = sizeValue.mailboxSize;
break;
}
}
}
2018-08-30 17:24:21 +08:00
if (!counters) {
responses.push(response);
continue;
}
let total, unseen;
2017-07-26 16:52:55 +08:00
2019-07-31 16:32:41 +08:00
counterOps.push(
(async () => {
try {
total = await getMailboxCounter(db, mailboxData._id, false);
} catch (err) {
// ignore
}
response.total = total;
})()
);
2018-08-30 17:24:21 +08:00
2019-07-31 16:32:41 +08:00
counterOps.push(
(async () => {
try {
unseen = await getMailboxCounter(db, mailboxData._id, 'unseen');
} catch (err) {
// ignore
}
response.unseen = unseen;
})()
);
2018-08-30 17:24:21 +08:00
responses.push(response);
2017-11-28 17:51:45 +08:00
}
2018-08-30 17:24:21 +08:00
2019-07-31 16:26:38 +08:00
if (counterOps.length) {
await Promise.all(counterOps);
}
return res.json({
2018-08-30 17:24:21 +08:00
success: true,
results: responses
});
})
);
2017-07-26 16:52:55 +08:00
2018-08-30 17:42:06 +08:00
server.post(
feat(apidocs): Autogenerate OpenAPI docs ZMS-100 (#552) * api.js added endpoint for generating openapi docs. added new info to one route in mailboxes.js and messages.js files so that the api docs generation can be done at all * try to first generate json representation of the api docs * add initial Joi Object parsing * api.js make generation dynamic. messages.js add schemas from separate file. messages-schemas.js used for messages endpoint schemas * add additions to schemas. Add new schemas to messages.js and also add response object there. Add response object parsing functionality to api.js * add initial openapi doc yml file generation * remove manual yaml parsing with js-yaml JSON -> YAML parsing * fix replaceWithRefs and parseComponentsDecoupled functions, refactor, remove unnecessary comments and logs * add support for another endpoint * move big code from api.js to tools * fix array type representation, fix response objects, add necessary data and changes to endpoints * redo include logic into exclude login * fix api generation in tools.js to accomodate new naming of objects * fix messages.js, add structuredClone check in tools.js * fix structured clone definition * add one endpoint in messages.js to the api generation * messages.js add one more endpoint to API generation * add response to prev commit. Add new endpoint to API generation. Archive message and archive messages * finish with post endpoints in messages.js * added general request and response schemas. Also added req and res schemas for messages * add multiple GET endpoints to API generation and changed them to new design. Use general schemas made earlier * fix incorrect import of successRes * fix mailboxes.js * refactor general-schemas.js. Fix searchSchema in messages.js. Mailboxes.js fix response * tools.js rename methodObj in API generation to operationObj * tools.js api generation remove string fallbacks * messages.js finish with GET endpoints, addition to API doc generation * for openApi doc generation use JSON now instead of YAML
2023-11-10 23:55:16 +08:00
{
path: '/users/:user/mailboxes',
summary: 'Create new Mailbox',
validationObjs: {
pathParams: { user: userId },
requestBody: {
path: Joi.string()
.regex(/\/{2,}|\/$/, { invert: true })
.required()
.description('Full path of the mailbox, folders are separated by slashes, ends with the mailbox name (unicode string)'),
hidden: booleanSchema.default(false).description('Is the folder hidden or not. Hidden folders can not be opened in IMAP.'),
retention: Joi.number()
.min(0)
.description('Retention policy for the created Mailbox. Milliseconds after a message added to mailbox expires. Set to 0 to disable.'),
sess: sessSchema,
ip: sessIPSchema
},
queryParams: {},
response: {
200: {
description: 'Success',
model: Joi.object({
success: successRes,
id: mailboxId
})
}
}
},
tags: ['Mailboxes']
},
tools.responseWrapper(async (req, res) => {
2018-08-30 17:42:06 +08:00
res.charSet('utf-8');
2017-07-26 16:52:55 +08:00
feat(apidocs): Autogenerate OpenAPI docs ZMS-100 (#552) * api.js added endpoint for generating openapi docs. added new info to one route in mailboxes.js and messages.js files so that the api docs generation can be done at all * try to first generate json representation of the api docs * add initial Joi Object parsing * api.js make generation dynamic. messages.js add schemas from separate file. messages-schemas.js used for messages endpoint schemas * add additions to schemas. Add new schemas to messages.js and also add response object there. Add response object parsing functionality to api.js * add initial openapi doc yml file generation * remove manual yaml parsing with js-yaml JSON -> YAML parsing * fix replaceWithRefs and parseComponentsDecoupled functions, refactor, remove unnecessary comments and logs * add support for another endpoint * move big code from api.js to tools * fix array type representation, fix response objects, add necessary data and changes to endpoints * redo include logic into exclude login * fix api generation in tools.js to accomodate new naming of objects * fix messages.js, add structuredClone check in tools.js * fix structured clone definition * add one endpoint in messages.js to the api generation * messages.js add one more endpoint to API generation * add response to prev commit. Add new endpoint to API generation. Archive message and archive messages * finish with post endpoints in messages.js * added general request and response schemas. Also added req and res schemas for messages * add multiple GET endpoints to API generation and changed them to new design. Use general schemas made earlier * fix incorrect import of successRes * fix mailboxes.js * refactor general-schemas.js. Fix searchSchema in messages.js. Mailboxes.js fix response * tools.js rename methodObj in API generation to operationObj * tools.js api generation remove string fallbacks * messages.js finish with GET endpoints, addition to API doc generation * for openApi doc generation use JSON now instead of YAML
2023-11-10 23:55:16 +08:00
const { pathParams, requestBody, queryParams } = req.route.spec.validationObjs;
const schema = Joi.object({
...pathParams,
...requestBody,
...queryParams
2018-08-30 17:42:06 +08:00
});
2017-07-26 16:52:55 +08:00
2020-07-20 01:51:06 +08:00
const result = schema.validate(req.params, {
2018-08-30 17:42:06 +08:00
abortEarly: false,
convert: true
2017-07-26 16:52:55 +08:00
});
2018-08-30 17:42:06 +08:00
if (result.error) {
res.status(400);
return res.json({
2018-08-30 17:42:06 +08:00
error: result.error.message,
code: 'InputValidationError',
details: tools.validationErrors(result)
2018-08-30 17:42:06 +08:00
});
}
2017-07-26 16:52:55 +08:00
2018-08-30 17:42:06 +08:00
// permissions check
if (req.user && req.user === result.value.user) {
req.validate(roles.can(req.role).createOwn('mailboxes'));
} else {
req.validate(roles.can(req.role).createAny('mailboxes'));
}
2017-07-26 16:52:55 +08:00
let user = new ObjectId(result.value.user);
2018-08-30 17:42:06 +08:00
let path = imapTools.normalizeMailbox(result.value.path);
let retention = result.value.retention;
let opts = {
2020-07-02 18:23:06 +08:00
subscribed: true,
hidden: !!result.value.hidden
2018-08-30 17:42:06 +08:00
};
2020-07-02 18:23:06 +08:00
2018-08-30 17:42:06 +08:00
if (retention) {
opts.retention = retention;
}
let status, id;
2017-07-26 16:52:55 +08:00
let data = await createMailbox(user, path, opts);
status = data.status;
id = data.id;
2017-07-26 16:52:55 +08:00
return res.json({
2017-07-26 16:52:55 +08:00
success: !!status,
id
});
2018-08-30 17:42:06 +08:00
})
);
2017-07-26 16:52:55 +08:00
2018-08-30 17:42:06 +08:00
server.get(
{
path: '/users/:user/mailboxes/:mailbox',
summary: 'Request Mailbox information',
tags: ['Mailboxes'],
validationObjs: {
requestBody: {},
queryParams: {
path: Joi.string()
.regex(/\/{2,}|\/$/, { invert: true })
.description('If mailbox is specified as `resolve` in the path then use this param as mailbox path instead of the given mailbox id.'),
sess: sessSchema,
ip: sessIPSchema
},
pathParams: {
user: userId,
mailbox: mailboxId.allow('resolve')
},
response: {
200: {
description: 'Success',
model: Joi.object({
success: successRes,
id: mailboxId,
name: Joi.string().required().description('Name for the mailbox (unicode string)'),
path: Joi.string()
.required()
.description('Full path of the mailbox, folders are separated by slashes, ends with the mailbox name (unicode string)'),
specialUse: Joi.string()
.required()
.example('\\Draft')
.description('Either special use identifier or null. One of Drafts, Junk, Sent or Trash'),
modifyIndex: Joi.number().required().description('Modification sequence number. Incremented on every change in the mailbox.'),
subscribed: booleanSchema.required().description('Mailbox subscription status. IMAP clients may unsubscribe from a folder.'),
hidden: booleanSchema.required().description('Is the folder hidden or not'),
total: Joi.number().required().description('How many messages are stored in this mailbox'),
unseen: Joi.number().required().description('How many unseen messages are stored in this mailbox')
})
}
}
}
},
tools.responseWrapper(async (req, res) => {
2018-08-30 17:42:06 +08:00
res.charSet('utf-8');
const { pathParams, requestBody, queryParams } = req.route.spec.validationObjs;
const schema = Joi.object({
...pathParams,
...requestBody,
...queryParams
2018-08-30 17:42:06 +08:00
});
2017-07-26 16:52:55 +08:00
2020-07-20 01:51:06 +08:00
const result = schema.validate(req.params, {
2018-08-30 17:42:06 +08:00
abortEarly: false,
convert: true
2017-07-26 16:52:55 +08:00
});
2018-08-30 17:42:06 +08:00
if (result.error) {
res.status(400);
return res.json({
2018-08-30 17:42:06 +08:00
error: result.error.message,
code: 'InputValidationError',
details: tools.validationErrors(result)
2018-08-30 17:42:06 +08:00
});
}
2018-01-25 03:04:53 +08:00
2018-08-30 17:42:06 +08:00
// permissions check
if (req.user && req.user === result.value.user) {
req.validate(roles.can(req.role).readOwn('mailboxes'));
} else {
req.validate(roles.can(req.role).readAny('mailboxes'));
}
2018-01-25 03:04:53 +08:00
let user = new ObjectId(result.value.user);
let mailbox = result.value.mailbox !== 'resolve' ? new ObjectId(result.value.mailbox) : 'resolve';
2018-08-30 17:42:06 +08:00
let userData;
try {
userData = await db.users.collection('users').findOne(
{
_id: user
},
{
projection: {
address: true
}
2018-01-25 03:04:53 +08:00
}
2018-08-30 17:42:06 +08:00
);
} catch (err) {
2021-05-20 19:47:20 +08:00
res.status(500);
return res.json({
2018-08-30 17:42:06 +08:00
error: 'MongoDB Error: ' + err.message,
code: 'InternalDatabaseError'
});
}
if (!userData) {
2021-05-20 19:47:20 +08:00
res.status(404);
return res.json({
2018-08-30 17:42:06 +08:00
error: 'This user does not exist',
code: 'UserNotFound'
});
}
2018-01-25 03:04:53 +08:00
2018-08-30 17:42:06 +08:00
let mailboxQuery = {
_id: mailbox,
user
};
2017-11-28 17:51:45 +08:00
2018-08-30 17:42:06 +08:00
if (mailbox === 'resolve') {
mailboxQuery = {
2020-07-16 16:15:04 +08:00
path: result.value.path,
2018-08-30 17:42:06 +08:00
user
};
}
2017-11-28 17:51:45 +08:00
2018-08-30 17:42:06 +08:00
let mailboxData;
try {
mailboxData = await db.database.collection('mailboxes').findOne(mailboxQuery);
} catch (err) {
2021-05-20 19:47:20 +08:00
res.status(500);
return res.json({
2018-08-30 17:42:06 +08:00
error: 'MongoDB Error: ' + err.message,
code: 'InternalDatabaseError'
2018-01-25 03:04:53 +08:00
});
2017-11-28 17:51:45 +08:00
}
2018-08-30 17:42:06 +08:00
if (!mailboxData) {
2021-05-20 19:47:20 +08:00
res.status(404);
return res.json({
2018-08-30 18:14:13 +08:00
error: 'This mailbox does not exist',
code: 'NoSuchMailbox'
2018-08-30 17:42:06 +08:00
});
}
mailbox = mailboxData._id;
let path = mailboxData.path.split('/');
let name = path.pop();
let total, unseen;
try {
total = await getMailboxCounter(db, mailboxData._id, false);
} catch (err) {
// ignore
}
try {
unseen = await getMailboxCounter(db, mailboxData._id, 'unseen');
} catch (err) {
// ignore
}
return res.json({
2018-08-30 17:42:06 +08:00
success: true,
id: mailbox,
name,
path: mailboxData.path,
specialUse: mailboxData.specialUse,
modifyIndex: mailboxData.modifyIndex,
subscribed: mailboxData.subscribed,
2020-07-02 18:08:16 +08:00
hidden: !!mailboxData.hidden,
2018-08-30 17:42:06 +08:00
total,
unseen
});
})
);
2017-07-26 16:52:55 +08:00
2018-08-30 17:47:31 +08:00
server.put(
{
path: '/users/:user/mailboxes/:mailbox',
summary: 'Update Mailbox information',
tags: ['Mailboxes'],
validationObjs: {
requestBody: {
path: Joi.string()
.regex(/\/{2,}|\/$/, { invert: true })
.description('Full path of the mailbox, use this to rename an existing Mailbox'),
retention: Joi.number()
.empty('')
.min(0)
.description(
'Retention policy for the Mailbox (in ms). Changing retention value only affects messages added to this folder after the change'
),
subscribed: booleanSchema.description('Change Mailbox subscription state'),
hidden: booleanSchema.description('Is the folder hidden or not. Hidden folders can not be opened in IMAP.'),
sess: sessSchema,
ip: sessIPSchema
},
pathParams: { user: userId, mailbox: mailboxId },
queryParams: {},
response: {
200: {
description: 'Success',
model: Joi.object({
success: successRes
})
}
}
}
},
tools.responseWrapper(async (req, res) => {
2018-08-30 17:47:31 +08:00
res.charSet('utf-8');
2017-07-26 16:52:55 +08:00
const { pathParams, requestBody, queryParams } = req.route.spec.validationObjs;
const schema = Joi.object({
...pathParams,
...requestBody,
...queryParams
2018-08-30 17:47:31 +08:00
});
2017-07-26 16:52:55 +08:00
2020-07-20 01:51:06 +08:00
const result = schema.validate(req.params, {
2018-08-30 17:47:31 +08:00
abortEarly: false,
convert: true
2017-07-26 16:52:55 +08:00
});
2018-08-30 17:47:31 +08:00
if (result.error) {
res.status(400);
return res.json({
2018-08-30 17:47:31 +08:00
error: result.error.message,
code: 'InputValidationError',
details: tools.validationErrors(result)
2018-08-30 17:47:31 +08:00
});
}
2017-07-26 16:52:55 +08:00
2018-08-30 17:47:31 +08:00
// permissions check
if (req.user && req.user === result.value.user) {
req.validate(roles.can(req.role).updateOwn('mailboxes'));
} else {
req.validate(roles.can(req.role).updateAny('mailboxes'));
2017-07-26 16:52:55 +08:00
}
let user = new ObjectId(result.value.user);
let mailbox = new ObjectId(result.value.mailbox);
2018-08-30 17:47:31 +08:00
let updates = {};
let update = false;
Object.keys(result.value || {}).forEach(key => {
if (!['user', 'mailbox'].includes(key)) {
updates[key] = result.value[key];
update = true;
}
2017-07-26 16:52:55 +08:00
});
2018-08-30 17:47:31 +08:00
if (!update) {
res.status(400);
return res.json({
2018-08-30 17:47:31 +08:00
error: 'Nothing was changed'
});
}
await updateMailbox(user, mailbox, updates);
2017-07-26 16:52:55 +08:00
return res.json({
2017-07-26 16:52:55 +08:00
success: true
});
2018-08-30 17:47:31 +08:00
})
);
2017-07-26 16:52:55 +08:00
2018-08-30 17:47:31 +08:00
server.del(
{
path: '/users/:user/mailboxes/:mailbox',
summary: 'Delete a Mailbox',
tags: ['Mailboxes'],
validationObjs: {
pathParams: { user: userId, mailbox: mailboxId },
queryParams: { sess: sessSchema, ip: sessIPSchema },
response: {
200: {
description: 'Success',
model: Joi.object({
success: successRes
})
}
}
}
},
tools.responseWrapper(async (req, res) => {
2018-08-30 17:47:31 +08:00
res.charSet('utf-8');
2017-07-26 16:52:55 +08:00
const { pathParams, requestBody, queryParams } = req.route.spec.validationObjs;
const schema = Joi.object({
...pathParams,
...requestBody,
...queryParams
2018-08-30 17:47:31 +08:00
});
2017-07-26 16:52:55 +08:00
2020-07-20 01:51:06 +08:00
const result = schema.validate(req.params, {
2018-08-30 17:47:31 +08:00
abortEarly: false,
convert: true
2017-07-26 16:52:55 +08:00
});
2018-08-30 17:47:31 +08:00
if (result.error) {
res.status(400);
return res.json({
2018-08-30 17:47:31 +08:00
error: result.error.message,
code: 'InputValidationError',
details: tools.validationErrors(result)
2018-08-30 17:47:31 +08:00
});
}
2017-07-26 16:52:55 +08:00
2018-08-30 17:47:31 +08:00
// permissions check
if (req.user && req.user === result.value.user) {
req.validate(roles.can(req.role).deleteOwn('mailboxes'));
} else {
req.validate(roles.can(req.role).deleteAny('mailboxes'));
}
let user = new ObjectId(result.value.user);
let mailbox = new ObjectId(result.value.mailbox);
2018-08-30 17:47:31 +08:00
await deleteMailbox(user, mailbox);
2017-07-26 16:52:55 +08:00
return res.json({
2017-07-26 16:52:55 +08:00
success: true
});
2018-08-30 17:47:31 +08:00
})
);
2017-07-26 16:52:55 +08:00
};