node library to send emails
This commit is contained in:
commit
9c836745bb
4
.gitignore
vendored
Executable file
4
.gitignore
vendored
Executable file
|
@ -0,0 +1,4 @@
|
|||
node_modules/
|
||||
npm-debug.log
|
||||
.DS_Store
|
||||
package-lock.json
|
100
README.md
Normal file
100
README.md
Normal file
|
@ -0,0 +1,100 @@
|
|||
# digi-sendzi ✉️
|
||||
|
||||
digi-sendzi is a simple yet powerful SMTP email-sending library built in JavaScript. It provides an easy-to-use interface for sending emails, supporting plain text, HTML content, and attachments.
|
||||
|
||||
## Features 🌟
|
||||
|
||||
- **Send emails using SMTP protocol**: Reliable and secure email delivery.
|
||||
- **Support for multipart emails**: Easily send both text and HTML formats.
|
||||
- **Attach files to emails**: Include attachments for richer communication.
|
||||
- **Basic authentication support**: Use your username and password for authentication.
|
||||
- **Simple error handling**: Manage errors with ease.
|
||||
|
||||
## Installation 🛠️
|
||||
|
||||
To install digi-sendzi, you can use npm:
|
||||
|
||||
```bash
|
||||
npm install digi-sendzi
|
||||
```
|
||||
## Usage 📧
|
||||
Basic Example
|
||||
Here’s a simple example of how to use digi-sendzi to send an email:
|
||||
|
||||
```javascript
|
||||
import { createTransport } from 'digi-sendzi';
|
||||
|
||||
const transporter = createTransport({
|
||||
host: 'smtp.example.com',
|
||||
port: 587,
|
||||
auth: {
|
||||
user: 'your-email@example.com',
|
||||
pass: 'your-email-password'
|
||||
}
|
||||
});
|
||||
|
||||
const mailOptions = {
|
||||
from: 'your-email@example.com',
|
||||
to: 'recipient@example.com',
|
||||
subject: 'Hello from digi-sendzi! 🌍',
|
||||
text: 'This is a plain text message.',
|
||||
html: '<strong>This is a bold HTML message.</strong>',
|
||||
attachments: [
|
||||
{
|
||||
filename: 'example.txt',
|
||||
content: 'Hello, this is the content of the file!',
|
||||
contentType: 'text/plain'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
transporter.sendMail(mailOptions)
|
||||
.then(() => {
|
||||
console.log('Email sent successfully! 🎉');
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error sending email:', error.message);
|
||||
});
|
||||
```
|
||||
## API Reference 📚
|
||||
|
||||
### createTransport(options)
|
||||
Creates a new Transporter instance.
|
||||
|
||||
**options:** An object containing the following properties:
|
||||
- **host** (string): The SMTP server host.
|
||||
- **port** (number, optional): The port to connect to (default is 25).
|
||||
- **auth** (object, optional): An object containing `user` and `pass` for authentication.
|
||||
|
||||
### Transporter.sendMail(mailOptions)
|
||||
Sends an email with the specified options.
|
||||
|
||||
**mailOptions:** An object containing the following properties:
|
||||
- **from** (string): Sender's email address (required).
|
||||
- **to** (string): Recipient's email address (required).
|
||||
- **subject** (string): Email subject (required).
|
||||
- **text** (string): Plain text body of the email (required).
|
||||
- **html** (string, optional): HTML body of the email.
|
||||
- **attachments** (array, optional): An array of attachment objects with `filename`, `content`, and `contentType`.
|
||||
|
||||
## Error Handling ⚠️
|
||||
digi-sendzi provides basic error handling. If any required fields are missing or an error occurs during the SMTP connection or email sending, an error will be thrown. You can catch these errors using a `.catch()` block when sending an email.
|
||||
|
||||
## Development 💻
|
||||
To run tests, ensure you have Jest installed as a dev dependency, and run:
|
||||
|
||||
```bash
|
||||
npm test
|
||||
```
|
||||
|
||||
## License 📝
|
||||
This project is licensed under the ISC License.
|
||||
|
||||
## Author ✍️
|
||||
DigiMantra
|
||||
|
||||
## Links
|
||||
|
||||
* **GitHub**: [digi-sendzi Repository](https://git.digimantra.com/inder-dml/digi-sendzi.git)
|
||||
* **NPM**: [digi-sendzi Package](https://www.npmjs.com/package/digi-sendzi)
|
||||
|
5
index.js
Normal file
5
index.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
import Transporter from './lib/transporter.js';
|
||||
|
||||
export function createTransport(options) {
|
||||
return new Transporter(options);
|
||||
}
|
36
lib/email.js
Normal file
36
lib/email.js
Normal file
|
@ -0,0 +1,36 @@
|
|||
class Email {
|
||||
constructor(from, to, subject, body, html = null, attachments = []) {
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
this.subject = subject;
|
||||
this.body = body;
|
||||
this.html = html; // Optional HTML version
|
||||
this.attachments = attachments;
|
||||
this.boundary = `----Boundary${Math.random().toString(36).substring(2)}`; // Unique boundary string
|
||||
}
|
||||
|
||||
format() {
|
||||
let emailString = `From: ${this.from}\r\nTo: ${this.to}\r\nSubject: ${this.subject}\r\nMIME-Version: 1.0\r\nContent-Type: multipart/mixed; boundary="${this.boundary}"\r\n\r\n`;
|
||||
|
||||
// Add body as text or both text and HTML parts
|
||||
if (this.body) {
|
||||
emailString += `--${this.boundary}\r\nContent-Type: text/plain; charset="UTF-8"\r\n\r\n${this.body}\r\n`;
|
||||
}
|
||||
if (this.html) {
|
||||
emailString += `--${this.boundary}\r\nContent-Type: text/html; charset="UTF-8"\r\n\r\n${this.html}\r\n`;
|
||||
}
|
||||
|
||||
// Add attachments if any
|
||||
if (this.attachments.length > 0) {
|
||||
this.attachments.forEach(attachment => {
|
||||
emailString += `--${this.boundary}\r\nContent-Disposition: attachment; filename="${attachment.filename}"\r\nContent-Type: ${attachment.contentType}; name="${attachment.filename}"\r\nContent-Transfer-Encoding: base64\r\n\r\n${attachment.content}\r\n`;
|
||||
});
|
||||
}
|
||||
|
||||
// Close the MIME message
|
||||
emailString += `--${this.boundary}--\r\n`;
|
||||
return emailString;
|
||||
}
|
||||
}
|
||||
|
||||
export default Email;
|
109
lib/smtp.js
Normal file
109
lib/smtp.js
Normal file
|
@ -0,0 +1,109 @@
|
|||
import { connect } from 'tls'; // For SSL and STARTTLS
|
||||
import { Socket } from 'net'; // For non-encrypted connections (before STARTTLS)
|
||||
|
||||
class SMTPClient {
|
||||
constructor(host, port = 587, auth) {
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
this.auth = auth;
|
||||
this.socket = null;
|
||||
}
|
||||
|
||||
connect() {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.port === 465) {
|
||||
// SSL connection (SMTPS)
|
||||
this.socket = connect({ host: this.host, port: this.port, rejectUnauthorized: false }, () => {
|
||||
console.log(`Connected to SMTP server with SSL at ${this.host}:${this.port}`);
|
||||
resolve();
|
||||
});
|
||||
} else if (this.port === 587) {
|
||||
// Non-encrypted connection, STARTTLS will be applied later
|
||||
this.socket = new Socket();
|
||||
this.socket.connect(this.port, this.host, () => {
|
||||
console.log(`Connected to SMTP server at ${this.host}:${this.port}`);
|
||||
resolve();
|
||||
});
|
||||
} else {
|
||||
// Plain non-encrypted connection (used for ports like 2525)
|
||||
this.socket = new Socket();
|
||||
this.socket.connect(this.port, this.host, () => {
|
||||
console.log(`Connected to SMTP server at ${this.host}:${this.port} (No SSL/TLS)`);
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
this.socket.on('error', (err) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async sendEmail(from, to, subject, body) {
|
||||
try {
|
||||
if (this.port === 587) {
|
||||
// If using STARTTLS
|
||||
await this.sendCommand(`EHLO ${this.host}`);
|
||||
await this.sendCommand(`STARTTLS`);
|
||||
this.upgradeToTLS(); // Upgrade connection to TLS after STARTTLS
|
||||
}
|
||||
|
||||
await this.sendCommand(`EHLO ${this.host}`); // Send EHLO again after STARTTLS or for SSL
|
||||
await this.sendCommand(`AUTH LOGIN`);
|
||||
await this.sendAuth();
|
||||
await this.sendCommand(`MAIL FROM:<${from}>`);
|
||||
await this.sendCommand(`RCPT TO:<${to}>`);
|
||||
await this.sendCommand('DATA');
|
||||
await this.sendCommand(`${body}\r\n.`);
|
||||
await this.sendCommand('QUIT');
|
||||
} catch (error) {
|
||||
throw new Error(`Error sending email: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
async sendAuth() {
|
||||
const base64User = Buffer.from(this.auth.user).toString('base64');
|
||||
const base64Pass = Buffer.from(this.auth.pass).toString('base64');
|
||||
await this.sendCommand(base64User);
|
||||
return await this.sendCommand(base64Pass);
|
||||
}
|
||||
|
||||
sendCommand(command) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.socket.write(`${command}\r\n`, (err) => {
|
||||
if (err) return reject(err);
|
||||
|
||||
this.socket.once('data', (data) => {
|
||||
const response = data.toString();
|
||||
console.log('SMTP Response:', response);
|
||||
|
||||
if (response.startsWith('250') || response.startsWith('354') || response.startsWith('235')) {
|
||||
resolve(response);
|
||||
} else if (response.startsWith('334')) {
|
||||
resolve(); // Continue authentication
|
||||
} else if (response.startsWith('221')) {
|
||||
console.log('SMTP session closed successfully.');
|
||||
resolve(response);
|
||||
} else if (response.startsWith('220')) {
|
||||
resolve(); // Initial greeting
|
||||
} else {
|
||||
console.error('SMTP Error:', response);
|
||||
reject(new Error(`SMTP Error: ${response}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Upgrade the current connection to TLS (STARTTLS mode)
|
||||
upgradeToTLS() {
|
||||
this.socket = connect({
|
||||
socket: this.socket, // Upgrade the existing socket to TLS
|
||||
rejectUnauthorized: false
|
||||
}, () => {
|
||||
console.log(`Connection upgraded to TLS`);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default SMTPClient;
|
33
lib/transporter.js
Normal file
33
lib/transporter.js
Normal file
|
@ -0,0 +1,33 @@
|
|||
import SMTPClient from './smtp.js';
|
||||
import Email from './email.js';
|
||||
|
||||
class Transporter {
|
||||
constructor(options) {
|
||||
this.host = options.host;
|
||||
this.port = options.port || 25;
|
||||
this.auth = options.auth;
|
||||
|
||||
this.client = new SMTPClient(this.host, this.port, this.auth);
|
||||
}
|
||||
|
||||
async sendMail(mailOptions) {
|
||||
const { from, to, subject, text, html, attachments } = mailOptions;
|
||||
|
||||
// Basic validation
|
||||
if (!from || !to || !subject) {
|
||||
throw new Error('Missing required email fields: from, to, subject');
|
||||
}
|
||||
|
||||
try {
|
||||
await this.client.connect();
|
||||
const email = new Email(from, to, subject, text, html, attachments);
|
||||
await this.client.sendEmail(from, to, subject, email.format());
|
||||
console.log('Email sent successfully!');
|
||||
} catch (error) {
|
||||
console.error('Error sending email:', error.message);
|
||||
throw new Error(`Failed to send email: ${error.message}`); // Propagate error to API
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Transporter;
|
20
package.json
Normal file
20
package.json
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"name": "digi-sendzi",
|
||||
"version": "1.0.0",
|
||||
"description": "A simple SMTP email sending library.",
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"test": "jest"
|
||||
},
|
||||
"keywords": [
|
||||
"email",
|
||||
"smtp",
|
||||
"mailer"
|
||||
],
|
||||
"author": "DigiMantra",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"jest": "^29.7.0"
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue