mirror of
https://github.com/nodemailer/wildduck.git
synced 2025-01-01 13:13:53 +08:00
Allow setting tags for addresses
This commit is contained in:
parent
879e25e649
commit
8896e03541
7 changed files with 347 additions and 108 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. Under construction, see old docs here: https://github.com/nodemailer/wildduck/blob/master/docs/api.md",
"title": "WildDuck API",
"url": "http://localhost:8080",
"sampleUrl": false,
"defaultVersion": "0.0.0",
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2018-01-15T11:52:17.862Z",
"url": "http://apidocjs.com",
"version": "0.17.6"
}
});
|
||||
define({
"name": "wildduck",
"version": "1.0.0",
"description": "WildDuck API docs. Under construction, see old docs here: https://github.com/nodemailer/wildduck/blob/master/docs/api.md",
"title": "WildDuck API",
"url": "http://localhost:8080",
"sampleUrl": false,
"defaultVersion": "0.0.0",
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2018-01-16T10:36:55.798Z",
"url": "http://apidocjs.com",
"version": "0.17.6"
}
});
|
||||
|
|
|
@ -1 +1 @@
|
|||
{
"name": "wildduck",
"version": "1.0.0",
"description": "WildDuck API docs. Under construction, see old docs here: https://github.com/nodemailer/wildduck/blob/master/docs/api.md",
"title": "WildDuck API",
"url": "http://localhost:8080",
"sampleUrl": false,
"defaultVersion": "0.0.0",
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2018-01-15T11:52:17.862Z",
"url": "http://apidocjs.com",
"version": "0.17.6"
}
}
|
||||
{
"name": "wildduck",
"version": "1.0.0",
"description": "WildDuck API docs. Under construction, see old docs here: https://github.com/nodemailer/wildduck/blob/master/docs/api.md",
"title": "WildDuck API",
"url": "http://localhost:8080",
"sampleUrl": false,
"defaultVersion": "0.0.0",
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2018-01-16T10:36:55.798Z",
"url": "http://apidocjs.com",
"version": "0.17.6"
}
}
|
||||
|
|
15
indexes.yaml
15
indexes.yaml
|
@ -65,6 +65,14 @@ indexes:
|
|||
key:
|
||||
addrview: 1
|
||||
|
||||
- collection: addresses
|
||||
type: users # index applies to users database
|
||||
index:
|
||||
name: address_tags
|
||||
key:
|
||||
tagsview: 1
|
||||
sparse: true
|
||||
|
||||
- collection: addresses
|
||||
type: users # index applies to users database
|
||||
index:
|
||||
|
@ -254,6 +262,13 @@ indexes:
|
|||
mailbox: 1
|
||||
idate: 1
|
||||
|
||||
- collection: messages
|
||||
index:
|
||||
name: by_idate_reverse
|
||||
key:
|
||||
mailbox: 1
|
||||
idate: -1
|
||||
|
||||
- collection: messages
|
||||
index:
|
||||
name: by_hdate
|
||||
|
|
|
@ -19,6 +19,8 @@ module.exports = (db, server) => {
|
|||
* }
|
||||
*
|
||||
* @apiParam {String} [query] Partial match of an address
|
||||
* @apiParam {String} [tags] Comma separated list of tags. The Address must have at least one to be set
|
||||
* @apiParam {String} [requiredTags] Comma separated list of tags. The Address must have all listed tags to be set
|
||||
* @apiParam {Number} [limit=20] How many records to return
|
||||
* @apiParam {Number} [page=1] Current page number. Informational only, page numbers start from 1
|
||||
* @apiParam {Number} [next] Cursor value for next page, retrieved from <code>nextCursor</code> response value
|
||||
|
@ -76,6 +78,14 @@ module.exports = (db, server) => {
|
|||
.trim()
|
||||
.empty('')
|
||||
.max(255),
|
||||
tags: Joi.string()
|
||||
.trim()
|
||||
.empty('')
|
||||
.max(1024),
|
||||
requiredTags: Joi.string()
|
||||
.trim()
|
||||
.empty('')
|
||||
.max(1024),
|
||||
limit: Joi.number()
|
||||
.default(20)
|
||||
.min(1)
|
||||
|
@ -121,6 +131,42 @@ module.exports = (db, server) => {
|
|||
}) ||
|
||||
{};
|
||||
|
||||
let tagSeen = new Set();
|
||||
|
||||
let requiredTags = (result.value.requiredTags || '')
|
||||
.split(',')
|
||||
.map(tag => tag.toLowerCase().trim())
|
||||
.filter(tag => {
|
||||
if (tag && !tagSeen.has(tag)) {
|
||||
tagSeen.add(tag);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
let tags = (result.value.tags || '')
|
||||
.split(',')
|
||||
.map(tag => tag.toLowerCase().trim())
|
||||
.filter(tag => {
|
||||
if (tag && !tagSeen.has(tag)) {
|
||||
tagSeen.add(tag);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
let tagsview = {};
|
||||
if (requiredTags.length) {
|
||||
tagsview.$all = requiredTags;
|
||||
}
|
||||
if (tags.length) {
|
||||
tagsview.$in = tags;
|
||||
}
|
||||
|
||||
if (requiredTags.length || tags.length) {
|
||||
filter.tagsview = tagsview;
|
||||
}
|
||||
|
||||
db.users.collection('addresses').count(filter, (err, total) => {
|
||||
if (err) {
|
||||
res.json({
|
||||
|
@ -136,6 +182,7 @@ module.exports = (db, server) => {
|
|||
_id: true,
|
||||
address: true,
|
||||
user: true,
|
||||
tags: true,
|
||||
targets: true
|
||||
},
|
||||
paginatedField: 'addrview',
|
||||
|
@ -171,7 +218,8 @@ module.exports = (db, server) => {
|
|||
id: addressData._id.toString(),
|
||||
address: addressData.address,
|
||||
user: addressData.user,
|
||||
forwarded: addressData.targets && true
|
||||
forwarded: addressData.targets && true,
|
||||
tags: addressData.tags || []
|
||||
}))
|
||||
};
|
||||
|
||||
|
@ -198,6 +246,7 @@ module.exports = (db, server) => {
|
|||
*
|
||||
* @apiParam {String} user ID of the User
|
||||
* @apiParam {String} address E-mail Address
|
||||
* @apiParam {String[]} [tags] A list of tags associated with this address
|
||||
* @apiParam {Boolean} [main=false] Indicates if this is the default address for the User
|
||||
* @apiParam {Boolean} [allowWildcard=false] If <code>true</code> then address value can be in the form of <code>*@example.com</code>, otherwise using * is not allowed
|
||||
*
|
||||
|
@ -246,7 +295,12 @@ module.exports = (db, server) => {
|
|||
.falsy(['N', 'false', 'no', 'off', 0, '']),
|
||||
allowWildcard: Joi.boolean()
|
||||
.truthy(['Y', 'true', 'yes', 'on', 1])
|
||||
.falsy(['N', 'false', 'no', 'off', 0, ''])
|
||||
.falsy(['N', 'false', 'no', 'off', 0, '']),
|
||||
tags: Joi.array().items(
|
||||
Joi.string()
|
||||
.trim()
|
||||
.max(128)
|
||||
)
|
||||
});
|
||||
|
||||
const result = Joi.validate(req.params, schema, {
|
||||
|
@ -298,6 +352,23 @@ module.exports = (db, server) => {
|
|||
}
|
||||
}
|
||||
|
||||
if (result.value.tags) {
|
||||
let tagSeen = new Set();
|
||||
let tags = result.value.tags
|
||||
.map(tag => tag.trim())
|
||||
.filter(tag => {
|
||||
if (tag && !tagSeen.has(tag.toLowerCase())) {
|
||||
tagSeen.add(tag.toLowerCase());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
})
|
||||
.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
|
||||
|
||||
result.value.tags = tags;
|
||||
result.value.tagsview = tags.map(tag => tag.toLowerCase());
|
||||
}
|
||||
|
||||
db.users.collection('users').findOne(
|
||||
{
|
||||
_id: user
|
||||
|
@ -344,53 +415,57 @@ module.exports = (db, server) => {
|
|||
return next();
|
||||
}
|
||||
|
||||
addressData = {
|
||||
user,
|
||||
address,
|
||||
addrview: address.substr(0, address.indexOf('@')).replace(/\./g, '') + address.substr(address.indexOf('@')),
|
||||
created: new Date()
|
||||
};
|
||||
|
||||
if (result.value.tags) {
|
||||
addressData.tags = result.value.tags;
|
||||
addressData.tagsview = result.value.tags;
|
||||
}
|
||||
|
||||
// insert alias address to email address registry
|
||||
db.users.collection('addresses').insertOne(
|
||||
{
|
||||
user,
|
||||
address,
|
||||
addrview: address.substr(0, address.indexOf('@')).replace(/\./g, '') + address.substr(address.indexOf('@')),
|
||||
created: new Date()
|
||||
},
|
||||
(err, r) => {
|
||||
if (err) {
|
||||
res.json({
|
||||
error: 'MongoDB Error: ' + err.message,
|
||||
code: 'InternalDatabaseError'
|
||||
});
|
||||
return next();
|
||||
}
|
||||
|
||||
let insertId = r.insertedId;
|
||||
|
||||
let done = () => {
|
||||
// ignore potential user update error
|
||||
res.json({
|
||||
success: !!insertId,
|
||||
id: insertId
|
||||
});
|
||||
return next();
|
||||
};
|
||||
|
||||
if (!userData.address || main) {
|
||||
// register this address as the default address for that user
|
||||
return db.users.collection('users').findOneAndUpdate(
|
||||
{
|
||||
_id: user
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
address
|
||||
}
|
||||
},
|
||||
{},
|
||||
done
|
||||
);
|
||||
}
|
||||
|
||||
done();
|
||||
db.users.collection('addresses').insertOne(addressData, (err, r) => {
|
||||
if (err) {
|
||||
res.json({
|
||||
error: 'MongoDB Error: ' + err.message,
|
||||
code: 'InternalDatabaseError'
|
||||
});
|
||||
return next();
|
||||
}
|
||||
);
|
||||
|
||||
let insertId = r.insertedId;
|
||||
|
||||
let done = () => {
|
||||
// ignore potential user update error
|
||||
res.json({
|
||||
success: !!insertId,
|
||||
id: insertId
|
||||
});
|
||||
return next();
|
||||
};
|
||||
|
||||
if (!userData.address || main) {
|
||||
// register this address as the default address for that user
|
||||
return db.users.collection('users').findOneAndUpdate(
|
||||
{
|
||||
_id: user
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
address
|
||||
}
|
||||
},
|
||||
{},
|
||||
done
|
||||
);
|
||||
}
|
||||
|
||||
done();
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -415,6 +490,7 @@ module.exports = (db, server) => {
|
|||
* @apiSuccess {String} results.address E-mail address string
|
||||
* @apiSuccess {Boolean} results.main Indicates if this is the default address for the User
|
||||
* @apiSuccess {String} results.created Datestring of the time the address was created
|
||||
* @apiSuccess {String[]} results.tags List of tags associated with the Address
|
||||
*
|
||||
* @apiError error Description of the error
|
||||
*
|
||||
|
@ -524,6 +600,7 @@ module.exports = (db, server) => {
|
|||
id: address._id,
|
||||
address: address.address,
|
||||
main: address.address === userData.address,
|
||||
tags: address.tags || [],
|
||||
created: address.created
|
||||
}))
|
||||
});
|
||||
|
@ -681,6 +758,7 @@ module.exports = (db, server) => {
|
|||
* @apiParam {String} user ID of the User
|
||||
* @apiParam {String} address ID of the Address
|
||||
* @apiParam {Boolean} main Indicates if this is the default address for the User
|
||||
* @apiParam {String[]} [tags] A list of tags associated with this address
|
||||
*
|
||||
* @apiSuccess {Boolean} success Indicates successful response
|
||||
*
|
||||
|
@ -705,7 +783,7 @@ module.exports = (db, server) => {
|
|||
* "error": "This user does not exist"
|
||||
* }
|
||||
*/
|
||||
server.put('/users/:user/addresses/:address', (req, res, next) => {
|
||||
server.put('/users/:user/addresses/:id', (req, res, next) => {
|
||||
res.charSet('utf-8');
|
||||
|
||||
const schema = Joi.object().keys({
|
||||
|
@ -714,7 +792,7 @@ module.exports = (db, server) => {
|
|||
.lowercase()
|
||||
.length(24)
|
||||
.required(),
|
||||
address: Joi.string()
|
||||
id: Joi.string()
|
||||
.hex()
|
||||
.lowercase()
|
||||
.length(24)
|
||||
|
@ -722,7 +800,12 @@ module.exports = (db, server) => {
|
|||
main: Joi.boolean()
|
||||
.truthy(['Y', 'true', 'yes', 'on', 1])
|
||||
.falsy(['N', 'false', 'no', 'off', 0, ''])
|
||||
.required()
|
||||
.required(),
|
||||
tags: Joi.array().items(
|
||||
Joi.string()
|
||||
.trim()
|
||||
.max(128)
|
||||
)
|
||||
});
|
||||
|
||||
const result = Joi.validate(req.params, schema, {
|
||||
|
@ -739,16 +822,43 @@ module.exports = (db, server) => {
|
|||
}
|
||||
|
||||
let user = new ObjectID(result.value.user);
|
||||
let address = new ObjectID(result.value.address);
|
||||
let id = new ObjectID(result.value.id);
|
||||
let main = result.value.main;
|
||||
|
||||
if (!main) {
|
||||
if (main === false) {
|
||||
res.json({
|
||||
error: 'Cannot unset main status'
|
||||
});
|
||||
return next();
|
||||
}
|
||||
|
||||
let updates = {};
|
||||
|
||||
if (result.value.address) {
|
||||
let address = tools.normalizeAddress(result.value.address);
|
||||
let addrview = address.substr(0, address.indexOf('@')).replace(/\./g, '') + address.substr(address.indexOf('@'));
|
||||
|
||||
updates.address = address;
|
||||
updates.addrview = addrview;
|
||||
}
|
||||
|
||||
if (result.value.tags) {
|
||||
let tagSeen = new Set();
|
||||
let tags = result.value.tags
|
||||
.map(tag => tag.trim())
|
||||
.filter(tag => {
|
||||
if (tag && !tagSeen.has(tag.toLowerCase())) {
|
||||
tagSeen.add(tag.toLowerCase());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
})
|
||||
.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
|
||||
|
||||
updates.tags = tags;
|
||||
updates.tagsview = tags.map(tag => tag.toLowerCase());
|
||||
}
|
||||
|
||||
db.users.collection('users').findOne(
|
||||
{
|
||||
_id: user
|
||||
|
@ -776,7 +886,7 @@ module.exports = (db, server) => {
|
|||
|
||||
db.users.collection('addresses').findOne(
|
||||
{
|
||||
_id: address
|
||||
_id: id
|
||||
},
|
||||
(err, addressData) => {
|
||||
if (err) {
|
||||
|
@ -796,48 +906,106 @@ module.exports = (db, server) => {
|
|||
return next();
|
||||
}
|
||||
|
||||
if (addressData.address === userData.address) {
|
||||
if (addressData.address.indexOf('*') >= 0 && result.value.address && result.value.address !== addressData.address) {
|
||||
res.json({
|
||||
error: 'Selected address is already the main email address for the user'
|
||||
error: 'Can not change special address',
|
||||
code: 'ChangeNotAllowed'
|
||||
});
|
||||
return next();
|
||||
}
|
||||
|
||||
if (addressData.address.indexOf('*') >= 0 && main) {
|
||||
if (result.value.address && result.value.address.indexOf('*') >= 0 && result.value.address !== addressData.address) {
|
||||
res.json({
|
||||
error: 'Can not change special address',
|
||||
code: 'ChangeNotAllowed'
|
||||
});
|
||||
return next();
|
||||
}
|
||||
|
||||
if ((result.value.address || addressData.address).indexOf('*') >= 0 && main) {
|
||||
res.json({
|
||||
error: 'Can not set wildcard address as default'
|
||||
});
|
||||
return next();
|
||||
}
|
||||
|
||||
// insert alias address to email address registry
|
||||
db.users.collection('users').findOneAndUpdate(
|
||||
{
|
||||
_id: user
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
address: addressData.address
|
||||
}
|
||||
},
|
||||
{
|
||||
returnOriginal: false
|
||||
},
|
||||
(err, r) => {
|
||||
if (err) {
|
||||
res.json({
|
||||
error: 'MongoDB Error: ' + err.message,
|
||||
code: 'InternalDatabaseError'
|
||||
});
|
||||
return next();
|
||||
}
|
||||
if (result.value.address && addressData.address === userData.address && result.value.address !== addressData.address) {
|
||||
// main address was changed, update user data as well
|
||||
main = true;
|
||||
addressData.address = result.value.address;
|
||||
}
|
||||
|
||||
let updateAddressData = done => {
|
||||
if (!Object.keys(updates).length) {
|
||||
return done();
|
||||
}
|
||||
db.users.collection('addresses').findOneAndUpdate(
|
||||
{
|
||||
_id: addressData._id
|
||||
},
|
||||
{
|
||||
$set: updates
|
||||
},
|
||||
{
|
||||
returnOriginal: false
|
||||
},
|
||||
err => {
|
||||
if (err) {
|
||||
if (err.code === 11000) {
|
||||
res.json({
|
||||
error: 'Address already exists',
|
||||
code: 'AddressExistsError'
|
||||
});
|
||||
} else {
|
||||
res.json({
|
||||
error: 'MongoDB Error: ' + err.message,
|
||||
code: 'InternalDatabaseError'
|
||||
});
|
||||
}
|
||||
return next();
|
||||
}
|
||||
return done();
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
updateAddressData(() => {
|
||||
if (!main) {
|
||||
// nothing to do anymore
|
||||
res.json({
|
||||
success: !!r.value
|
||||
success: true
|
||||
});
|
||||
return next();
|
||||
}
|
||||
);
|
||||
|
||||
db.users.collection('users').findOneAndUpdate(
|
||||
{
|
||||
_id: user
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
address: addressData.address
|
||||
}
|
||||
},
|
||||
{
|
||||
returnOriginal: false
|
||||
},
|
||||
(err, r) => {
|
||||
if (err) {
|
||||
res.json({
|
||||
error: 'MongoDB Error: ' + err.message,
|
||||
code: 'InternalDatabaseError'
|
||||
});
|
||||
return next();
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: !!r.value
|
||||
});
|
||||
return next();
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -1006,6 +1174,7 @@ module.exports = (db, server) => {
|
|||
* @apiParam {String[]} [targets] An array of forwarding targets. The value could either be an email address or a relay url to next MX server ("smtp://mx2.zone.eu:25")
|
||||
* @apiParam {Number} [forwards] Daily allowed forwarding count for this address
|
||||
* @apiParam {Boolean} [allowWildcard=false] If <code>true</code> then address value can be in the form of <code>*@example.com</code>, otherwise using * is not allowed
|
||||
* @apiParam {String[]} [tags] A list of tags associated with this address
|
||||
* @apiParam {Object} [autoreply] Autoreply information
|
||||
* @apiParam {Boolean} [autoreply.status] If true, then autoreply is enabled for this address
|
||||
* @apiParam {String} [autoreply.start] Either a date string or boolean false to disable start time checks
|
||||
|
@ -1089,7 +1258,12 @@ module.exports = (db, server) => {
|
|||
.empty('')
|
||||
.trim()
|
||||
.max(128 * 1024)
|
||||
})
|
||||
}),
|
||||
tags: Joi.array().items(
|
||||
Joi.string()
|
||||
.trim()
|
||||
.max(128)
|
||||
)
|
||||
});
|
||||
|
||||
const result = Joi.validate(req.params, schema, {
|
||||
|
@ -1137,6 +1311,23 @@ module.exports = (db, server) => {
|
|||
};
|
||||
}
|
||||
|
||||
if (result.value.tags) {
|
||||
let tagSeen = new Set();
|
||||
let tags = result.value.tags
|
||||
.map(tag => tag.trim())
|
||||
.filter(tag => {
|
||||
if (tag && !tagSeen.has(tag.toLowerCase())) {
|
||||
tagSeen.add(tag.toLowerCase());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
})
|
||||
.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
|
||||
|
||||
result.value.tags = tags;
|
||||
result.value.tagsview = tags.map(tag => tag.toLowerCase());
|
||||
}
|
||||
|
||||
// needed to resolve users for addresses
|
||||
let addrlist = [];
|
||||
let cachedAddrviews = new WeakMap();
|
||||
|
@ -1258,33 +1449,38 @@ module.exports = (db, server) => {
|
|||
|
||||
resolveUsers(() => {
|
||||
// insert alias address to email address registry
|
||||
db.users.collection('addresses').insertOne(
|
||||
{
|
||||
address,
|
||||
addrview: address.substr(0, address.indexOf('@')).replace(/\./g, '') + address.substr(address.indexOf('@')),
|
||||
targets,
|
||||
forwards,
|
||||
autoreply: result.value.autoreply,
|
||||
created: new Date()
|
||||
},
|
||||
(err, r) => {
|
||||
if (err) {
|
||||
res.json({
|
||||
error: 'MongoDB Error: ' + err.message,
|
||||
code: 'InternalDatabaseError'
|
||||
});
|
||||
return next();
|
||||
}
|
||||
|
||||
let insertId = r.insertedId;
|
||||
let addressData = {
|
||||
address,
|
||||
addrview: address.substr(0, address.indexOf('@')).replace(/\./g, '') + address.substr(address.indexOf('@')),
|
||||
targets,
|
||||
forwards,
|
||||
autoreply: result.value.autoreply,
|
||||
created: new Date()
|
||||
};
|
||||
|
||||
if (result.value.tags) {
|
||||
addressData.tags = result.value.tags;
|
||||
addressData.tagsview = result.value.tags;
|
||||
}
|
||||
|
||||
db.users.collection('addresses').insertOne(addressData, (err, r) => {
|
||||
if (err) {
|
||||
res.json({
|
||||
success: !!insertId,
|
||||
id: insertId
|
||||
error: 'MongoDB Error: ' + err.message,
|
||||
code: 'InternalDatabaseError'
|
||||
});
|
||||
return next();
|
||||
}
|
||||
);
|
||||
|
||||
let insertId = r.insertedId;
|
||||
|
||||
res.json({
|
||||
success: !!insertId,
|
||||
id: insertId
|
||||
});
|
||||
return next();
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
@ -1304,6 +1500,7 @@ module.exports = (db, server) => {
|
|||
* @apiParam {String} [address] New address. Only affects normal addresses, special addresses that include \* can not be changed
|
||||
* @apiParam {String[]} [targets] An array of forwarding targets. The value could either be an email address or a relay url to next MX server ("smtp://mx2.zone.eu:25"). If set then overwrites previous targets array
|
||||
* @apiParam {Number} [forwards] Daily allowed forwarding count for this address
|
||||
* @apiParam {String[]} [tags] A list of tags associated with this address
|
||||
* @apiParam {Object} [autoreply] Autoreply information
|
||||
* @apiParam {Boolean} [autoreply.status] If true, then autoreply is enabled for this address
|
||||
* @apiParam {String} [autoreply.start] Either a date string or boolean false to disable start time checks
|
||||
|
@ -1376,7 +1573,12 @@ module.exports = (db, server) => {
|
|||
.empty('')
|
||||
.trim()
|
||||
.max(128 * 1024)
|
||||
})
|
||||
}),
|
||||
tags: Joi.array().items(
|
||||
Joi.string()
|
||||
.trim()
|
||||
.max(128)
|
||||
)
|
||||
});
|
||||
|
||||
const result = Joi.validate(req.params, schema, {
|
||||
|
@ -1432,6 +1634,23 @@ module.exports = (db, server) => {
|
|||
});
|
||||
}
|
||||
|
||||
if (result.value.tags) {
|
||||
let tagSeen = new Set();
|
||||
let tags = result.value.tags
|
||||
.map(tag => tag.trim())
|
||||
.filter(tag => {
|
||||
if (tag && !tagSeen.has(tag.toLowerCase())) {
|
||||
tagSeen.add(tag.toLowerCase());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
})
|
||||
.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
|
||||
|
||||
updates.tags = tags;
|
||||
updates.tagsview = tags.map(tag => tag.toLowerCase());
|
||||
}
|
||||
|
||||
db.users.collection('addresses').findOne(
|
||||
{
|
||||
_id: id
|
||||
|
@ -1718,6 +1937,7 @@ module.exports = (db, server) => {
|
|||
* @apiSuccess {String} autoreply.text Autoreply plaintext content
|
||||
* @apiSuccess {String} autoreply.html Autoreply HTML content
|
||||
* @apiSuccess {String} created Datestring of the time the address was created
|
||||
* @apiSuccess {String[]} results.tags List of tags associated with the Address
|
||||
*
|
||||
* @apiError error Description of the error
|
||||
*
|
||||
|
@ -1824,6 +2044,7 @@ module.exports = (db, server) => {
|
|||
}
|
||||
},
|
||||
autoreply: addressData.autoreply || { status: false },
|
||||
tags: addressData.tags || [],
|
||||
created: addressData.created
|
||||
});
|
||||
|
||||
|
@ -1860,6 +2081,7 @@ module.exports = (db, server) => {
|
|||
* @apiSuccess {String} autoreply.subject Autoreply subject line
|
||||
* @apiSuccess {String} autoreply.text Autoreply plaintext content
|
||||
* @apiSuccess {String} autoreply.html Autoreply HTML content
|
||||
* @apiSuccess {String[]} tags List of tags associated with the Address
|
||||
* @apiSuccess {String} created Datestring of the time the address was created
|
||||
*
|
||||
* @apiError error Description of the error
|
||||
|
@ -1968,6 +2190,7 @@ module.exports = (db, server) => {
|
|||
id: addressData._id,
|
||||
address: addressData.address,
|
||||
user: addressData.user,
|
||||
tags: addressData.tags || [],
|
||||
created: addressData.created
|
||||
});
|
||||
return next();
|
||||
|
@ -2001,6 +2224,7 @@ module.exports = (db, server) => {
|
|||
}
|
||||
},
|
||||
autoreply: addressData.autoreply || { status: false },
|
||||
tags: addressData.tags || [],
|
||||
created: addressData.created
|
||||
});
|
||||
|
||||
|
|
|
@ -235,7 +235,7 @@ module.exports = (db, server, messageHandler) => {
|
|||
draft: true,
|
||||
thread: true
|
||||
},
|
||||
paginatedField: 'uid',
|
||||
paginatedField: 'idate',
|
||||
sortAscending
|
||||
};
|
||||
|
||||
|
@ -1568,7 +1568,7 @@ module.exports = (db, server, messageHandler) => {
|
|||
.lowercase()
|
||||
.length(24),
|
||||
message: Joi.string()
|
||||
.regex(/^\d+(,\d+)*$|^\d+:\d+$|/i)
|
||||
.regex(/^\d+(,\d+)*$|^\d+:\d+$/i)
|
||||
.required(),
|
||||
seen: Joi.boolean()
|
||||
.truthy(['Y', 'true', 'yes', 'on', 1])
|
||||
|
|
Loading…
Reference in a new issue