mirror of
https://github.com/nodemailer/wildduck.git
synced 2024-12-26 18:01:01 +08:00
v1.10.14
This commit is contained in:
parent
95a7bb3b07
commit
b8af48c13f
8 changed files with 210 additions and 27 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1 +1 @@
|
|||
define({
"name": "wildduck",
"version": "1.0.0",
"description": "WildDuck API docs",
"title": "WildDuck API",
"url": "https://api.wildduck.email",
"sampleUrl": false,
"defaultVersion": "0.0.0",
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2018-11-14T11:21:41.763Z",
"url": "http://apidocjs.com",
"version": "0.17.6"
}
});
|
||||
define({
"name": "wildduck",
"version": "1.0.0",
"description": "WildDuck API docs",
"title": "WildDuck API",
"url": "https://api.wildduck.email",
"sampleUrl": false,
"defaultVersion": "0.0.0",
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2018-11-23T18:50:49.585Z",
"url": "http://apidocjs.com",
"version": "0.17.6"
}
});
|
||||
|
|
|
@ -1 +1 @@
|
|||
{
"name": "wildduck",
"version": "1.0.0",
"description": "WildDuck API docs",
"title": "WildDuck API",
"url": "https://api.wildduck.email",
"sampleUrl": false,
"defaultVersion": "0.0.0",
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2018-11-14T11:21:41.763Z",
"url": "http://apidocjs.com",
"version": "0.17.6"
}
}
|
||||
{
"name": "wildduck",
"version": "1.0.0",
"description": "WildDuck API docs",
"title": "WildDuck API",
"url": "https://api.wildduck.email",
"sampleUrl": false,
"defaultVersion": "0.0.0",
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2018-11-23T18:50:49.585Z",
"url": "http://apidocjs.com",
"version": "0.17.6"
}
}
|
||||
|
|
116
lib/api/users.js
116
lib/api/users.js
|
@ -1565,7 +1565,6 @@ module.exports = (db, server, userHandler) => {
|
|||
* }
|
||||
*
|
||||
* @apiParam {String} id Users unique ID.
|
||||
* @apiParam {String} [reason] Message to be shown to connected IMAP client
|
||||
*
|
||||
* @apiSuccess {Boolean} success Indicates successful response
|
||||
* @apiSuccess {Number} storageUsed Calculated quota usage for the user
|
||||
|
@ -1664,30 +1663,23 @@ module.exports = (db, server, userHandler) => {
|
|||
// NB! Scattered query
|
||||
storageData = await db.database
|
||||
.collection('messages')
|
||||
.aggregate(
|
||||
[
|
||||
{
|
||||
$match: {
|
||||
user
|
||||
}
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: {
|
||||
user: '$user'
|
||||
},
|
||||
storageUsed: {
|
||||
$sum: '$size'
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
.aggregate([
|
||||
{
|
||||
cursor: {
|
||||
batchSize: 1
|
||||
$match: {
|
||||
user
|
||||
}
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: {
|
||||
user: '$user'
|
||||
},
|
||||
storageUsed: {
|
||||
$sum: '$size'
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
])
|
||||
.toArray();
|
||||
} catch (err) {
|
||||
res.json({
|
||||
|
@ -1742,6 +1734,86 @@ module.exports = (db, server, userHandler) => {
|
|||
})
|
||||
);
|
||||
|
||||
/**
|
||||
* @api {post} /quota/reset Recalculate Quota for all Users
|
||||
* @apiName PostUserQuotaAll
|
||||
* @apiGroup Users
|
||||
* @apiDescription This method recalculates quota usage for all Users. Normally not needed, only use it if quota numbers are way off.
|
||||
* This method is not transactional, so if the user is currently receiving new messages then the resulting value is not exact.
|
||||
* @apiHeader {String} X-Access-Token Optional access token if authentication is enabled
|
||||
* @apiHeaderExample {json} Header-Example:
|
||||
* {
|
||||
* "X-Access-Token": "59fc66a03e54454869460e45"
|
||||
* }
|
||||
*
|
||||
* @apiParam {String} id Users unique ID.
|
||||
*
|
||||
* @apiSuccess {Boolean} success Indicates successful response
|
||||
*
|
||||
* @apiError error Description of the error
|
||||
*
|
||||
* @apiExample {curl} Example usage:
|
||||
* curl -i -XPOST http://localhost:8080/quota/reset \
|
||||
* -H 'Content-type: application/json' \
|
||||
* -d '{}'
|
||||
*
|
||||
* @apiSuccessExample {json} Success-Response:
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "success": true
|
||||
* }
|
||||
*
|
||||
* @apiErrorExample {json} Error-Response:
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* "error": "Failed to process request"
|
||||
* }
|
||||
*/
|
||||
server.post(
|
||||
'/quota/reset',
|
||||
tools.asyncifyJson(async (req, res, next) => {
|
||||
res.charSet('utf-8');
|
||||
|
||||
const schema = Joi.object().keys({
|
||||
sess: Joi.string().max(255),
|
||||
ip: Joi.string().ip({
|
||||
version: ['ipv4', 'ipv6'],
|
||||
cidr: 'forbidden'
|
||||
})
|
||||
});
|
||||
|
||||
const result = Joi.validate(req.params, schema, {
|
||||
abortEarly: false,
|
||||
convert: true
|
||||
});
|
||||
|
||||
if (result.error) {
|
||||
res.json({
|
||||
error: result.error.message,
|
||||
code: 'InputValidationError'
|
||||
});
|
||||
return next();
|
||||
}
|
||||
|
||||
// permissions check
|
||||
req.validate(roles.can(req.role).updateAny('users'));
|
||||
|
||||
let now = new Date();
|
||||
await db.database.collection('tasks').insertOne({
|
||||
task: 'quota',
|
||||
locked: false,
|
||||
lockedUntil: now,
|
||||
created: now,
|
||||
status: 'queued'
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true
|
||||
});
|
||||
return next();
|
||||
})
|
||||
);
|
||||
|
||||
/**
|
||||
* @api {post} /users/:id/password/reset Reset password for an User
|
||||
* @apiName ResetUserPassword
|
||||
|
|
93
lib/tasks/quota.js
Normal file
93
lib/tasks/quota.js
Normal file
|
@ -0,0 +1,93 @@
|
|||
'use strict';
|
||||
|
||||
const log = require('npmlog');
|
||||
const db = require('../db');
|
||||
|
||||
module.exports = (taskData, options, callback) => {
|
||||
let cursor = db.users
|
||||
.collection('users')
|
||||
.find({})
|
||||
.project({ _id: true, storageUsed: true });
|
||||
|
||||
let processNext = () => {
|
||||
cursor.next((err, userData) => {
|
||||
if (err) {
|
||||
log.error('Tasks', 'task=quota id=%s error=%s', taskData._id, err.message);
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
if (!userData) {
|
||||
return cursor.close(() => callback(null, true));
|
||||
}
|
||||
|
||||
db.database
|
||||
.collection('messages')
|
||||
.aggregate([
|
||||
{
|
||||
$match: {
|
||||
user: userData._id
|
||||
}
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: {
|
||||
user: '$user'
|
||||
},
|
||||
storageUsed: {
|
||||
$sum: '$size'
|
||||
}
|
||||
}
|
||||
}
|
||||
])
|
||||
.toArray((err, storageData) => {
|
||||
if (err) {
|
||||
log.error('Tasks', 'task=quota id=%s user=%s error=%s', taskData._id, userData._id, err.message);
|
||||
return setTimeout(processNext, 5000);
|
||||
}
|
||||
|
||||
let storageUsed = (storageData && storageData[0] && storageData[0].storageUsed) || 0;
|
||||
if (storageUsed === userData.storageUsed) {
|
||||
log.info(
|
||||
'Tasks',
|
||||
'task=quota id=%s user=%s stored=%s calculated=%s updated=%s',
|
||||
taskData._id,
|
||||
userData._id,
|
||||
userData.storageUsed,
|
||||
storageUsed,
|
||||
'no'
|
||||
);
|
||||
return setImmediate(processNext);
|
||||
}
|
||||
|
||||
db.users.collection('users').updateOne(
|
||||
{
|
||||
_id: userData._id
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
storageUsed: Number(storageUsed) || 0
|
||||
}
|
||||
},
|
||||
(err, r) => {
|
||||
if (err) {
|
||||
log.error('Tasks', 'task=quota id=%s user=%s error=%s', taskData._id, userData._id, err.message);
|
||||
return setTimeout(processNext, 5000);
|
||||
}
|
||||
log.info(
|
||||
'Tasks',
|
||||
'task=quota id=%s user=%s stored=%s calculated=%s updated=%s',
|
||||
taskData._id,
|
||||
userData._id,
|
||||
userData.storageUsed,
|
||||
storageUsed,
|
||||
r.modifiedCount ? 'yes' : 'no'
|
||||
);
|
||||
return setImmediate(processNext);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
processNext();
|
||||
};
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "wildduck",
|
||||
"version": "1.10.13",
|
||||
"version": "1.10.14",
|
||||
"description": "IMAP/POP3 server built with Node.js and MongoDB",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
|
|
18
tasks.js
18
tasks.js
|
@ -11,6 +11,8 @@ const MessageHandler = require('./lib/message-handler');
|
|||
const setupIndexes = yaml.safeLoad(fs.readFileSync(__dirname + '/indexes.yaml', 'utf8'));
|
||||
|
||||
const taskRestore = require('./lib/tasks/restore');
|
||||
const taskUserDelete = require('./lib/tasks/user-delete');
|
||||
const taskQuota = require('./lib/tasks/quota');
|
||||
|
||||
let messageHandler;
|
||||
let gcTimeout;
|
||||
|
@ -462,6 +464,22 @@ function processTask(taskData, callback) {
|
|||
callback(null, true);
|
||||
}
|
||||
);
|
||||
case 'user-delete':
|
||||
return taskUserDelete(taskData, {}, err => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
// release
|
||||
callback(null, true);
|
||||
});
|
||||
case 'quota':
|
||||
return taskQuota(taskData, {}, err => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
// release
|
||||
callback(null, true);
|
||||
});
|
||||
default:
|
||||
// release task by returning true
|
||||
return callback(null, true);
|
||||
|
|
Loading…
Reference in a new issue