mirror of
https://github.com/nodemailer/wildduck.git
synced 2025-09-08 22:24:29 +08:00
Changed db structure for handling addresses - username is not an address
This commit is contained in:
parent
e0a4ce7b23
commit
f9413d5877
3 changed files with 124 additions and 150 deletions
31
README.md
31
README.md
|
@ -118,18 +118,18 @@ Users can be managed with HTTP requests against Wild Duck API
|
|||
|
||||
### POST /user/create
|
||||
|
||||
Creates a new user. Even though you can use internationalized addresses, it would probably be better to create an ASCII email address as username and add the internationalized address as an alias. otherwise you might get into compatibility issues with email clients that do not support unicode usernames for logging in.
|
||||
Creates a new user.
|
||||
|
||||
Arguments
|
||||
|
||||
- **username** is an email address of the user. Username can not contain + as plus is used to mark recipient labels. Unicode is allowed both in user part and the domain part of the address.
|
||||
- **username** is the username of the user. This is not an email address but authentication username, use only letters and numbers
|
||||
- **password** is the password for the user
|
||||
|
||||
**Example**
|
||||
|
||||
```
|
||||
curl -XPOST "http://localhost:8080/user/create" -H 'content-type: application/json' -d '{
|
||||
"username": "username@example.com",
|
||||
"username": "testuser",
|
||||
"password": "secretpass"
|
||||
}'
|
||||
```
|
||||
|
@ -139,28 +139,27 @@ The response for successful operation should look like this:
|
|||
```json
|
||||
{
|
||||
"success": true,
|
||||
"id": "58d28b91d3e6af19d013315e",
|
||||
"username": "username@example.com"
|
||||
"username": "testuser"
|
||||
}
|
||||
```
|
||||
|
||||
After you have created an user you can use these credentials to log in to the IMAP server. Additionally the LMTP and SMTP servers starts accepting mail for this email address.
|
||||
After you have created an user you can use these credentials to log in to the IMAP server. To be able to receive mail for that user you need to register an email address.
|
||||
|
||||
### POST /user/alias/create
|
||||
### POST /user/address/create
|
||||
|
||||
Creates a new alias for an existing user. You can use internationalized email addresses like _андрис@уайлддак.орг_ for aliases
|
||||
Creates a new email address alias for an existing user. You can use internationalized email addresses like _андрис@уайлддак.орг_.
|
||||
|
||||
Arguments
|
||||
|
||||
- **user** is the user ID
|
||||
- **alias** is the email address to use as an alias for this user
|
||||
- **username** is the username
|
||||
- **address** is the email address to use as an alias for this user
|
||||
|
||||
**Example**
|
||||
|
||||
```
|
||||
curl -XPOST "http://localhost:8080/user/alias/create" -H 'content-type: application/json' -d '{
|
||||
"user": "58d28b91d3e6af19d013315e",
|
||||
"alias": "alias@example.com"
|
||||
curl -XPOST "http://localhost:8080/user/address/create" -H 'content-type: application/json' -d '{
|
||||
"username": "testuser",
|
||||
"address": "user@example.com"
|
||||
}'
|
||||
```
|
||||
|
||||
|
@ -169,12 +168,12 @@ The response for successful operation should look like this:
|
|||
```json
|
||||
{
|
||||
"success": true,
|
||||
"id": "58bd6815dddb5ac5063d3590",
|
||||
"username": "username@example.com"
|
||||
"username": "testuser",
|
||||
"address": "user@example.com"
|
||||
}
|
||||
```
|
||||
|
||||
After you have created an user you can use these credentials to log in to the IMAP server. Additionally the LMTP and SMTP servers starts accepting mail for this email address.
|
||||
After you have registered a new address then LMTP and SMTP maildrop servers start accepting mail for it and store the messages to the users mailbox.
|
||||
|
||||
## Testing
|
||||
|
||||
|
|
241
api.js
241
api.js
|
@ -8,7 +8,6 @@ const MongoClient = mongodb.MongoClient;
|
|||
const Joi = require('joi');
|
||||
const bcrypt = require('bcryptjs');
|
||||
const tools = require('./lib/tools');
|
||||
const ObjectID = require('mongodb').ObjectID;
|
||||
|
||||
let database;
|
||||
|
||||
|
@ -23,14 +22,12 @@ server.use(restify.bodyParser({
|
|||
|
||||
server.post('/user/create', (req, res, next) => {
|
||||
const schema = Joi.object().keys({
|
||||
username: Joi.string().email().required(),
|
||||
username: Joi.string().alphanum().lowercase().min(3).max(30).required(),
|
||||
password: Joi.string().min(3).max(100).required()
|
||||
});
|
||||
|
||||
let username = req.params.username;
|
||||
|
||||
const result = Joi.validate({
|
||||
username: username.replace(/[\u0080-\uFFFF]/g, 'x'),
|
||||
username: req.params.username,
|
||||
password: req.params.password
|
||||
}, schema, {
|
||||
abortEarly: false,
|
||||
|
@ -45,19 +42,12 @@ server.post('/user/create', (req, res, next) => {
|
|||
return next();
|
||||
}
|
||||
|
||||
username = tools.normalizeAddress(username);
|
||||
let username = result.value.username;
|
||||
let password = result.value.password;
|
||||
|
||||
if (username.indexOf('+') >= 0) {
|
||||
res.json({
|
||||
error: 'Username can not contain +'
|
||||
});
|
||||
return next();
|
||||
}
|
||||
|
||||
database.collection('users').findOne({
|
||||
username
|
||||
}, (err, user) => {
|
||||
}, (err, userData) => {
|
||||
if (err) {
|
||||
res.json({
|
||||
error: 'MongoDB Error: ' + err.message,
|
||||
|
@ -65,7 +55,7 @@ server.post('/user/create', (req, res, next) => {
|
|||
});
|
||||
return next();
|
||||
}
|
||||
if (user) {
|
||||
if (userData) {
|
||||
res.json({
|
||||
error: 'This username already exists',
|
||||
username
|
||||
|
@ -73,31 +63,61 @@ server.post('/user/create', (req, res, next) => {
|
|||
return next();
|
||||
}
|
||||
|
||||
database.collection('addresses').findOne({
|
||||
address: username
|
||||
}, (err, address) => {
|
||||
// Insert
|
||||
let hash = bcrypt.hashSync(password, 11);
|
||||
database.collection('users').insertOne({
|
||||
username,
|
||||
password: hash,
|
||||
address: false,
|
||||
created: new Date()
|
||||
}, (err, result) => {
|
||||
if (err) {
|
||||
res.json({
|
||||
error: 'MongoDB Error: ' + err.message,
|
||||
address: username
|
||||
});
|
||||
return next();
|
||||
}
|
||||
if (address) {
|
||||
res.json({
|
||||
error: 'This email address already exists',
|
||||
address: username
|
||||
username
|
||||
});
|
||||
return next();
|
||||
}
|
||||
|
||||
// Insert
|
||||
let hash = bcrypt.hashSync(password, 11);
|
||||
database.collection('users').insertOne({
|
||||
username,
|
||||
password: hash,
|
||||
created: new Date()
|
||||
}, (err, result) => {
|
||||
let user = result.insertedId;
|
||||
|
||||
// create folders for user
|
||||
let uidValidity = Math.floor(Date.now() / 1000);
|
||||
database.collection('mailboxes').insertMany([{
|
||||
user,
|
||||
path: 'INBOX',
|
||||
uidValidity,
|
||||
uidNext: 1,
|
||||
modifyIndex: 0,
|
||||
subscribed: true
|
||||
}, {
|
||||
user,
|
||||
path: 'Sent Mail',
|
||||
specialUse: '\\Sent',
|
||||
uidValidity,
|
||||
uidNext: 1,
|
||||
modifyIndex: 0,
|
||||
subscribed: true
|
||||
}, {
|
||||
user,
|
||||
path: 'Trash',
|
||||
specialUse: '\\Trash',
|
||||
uidValidity,
|
||||
uidNext: 1,
|
||||
modifyIndex: 0,
|
||||
subscribed: true
|
||||
}, {
|
||||
user,
|
||||
path: 'Junk',
|
||||
specialUse: '\\Junk',
|
||||
uidValidity,
|
||||
uidNext: 1,
|
||||
modifyIndex: 0,
|
||||
subscribed: true
|
||||
}], {
|
||||
w: 1,
|
||||
ordered: false
|
||||
}, err => {
|
||||
if (err) {
|
||||
res.json({
|
||||
error: 'MongoDB Error: ' + err.message,
|
||||
|
@ -106,93 +126,32 @@ server.post('/user/create', (req, res, next) => {
|
|||
return next();
|
||||
}
|
||||
|
||||
let userId = result.insertedId;
|
||||
|
||||
// insert address to email address registry
|
||||
database.collection('addresses').insertOne({
|
||||
user: userId,
|
||||
address: username,
|
||||
created: new Date()
|
||||
}, err => {
|
||||
if (err) {
|
||||
res.json({
|
||||
error: 'MongoDB Error: ' + err.message,
|
||||
username
|
||||
});
|
||||
return next();
|
||||
}
|
||||
|
||||
// create folders for user
|
||||
let uidValidity = Math.floor(Date.now() / 1000);
|
||||
database.collection('mailboxes').insertMany([{
|
||||
user: userId,
|
||||
path: 'INBOX',
|
||||
uidValidity,
|
||||
uidNext: 1,
|
||||
modifyIndex: 0,
|
||||
subscribed: true
|
||||
}, {
|
||||
user: userId,
|
||||
path: 'Sent Mail',
|
||||
specialUse: '\\Sent',
|
||||
uidValidity,
|
||||
uidNext: 1,
|
||||
modifyIndex: 0,
|
||||
subscribed: true
|
||||
}, {
|
||||
user: userId,
|
||||
path: 'Trash',
|
||||
specialUse: '\\Trash',
|
||||
uidValidity,
|
||||
uidNext: 1,
|
||||
modifyIndex: 0,
|
||||
subscribed: true
|
||||
}, {
|
||||
user: userId,
|
||||
path: 'Junk',
|
||||
specialUse: '\\Junk',
|
||||
uidValidity,
|
||||
uidNext: 1,
|
||||
modifyIndex: 0,
|
||||
subscribed: true
|
||||
}], {
|
||||
w: 1,
|
||||
ordered: false
|
||||
}, err => {
|
||||
if (err) {
|
||||
res.json({
|
||||
error: 'MongoDB Error: ' + err.message,
|
||||
username
|
||||
});
|
||||
return next();
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
id: userId,
|
||||
username
|
||||
});
|
||||
|
||||
return next();
|
||||
});
|
||||
res.json({
|
||||
success: true,
|
||||
username
|
||||
});
|
||||
|
||||
return next();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
server.post('/user/alias/create', (req, res, next) => {
|
||||
server.post('/user/address/create', (req, res, next) => {
|
||||
const schema = Joi.object().keys({
|
||||
user: Joi.string().hex().length(24).required(),
|
||||
alias: Joi.string().email().required()
|
||||
username: Joi.string().alphanum().lowercase().min(3).max(30).required(),
|
||||
address: Joi.string().email().required(),
|
||||
main: Joi.boolean().truthy(['Y', 'true', 'yes', 1]).optional()
|
||||
});
|
||||
|
||||
let userId = req.params.user;
|
||||
let alias = req.params.alias;
|
||||
let username = req.params.username;
|
||||
let address = req.params.address;
|
||||
let main = req.params.main;
|
||||
|
||||
const result = Joi.validate({
|
||||
user: userId,
|
||||
alias: alias.replace(/[\u0080-\uFFFF]/g, 'x')
|
||||
username,
|
||||
address: (address || '').replace(/[\u0080-\uFFFF]/g, 'x'),
|
||||
main
|
||||
}, schema, {
|
||||
abortEarly: false,
|
||||
convert: true,
|
||||
|
@ -206,10 +165,11 @@ server.post('/user/alias/create', (req, res, next) => {
|
|||
return next();
|
||||
}
|
||||
|
||||
userId = new ObjectID(userId);
|
||||
alias = tools.normalizeAddress(alias);
|
||||
username = result.value.username;
|
||||
address = tools.normalizeAddress(address);
|
||||
main = result.value.main;
|
||||
|
||||
if (alias.indexOf('+') >= 0) {
|
||||
if (address.indexOf('+') >= 0) {
|
||||
res.json({
|
||||
error: 'Address can not contain +'
|
||||
});
|
||||
|
@ -217,63 +177,78 @@ server.post('/user/alias/create', (req, res, next) => {
|
|||
}
|
||||
|
||||
database.collection('users').findOne({
|
||||
_id: userId
|
||||
}, (err, user) => {
|
||||
username
|
||||
}, (err, userData) => {
|
||||
if (err) {
|
||||
res.json({
|
||||
error: 'MongoDB Error: ' + err.message,
|
||||
user: userId.toString()
|
||||
username
|
||||
});
|
||||
return next();
|
||||
}
|
||||
if (!user) {
|
||||
if (!userData) {
|
||||
res.json({
|
||||
error: 'This user does not exist',
|
||||
user: userId.toString()
|
||||
username
|
||||
});
|
||||
return next();
|
||||
}
|
||||
|
||||
database.collection('addresses').findOne({
|
||||
address: alias
|
||||
}, (err, address) => {
|
||||
address
|
||||
}, (err, addressData) => {
|
||||
if (err) {
|
||||
res.json({
|
||||
error: 'MongoDB Error: ' + err.message,
|
||||
address: alias
|
||||
username,
|
||||
address
|
||||
});
|
||||
return next();
|
||||
}
|
||||
if (address) {
|
||||
if (addressData) {
|
||||
res.json({
|
||||
error: 'This email address already exists',
|
||||
address: alias
|
||||
username,
|
||||
address
|
||||
});
|
||||
return next();
|
||||
}
|
||||
|
||||
|
||||
// insert alias address to email address registry
|
||||
database.collection('addresses').insertOne({
|
||||
user: userId,
|
||||
address: alias,
|
||||
user: userData._id,
|
||||
address,
|
||||
created: new Date()
|
||||
}, (err, result) => {
|
||||
}, err => {
|
||||
if (err) {
|
||||
res.json({
|
||||
error: 'MongoDB Error: ' + err.message,
|
||||
address: alias
|
||||
address
|
||||
});
|
||||
return next();
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
id: result.insertedId,
|
||||
alias
|
||||
});
|
||||
let done = () => {
|
||||
res.json({
|
||||
success: true,
|
||||
username,
|
||||
address
|
||||
});
|
||||
return next();
|
||||
};
|
||||
|
||||
return next();
|
||||
if (!userData.address || main) {
|
||||
// register this address as the default address for that user
|
||||
return database.collection('users').findOneAndUpdate({
|
||||
_id: userData._id
|
||||
}, {
|
||||
$set: {
|
||||
address
|
||||
}
|
||||
}, {}, done);
|
||||
}
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "wildduck",
|
||||
"version": "1.0.5",
|
||||
"version": "1.0.6",
|
||||
"description": "IMAP server built with Node.js and MongoDB",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
|
|
Loading…
Add table
Reference in a new issue