mirror of
https://github.com/nodemailer/wildduck.git
synced 2024-09-20 07:16:05 +08:00
added new API endpoint to get info about deleted users
This commit is contained in:
parent
c50c5097f0
commit
5536bc0f93
|
@ -1,6 +1,6 @@
|
|||
# If enabled then WildDuck exposes a LMTP interface for pushing messages to mail store
|
||||
# NB! If you are using WildDuck plugin for Haraka then LMTP is not needed
|
||||
enabled=true
|
||||
enabled=false
|
||||
port=2424
|
||||
|
||||
# by default bind to localhost only
|
||||
|
|
|
@ -2350,6 +2350,29 @@ paths:
|
|||
type: string
|
||||
|
||||
'/users/{id}/restore':
|
||||
get:
|
||||
tags:
|
||||
- Users
|
||||
summary: Return recovery info for a deleted user
|
||||
operationId: restoreUserInfo
|
||||
parameters:
|
||||
- name: sess
|
||||
in: query
|
||||
description: Session identifier for the logs
|
||||
schema:
|
||||
type: string
|
||||
- name: ip
|
||||
in: query
|
||||
description: IP address for the logs
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: Success
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RecoverInfoResponse'
|
||||
post:
|
||||
tags:
|
||||
- Users
|
||||
|
@ -4109,6 +4132,50 @@ components:
|
|||
description: Unique ID (24 byte hex)
|
||||
example: '609d201236d1d936948f23b1'
|
||||
|
||||
RecoverInfoResponse:
|
||||
type: object
|
||||
required:
|
||||
- success
|
||||
- user
|
||||
- username
|
||||
- storageUsed
|
||||
- tags
|
||||
- deleted
|
||||
- recoverableAddresses
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
description: Indicates successful response
|
||||
example: true
|
||||
user:
|
||||
type: string
|
||||
description: ID of the deleted User
|
||||
example: '609d201236d1d936948f23b1'
|
||||
username:
|
||||
type: string
|
||||
description: Username of the User
|
||||
example: andris
|
||||
storageUsed:
|
||||
type: number
|
||||
description: Calculated quota usage for the user
|
||||
example: 2423070
|
||||
tags:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: List of tags associated with the User
|
||||
example: ['domain:andrisreinman.com']
|
||||
deleted:
|
||||
type: string
|
||||
description: Datestring of the time the user was deleted
|
||||
format: date-time
|
||||
recoverableAddresses:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: List of email addresses that can be restored
|
||||
example: ['andris@andrisreinman.com']
|
||||
|
||||
GetAllowedDomainResponse:
|
||||
required:
|
||||
- success
|
||||
|
|
|
@ -192,7 +192,6 @@ const acquireCert = async (domain, acmeOptions, certificateData, certHandler) =>
|
|||
}
|
||||
|
||||
let lock = await getLock(domainOpLockKey, 10 * 60 * 1000, 3 * 60 * 1000);
|
||||
console.log(lock);
|
||||
if (!lock.success) {
|
||||
return certificateData;
|
||||
}
|
||||
|
|
|
@ -1401,6 +1401,66 @@ module.exports = (db, server, userHandler) => {
|
|||
})
|
||||
);
|
||||
|
||||
server.get(
|
||||
'/users/:user/restore',
|
||||
tools.asyncifyJson(async (req, res, next) => {
|
||||
res.charSet('utf-8');
|
||||
|
||||
const schema = Joi.object().keys({
|
||||
user: Joi.string().hex().lowercase().length(24).required(),
|
||||
sess: sessSchema,
|
||||
ip: sessIPSchema
|
||||
});
|
||||
|
||||
const result = schema.validate(req.params, {
|
||||
abortEarly: false,
|
||||
convert: true
|
||||
});
|
||||
|
||||
if (result.error) {
|
||||
res.status(400);
|
||||
res.json({
|
||||
error: result.error.message,
|
||||
code: 'InputValidationError',
|
||||
details: tools.validationErrors(result)
|
||||
});
|
||||
return next();
|
||||
}
|
||||
|
||||
// permissions check
|
||||
if (req.user && req.user === result.value.user) {
|
||||
req.validate(roles.can(req.role).readOwn('users'));
|
||||
} else {
|
||||
req.validate(roles.can(req.role).readAny('users'));
|
||||
}
|
||||
|
||||
let user = new ObjectID(result.value.user);
|
||||
|
||||
let userInfo;
|
||||
try {
|
||||
userInfo = await userHandler.restoreInfo(user);
|
||||
} catch (err) {
|
||||
res.status(err.responseCode || 500); // TODO: use response code specific status
|
||||
res.json({
|
||||
error: err.message,
|
||||
code: err.code
|
||||
});
|
||||
return next();
|
||||
}
|
||||
|
||||
res.json(
|
||||
Object.assign(
|
||||
{
|
||||
success: !!userInfo
|
||||
},
|
||||
userInfo
|
||||
)
|
||||
);
|
||||
|
||||
return next();
|
||||
})
|
||||
);
|
||||
|
||||
server.post(
|
||||
'/users/:user/restore',
|
||||
tools.asyncifyJson(async (req, res, next) => {
|
||||
|
|
|
@ -3551,6 +3551,40 @@ class UserHandler {
|
|||
return result;
|
||||
}
|
||||
|
||||
// Return information about deleted user
|
||||
async restoreInfo(user) {
|
||||
let result = {
|
||||
user: user.toString()
|
||||
};
|
||||
|
||||
let existingAccount = await this.users.collection('deletedusers').findOne({ _id: user });
|
||||
if (!existingAccount) {
|
||||
let err = new Error('Delete account was not found');
|
||||
err.responseCode = 404;
|
||||
err.code = 'AccountNotFound';
|
||||
throw err;
|
||||
}
|
||||
|
||||
result.username = existingAccount.username;
|
||||
result.storageUsed = existingAccount.storageUsed;
|
||||
result.tags = existingAccount.tags;
|
||||
|
||||
result.deleted = existingAccount.deleteInfo.deletedAt.toISOString();
|
||||
|
||||
// Step 2. restore addresses
|
||||
let recoverableAddresses = [];
|
||||
for (let address of existingAccount.deleteInfo.addresses || []) {
|
||||
let existingAddress = await this.users.collection('addresses').findOne(address);
|
||||
if (!existingAddress || existingAddress.user.equals(user)) {
|
||||
recoverableAddresses.push(address.address);
|
||||
}
|
||||
}
|
||||
|
||||
result.recoverableAddresses = recoverableAddresses;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// This method restores a user that is queued for deletion
|
||||
async restore(user, options) {
|
||||
options = options || {};
|
||||
|
|
Loading…
Reference in a new issue