mirror of
https://github.com/nodemailer/wildduck.git
synced 2025-10-08 21:06:47 +08:00
added api docs for filters
This commit is contained in:
parent
6a56201b78
commit
9dd052e18c
6 changed files with 619 additions and 263 deletions
16
README.md
16
README.md
|
@ -88,7 +88,7 @@ node server.js --config=/etc/wildduck.toml
|
||||||
|
|
||||||
### Step 4\. Create an user account
|
### Step 4\. Create an user account
|
||||||
|
|
||||||
See [API Docs](https://github.com/nodemailer/wildduck/wiki/API-Docs#add-a-new-user) for details about creating new user accounts
|
See [API Docs](https://api.wildduck.email/#api-Users-PostUser) for details about creating new user accounts
|
||||||
|
|
||||||
### Step 5\. Use an IMAP/POP3 client to log in
|
### Step 5\. Use an IMAP/POP3 client to log in
|
||||||
|
|
||||||
|
@ -107,15 +107,15 @@ Any IMAP or POP3 client will do. Use the credentials from step 4\. to log in.
|
||||||
|
|
||||||
Users, mailboxes and messages can be managed with HTTP requests against Wild Duck API
|
Users, mailboxes and messages can be managed with HTTP requests against Wild Duck API
|
||||||
|
|
||||||
### [API Docs](https://github.com/nodemailer/wildduck/wiki/API-Docs)
|
**[API Docs](https://api.wildduck.email/)**
|
||||||
|
|
||||||
# FAQ
|
# FAQ
|
||||||
|
|
||||||
### Does it work?
|
### Does it work?
|
||||||
|
|
||||||
Yes, it does. You can run the server and get working IMAP and POP3 servers for mail store, LMTP server for pushing messages to the mail store and HTTP API
|
Yes, it does. You can run the server and get working IMAP and POP3 servers for mail store, LMTP server for pushing messages to the mail store and
|
||||||
server to create new users. All handled by Node.js, MongoDB and Redis, no additional dependencies needed. Provided services can be disabled and enabled one by
|
[HTTP API](https://api.wildduck.email/) server to create new users. All handled by Node.js, MongoDB and Redis, no additional dependencies needed. Provided
|
||||||
one so, for example you could process just IMAP in one host and LMTP in another.
|
services can be disabled and enabled one by one so, for example you could process just IMAP in one host and LMTP in another.
|
||||||
|
|
||||||
### How is security implemented in Wild Duck?
|
### How is security implemented in Wild Duck?
|
||||||
|
|
||||||
|
@ -134,8 +134,8 @@ Read about Wild Duck security implementation from the [Wiki](https://github.com/
|
||||||
5. **Works on any OS including Windows.** At least if you get MongoDB and Redis running first.
|
5. **Works on any OS including Windows.** At least if you get MongoDB and Redis running first.
|
||||||
6. Focus on **internationalization**, ie. supporting email addresses with non-ascii characters
|
6. Focus on **internationalization**, ie. supporting email addresses with non-ascii characters
|
||||||
7. **Deduplication of attachments.** If the same attachment is referenced by different messages then only a single copy of the attachment is stored.
|
7. **Deduplication of attachments.** If the same attachment is referenced by different messages then only a single copy of the attachment is stored.
|
||||||
8. Access messages both using **IMAP and HTTP API**. The latter serves parsed data, so no need to fetch RFC822 messages and parse out html, plaintext content or
|
8. Access messages both using **IMAP and [HTTP API](https://api.wildduck.email/)**. The latter serves parsed data, so no need to fetch RFC822 messages and parse
|
||||||
attachments. It is super easy to create a webmail interface on top of this.
|
out html, plaintext content or attachments. It is super easy to create a webmail interface on top of this.
|
||||||
9. Built in **address labels**: _username+label@example.com_ is delivered to _username@example.com_
|
9. Built in **address labels**: _username+label@example.com_ is delivered to _username@example.com_
|
||||||
10. Dots in usernames and addresses are informational only. username@example.com is the same as user.name@example.com
|
10. Dots in usernames and addresses are informational only. username@example.com is the same as user.name@example.com
|
||||||
11. **HTTP Event Source** to push modifications in user email account to browser for super snappy webmail clients
|
11. **HTTP Event Source** to push modifications in user email account to browser for super snappy webmail clients
|
||||||
|
@ -250,7 +250,7 @@ If a messages is deleted by a client this message gets marked as Seen and moved
|
||||||
|
|
||||||
Wild Duck has built-in message filtering in LMTP server. This is somewhat similar to Sieve even though the filters are not scripts.
|
Wild Duck has built-in message filtering in LMTP server. This is somewhat similar to Sieve even though the filters are not scripts.
|
||||||
|
|
||||||
Filters can be managed via the [Wild Duck API](https://github.com/nodemailer/wildduck/wiki/API-Docs).
|
Filters can be managed via the [Wild Duck API](https://api.wildduck.email/#api-Filters).
|
||||||
|
|
||||||
## IMAP Protocol Differences
|
## IMAP Protocol Differences
|
||||||
|
|
||||||
|
|
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": "http://localhost:8080",
"sampleUrl": false,
"defaultVersion": "0.0.0",
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2017-11-27T12:20:35.334Z",
"url": "http://apidocjs.com",
"version": "0.17.6"
}
});
|
define({
"name": "wildduck",
"version": "1.0.0",
"description": "WildDuck API docs",
"title": "WildDuck API",
"url": "http://localhost:8080",
"sampleUrl": false,
"defaultVersion": "0.0.0",
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2017-11-27T13:55:26.682Z",
"url": "http://apidocjs.com",
"version": "0.17.6"
}
});
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
{
"name": "wildduck",
"version": "1.0.0",
"description": "WildDuck API docs",
"title": "WildDuck API",
"url": "http://localhost:8080",
"sampleUrl": false,
"defaultVersion": "0.0.0",
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2017-11-27T12:20:35.334Z",
"url": "http://apidocjs.com",
"version": "0.17.6"
}
}
|
{
"name": "wildduck",
"version": "1.0.0",
"description": "WildDuck API docs",
"title": "WildDuck API",
"url": "http://localhost:8080",
"sampleUrl": false,
"defaultVersion": "0.0.0",
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2017-11-27T13:55:26.682Z",
"url": "http://apidocjs.com",
"version": "0.17.6"
}
}
|
||||||
|
|
|
@ -5,11 +5,69 @@ const ObjectID = require('mongodb').ObjectID;
|
||||||
const urllib = require('url');
|
const urllib = require('url');
|
||||||
|
|
||||||
module.exports = (db, server) => {
|
module.exports = (db, server) => {
|
||||||
|
/**
|
||||||
|
* @api {get} /users/:user/filters List Filters for an User
|
||||||
|
* @apiName GetFilters
|
||||||
|
* @apiGroup Filters
|
||||||
|
* @apiHeader {String} X-Access-Token Optional access token if authentication is enabled
|
||||||
|
* @apiHeaderExample {json} Header-Example:
|
||||||
|
* {
|
||||||
|
* "X-Access-Token": "59fc66a03e54454869460e45"
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @apiParam {String} user Users unique ID
|
||||||
|
*
|
||||||
|
* @apiSuccess {Boolean} success Indicates successful response
|
||||||
|
* @apiSuccess {Object[]} results Filter description
|
||||||
|
* @apiSuccess {String} results.id Filter ID
|
||||||
|
* @apiSuccess {String} results.name Name for the filter
|
||||||
|
* @apiSuccess {String} results.created Datestring of the time the filter was created
|
||||||
|
* @apiSuccess {Array[]} results.query A list of query descriptions
|
||||||
|
* @apiSuccess {Array[]} results.action A list of action descriptions
|
||||||
|
*
|
||||||
|
* @apiError error Description of the error
|
||||||
|
*
|
||||||
|
* @apiExample {curl} Example usage:
|
||||||
|
* curl -i http://localhost:8080/users/5a1bda70bfbd1442cd96c6f0/filters
|
||||||
|
*
|
||||||
|
* @apiSuccessExample {json} Success-Response:
|
||||||
|
* HTTP/1.1 200 OK
|
||||||
|
* {
|
||||||
|
* "success": true,
|
||||||
|
* "results": [
|
||||||
|
* {
|
||||||
|
* "id": "5a1c0ee490a34c67e266931c",
|
||||||
|
* "query": [
|
||||||
|
* [
|
||||||
|
* "from",
|
||||||
|
* "(Mäger)"
|
||||||
|
* ]
|
||||||
|
* ],
|
||||||
|
* "action": [
|
||||||
|
* [
|
||||||
|
* "mark as read"
|
||||||
|
* ]
|
||||||
|
* ],
|
||||||
|
* "created": "2017-11-27T13:11:00.835Z"
|
||||||
|
* }
|
||||||
|
* ]
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @apiErrorExample {json} Error-Response:
|
||||||
|
* HTTP/1.1 200 OK
|
||||||
|
* {
|
||||||
|
* "error": "This user does not exist"
|
||||||
|
* }
|
||||||
|
*/
|
||||||
server.get('/users/:user/filters', (req, res, next) => {
|
server.get('/users/:user/filters', (req, res, next) => {
|
||||||
res.charSet('utf-8');
|
res.charSet('utf-8');
|
||||||
|
|
||||||
const schema = Joi.object().keys({
|
const schema = Joi.object().keys({
|
||||||
user: Joi.string().hex().lowercase().length(24).required()
|
user: Joi.string()
|
||||||
|
.hex()
|
||||||
|
.lowercase()
|
||||||
|
.length(24)
|
||||||
|
.required()
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = Joi.validate(req.params, schema, {
|
const result = Joi.validate(req.params, schema, {
|
||||||
|
@ -26,92 +84,155 @@ module.exports = (db, server) => {
|
||||||
|
|
||||||
let user = new ObjectID(result.value.user);
|
let user = new ObjectID(result.value.user);
|
||||||
|
|
||||||
db.users.collection('users').findOne({
|
db.users.collection('users').findOne(
|
||||||
_id: user
|
{
|
||||||
}, {
|
_id: user
|
||||||
fields: {
|
},
|
||||||
address: true
|
{
|
||||||
}
|
fields: {
|
||||||
}, (err, userData) => {
|
address: true
|
||||||
if (err) {
|
}
|
||||||
res.json({
|
},
|
||||||
error: 'MongoDB Error: ' + err.message
|
(err, userData) => {
|
||||||
});
|
if (err) {
|
||||||
return next();
|
res.json({
|
||||||
}
|
error: 'MongoDB Error: ' + err.message
|
||||||
if (!userData) {
|
});
|
||||||
res.json({
|
return next();
|
||||||
error: 'This user does not exist'
|
}
|
||||||
});
|
if (!userData) {
|
||||||
return next();
|
res.json({
|
||||||
}
|
error: 'This user does not exist'
|
||||||
|
});
|
||||||
db.database
|
return next();
|
||||||
.collection('mailboxes')
|
}
|
||||||
.find({
|
|
||||||
user
|
|
||||||
})
|
|
||||||
.project({ _id: 1, path: 1 })
|
|
||||||
.sort({ _id: 1 })
|
|
||||||
.toArray((err, mailboxes) => {
|
|
||||||
if (err) {
|
|
||||||
res.json({
|
|
||||||
error: 'MongoDB Error: ' + err.message
|
|
||||||
});
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mailboxes) {
|
|
||||||
mailboxes = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
db.database
|
|
||||||
.collection('filters')
|
|
||||||
.find({
|
|
||||||
user
|
|
||||||
})
|
|
||||||
.sort({
|
|
||||||
_id: 1
|
|
||||||
})
|
|
||||||
.toArray((err, filters) => {
|
|
||||||
if (err) {
|
|
||||||
res.json({
|
|
||||||
error: 'MongoDB Error: ' + err.message
|
|
||||||
});
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!filters) {
|
|
||||||
filters = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
db.database
|
||||||
|
.collection('mailboxes')
|
||||||
|
.find({
|
||||||
|
user
|
||||||
|
})
|
||||||
|
.project({ _id: 1, path: 1 })
|
||||||
|
.sort({ _id: 1 })
|
||||||
|
.toArray((err, mailboxes) => {
|
||||||
|
if (err) {
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
error: 'MongoDB Error: ' + err.message
|
||||||
|
|
||||||
results: filters.map(filter => {
|
|
||||||
let descriptions = getFilterStrings(filter, mailboxes);
|
|
||||||
return {
|
|
||||||
id: filter._id,
|
|
||||||
name: filter.name,
|
|
||||||
query: descriptions.query,
|
|
||||||
action: descriptions.action,
|
|
||||||
created: filter.created
|
|
||||||
};
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return next();
|
return next();
|
||||||
});
|
}
|
||||||
});
|
|
||||||
});
|
if (!mailboxes) {
|
||||||
|
mailboxes = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
db.database
|
||||||
|
.collection('filters')
|
||||||
|
.find({
|
||||||
|
user
|
||||||
|
})
|
||||||
|
.sort({
|
||||||
|
_id: 1
|
||||||
|
})
|
||||||
|
.toArray((err, filters) => {
|
||||||
|
if (err) {
|
||||||
|
res.json({
|
||||||
|
error: 'MongoDB Error: ' + err.message
|
||||||
|
});
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!filters) {
|
||||||
|
filters = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
|
||||||
|
results: filters.map(filter => {
|
||||||
|
let descriptions = getFilterStrings(filter, mailboxes);
|
||||||
|
return {
|
||||||
|
id: filter._id,
|
||||||
|
name: filter.name,
|
||||||
|
query: descriptions.query,
|
||||||
|
action: descriptions.action,
|
||||||
|
created: filter.created
|
||||||
|
};
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
return next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api {get} /users/:user/filters/:filter Request Filter information
|
||||||
|
* @apiName GetFilter
|
||||||
|
* @apiGroup Filters
|
||||||
|
* @apiHeader {String} X-Access-Token Optional access token if authentication is enabled
|
||||||
|
* @apiHeaderExample {json} Header-Example:
|
||||||
|
* {
|
||||||
|
* "X-Access-Token": "59fc66a03e54454869460e45"
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @apiParam {String} user Users unique ID.
|
||||||
|
* @apiParam {String} filter Filters unique ID.
|
||||||
|
*
|
||||||
|
* @apiSuccess {Boolean} success Indicates successful response
|
||||||
|
* @apiSuccess {String} id ID for the Filter
|
||||||
|
* @apiSuccess {String} name Name of the Filter
|
||||||
|
* @apiSuccess {String} query_from Partial match for the From: header (case insensitive)
|
||||||
|
* @apiSuccess {String} query_to Partial match for the To:/Cc: headers (case insensitive)
|
||||||
|
* @apiSuccess {String} query_subject Partial match for the Subject: header (case insensitive)
|
||||||
|
* @apiSuccess {String} query_text Fulltext search against message text
|
||||||
|
* @apiSuccess {Bolean} query_ha Does a message have to have an attachment or not
|
||||||
|
* @apiSuccess {Number} query_size Message size in bytes. If the value is a positive number then message needs to be larger, if negative then message needs to be smaller than abs(size) value
|
||||||
|
* @apiSuccess {Bolean} action_seen If true then mark matching messages as Seen
|
||||||
|
* @apiSuccess {Bolean} action_flag If true then mark matching messages as Flagged
|
||||||
|
* @apiSuccess {Bolean} action_delete If true then do not store matching messages
|
||||||
|
* @apiSuccess {Bolean} action_spam If true then store matching messags to Junk Mail folder
|
||||||
|
* @apiSuccess {String} action_mailbox Mailbox ID to store matching messages to
|
||||||
|
* @apiSuccess {String} action_forward An email address where matching messages should be forwarded to
|
||||||
|
* @apiSuccess {String} action_targetUrl An URL where matching messages should be POSTed to
|
||||||
|
*
|
||||||
|
* @apiError error Description of the error
|
||||||
|
*
|
||||||
|
* @apiExample {curl} Example usage:
|
||||||
|
* curl -i http://localhost:8080/users/59fc66a03e54454869460e45/filters/5a1c0ee490a34c67e266931c
|
||||||
|
*
|
||||||
|
* @apiSuccessExample {json} Success-Response:
|
||||||
|
* HTTP/1.1 200 OK
|
||||||
|
* {
|
||||||
|
* "success": true,
|
||||||
|
* "id": "5a1c0ee490a34c67e266931c",
|
||||||
|
* "created": "2017-11-27T13:11:00.835Z",
|
||||||
|
* "query_from": "Mäger",
|
||||||
|
* "action_seen": true
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @apiErrorExample {json} Error-Response:
|
||||||
|
* HTTP/1.1 200 OK
|
||||||
|
* {
|
||||||
|
* "error": "This filter does not exist"
|
||||||
|
* }
|
||||||
|
*/
|
||||||
server.get('/users/:user/filters/:filter', (req, res, next) => {
|
server.get('/users/:user/filters/:filter', (req, res, next) => {
|
||||||
res.charSet('utf-8');
|
res.charSet('utf-8');
|
||||||
|
|
||||||
const schema = Joi.object().keys({
|
const schema = Joi.object().keys({
|
||||||
user: Joi.string().hex().lowercase().length(24).required(),
|
user: Joi.string()
|
||||||
filter: Joi.string().hex().lowercase().length(24).required()
|
.hex()
|
||||||
|
.lowercase()
|
||||||
|
.length(24)
|
||||||
|
.required(),
|
||||||
|
filter: Joi.string()
|
||||||
|
.hex()
|
||||||
|
.lowercase()
|
||||||
|
.length(24)
|
||||||
|
.required()
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = Joi.validate(req.params, schema, {
|
const result = Joi.validate(req.params, schema, {
|
||||||
|
@ -129,76 +250,119 @@ module.exports = (db, server) => {
|
||||||
let user = new ObjectID(result.value.user);
|
let user = new ObjectID(result.value.user);
|
||||||
let filter = new ObjectID(result.value.filter);
|
let filter = new ObjectID(result.value.filter);
|
||||||
|
|
||||||
db.database.collection('filters').findOne({
|
db.database.collection('filters').findOne(
|
||||||
_id: filter,
|
{
|
||||||
user
|
_id: filter,
|
||||||
}, (err, filterData) => {
|
user
|
||||||
if (err) {
|
},
|
||||||
res.json({
|
(err, filterData) => {
|
||||||
error: 'MongoDB Error: ' + err.message
|
if (err) {
|
||||||
});
|
res.json({
|
||||||
return next();
|
error: 'MongoDB Error: ' + err.message
|
||||||
}
|
|
||||||
if (!filterData) {
|
|
||||||
res.json({
|
|
||||||
error: 'This filter does not exist'
|
|
||||||
});
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
|
|
||||||
db.database
|
|
||||||
.collection('mailboxes')
|
|
||||||
.find({
|
|
||||||
user
|
|
||||||
})
|
|
||||||
.project({ _id: 1, path: 1 })
|
|
||||||
.sort({ _id: 1 })
|
|
||||||
.toArray((err, mailboxes) => {
|
|
||||||
if (err) {
|
|
||||||
res.json({
|
|
||||||
error: 'MongoDB Error: ' + err.message
|
|
||||||
});
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mailboxes) {
|
|
||||||
mailboxes = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
let result = {
|
|
||||||
success: true,
|
|
||||||
id: filterData._id,
|
|
||||||
name: filterData.name,
|
|
||||||
created: filterData.created
|
|
||||||
};
|
|
||||||
|
|
||||||
Object.keys((filterData.query && filterData.query.headers) || {}).forEach(key => {
|
|
||||||
result['query_' + key] = filterData.query.headers[key];
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.keys(filterData.query || {}).forEach(key => {
|
|
||||||
if (key !== 'headers') {
|
|
||||||
result['query_' + key] = filterData.query[key];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Object.keys(filterData.action || {}).forEach(key => {
|
|
||||||
result['action_' + key] = filterData.action[key];
|
|
||||||
});
|
|
||||||
|
|
||||||
res.json(result);
|
|
||||||
|
|
||||||
return next();
|
return next();
|
||||||
});
|
}
|
||||||
});
|
if (!filterData) {
|
||||||
|
res.json({
|
||||||
|
error: 'This filter does not exist'
|
||||||
|
});
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
db.database
|
||||||
|
.collection('mailboxes')
|
||||||
|
.find({
|
||||||
|
user
|
||||||
|
})
|
||||||
|
.project({ _id: 1, path: 1 })
|
||||||
|
.sort({ _id: 1 })
|
||||||
|
.toArray((err, mailboxes) => {
|
||||||
|
if (err) {
|
||||||
|
res.json({
|
||||||
|
error: 'MongoDB Error: ' + err.message
|
||||||
|
});
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mailboxes) {
|
||||||
|
mailboxes = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = {
|
||||||
|
success: true,
|
||||||
|
id: filterData._id,
|
||||||
|
name: filterData.name,
|
||||||
|
created: filterData.created
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.keys((filterData.query && filterData.query.headers) || {}).forEach(key => {
|
||||||
|
result['query_' + key] = filterData.query.headers[key];
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.keys(filterData.query || {}).forEach(key => {
|
||||||
|
if (key !== 'headers') {
|
||||||
|
result['query_' + key] = filterData.query[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.keys(filterData.action || {}).forEach(key => {
|
||||||
|
result['action_' + key] = filterData.action[key];
|
||||||
|
});
|
||||||
|
|
||||||
|
res.json(result);
|
||||||
|
|
||||||
|
return next();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api {delete} /users/:user/filters/:filter Delete a Filter
|
||||||
|
* @apiName DeleteFilter
|
||||||
|
* @apiGroup Filters
|
||||||
|
* @apiHeader {String} X-Access-Token Optional access token if authentication is enabled
|
||||||
|
* @apiHeaderExample {json} Header-Example:
|
||||||
|
* {
|
||||||
|
* "X-Access-Token": "59fc66a03e54454869460e45"
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @apiParam {String} user Users unique ID
|
||||||
|
* @apiParam {String} filter Filters unique ID
|
||||||
|
*
|
||||||
|
* @apiSuccess {Boolean} success Indicates successful response
|
||||||
|
*
|
||||||
|
* @apiError error Description of the error
|
||||||
|
*
|
||||||
|
* @apiExample {curl} Example usage:
|
||||||
|
* curl -i -XDELETE http://localhost:8080/users/59fc66a03e54454869460e45/filters/5a1c0ee490a34c67e266931c
|
||||||
|
*
|
||||||
|
* @apiSuccessExample {json} Success-Response:
|
||||||
|
* HTTP/1.1 200 OK
|
||||||
|
* {
|
||||||
|
* "success": true
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @apiErrorExample {json} Error-Response:
|
||||||
|
* HTTP/1.1 200 OK
|
||||||
|
* {
|
||||||
|
* "error": "This filter does not exist"
|
||||||
|
* }
|
||||||
|
*/
|
||||||
server.del('/users/:user/filters/:filter', (req, res, next) => {
|
server.del('/users/:user/filters/:filter', (req, res, next) => {
|
||||||
res.charSet('utf-8');
|
res.charSet('utf-8');
|
||||||
|
|
||||||
const schema = Joi.object().keys({
|
const schema = Joi.object().keys({
|
||||||
user: Joi.string().hex().lowercase().length(24).required(),
|
user: Joi.string()
|
||||||
filter: Joi.string().hex().lowercase().length(24).required()
|
.hex()
|
||||||
|
.lowercase()
|
||||||
|
.length(24)
|
||||||
|
.required(),
|
||||||
|
filter: Joi.string()
|
||||||
|
.hex()
|
||||||
|
.lowercase()
|
||||||
|
.length(24)
|
||||||
|
.required()
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = Joi.validate(req.params, schema, {
|
const result = Joi.validate(req.params, schema, {
|
||||||
|
@ -216,54 +380,144 @@ module.exports = (db, server) => {
|
||||||
let user = new ObjectID(result.value.user);
|
let user = new ObjectID(result.value.user);
|
||||||
let filter = new ObjectID(result.value.filter);
|
let filter = new ObjectID(result.value.filter);
|
||||||
|
|
||||||
db.database.collection('filters').deleteOne({
|
db.database.collection('filters').deleteOne(
|
||||||
_id: filter,
|
{
|
||||||
user
|
_id: filter,
|
||||||
}, (err, r) => {
|
user
|
||||||
if (err) {
|
},
|
||||||
|
(err, r) => {
|
||||||
|
if (err) {
|
||||||
|
res.json({
|
||||||
|
error: 'MongoDB Error: ' + err.message
|
||||||
|
});
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!r.deletedCount) {
|
||||||
|
res.status(404);
|
||||||
|
res.json({
|
||||||
|
error: 'Filter was not found'
|
||||||
|
});
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
error: 'MongoDB Error: ' + err.message
|
success: true
|
||||||
});
|
});
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
|
);
|
||||||
if (!r.deletedCount) {
|
|
||||||
res.status(404);
|
|
||||||
res.json({
|
|
||||||
error: 'Filter was not found'
|
|
||||||
});
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
success: true
|
|
||||||
});
|
|
||||||
return next();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api {post} /users/:user/filters Create new Filter
|
||||||
|
* @apiName PostFilter
|
||||||
|
* @apiGroup Filters
|
||||||
|
* @apiHeader {String} X-Access-Token Optional access token if authentication is enabled
|
||||||
|
* @apiHeaderExample {json} Header-Example:
|
||||||
|
* {
|
||||||
|
* "X-Access-Token": "59fc66a03e54454869460e45"
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @apiParam {String} user Users unique ID.
|
||||||
|
* @apiParam {String} [name] Name of the Filter
|
||||||
|
* @apiParam {String} [query_from] Partial match for the From: header (case insensitive)
|
||||||
|
* @apiParam {String} [query_to] Partial match for the To:/Cc: headers (case insensitive)
|
||||||
|
* @apiParam {String} [query_subject] Partial match for the Subject: header (case insensitive)
|
||||||
|
* @apiParam {String} [query_text] Fulltext search against message text
|
||||||
|
* @apiParam {Bolean} [query_ha] Does a message have to have an attachment or not
|
||||||
|
* @apiParam {Number} [query_size] Message size in bytes. If the value is a positive number then message needs to be larger, if negative then message needs to be smaller than abs(size) value
|
||||||
|
* @apiParam {Bolean} [action_seen] If true then mark matching messages as Seen
|
||||||
|
* @apiParam {Bolean} [action_flag] If true then mark matching messages as Flagged
|
||||||
|
* @apiParam {Bolean} [action_delete] If true then do not store matching messages
|
||||||
|
* @apiParam {Bolean} [action_spam] If true then store matching messags to Junk Mail folder
|
||||||
|
* @apiParam {String} [action_mailbox] Mailbox ID to store matching messages to
|
||||||
|
* @apiParam {String} [action_forward] An email address where matching messages should be forwarded to
|
||||||
|
* @apiParam {String} [action_targetUrl] An URL where matching messages should be POSTed to
|
||||||
|
*
|
||||||
|
* @apiSuccess {Boolean} success Indicates successful response
|
||||||
|
* @apiSuccess {String} id ID for the created Filter
|
||||||
|
*
|
||||||
|
* @apiError error Description of the error
|
||||||
|
*
|
||||||
|
* @apiExample {curl} Example usage:
|
||||||
|
* curl -i -XPOST http://localhost:8080/users/5a1bda70bfbd1442cd96c6f0/filters \
|
||||||
|
* -H 'Content-type: application/json' \
|
||||||
|
* -d '{
|
||||||
|
* "query_from": "Mäger",
|
||||||
|
* "action_seen": true
|
||||||
|
* }'
|
||||||
|
*
|
||||||
|
* @apiSuccessExample {json} Success-Response:
|
||||||
|
* HTTP/1.1 200 OK
|
||||||
|
* {
|
||||||
|
* "success": true,
|
||||||
|
* "id": "5a1c0ee490a34c67e266931c"
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @apiErrorExample {json} Error-Response:
|
||||||
|
* HTTP/1.1 200 OK
|
||||||
|
* {
|
||||||
|
* "error": "Empty filter query"
|
||||||
|
* }
|
||||||
|
*/
|
||||||
server.post('/users/:user/filters', (req, res, next) => {
|
server.post('/users/:user/filters', (req, res, next) => {
|
||||||
res.charSet('utf-8');
|
res.charSet('utf-8');
|
||||||
|
|
||||||
const schema = Joi.object().keys({
|
const schema = Joi.object().keys({
|
||||||
user: Joi.string().hex().lowercase().length(24).required(),
|
user: Joi.string()
|
||||||
|
.hex()
|
||||||
|
.lowercase()
|
||||||
|
.length(24)
|
||||||
|
.required(),
|
||||||
|
|
||||||
name: Joi.string().trim().max(255).empty(''),
|
name: Joi.string()
|
||||||
|
.trim()
|
||||||
|
.max(255)
|
||||||
|
.empty(''),
|
||||||
|
|
||||||
query_from: Joi.string().trim().max(255).empty(''),
|
query_from: Joi.string()
|
||||||
query_to: Joi.string().trim().max(255).empty(''),
|
.trim()
|
||||||
query_subject: Joi.string().trim().max(255).empty(''),
|
.max(255)
|
||||||
query_text: Joi.string().trim().max(255).empty(''),
|
.empty(''),
|
||||||
query_ha: Joi.boolean().truthy(['Y', 'true', 'yes', 1]).empty(''),
|
query_to: Joi.string()
|
||||||
|
.trim()
|
||||||
|
.max(255)
|
||||||
|
.empty(''),
|
||||||
|
query_subject: Joi.string()
|
||||||
|
.trim()
|
||||||
|
.max(255)
|
||||||
|
.empty(''),
|
||||||
|
query_text: Joi.string()
|
||||||
|
.trim()
|
||||||
|
.max(255)
|
||||||
|
.empty(''),
|
||||||
|
query_ha: Joi.boolean()
|
||||||
|
.truthy(['Y', 'true', 'yes', 1])
|
||||||
|
.empty(''),
|
||||||
query_size: Joi.number().empty(''),
|
query_size: Joi.number().empty(''),
|
||||||
|
|
||||||
action_seen: Joi.boolean().truthy(['Y', 'true', 'yes', 1]).empty(''),
|
action_seen: Joi.boolean()
|
||||||
action_flag: Joi.boolean().truthy(['Y', 'true', 'yes', 1]).empty(''),
|
.truthy(['Y', 'true', 'yes', 1])
|
||||||
action_delete: Joi.boolean().truthy(['Y', 'true', 'yes', 1]).empty(''),
|
.empty(''),
|
||||||
action_spam: Joi.boolean().truthy(['Y', 'true', 'yes', 1]).empty(''),
|
action_flag: Joi.boolean()
|
||||||
|
.truthy(['Y', 'true', 'yes', 1])
|
||||||
|
.empty(''),
|
||||||
|
action_delete: Joi.boolean()
|
||||||
|
.truthy(['Y', 'true', 'yes', 1])
|
||||||
|
.empty(''),
|
||||||
|
action_spam: Joi.boolean()
|
||||||
|
.truthy(['Y', 'true', 'yes', 1])
|
||||||
|
.empty(''),
|
||||||
|
|
||||||
action_mailbox: Joi.string().hex().lowercase().length(24).empty(''),
|
action_mailbox: Joi.string()
|
||||||
action_forward: Joi.string().email().empty(''),
|
.hex()
|
||||||
|
.lowercase()
|
||||||
|
.length(24)
|
||||||
|
.empty(''),
|
||||||
|
action_forward: Joi.string()
|
||||||
|
.email()
|
||||||
|
.empty(''),
|
||||||
action_targetUrl: Joi.string()
|
action_targetUrl: Joi.string()
|
||||||
.uri({
|
.uri({
|
||||||
scheme: ['http', 'https'],
|
scheme: ['http', 'https'],
|
||||||
|
@ -343,26 +597,29 @@ module.exports = (db, server) => {
|
||||||
if (!result.value.action_mailbox) {
|
if (!result.value.action_mailbox) {
|
||||||
return done();
|
return done();
|
||||||
}
|
}
|
||||||
db.database.collection('mailboxes').findOne({
|
db.database.collection('mailboxes').findOne(
|
||||||
_id: new ObjectID(result.value.action_mailbox),
|
{
|
||||||
user
|
_id: new ObjectID(result.value.action_mailbox),
|
||||||
}, (err, mailboxData) => {
|
user
|
||||||
if (err) {
|
},
|
||||||
res.json({
|
(err, mailboxData) => {
|
||||||
error: 'MongoDB Error: ' + err.message
|
if (err) {
|
||||||
});
|
res.json({
|
||||||
return next();
|
error: 'MongoDB Error: ' + err.message
|
||||||
|
});
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
if (!mailboxData) {
|
||||||
|
res.json({
|
||||||
|
error: 'This mailbox does not exist'
|
||||||
|
});
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
filterData.action.mailbox = mailboxData._id;
|
||||||
|
hasAction = true;
|
||||||
|
done();
|
||||||
}
|
}
|
||||||
if (!mailboxData) {
|
);
|
||||||
res.json({
|
|
||||||
error: 'This mailbox does not exist'
|
|
||||||
});
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
filterData.action.mailbox = mailboxData._id;
|
|
||||||
hasAction = true;
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
checkFilterMailbox(() => {
|
checkFilterMailbox(() => {
|
||||||
|
@ -380,67 +637,163 @@ module.exports = (db, server) => {
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
|
|
||||||
db.users.collection('users').findOne({
|
db.users.collection('users').findOne(
|
||||||
_id: user
|
{
|
||||||
}, {
|
_id: user
|
||||||
fields: {
|
},
|
||||||
address: true
|
{
|
||||||
}
|
fields: {
|
||||||
}, (err, userData) => {
|
address: true
|
||||||
if (err) {
|
}
|
||||||
res.json({
|
},
|
||||||
error: 'MongoDB Error: ' + err.message
|
(err, userData) => {
|
||||||
});
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
if (!userData) {
|
|
||||||
res.json({
|
|
||||||
error: 'This user does not exist'
|
|
||||||
});
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
|
|
||||||
db.database.collection('filters').insertOne(filterData, (err, r) => {
|
|
||||||
if (err) {
|
if (err) {
|
||||||
res.json({
|
res.json({
|
||||||
error: 'MongoDB Error: ' + err.message
|
error: 'MongoDB Error: ' + err.message
|
||||||
});
|
});
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
|
if (!userData) {
|
||||||
|
res.json({
|
||||||
|
error: 'This user does not exist'
|
||||||
|
});
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
res.json({
|
db.database.collection('filters').insertOne(filterData, (err, r) => {
|
||||||
success: !!r.insertedCount,
|
if (err) {
|
||||||
id: filterData._id
|
res.json({
|
||||||
|
error: 'MongoDB Error: ' + err.message
|
||||||
|
});
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: !!r.insertedCount,
|
||||||
|
id: filterData._id
|
||||||
|
});
|
||||||
|
return next();
|
||||||
});
|
});
|
||||||
return next();
|
}
|
||||||
});
|
);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api {put} /users/:user/filters/:filter Update Filter information
|
||||||
|
* @apiName PutFilter
|
||||||
|
* @apiGroup Filters
|
||||||
|
* @apiDescription This method updates Filter data. To unset a value, use empty strings
|
||||||
|
* @apiHeader {String} X-Access-Token Optional access token if authentication is enabled
|
||||||
|
* @apiHeaderExample {json} Header-Example:
|
||||||
|
* {
|
||||||
|
* "X-Access-Token": "59fc66a03e54454869460e45"
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @apiParam {String} user Users unique ID.
|
||||||
|
* @apiParam {String} filter Filters unique ID.
|
||||||
|
* @apiParam {String} [name] Name of the Filter
|
||||||
|
* @apiParam {String} [query_from] Partial match for the From: header (case insensitive)
|
||||||
|
* @apiParam {String} [query_to] Partial match for the To:/Cc: headers (case insensitive)
|
||||||
|
* @apiParam {String} [query_subject] Partial match for the Subject: header (case insensitive)
|
||||||
|
* @apiParam {String} [query_text] Fulltext search against message text
|
||||||
|
* @apiParam {Bolean} [query_ha] Does a message have to have an attachment or not
|
||||||
|
* @apiParam {Number} [query_size] Message size in bytes. If the value is a positive number then message needs to be larger, if negative then message needs to be smaller than abs(size) value
|
||||||
|
* @apiParam {Bolean} [action_seen] If true then mark matching messages as Seen
|
||||||
|
* @apiParam {Bolean} [action_flag] If true then mark matching messages as Flagged
|
||||||
|
* @apiParam {Bolean} [action_delete] If true then do not store matching messages
|
||||||
|
* @apiParam {Bolean} [action_spam] If true then store matching messags to Junk Mail folder
|
||||||
|
* @apiParam {String} [action_mailbox] Mailbox ID to store matching messages to
|
||||||
|
* @apiParam {String} [action_forward] An email address where matching messages should be forwarded to
|
||||||
|
* @apiParam {String} [action_targetUrl] An URL where matching messages should be POSTed to
|
||||||
|
*
|
||||||
|
* @apiSuccess {Boolean} success Indicates successful response
|
||||||
|
* @apiSuccess {String} id ID for the created Filter
|
||||||
|
*
|
||||||
|
* @apiError error Description of the error
|
||||||
|
*
|
||||||
|
* @apiExample {curl} Example usage:
|
||||||
|
* curl -i -XPUT http://localhost:8080/users/59fc66a03e54454869460e45/filters/5a1c0ee490a34c67e266931c \
|
||||||
|
* -H 'Content-type: application/json' \
|
||||||
|
* -d '{
|
||||||
|
* "action_seen": "",
|
||||||
|
* "action_flag": true
|
||||||
|
* }'
|
||||||
|
*
|
||||||
|
* @apiSuccessExample {json} Success-Response:
|
||||||
|
* HTTP/1.1 200 OK
|
||||||
|
* {
|
||||||
|
* "success": true
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @apiErrorExample {json} Error-Response:
|
||||||
|
* HTTP/1.1 200 OK
|
||||||
|
* {
|
||||||
|
* "error": "Empty filter query"
|
||||||
|
* }
|
||||||
|
*/
|
||||||
server.put('/users/:user/filters/:filter', (req, res, next) => {
|
server.put('/users/:user/filters/:filter', (req, res, next) => {
|
||||||
res.charSet('utf-8');
|
res.charSet('utf-8');
|
||||||
|
|
||||||
const schema = Joi.object().keys({
|
const schema = Joi.object().keys({
|
||||||
user: Joi.string().hex().lowercase().length(24).required(),
|
user: Joi.string()
|
||||||
filter: Joi.string().hex().lowercase().length(24).required(),
|
.hex()
|
||||||
|
.lowercase()
|
||||||
|
.length(24)
|
||||||
|
.required(),
|
||||||
|
filter: Joi.string()
|
||||||
|
.hex()
|
||||||
|
.lowercase()
|
||||||
|
.length(24)
|
||||||
|
.required(),
|
||||||
|
|
||||||
name: Joi.string().trim().max(255).empty(''),
|
name: Joi.string()
|
||||||
|
.trim()
|
||||||
|
.max(255)
|
||||||
|
.empty(''),
|
||||||
|
|
||||||
query_from: Joi.string().trim().max(255).empty(''),
|
query_from: Joi.string()
|
||||||
query_to: Joi.string().trim().max(255).empty(''),
|
.trim()
|
||||||
query_subject: Joi.string().trim().max(255).empty(''),
|
.max(255)
|
||||||
query_text: Joi.string().trim().max(255).empty(''),
|
.empty(''),
|
||||||
query_ha: Joi.boolean().truthy(['Y', 'true', 'yes', 1]).empty(''),
|
query_to: Joi.string()
|
||||||
|
.trim()
|
||||||
|
.max(255)
|
||||||
|
.empty(''),
|
||||||
|
query_subject: Joi.string()
|
||||||
|
.trim()
|
||||||
|
.max(255)
|
||||||
|
.empty(''),
|
||||||
|
query_text: Joi.string()
|
||||||
|
.trim()
|
||||||
|
.max(255)
|
||||||
|
.empty(''),
|
||||||
|
query_ha: Joi.boolean()
|
||||||
|
.truthy(['Y', 'true', 'yes', 1])
|
||||||
|
.empty(''),
|
||||||
query_size: Joi.number().empty(''),
|
query_size: Joi.number().empty(''),
|
||||||
|
|
||||||
action_seen: Joi.boolean().truthy(['Y', 'true', 'yes', 1]).empty(''),
|
action_seen: Joi.boolean()
|
||||||
action_flag: Joi.boolean().truthy(['Y', 'true', 'yes', 1]).empty(''),
|
.truthy(['Y', 'true', 'yes', 1])
|
||||||
action_delete: Joi.boolean().truthy(['Y', 'true', 'yes', 1]).empty(''),
|
.empty(''),
|
||||||
action_spam: Joi.boolean().truthy(['Y', 'true', 'yes', 1]).empty(''),
|
action_flag: Joi.boolean()
|
||||||
|
.truthy(['Y', 'true', 'yes', 1])
|
||||||
|
.empty(''),
|
||||||
|
action_delete: Joi.boolean()
|
||||||
|
.truthy(['Y', 'true', 'yes', 1])
|
||||||
|
.empty(''),
|
||||||
|
action_spam: Joi.boolean()
|
||||||
|
.truthy(['Y', 'true', 'yes', 1])
|
||||||
|
.empty(''),
|
||||||
|
|
||||||
action_mailbox: Joi.string().hex().lowercase().length(24).empty(''),
|
action_mailbox: Joi.string()
|
||||||
action_forward: Joi.string().email().empty(''),
|
.hex()
|
||||||
|
.lowercase()
|
||||||
|
.length(24)
|
||||||
|
.empty(''),
|
||||||
|
action_forward: Joi.string()
|
||||||
|
.email()
|
||||||
|
.empty(''),
|
||||||
action_targetUrl: Joi.string()
|
action_targetUrl: Joi.string()
|
||||||
.uri({
|
.uri({
|
||||||
scheme: ['http', 'https'],
|
scheme: ['http', 'https'],
|
||||||
|
@ -537,26 +890,29 @@ module.exports = (db, server) => {
|
||||||
}
|
}
|
||||||
return done();
|
return done();
|
||||||
}
|
}
|
||||||
db.database.collection('mailboxes').findOne({
|
db.database.collection('mailboxes').findOne(
|
||||||
_id: new ObjectID(result.value.action_mailbox),
|
{
|
||||||
user
|
_id: new ObjectID(result.value.action_mailbox),
|
||||||
}, (err, mailboxData) => {
|
user
|
||||||
if (err) {
|
},
|
||||||
res.json({
|
(err, mailboxData) => {
|
||||||
error: 'MongoDB Error: ' + err.message
|
if (err) {
|
||||||
});
|
res.json({
|
||||||
return next();
|
error: 'MongoDB Error: ' + err.message
|
||||||
|
});
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
if (!mailboxData) {
|
||||||
|
res.json({
|
||||||
|
error: 'This mailbox does not exist'
|
||||||
|
});
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
$set['action.mailbox'] = mailboxData._id;
|
||||||
|
hasAction = true;
|
||||||
|
done();
|
||||||
}
|
}
|
||||||
if (!mailboxData) {
|
);
|
||||||
res.json({
|
|
||||||
error: 'This mailbox does not exist'
|
|
||||||
});
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
$set['action.mailbox'] = mailboxData._id;
|
|
||||||
hasAction = true;
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
checkFilterMailbox(() => {
|
checkFilterMailbox(() => {
|
||||||
|
|
Loading…
Add table
Reference in a new issue