mirror of
https://github.com/nodemailer/wildduck.git
synced 2025-09-08 14:15:40 +08:00
fix(api-listings-pagination): ZMS-225 Encode page in next and previous cursor (#818)
* implement a mongopaging wrapper to encode page in the cursor * update mongopaging validation schema to properly use base64url encoding * list messages endpoint use embedded page in cursor instead of explicit user-provided * addresses, auth, certs for listing endpoints use mongopaging wrapper * dkim, domainaliases, filters - listing endpoints use mongopaging wrapper * messages, storage, users, webhooks - listing endpoints use mongopaging wrapper * mongopaging wrapper - if page negative due to externally crafter cursor, default to page 1
This commit is contained in:
parent
2f6591170e
commit
905b463a63
12 changed files with 234 additions and 192 deletions
|
@ -2,13 +2,12 @@
|
|||
|
||||
const config = require('wild-config');
|
||||
const Joi = require('joi');
|
||||
const MongoPaging = require('mongo-cursor-pagination');
|
||||
const ObjectId = require('mongodb').ObjectId;
|
||||
const tools = require('../tools');
|
||||
const consts = require('../consts');
|
||||
const roles = require('../roles');
|
||||
const libmime = require('libmime');
|
||||
const { nextPageCursorSchema, previousPageCursorSchema, pageNrSchema, sessSchema, sessIPSchema, booleanSchema, metaDataSchema } = require('../schemas');
|
||||
const { nextPageCursorSchema, previousPageCursorSchema, sessSchema, sessIPSchema, booleanSchema, metaDataSchema } = require('../schemas');
|
||||
const log = require('npmlog');
|
||||
const isemail = require('isemail');
|
||||
const {
|
||||
|
@ -29,6 +28,7 @@ const {
|
|||
} = require('../schemas/response/addresses-schemas');
|
||||
const { userId, addressEmail, addressId } = require('../schemas/request/general-schemas');
|
||||
const { Autoreply } = require('../schemas/request/addresses-schemas');
|
||||
const { mongopagingFindWrapper } = require('../mongopaging-find-wrapper');
|
||||
|
||||
module.exports = (db, server, userHandler, settingsHandler) => {
|
||||
server.get(
|
||||
|
@ -53,7 +53,6 @@ module.exports = (db, server, userHandler, settingsHandler) => {
|
|||
limit: Joi.number().default(20).min(1).max(250).description('How many records to return'),
|
||||
next: nextPageCursorSchema,
|
||||
previous: previousPageCursorSchema,
|
||||
page: pageNrSchema,
|
||||
sess: sessSchema,
|
||||
ip: sessIPSchema
|
||||
},
|
||||
|
@ -122,7 +121,6 @@ module.exports = (db, server, userHandler, settingsHandler) => {
|
|||
let query = result.value.query;
|
||||
let forward = result.value.forward;
|
||||
let limit = result.value.limit;
|
||||
let page = result.value.page;
|
||||
let pageNext = result.value.next;
|
||||
let pagePrevious = result.value.previous;
|
||||
|
||||
|
@ -216,13 +214,14 @@ module.exports = (db, server, userHandler, settingsHandler) => {
|
|||
|
||||
if (pageNext) {
|
||||
opts.next = pageNext;
|
||||
} else if ((!page || page > 1) && pagePrevious) {
|
||||
}
|
||||
if (pagePrevious) {
|
||||
opts.previous = pagePrevious;
|
||||
}
|
||||
|
||||
let listing;
|
||||
let listingWrapper;
|
||||
try {
|
||||
listing = await MongoPaging.find(db.users.collection('addresses'), opts);
|
||||
listingWrapper = await mongopagingFindWrapper(db.users.collection('addresses'), opts);
|
||||
} catch (err) {
|
||||
res.status(500);
|
||||
return res.json({
|
||||
|
@ -231,18 +230,14 @@ module.exports = (db, server, userHandler, settingsHandler) => {
|
|||
});
|
||||
}
|
||||
|
||||
if (!listing.hasPrevious) {
|
||||
page = 1;
|
||||
}
|
||||
|
||||
let response = {
|
||||
success: true,
|
||||
query,
|
||||
total,
|
||||
page,
|
||||
previousCursor: listing.hasPrevious ? listing.previous : false,
|
||||
nextCursor: listing.hasNext ? listing.next : false,
|
||||
results: (listing.results || []).map(addressData => {
|
||||
page: listingWrapper.page,
|
||||
previousCursor: listingWrapper.previousCursor,
|
||||
nextCursor: listingWrapper.nextCursor,
|
||||
results: (listingWrapper.listing.results || []).map(addressData => {
|
||||
let values = {
|
||||
id: addressData._id.toString(),
|
||||
name: addressData.name || undefined,
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
'use strict';
|
||||
|
||||
const Joi = require('joi');
|
||||
const MongoPaging = require('mongo-cursor-pagination');
|
||||
const ObjectId = require('mongodb').ObjectId;
|
||||
const tools = require('../tools');
|
||||
const roles = require('../roles');
|
||||
const { nextPageCursorSchema, previousPageCursorSchema, pageNrSchema, sessSchema, sessIPSchema, booleanSchema, usernameSchema } = require('../schemas');
|
||||
const { nextPageCursorSchema, previousPageCursorSchema, sessSchema, sessIPSchema, booleanSchema, usernameSchema } = require('../schemas');
|
||||
const { successRes } = require('../schemas/response/general-schemas');
|
||||
const { userId } = require('../schemas/request/general-schemas');
|
||||
const { mongopagingFindWrapper } = require('../mongopaging-find-wrapper');
|
||||
|
||||
module.exports = (db, server, userHandler) => {
|
||||
server.post(
|
||||
|
@ -327,7 +327,6 @@ module.exports = (db, server, userHandler) => {
|
|||
limit: Joi.number().default(20).min(1).max(250).description('How many records to return'),
|
||||
next: nextPageCursorSchema,
|
||||
previous: previousPageCursorSchema,
|
||||
page: pageNrSchema,
|
||||
filterip: sessIPSchema.description('Limit listing only to values with specific IP address'),
|
||||
|
||||
sess: sessSchema,
|
||||
|
@ -409,7 +408,6 @@ module.exports = (db, server, userHandler) => {
|
|||
let action = result.value.action;
|
||||
let ip = result.value.filterIp;
|
||||
|
||||
let page = result.value.page;
|
||||
let pageNext = result.value.next;
|
||||
let pagePrevious = result.value.previous;
|
||||
|
||||
|
@ -456,13 +454,14 @@ module.exports = (db, server, userHandler) => {
|
|||
|
||||
if (pageNext) {
|
||||
opts.next = pageNext;
|
||||
} else if ((!page || page > 1) && pagePrevious) {
|
||||
}
|
||||
if (pagePrevious) {
|
||||
opts.previous = pagePrevious;
|
||||
}
|
||||
|
||||
let listing;
|
||||
let listingWrapper;
|
||||
try {
|
||||
listing = await MongoPaging.find(db.users.collection('authlog'), opts);
|
||||
listingWrapper = await mongopagingFindWrapper(db.users.collection('authlog'), opts);
|
||||
} catch (err) {
|
||||
res.status(500);
|
||||
return res.json({
|
||||
|
@ -471,18 +470,14 @@ module.exports = (db, server, userHandler) => {
|
|||
});
|
||||
}
|
||||
|
||||
if (!listing.hasPrevious) {
|
||||
page = 1;
|
||||
}
|
||||
|
||||
let response = {
|
||||
success: true,
|
||||
action,
|
||||
total,
|
||||
page,
|
||||
previousCursor: listing.hasPrevious ? listing.previous : false,
|
||||
nextCursor: listing.hasNext ? listing.next : false,
|
||||
results: (listing.results || []).map(resultData => {
|
||||
page: listingWrapper.page,
|
||||
previousCursor: listingWrapper.previousCursor,
|
||||
nextCursor: listingWrapper.nextCursor,
|
||||
results: (listingWrapper.listing.results || []).map(resultData => {
|
||||
let response = {
|
||||
id: (resultData._id || '').toString()
|
||||
};
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
|
||||
const config = require('wild-config');
|
||||
const Joi = require('joi');
|
||||
const MongoPaging = require('mongo-cursor-pagination');
|
||||
const ObjectId = require('mongodb').ObjectId;
|
||||
const CertHandler = require('../cert-handler');
|
||||
const TaskHandler = require('../task-handler');
|
||||
const tools = require('../tools');
|
||||
const roles = require('../roles');
|
||||
const { nextPageCursorSchema, previousPageCursorSchema, pageNrSchema, sessSchema, sessIPSchema, booleanSchema } = require('../schemas');
|
||||
const { nextPageCursorSchema, previousPageCursorSchema, sessSchema, sessIPSchema, booleanSchema } = require('../schemas');
|
||||
const { successRes, totalRes, pageRes, previousCursorRes, nextCursorRes } = require('../schemas/response/general-schemas');
|
||||
const { mongopagingFindWrapper } = require('../mongopaging-find-wrapper');
|
||||
|
||||
const certificateSchema = Joi.string()
|
||||
.empty('')
|
||||
|
@ -49,7 +49,6 @@ module.exports = (db, server) => {
|
|||
limit: Joi.number().default(20).min(1).max(250).description('How many records to return'),
|
||||
next: nextPageCursorSchema,
|
||||
previous: previousPageCursorSchema,
|
||||
page: pageNrSchema,
|
||||
sess: sessSchema,
|
||||
ip: sessIPSchema
|
||||
},
|
||||
|
@ -131,7 +130,6 @@ module.exports = (db, server) => {
|
|||
let query = result.value.query;
|
||||
let altNames = result.value.altNames;
|
||||
let limit = result.value.limit;
|
||||
let page = result.value.page;
|
||||
let pageNext = result.value.next;
|
||||
let pagePrevious = result.value.previous;
|
||||
|
||||
|
@ -174,13 +172,14 @@ module.exports = (db, server) => {
|
|||
|
||||
if (pageNext) {
|
||||
opts.next = pageNext;
|
||||
} else if ((!page || page > 1) && pagePrevious) {
|
||||
}
|
||||
if (pagePrevious) {
|
||||
opts.previous = pagePrevious;
|
||||
}
|
||||
|
||||
let listing;
|
||||
let listingWrapper;
|
||||
try {
|
||||
listing = await MongoPaging.find(db.database.collection('certs'), opts);
|
||||
listingWrapper = await mongopagingFindWrapper(db.database.collection('certs'), opts);
|
||||
} catch (err) {
|
||||
res.status(500);
|
||||
return res.json({
|
||||
|
@ -189,18 +188,14 @@ module.exports = (db, server) => {
|
|||
});
|
||||
}
|
||||
|
||||
if (!listing.hasPrevious) {
|
||||
page = 1;
|
||||
}
|
||||
|
||||
let response = {
|
||||
success: true,
|
||||
query,
|
||||
total,
|
||||
page,
|
||||
previousCursor: listing.hasPrevious ? listing.previous : false,
|
||||
nextCursor: listing.hasNext ? listing.next : false,
|
||||
results: (listing.results || []).map(certData => ({
|
||||
page: listingWrapper.page,
|
||||
previousCursor: listingWrapper.previousCursor,
|
||||
nextCursor: listingWrapper.nextCursor,
|
||||
results: (listingWrapper.listing.results || []).map(certData => ({
|
||||
id: certData._id.toString(),
|
||||
servername: certData.servername,
|
||||
description: certData.description,
|
||||
|
|
|
@ -2,13 +2,13 @@
|
|||
|
||||
const config = require('wild-config');
|
||||
const Joi = require('joi');
|
||||
const MongoPaging = require('mongo-cursor-pagination');
|
||||
const ObjectId = require('mongodb').ObjectId;
|
||||
const DkimHandler = require('../dkim-handler');
|
||||
const tools = require('../tools');
|
||||
const roles = require('../roles');
|
||||
const { nextPageCursorSchema, previousPageCursorSchema, pageNrSchema, sessSchema, sessIPSchema } = require('../schemas');
|
||||
const { nextPageCursorSchema, previousPageCursorSchema, sessSchema, sessIPSchema } = require('../schemas');
|
||||
const { successRes, totalRes, pageRes, previousCursorRes, nextCursorRes } = require('../schemas/response/general-schemas');
|
||||
const { mongopagingFindWrapper } = require('../mongopaging-find-wrapper');
|
||||
|
||||
module.exports = (db, server) => {
|
||||
const dkimHandler = new DkimHandler({
|
||||
|
@ -31,7 +31,6 @@ module.exports = (db, server) => {
|
|||
limit: Joi.number().default(20).min(1).max(250).description('How many records to return'),
|
||||
next: nextPageCursorSchema,
|
||||
previous: previousPageCursorSchema,
|
||||
page: pageNrSchema,
|
||||
sess: sessSchema,
|
||||
ip: sessIPSchema
|
||||
},
|
||||
|
@ -97,7 +96,6 @@ module.exports = (db, server) => {
|
|||
|
||||
let query = result.value.query;
|
||||
let limit = result.value.limit;
|
||||
let page = result.value.page;
|
||||
let pageNext = result.value.next;
|
||||
let pagePrevious = result.value.previous;
|
||||
|
||||
|
@ -121,13 +119,14 @@ module.exports = (db, server) => {
|
|||
|
||||
if (pageNext) {
|
||||
opts.next = pageNext;
|
||||
} else if ((!page || page > 1) && pagePrevious) {
|
||||
}
|
||||
if (pagePrevious) {
|
||||
opts.previous = pagePrevious;
|
||||
}
|
||||
|
||||
let listing;
|
||||
let listingWrapper;
|
||||
try {
|
||||
listing = await MongoPaging.find(db.database.collection('dkim'), opts);
|
||||
listingWrapper = await mongopagingFindWrapper(db.database.collection('dkim'), opts);
|
||||
} catch (err) {
|
||||
res.status(500);
|
||||
return res.json({
|
||||
|
@ -136,18 +135,14 @@ module.exports = (db, server) => {
|
|||
});
|
||||
}
|
||||
|
||||
if (!listing.hasPrevious) {
|
||||
page = 1;
|
||||
}
|
||||
|
||||
let response = {
|
||||
success: true,
|
||||
query,
|
||||
total,
|
||||
page,
|
||||
previousCursor: listing.hasPrevious ? listing.previous : false,
|
||||
nextCursor: listing.hasNext ? listing.next : false,
|
||||
results: (listing.results || []).map(dkimData => ({
|
||||
page: listingWrapper.page,
|
||||
previousCursor: listingWrapper.previousCursor,
|
||||
nextCursor: listingWrapper.nextCursor,
|
||||
results: (listingWrapper.listing.results || []).map(dkimData => ({
|
||||
id: dkimData._id.toString(),
|
||||
domain: dkimData.domain,
|
||||
selector: dkimData.selector,
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
'use strict';
|
||||
|
||||
const Joi = require('joi');
|
||||
const MongoPaging = require('mongo-cursor-pagination');
|
||||
const ObjectId = require('mongodb').ObjectId;
|
||||
const tools = require('../tools');
|
||||
const roles = require('../roles');
|
||||
const { nextPageCursorSchema, previousPageCursorSchema, pageNrSchema, sessSchema, sessIPSchema } = require('../schemas');
|
||||
const { nextPageCursorSchema, previousPageCursorSchema, sessSchema, sessIPSchema } = require('../schemas');
|
||||
const { publish, DOMAINALIAS_CREATED, DOMAINALIAS_DELETED } = require('../events');
|
||||
const { successRes, totalRes, pageRes, previousCursorRes, nextCursorRes } = require('../schemas/response/general-schemas');
|
||||
const { mongopagingFindWrapper } = require('../mongopaging-find-wrapper');
|
||||
|
||||
module.exports = (db, server) => {
|
||||
server.get(
|
||||
|
@ -24,7 +24,6 @@ module.exports = (db, server) => {
|
|||
limit: Joi.number().default(20).min(1).max(250).description('How many records to return'),
|
||||
next: nextPageCursorSchema,
|
||||
previous: previousPageCursorSchema,
|
||||
page: pageNrSchema,
|
||||
sess: sessSchema,
|
||||
ip: sessIPSchema
|
||||
},
|
||||
|
@ -83,7 +82,6 @@ module.exports = (db, server) => {
|
|||
|
||||
let query = result.value.query;
|
||||
let limit = result.value.limit;
|
||||
let page = result.value.page;
|
||||
let pageNext = result.value.next;
|
||||
let pagePrevious = result.value.previous;
|
||||
|
||||
|
@ -128,13 +126,14 @@ module.exports = (db, server) => {
|
|||
|
||||
if (pageNext) {
|
||||
opts.next = pageNext;
|
||||
} else if ((!page || page > 1) && pagePrevious) {
|
||||
}
|
||||
if (pagePrevious) {
|
||||
opts.previous = pagePrevious;
|
||||
}
|
||||
|
||||
let listing;
|
||||
let listingWrapper;
|
||||
try {
|
||||
listing = await MongoPaging.find(db.users.collection('domainaliases'), opts);
|
||||
listingWrapper = await mongopagingFindWrapper(db.users.collection('domainaliases'), opts);
|
||||
} catch (err) {
|
||||
res.status(500);
|
||||
return res.json({
|
||||
|
@ -143,18 +142,14 @@ module.exports = (db, server) => {
|
|||
});
|
||||
}
|
||||
|
||||
if (!listing.hasPrevious) {
|
||||
page = 1;
|
||||
}
|
||||
|
||||
let response = {
|
||||
success: true,
|
||||
query,
|
||||
total,
|
||||
page,
|
||||
previousCursor: listing.hasPrevious ? listing.previous : false,
|
||||
nextCursor: listing.hasNext ? listing.next : false,
|
||||
results: (listing.results || []).map(domainData => ({
|
||||
page: listingWrapper.page,
|
||||
previousCursor: listingWrapper.previousCursor,
|
||||
nextCursor: listingWrapper.nextCursor,
|
||||
results: (listingWrapper.listing.results || []).map(domainData => ({
|
||||
id: domainData._id.toString(),
|
||||
alias: domainData.alias,
|
||||
domain: domainData.domain
|
||||
|
|
|
@ -3,16 +3,16 @@
|
|||
const log = require('npmlog');
|
||||
const Joi = require('joi');
|
||||
const ObjectId = require('mongodb').ObjectId;
|
||||
const MongoPaging = require('mongo-cursor-pagination');
|
||||
const urllib = require('url');
|
||||
const tools = require('../tools');
|
||||
const roles = require('../roles');
|
||||
const { nextPageCursorSchema, previousPageCursorSchema, pageNrSchema, sessSchema, sessIPSchema, booleanSchema, metaDataSchema } = require('../schemas');
|
||||
const { nextPageCursorSchema, previousPageCursorSchema, sessSchema, sessIPSchema, booleanSchema, metaDataSchema } = require('../schemas');
|
||||
const { publish, FILTER_DELETED, FILTER_CREATED, FORWARD_ADDED } = require('../events');
|
||||
const { successRes, totalRes, previousCursorRes, nextCursorRes } = require('../schemas/response/general-schemas');
|
||||
const { GetAllFiltersResult, GetFiltersResult } = require('../schemas/response/filters-schemas');
|
||||
const { FilterQuery, FilterAction } = require('../schemas/request/filters-schemas');
|
||||
const { userId, filterId } = require('../schemas/request/general-schemas');
|
||||
const { mongopagingFindWrapper } = require('../mongopaging-find-wrapper');
|
||||
|
||||
module.exports = (db, server, userHandler, settingsHandler) => {
|
||||
server.get(
|
||||
|
@ -29,7 +29,6 @@ module.exports = (db, server, userHandler, settingsHandler) => {
|
|||
limit: Joi.number().default(20).min(1).max(250).description('How many records to return'),
|
||||
next: nextPageCursorSchema,
|
||||
previous: previousPageCursorSchema,
|
||||
page: pageNrSchema,
|
||||
sess: sessSchema,
|
||||
ip: sessIPSchema
|
||||
},
|
||||
|
@ -90,7 +89,6 @@ module.exports = (db, server, userHandler, settingsHandler) => {
|
|||
|
||||
let forward = result.value.forward;
|
||||
let limit = result.value.limit;
|
||||
let page = result.value.page;
|
||||
let pageNext = result.value.next;
|
||||
let pagePrevious = result.value.previous;
|
||||
|
||||
|
@ -119,13 +117,14 @@ module.exports = (db, server, userHandler, settingsHandler) => {
|
|||
|
||||
if (pageNext) {
|
||||
opts.next = pageNext;
|
||||
} else if ((!page || page > 1) && pagePrevious) {
|
||||
}
|
||||
if (pagePrevious) {
|
||||
opts.previous = pagePrevious;
|
||||
}
|
||||
|
||||
let listing;
|
||||
let listingWrapper;
|
||||
try {
|
||||
listing = await MongoPaging.find(db.database.collection('filters'), opts);
|
||||
listingWrapper = await mongopagingFindWrapper(db.database.collection('filters'), opts);
|
||||
} catch (err) {
|
||||
res.status(500);
|
||||
return res.json({
|
||||
|
@ -134,13 +133,9 @@ module.exports = (db, server, userHandler, settingsHandler) => {
|
|||
});
|
||||
}
|
||||
|
||||
if (!listing.hasPrevious) {
|
||||
page = 1;
|
||||
}
|
||||
|
||||
let mailboxList = Array.from(
|
||||
new Set(
|
||||
(listing.results || [])
|
||||
(listingWrapper.listing.results || [])
|
||||
.map(filterData => {
|
||||
if (filterData.action && filterData.action.mailbox) {
|
||||
return filterData.action.mailbox.toString();
|
||||
|
@ -174,10 +169,10 @@ module.exports = (db, server, userHandler, settingsHandler) => {
|
|||
let response = {
|
||||
success: true,
|
||||
total,
|
||||
page,
|
||||
previousCursor: listing.hasPrevious ? listing.previous : false,
|
||||
nextCursor: listing.hasNext ? listing.next : false,
|
||||
results: (listing.results || []).map(filterData => {
|
||||
page: listingWrapper.page,
|
||||
previousCursor: listingWrapper.previousCursor,
|
||||
nextCursor: listingWrapper.nextCursor,
|
||||
results: (listingWrapper.listing.results || []).map(filterData => {
|
||||
let descriptions = getFilterStrings(filterData, mailboxes);
|
||||
|
||||
let values = {
|
||||
|
|
|
@ -4,7 +4,6 @@ const config = require('wild-config');
|
|||
const log = require('npmlog');
|
||||
const libmime = require('libmime');
|
||||
const Joi = require('joi');
|
||||
const MongoPaging = require('mongo-cursor-pagination');
|
||||
const addressparser = require('nodemailer/lib/addressparser');
|
||||
const MailComposer = require('nodemailer/lib/mail-composer');
|
||||
const { htmlToText } = require('html-to-text');
|
||||
|
@ -17,7 +16,7 @@ const forward = require('../forward');
|
|||
const Maildropper = require('../maildropper');
|
||||
const util = require('util');
|
||||
const roles = require('../roles');
|
||||
const { nextPageCursorSchema, previousPageCursorSchema, pageNrSchema, sessSchema, sessIPSchema, booleanSchema, metaDataSchema } = require('../schemas');
|
||||
const { nextPageCursorSchema, previousPageCursorSchema, sessSchema, sessIPSchema, booleanSchema, metaDataSchema } = require('../schemas');
|
||||
const { preprocessAttachments } = require('../data-url');
|
||||
const TaskHandler = require('../task-handler');
|
||||
const { prepareSearchFilter, uidRangeStringToQuery } = require('../prepare-search-filter');
|
||||
|
@ -38,6 +37,7 @@ const {
|
|||
const { userId, mailboxId, messageId } = require('../schemas/request/general-schemas');
|
||||
const { MsgEnvelope } = require('../schemas/response/messages-schemas');
|
||||
const { successRes } = require('../schemas/response/general-schemas');
|
||||
const { mongopagingFindWrapper } = require('../mongopaging-find-wrapper');
|
||||
|
||||
module.exports = (db, server, messageHandler, userHandler, storageHandler, settingsHandler) => {
|
||||
let maildrop = new Maildropper({
|
||||
|
@ -330,7 +330,6 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
|
|||
order: Joi.any().empty('').allow('asc', 'desc').default('desc').description('Ordering of the records by insert date'),
|
||||
next: nextPageCursorSchema,
|
||||
previous: previousPageCursorSchema,
|
||||
page: pageNrSchema,
|
||||
sess: sessSchema,
|
||||
ip: sessIPSchema,
|
||||
includeHeaders: Joi.alternatives()
|
||||
|
@ -466,7 +465,6 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
|
|||
let mailbox = new ObjectId(result.value.mailbox);
|
||||
let limit = result.value.limit;
|
||||
let threadCounters = result.value.threadCounters;
|
||||
let page = result.value.page;
|
||||
let pageNext = result.value.next;
|
||||
let pagePrevious = result.value.previous;
|
||||
let sortAscending = result.value.order === 'asc';
|
||||
|
@ -569,13 +567,14 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
|
|||
|
||||
if (pageNext) {
|
||||
opts.next = pageNext;
|
||||
} else if ((!page || page > 1) && pagePrevious) {
|
||||
}
|
||||
if (pagePrevious) {
|
||||
opts.previous = pagePrevious;
|
||||
}
|
||||
|
||||
let listing;
|
||||
let listingWrapper;
|
||||
try {
|
||||
listing = await MongoPaging.find(db.database.collection('messages'), opts);
|
||||
listingWrapper = await mongopagingFindWrapper(db.database.collection('messages'), opts);
|
||||
} catch (err) {
|
||||
res.status(500);
|
||||
return res.json({
|
||||
|
@ -584,24 +583,20 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
|
|||
});
|
||||
}
|
||||
|
||||
if (!listing.hasPrevious) {
|
||||
page = 1;
|
||||
}
|
||||
|
||||
if (threadCounters) {
|
||||
listing.results = await addThreadCountersToMessageList(user, listing.results);
|
||||
listingWrapper.listing.results = await addThreadCountersToMessageList(user, listingWrapper.listing.results);
|
||||
}
|
||||
|
||||
await applyBimiToListing(listing.results);
|
||||
await applyBimiToListing(listingWrapper.listing.results);
|
||||
|
||||
let response = {
|
||||
success: true,
|
||||
total,
|
||||
page,
|
||||
previousCursor: listing.hasPrevious ? listing.previous : false,
|
||||
nextCursor: listing.hasNext ? listing.next : false,
|
||||
page: listingWrapper.page,
|
||||
previousCursor: listingWrapper.previousCursor,
|
||||
nextCursor: listingWrapper.nextCursor,
|
||||
specialUse: mailboxData.specialUse,
|
||||
results: (listing.results || []).map(entry => formatMessageListing(entry, includeHeaders))
|
||||
results: (listingWrapper.listing.results || []).map(entry => formatMessageListing(entry, includeHeaders))
|
||||
};
|
||||
|
||||
return res.json(response);
|
||||
|
@ -681,8 +676,7 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
|
|||
.example('List-ID, MIME-Version')
|
||||
.description('Comma separated list of header keys to include in the response'),
|
||||
next: nextPageCursorSchema,
|
||||
previous: previousPageCursorSchema,
|
||||
page: pageNrSchema
|
||||
previous: previousPageCursorSchema
|
||||
}
|
||||
},
|
||||
pathParams: { user: userId },
|
||||
|
@ -792,7 +786,6 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
|
|||
let user = new ObjectId(result.value.user);
|
||||
let threadCounters = result.value.threadCounters;
|
||||
let limit = result.value.limit;
|
||||
let page = result.value.page;
|
||||
let pageNext = result.value.next;
|
||||
let pagePrevious = result.value.previous;
|
||||
let order = result.value.order;
|
||||
|
@ -890,13 +883,14 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
|
|||
|
||||
if (pageNext) {
|
||||
opts.next = pageNext;
|
||||
} else if ((!page || page > 1) && pagePrevious) {
|
||||
}
|
||||
if (pagePrevious) {
|
||||
opts.previous = pagePrevious;
|
||||
}
|
||||
|
||||
let listing;
|
||||
let listingWrapper;
|
||||
try {
|
||||
listing = await MongoPaging.find(db.database.collection('messages'), opts);
|
||||
listingWrapper = await mongopagingFindWrapper(db.database.collection('messages'), opts);
|
||||
} catch (err) {
|
||||
res.status(500);
|
||||
return res.json({
|
||||
|
@ -905,24 +899,20 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
|
|||
});
|
||||
}
|
||||
|
||||
if (!listing.hasPrevious) {
|
||||
page = 1;
|
||||
}
|
||||
|
||||
if (threadCounters) {
|
||||
listing.results = await addThreadCountersToMessageList(user, listing.results);
|
||||
listingWrapper.listing.results = await addThreadCountersToMessageList(user, listingWrapper.listing.results);
|
||||
}
|
||||
|
||||
await applyBimiToListing(listing.results);
|
||||
await applyBimiToListing(listingWrapper.listing.results);
|
||||
|
||||
let response = {
|
||||
success: true,
|
||||
query,
|
||||
total,
|
||||
page,
|
||||
previousCursor: listing.hasPrevious ? listing.previous : false,
|
||||
nextCursor: listing.hasNext ? listing.next : false,
|
||||
results: (listing.results || []).map(entry => formatMessageListing(entry, includeHeaders))
|
||||
page: listingWrapper.page,
|
||||
previousCursor: listingWrapper.previousCursor,
|
||||
nextCursor: listingWrapper.nextCursor,
|
||||
results: (listingWrapper.listing.results || []).map(entry => formatMessageListing(entry, includeHeaders))
|
||||
};
|
||||
|
||||
return res.json(response);
|
||||
|
@ -3251,7 +3241,6 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
|
|||
.empty('')
|
||||
.example('List-ID, MIME-Version')
|
||||
.description('Comma separated list of header keys to include in the response'),
|
||||
page: pageNrSchema,
|
||||
sess: sessSchema,
|
||||
ip: sessIPSchema
|
||||
},
|
||||
|
@ -3360,7 +3349,6 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
|
|||
|
||||
let user = new ObjectId(result.value.user);
|
||||
let limit = result.value.limit;
|
||||
let page = result.value.page;
|
||||
let pageNext = result.value.next;
|
||||
let pagePrevious = result.value.previous;
|
||||
let sortAscending = result.value.order === 'asc';
|
||||
|
@ -3421,13 +3409,14 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
|
|||
|
||||
if (pageNext) {
|
||||
opts.next = pageNext;
|
||||
} else if ((!page || page > 1) && pagePrevious) {
|
||||
}
|
||||
if (pagePrevious) {
|
||||
opts.previous = pagePrevious;
|
||||
}
|
||||
|
||||
let listing;
|
||||
let listingWrapper;
|
||||
try {
|
||||
listing = await MongoPaging.find(db.database.collection('archived'), opts);
|
||||
listingWrapper = await mongopagingFindWrapper(db.database.collection('archived'), opts);
|
||||
} catch (err) {
|
||||
res.status(500);
|
||||
return res.json({
|
||||
|
@ -3436,17 +3425,13 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
|
|||
});
|
||||
}
|
||||
|
||||
if (!listing.hasPrevious) {
|
||||
page = 1;
|
||||
}
|
||||
|
||||
let response = {
|
||||
success: true,
|
||||
total,
|
||||
page,
|
||||
previousCursor: listing.hasPrevious ? listing.previous : false,
|
||||
nextCursor: listing.hasNext ? listing.next : false,
|
||||
results: (listing.results || [])
|
||||
page: listingWrapper.page,
|
||||
previousCursor: listingWrapper.previousCursor,
|
||||
nextCursor: listingWrapper.nextCursor,
|
||||
results: (listingWrapper.listing.results || [])
|
||||
.map(m => {
|
||||
// prepare message for output
|
||||
m.uid = m._id;
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
'use strict';
|
||||
|
||||
const Joi = require('joi');
|
||||
const MongoPaging = require('mongo-cursor-pagination');
|
||||
const ObjectId = require('mongodb').ObjectId;
|
||||
const tools = require('../tools');
|
||||
const roles = require('../roles');
|
||||
const consts = require('../consts');
|
||||
const { nextPageCursorSchema, previousPageCursorSchema, pageNrSchema, sessSchema, sessIPSchema } = require('../schemas');
|
||||
const { nextPageCursorSchema, previousPageCursorSchema, sessSchema, sessIPSchema } = require('../schemas');
|
||||
const { userId } = require('../schemas/request/general-schemas');
|
||||
const { successRes, totalRes, pageRes, previousCursorRes, nextCursorRes } = require('../schemas/response/general-schemas');
|
||||
const { mongopagingFindWrapper } = require('../mongopaging-find-wrapper');
|
||||
|
||||
module.exports = (db, server, storageHandler) => {
|
||||
server.post(
|
||||
|
@ -137,7 +137,6 @@ module.exports = (db, server, storageHandler) => {
|
|||
limit: Joi.number().default(20).min(1).max(250).description('How many records to return'),
|
||||
next: nextPageCursorSchema,
|
||||
previous: previousPageCursorSchema,
|
||||
page: pageNrSchema,
|
||||
sess: sessSchema,
|
||||
ip: sessIPSchema
|
||||
},
|
||||
|
@ -238,7 +237,6 @@ module.exports = (db, server, storageHandler) => {
|
|||
|
||||
let query = result.value.query;
|
||||
let limit = result.value.limit;
|
||||
let page = result.value.page;
|
||||
let pageNext = result.value.next;
|
||||
let pagePrevious = result.value.previous;
|
||||
|
||||
|
@ -263,13 +261,14 @@ module.exports = (db, server, storageHandler) => {
|
|||
|
||||
if (pageNext) {
|
||||
opts.next = pageNext;
|
||||
} else if ((!page || page > 1) && pagePrevious) {
|
||||
}
|
||||
if (pagePrevious) {
|
||||
opts.previous = pagePrevious;
|
||||
}
|
||||
|
||||
let listing;
|
||||
let listingWrapper;
|
||||
try {
|
||||
listing = await MongoPaging.find(db.gridfs.collection('storage.files'), opts);
|
||||
listingWrapper = await mongopagingFindWrapper(db.gridfs.collection('storage.files'), opts);
|
||||
} catch (err) {
|
||||
res.status(500);
|
||||
return res.json({
|
||||
|
@ -278,18 +277,14 @@ module.exports = (db, server, storageHandler) => {
|
|||
});
|
||||
}
|
||||
|
||||
if (!listing.hasPrevious) {
|
||||
page = 1;
|
||||
}
|
||||
|
||||
let response = {
|
||||
success: true,
|
||||
query,
|
||||
total,
|
||||
page,
|
||||
previousCursor: listing.hasPrevious ? listing.previous : false,
|
||||
nextCursor: listing.hasNext ? listing.next : false,
|
||||
results: (listing.results || []).map(fileData => ({
|
||||
page: listingWrapper.page,
|
||||
previousCursor: listingWrapper.previousCursor,
|
||||
nextCursor: listingWrapper.nextCursor,
|
||||
results: (listingWrapper.listing.results || []).map(fileData => ({
|
||||
id: fileData._id.toString(),
|
||||
filename: fileData.filename || undefined,
|
||||
contentType: fileData.contentType || undefined,
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
const log = require('npmlog');
|
||||
const config = require('wild-config');
|
||||
const Joi = require('joi');
|
||||
const MongoPaging = require('mongo-cursor-pagination');
|
||||
const ObjectId = require('mongodb').ObjectId;
|
||||
const tools = require('../tools');
|
||||
const errors = require('../errors');
|
||||
|
@ -13,22 +12,14 @@ const consts = require('../consts');
|
|||
const roles = require('../roles');
|
||||
const imapTools = require('../../imap-core/lib/imap-tools');
|
||||
const pwnedpasswords = require('pwnedpasswords');
|
||||
const {
|
||||
nextPageCursorSchema,
|
||||
previousPageCursorSchema,
|
||||
pageNrSchema,
|
||||
sessSchema,
|
||||
sessIPSchema,
|
||||
booleanSchema,
|
||||
metaDataSchema,
|
||||
usernameSchema
|
||||
} = require('../schemas');
|
||||
const { nextPageCursorSchema, previousPageCursorSchema, sessSchema, sessIPSchema, booleanSchema, metaDataSchema, usernameSchema } = require('../schemas');
|
||||
const TaskHandler = require('../task-handler');
|
||||
const { publish, FORWARD_ADDED } = require('../events');
|
||||
const { ExportStream, ImportStream } = require('../export');
|
||||
const { successRes, totalRes, pageRes, previousCursorRes, nextCursorRes, quotaRes } = require('../schemas/response/general-schemas');
|
||||
const { GetUsersResult } = require('../schemas/response/users-schemas');
|
||||
const { userId } = require('../schemas/request/general-schemas');
|
||||
const { mongopagingFindWrapper } = require('../mongopaging-find-wrapper');
|
||||
|
||||
const FEATURE_FLAGS = ['indexing'];
|
||||
|
||||
|
@ -58,7 +49,6 @@ module.exports = (db, server, userHandler, settingsHandler) => {
|
|||
limit: Joi.number().default(20).min(1).max(250).description('How many records to return'),
|
||||
next: nextPageCursorSchema,
|
||||
previous: previousPageCursorSchema,
|
||||
page: pageNrSchema,
|
||||
sess: sessSchema,
|
||||
ip: sessIPSchema
|
||||
},
|
||||
|
@ -120,7 +110,6 @@ module.exports = (db, server, userHandler, settingsHandler) => {
|
|||
let forward = result.value.forward;
|
||||
|
||||
let limit = result.value.limit;
|
||||
let page = result.value.page;
|
||||
let pageNext = result.value.next;
|
||||
let pagePrevious = result.value.previous;
|
||||
|
||||
|
@ -232,13 +221,14 @@ module.exports = (db, server, userHandler, settingsHandler) => {
|
|||
|
||||
if (pageNext) {
|
||||
opts.next = pageNext;
|
||||
} else if ((!page || page > 1) && pagePrevious) {
|
||||
}
|
||||
if (pagePrevious) {
|
||||
opts.previous = pagePrevious;
|
||||
}
|
||||
|
||||
let listing;
|
||||
let listingWrapper;
|
||||
try {
|
||||
listing = await MongoPaging.find(db.users.collection('users'), opts);
|
||||
listingWrapper = await mongopagingFindWrapper(db.users.collection('users'), opts);
|
||||
} catch (err) {
|
||||
res.status(500);
|
||||
return res.json({
|
||||
|
@ -247,20 +237,16 @@ module.exports = (db, server, userHandler, settingsHandler) => {
|
|||
});
|
||||
}
|
||||
|
||||
if (!listing.hasPrevious) {
|
||||
page = 1;
|
||||
}
|
||||
|
||||
let settings = await settingsHandler.getMulti(['const:max:storage']);
|
||||
|
||||
let response = {
|
||||
success: true,
|
||||
query,
|
||||
total,
|
||||
page,
|
||||
previousCursor: listing.hasPrevious ? listing.previous : false,
|
||||
nextCursor: listing.hasNext ? listing.next : false,
|
||||
results: (listing.results || []).map(userData => {
|
||||
page: listingWrapper.page,
|
||||
previousCursor: listingWrapper.previousCursor,
|
||||
nextCursor: listingWrapper.nextCursor,
|
||||
results: (listingWrapper.listing.results || []).map(userData => {
|
||||
let values = {
|
||||
id: userData._id.toString(),
|
||||
username: userData.username,
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
'use strict';
|
||||
|
||||
const Joi = require('joi');
|
||||
const MongoPaging = require('mongo-cursor-pagination');
|
||||
const ObjectId = require('mongodb').ObjectId;
|
||||
const tools = require('../tools');
|
||||
const roles = require('../roles');
|
||||
const { nextPageCursorSchema, previousPageCursorSchema, pageNrSchema, sessSchema, sessIPSchema } = require('../schemas');
|
||||
const { nextPageCursorSchema, previousPageCursorSchema, sessSchema, sessIPSchema } = require('../schemas');
|
||||
const { successRes, totalRes, pageRes, previousCursorRes, nextCursorRes } = require('../schemas/response/general-schemas');
|
||||
const { mongopagingFindWrapper } = require('../mongopaging-find-wrapper');
|
||||
|
||||
module.exports = (db, server) => {
|
||||
server.get(
|
||||
|
@ -27,7 +27,6 @@ module.exports = (db, server) => {
|
|||
limit: Joi.number().default(20).min(1).max(250).description('How many records to return'),
|
||||
next: nextPageCursorSchema,
|
||||
previous: previousPageCursorSchema,
|
||||
page: pageNrSchema,
|
||||
sess: sessSchema,
|
||||
ip: sessIPSchema
|
||||
},
|
||||
|
@ -110,7 +109,6 @@ module.exports = (db, server) => {
|
|||
}
|
||||
|
||||
let limit = result.value.limit;
|
||||
let page = result.value.page;
|
||||
let pageNext = result.value.next;
|
||||
let pagePrevious = result.value.previous;
|
||||
|
||||
|
@ -137,13 +135,14 @@ module.exports = (db, server) => {
|
|||
|
||||
if (pageNext) {
|
||||
opts.next = pageNext;
|
||||
} else if ((!page || page > 1) && pagePrevious) {
|
||||
}
|
||||
if (pagePrevious) {
|
||||
opts.previous = pagePrevious;
|
||||
}
|
||||
|
||||
let listing;
|
||||
let listingWrapper;
|
||||
try {
|
||||
listing = await MongoPaging.find(db.users.collection('webhooks'), opts);
|
||||
listingWrapper = await mongopagingFindWrapper(db.users.collection('webhooks'), opts);
|
||||
} catch (err) {
|
||||
res.status(500);
|
||||
return res.json({
|
||||
|
@ -152,19 +151,15 @@ module.exports = (db, server) => {
|
|||
});
|
||||
}
|
||||
|
||||
if (!listing.hasPrevious) {
|
||||
page = 1;
|
||||
}
|
||||
|
||||
let response = {
|
||||
success: true,
|
||||
type: result.value.type,
|
||||
user,
|
||||
total,
|
||||
page,
|
||||
previousCursor: listing.hasPrevious ? listing.previous : false,
|
||||
nextCursor: listing.hasNext ? listing.next : false,
|
||||
results: (listing.results || []).map(webhookData => {
|
||||
page: listingWrapper.page,
|
||||
previousCursor: listingWrapper.previousCursor,
|
||||
nextCursor: listingWrapper.nextCursor,
|
||||
results: (listingWrapper.listing.results || []).map(webhookData => {
|
||||
let values = {
|
||||
id: webhookData._id.toString(),
|
||||
type: webhookData.type,
|
||||
|
|
111
lib/mongopaging-find-wrapper.js
Normal file
111
lib/mongopaging-find-wrapper.js
Normal file
|
@ -0,0 +1,111 @@
|
|||
'use strict';
|
||||
|
||||
const MongoPaging = require('mongo-cursor-pagination');
|
||||
const { EJSON } = require('bson');
|
||||
|
||||
const mongopagingFindWrapper = async (collection, opts) => {
|
||||
let currentPage = 1;
|
||||
|
||||
const pageNextOriginalCursor = getCursorFromCursorWrapper(opts.next);
|
||||
const pagePrevOriginalCursor = getCursorFromCursorWrapper(opts.previous);
|
||||
|
||||
if (pageNextOriginalCursor) {
|
||||
// Have next cursor
|
||||
const pageFromNextCursor = getPageFromMongopagingCursor(opts.next);
|
||||
opts.next = pageNextOriginalCursor; // For mongopaging only preserve the original inner cursor
|
||||
currentPage = pageFromNextCursor;
|
||||
} else if (pagePrevOriginalCursor) {
|
||||
// Have prev cursor
|
||||
delete opts.next; // Previous cursor overwrites next
|
||||
const pageFromPreviousCursor = getPageFromMongopagingCursor(opts.previous);
|
||||
opts.previous = pagePrevOriginalCursor; // For mongopaging only preserve the original inner cursor
|
||||
currentPage = pageFromPreviousCursor;
|
||||
}
|
||||
|
||||
const listing = await MongoPaging.find(collection, opts);
|
||||
|
||||
if (!listing.hasPrevious) {
|
||||
currentPage = 1;
|
||||
} else if (currentPage < 1) {
|
||||
// Against crafted cursors with negative pages
|
||||
currentPage = 1;
|
||||
}
|
||||
|
||||
let nextCursor = listing.hasNext ? listing.next : false;
|
||||
|
||||
if (nextCursor) {
|
||||
nextCursor = setPageToMongopagingCursor(nextCursor, currentPage + 1);
|
||||
}
|
||||
|
||||
let previousCursor = listing.hasPrevious ? listing.previous : false;
|
||||
|
||||
if (previousCursor) {
|
||||
previousCursor = setPageToMongopagingCursor(previousCursor, currentPage - 1 < 0 ? 0 : currentPage - 1);
|
||||
}
|
||||
|
||||
return {
|
||||
listing,
|
||||
nextCursor,
|
||||
previousCursor,
|
||||
page: currentPage
|
||||
};
|
||||
};
|
||||
|
||||
function getPageFromMongopagingCursor(cursorString) {
|
||||
if (!cursorString) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
try {
|
||||
const cursorObjStr = EJSON.deserialize(Buffer.from(cursorString, 'base64url').toString());
|
||||
|
||||
const cursorWrapperArr = EJSON.parse(cursorObjStr);
|
||||
|
||||
if (cursorWrapperArr.length >= 2) {
|
||||
return cursorWrapperArr[1];
|
||||
}
|
||||
|
||||
return 1; // Fallback
|
||||
} catch {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
function setPageToMongopagingCursor(cursorString, page) {
|
||||
if (!cursorString) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const cursorWrapperArr = [];
|
||||
cursorWrapperArr.push(Buffer.from(cursorString, 'base64url').toString()); // Preserve original string. Decode base64url to not double base64url encode
|
||||
cursorWrapperArr.push(page);
|
||||
|
||||
const newCursorString = Buffer.from(EJSON.stringify(EJSON.serialize(cursorWrapperArr))).toString('base64url');
|
||||
return newCursorString;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function getCursorFromCursorWrapper(cursorWrapperString) {
|
||||
if (!cursorWrapperString) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const cursorObjStr = EJSON.deserialize(Buffer.from(cursorWrapperString, 'base64url').toString());
|
||||
|
||||
const cursorWrapperArr = EJSON.parse(cursorObjStr);
|
||||
return Buffer.from(cursorWrapperArr[0]).toString('base64url');
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
mongopagingFindWrapper,
|
||||
getPageFromMongopagingCursor,
|
||||
setPageToMongopagingCursor,
|
||||
getCursorFromCursorWrapper
|
||||
};
|
|
@ -41,7 +41,7 @@ const mongoCursorValidator = () => (value, helpers) => {
|
|||
return helpers.error('any.invalid');
|
||||
}
|
||||
try {
|
||||
EJSON.parse(Buffer.from(value, 'base64'));
|
||||
EJSON.parse(Buffer.from(value, 'base64url'));
|
||||
} catch (E) {
|
||||
return helpers.error('any.invalid');
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue