Initial commit
This commit is contained in:
620
skills/mailhog/references/api-endpoints.md
Normal file
620
skills/mailhog/references/api-endpoints.md
Normal file
@@ -0,0 +1,620 @@
|
||||
# MailHog API Complete Reference
|
||||
|
||||
## Base URL
|
||||
|
||||
```
|
||||
http://localhost:8025/api/v1
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
If authentication is enabled via `-auth-file`, include Basic Auth headers:
|
||||
|
||||
```bash
|
||||
curl -u username:password http://localhost:8025/api/v1/messages
|
||||
```
|
||||
|
||||
## Message Endpoints
|
||||
|
||||
### Get All Messages
|
||||
|
||||
```bash
|
||||
curl http://localhost:8025/api/v1/messages
|
||||
```
|
||||
|
||||
**Response Format:**
|
||||
```json
|
||||
{
|
||||
"total": 2,
|
||||
"count": 2,
|
||||
"start": 0,
|
||||
"items": [
|
||||
{
|
||||
"ID": "1234567890abcdef1234567890abcdef",
|
||||
"From": "sender@example.com",
|
||||
"To": [
|
||||
{
|
||||
"Address": "recipient@example.com",
|
||||
"Name": "Recipient Name"
|
||||
}
|
||||
],
|
||||
"Subject": "Test Email",
|
||||
"Date": "2023-01-01T12:00:00Z",
|
||||
"Content": {
|
||||
"Headers": {
|
||||
"Content-Type": ["text/plain; charset=utf-8"],
|
||||
"Subject": ["Test Email"],
|
||||
"From": ["sender@example.com"],
|
||||
"To": ["recipient@example.com"]
|
||||
},
|
||||
"Body": "This is the email body",
|
||||
"Size": 1024
|
||||
},
|
||||
"MIME": {
|
||||
"Parts": [...]
|
||||
},
|
||||
"Created": "2023-01-01T12:00:00Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Get Specific Message
|
||||
|
||||
```bash
|
||||
curl http://localhost:8025/api/v1/messages/{message-id}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl http://localhost:8025/api/v1/messages/1234567890abcdef1234567890abcdef
|
||||
```
|
||||
|
||||
### Get Messages with Limits
|
||||
|
||||
```bash
|
||||
# Get first 10 messages
|
||||
curl "http://localhost:8025/api/v1/messages?limit=10"
|
||||
|
||||
# Get messages with offset (pagination)
|
||||
curl "http://localhost:8025/api/v1/messages?start=10&limit=10"
|
||||
|
||||
# Get recent messages (sorted by creation date)
|
||||
curl "http://localhost:8025/api/v1/messages?start=0&limit=20"
|
||||
```
|
||||
|
||||
### Delete All Messages
|
||||
|
||||
```bash
|
||||
curl -X DELETE http://localhost:8025/api/v1/messages
|
||||
```
|
||||
|
||||
### Delete Specific Message
|
||||
|
||||
```bash
|
||||
curl -X DELETE http://localhost:8025/api/v1/messages/{message-id}
|
||||
```
|
||||
|
||||
## Search Endpoints
|
||||
|
||||
### Search Messages
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8025/api/v1/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query": "search term"}'
|
||||
```
|
||||
|
||||
**Advanced Search Examples:**
|
||||
```bash
|
||||
# Search by subject
|
||||
curl -X POST http://localhost:8025/api/v1/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query": "subject:welcome"}'
|
||||
|
||||
# Search by sender
|
||||
curl -X POST http://localhost:8025/api/v1/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query": "from:user@example.com"}'
|
||||
|
||||
# Search by recipient
|
||||
curl -X POST http://localhost:8025/api/v1/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query": "to:recipient@example.com"}'
|
||||
|
||||
# Search body content
|
||||
curl -X POST http://localhost:8025/api/v1/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query": "content:confirmation link"}'
|
||||
```
|
||||
|
||||
**Search Response:**
|
||||
```json
|
||||
{
|
||||
"total": 1,
|
||||
"count": 1,
|
||||
"start": 0,
|
||||
"items": [...]
|
||||
}
|
||||
```
|
||||
|
||||
## MIME and Attachment Endpoints
|
||||
|
||||
### Get MIME Content
|
||||
|
||||
```bash
|
||||
curl http://localhost:8025/api/v1/messages/{message-id}/mime
|
||||
```
|
||||
|
||||
### Download Attachment
|
||||
|
||||
```bash
|
||||
curl http://localhost:8025/api/v1/messages/{message-id}/attachments/{attachment-id}
|
||||
```
|
||||
|
||||
### List Attachments
|
||||
|
||||
```bash
|
||||
curl http://localhost:8025/api/v1/messages/{message-id}/attachments
|
||||
```
|
||||
|
||||
## Release Endpoints (Outgoing SMTP)
|
||||
|
||||
### Release One Message
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8025/api/v1/messages/{message-id}/release
|
||||
```
|
||||
|
||||
**With Custom Recipients:**
|
||||
```bash
|
||||
curl -X POST http://localhost:8025/api/v1/messages/{message-id}/release \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"recipients": ["alt@example.com"]}'
|
||||
```
|
||||
|
||||
### Release Multiple Messages
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8025/api/v1/messages/release \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"ids": ["id1", "id2", "id3"]}'
|
||||
```
|
||||
|
||||
## Status and Health Endpoints
|
||||
|
||||
### Get Server Status
|
||||
|
||||
```bash
|
||||
curl http://localhost:8025/api/v1/status
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"messages": 42,
|
||||
"storage": "memory",
|
||||
"version": "v1.0.0"
|
||||
}
|
||||
```
|
||||
|
||||
### Health Check
|
||||
|
||||
```bash
|
||||
curl -f http://localhost:8025/api/v1/health
|
||||
```
|
||||
|
||||
## Utility Endpoints
|
||||
|
||||
### Get Raw Email Source
|
||||
|
||||
```bash
|
||||
curl http://localhost:8025/api/v1/messages/{message-id}/raw
|
||||
```
|
||||
|
||||
### Parse Email Headers
|
||||
|
||||
```bash
|
||||
curl http://localhost:8025/api/v1/messages/{message-id}/headers
|
||||
```
|
||||
|
||||
## WebSockets
|
||||
|
||||
### Real-time Message Updates
|
||||
|
||||
```javascript
|
||||
const ws = new WebSocket('ws://localhost:8025/api/v1/websocket');
|
||||
|
||||
ws.onmessage = function(event) {
|
||||
const data = JSON.parse(event.data);
|
||||
|
||||
switch(data.action) {
|
||||
case 'messages':
|
||||
console.log('New messages:', data.messages);
|
||||
break;
|
||||
case 'deleted':
|
||||
console.log('Deleted messages:', data.ids);
|
||||
break;
|
||||
case 'released':
|
||||
console.log('Released messages:', data.ids);
|
||||
break;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
**WebSocket Message Formats:**
|
||||
|
||||
**New Message Notification:**
|
||||
```json
|
||||
{
|
||||
"action": "messages",
|
||||
"messages": [
|
||||
{
|
||||
"ID": "message-id",
|
||||
"From": "sender@example.com",
|
||||
"To": [{"Address": "recipient@example.com"}],
|
||||
"Subject": "Test",
|
||||
"Created": "2023-01-01T12:00:00Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Message Deleted:**
|
||||
```json
|
||||
{
|
||||
"action": "deleted",
|
||||
"ids": ["message-id"]
|
||||
}
|
||||
```
|
||||
|
||||
**Message Released:**
|
||||
```json
|
||||
{
|
||||
"action": "released",
|
||||
"ids": ["message-id"]
|
||||
}
|
||||
```
|
||||
|
||||
## JavaScript Client Examples
|
||||
|
||||
### Basic API Client
|
||||
|
||||
```javascript
|
||||
class MailHogClient {
|
||||
constructor(baseUrl = 'http://localhost:8025', auth = null) {
|
||||
this.baseUrl = baseUrl;
|
||||
this.auth = auth;
|
||||
}
|
||||
|
||||
async request(endpoint, options = {}) {
|
||||
const url = `${this.baseUrl}/api/v1${endpoint}`;
|
||||
const config = {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options.headers
|
||||
},
|
||||
...options
|
||||
};
|
||||
|
||||
if (this.auth) {
|
||||
config.headers.Authorization = `Basic ${btoa(this.auth)}`;
|
||||
}
|
||||
|
||||
const response = await fetch(url, config);
|
||||
return response.json();
|
||||
}
|
||||
|
||||
async getMessages(limit = 50, start = 0) {
|
||||
return this.request(`/messages?limit=${limit}&start=${start}`);
|
||||
}
|
||||
|
||||
async getMessage(id) {
|
||||
return this.request(`/messages/${id}`);
|
||||
}
|
||||
|
||||
async searchMessages(query) {
|
||||
return this.request('/search', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ query })
|
||||
});
|
||||
}
|
||||
|
||||
async deleteMessages(ids) {
|
||||
if (ids === 'all') {
|
||||
return this.request('/messages', { method: 'DELETE' });
|
||||
}
|
||||
return this.request(`/messages/${ids}`, { method: 'DELETE' });
|
||||
}
|
||||
|
||||
async releaseMessage(id, recipients = null) {
|
||||
const body = recipients ? { recipients } : undefined;
|
||||
return this.request(`/messages/${id}/release`, {
|
||||
method: 'POST',
|
||||
body: body ? JSON.stringify(body) : undefined
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Usage
|
||||
const client = new MailHogClient();
|
||||
|
||||
// Get all messages
|
||||
client.getMessages().then(data => console.log(data.total));
|
||||
|
||||
// Search for specific emails
|
||||
client.searchMessages('subject:welcome').then(data => console.log(data.items));
|
||||
```
|
||||
|
||||
### WebSocket Integration
|
||||
|
||||
```javascript
|
||||
class MailHogWebSocket {
|
||||
constructor(baseUrl = 'ws://localhost:8025') {
|
||||
this.baseUrl = baseUrl;
|
||||
this.callbacks = {};
|
||||
}
|
||||
|
||||
connect() {
|
||||
this.ws = new WebSocket(`${this.baseUrl}/api/v1/websocket`);
|
||||
|
||||
this.ws.onmessage = (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
|
||||
if (this.callbacks[data.action]) {
|
||||
this.callbacks[data.action](data);
|
||||
}
|
||||
};
|
||||
|
||||
this.ws.onerror = (error) => {
|
||||
console.error('WebSocket error:', error);
|
||||
};
|
||||
|
||||
this.ws.onclose = () => {
|
||||
console.log('WebSocket connection closed');
|
||||
// Auto-reconnect
|
||||
setTimeout(() => this.connect(), 5000);
|
||||
};
|
||||
}
|
||||
|
||||
on(action, callback) {
|
||||
this.callbacks[action] = callback;
|
||||
}
|
||||
|
||||
onNewMessages(callback) {
|
||||
this.on('messages', callback);
|
||||
}
|
||||
|
||||
onMessagesDeleted(callback) {
|
||||
this.on('deleted', callback);
|
||||
}
|
||||
|
||||
onMessagesReleased(callback) {
|
||||
this.on('released', callback);
|
||||
}
|
||||
}
|
||||
|
||||
// Usage
|
||||
const ws = new MailHogWebSocket();
|
||||
ws.connect();
|
||||
|
||||
ws.onNewMessages((data) => {
|
||||
console.log('New messages received:', data.messages);
|
||||
});
|
||||
```
|
||||
|
||||
## Python Client Examples
|
||||
|
||||
### Requests-based Client
|
||||
|
||||
```python
|
||||
import requests
|
||||
import json
|
||||
from typing import List, Dict, Optional
|
||||
|
||||
class MailHogClient:
|
||||
def __init__(self, base_url: str = "http://localhost:8025", auth: tuple = None):
|
||||
self.base_url = base_url
|
||||
self.auth = auth
|
||||
self.api_url = f"{base_url}/api/v1"
|
||||
|
||||
def _request(self, endpoint: str, method: str = "GET", data: dict = None) -> dict:
|
||||
url = f"{self.api_url}{endpoint}"
|
||||
response = requests.request(
|
||||
method,
|
||||
url,
|
||||
json=data,
|
||||
auth=self.auth
|
||||
)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
def get_messages(self, limit: int = 50, start: int = 0) -> dict:
|
||||
return self._request(f"/messages?limit={limit}&start={start}")
|
||||
|
||||
def get_message(self, message_id: str) -> dict:
|
||||
return self._request(f"/messages/{message_id}")
|
||||
|
||||
def search_messages(self, query: str) -> dict:
|
||||
return self._request("/search", method="POST", data={"query": query})
|
||||
|
||||
def delete_messages(self, message_ids: str = "all") -> dict:
|
||||
if message_ids == "all":
|
||||
return self._request("/messages", method="DELETE")
|
||||
return self._request(f"/messages/{message_ids}", method="DELETE")
|
||||
|
||||
def release_message(self, message_id: str, recipients: List[str] = None) -> dict:
|
||||
data = {"recipients": recipients} if recipients else None
|
||||
return self._request(f"/messages/{message_id}/release", method="POST", data=data)
|
||||
|
||||
# Usage
|
||||
client = MailHogClient()
|
||||
|
||||
# Get all messages
|
||||
messages = client.get_messages()
|
||||
print(f"Total messages: {messages['total']}")
|
||||
|
||||
# Search for specific emails
|
||||
search_results = client.search_messages("subject:welcome")
|
||||
print(f"Found {search_results['total']} messages")
|
||||
```
|
||||
|
||||
## Testing Examples
|
||||
|
||||
### Integration Test Example
|
||||
|
||||
```javascript
|
||||
// Jest/Mocha integration test example
|
||||
describe('Email Functionality', () => {
|
||||
const mailhog = new MailHogClient();
|
||||
|
||||
beforeEach(async () => {
|
||||
// Clear all messages before each test
|
||||
await mailhog.deleteMessages('all');
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
// Clean up after each test
|
||||
await mailhog.deleteMessages('all');
|
||||
});
|
||||
|
||||
it('should send welcome email', async () => {
|
||||
// Trigger email sending in your application
|
||||
await sendWelcomeEmail('test@example.com');
|
||||
|
||||
// Wait for email to be processed
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
// Verify email was received
|
||||
const messages = await mailhog.searchMessages('to:test@example.com');
|
||||
expect(messages.total).toBe(1);
|
||||
|
||||
const message = messages.items[0];
|
||||
expect(message.Subject).toContain('Welcome');
|
||||
expect(message.From).toContain('noreply@yourapp.com');
|
||||
});
|
||||
|
||||
it('should handle bulk email sending', async () => {
|
||||
const recipients = ['user1@test.com', 'user2@test.com', 'user3@test.com'];
|
||||
|
||||
// Send bulk emails
|
||||
await sendBulkNotifications(recipients);
|
||||
|
||||
// Wait for processing
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
|
||||
// Verify all emails were received
|
||||
const messages = await mailhog.getMessages();
|
||||
expect(messages.total).toBe(recipients.length);
|
||||
|
||||
// Verify recipients
|
||||
const recipientAddresses = messages.items.flatMap(msg =>
|
||||
msg.To.map(to => to.Address)
|
||||
);
|
||||
recipients.forEach(recipient => {
|
||||
expect(recipientAddresses).toContain(recipient);
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Load Testing Example
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# load_test_mailhog.sh
|
||||
|
||||
BASE_URL="http://localhost:8025"
|
||||
CONCURRENT_REQUESTS=50
|
||||
TOTAL_REQUESTS=1000
|
||||
|
||||
echo "Starting MailHog API load test..."
|
||||
|
||||
# Create temp file for results
|
||||
RESULT_FILE=$(mktemp)
|
||||
|
||||
# Run concurrent curl requests
|
||||
for ((i=1; i<=CONCURRENT_REQUESTS; i++)); do
|
||||
{
|
||||
for ((j=1; j<=TOTAL_REQUESTS/CONCURRENT_REQUESTS; j++)); do
|
||||
start_time=$(date +%s%N)
|
||||
curl -s -o /dev/null -w "%{http_code},%{time_total}" \
|
||||
"$BASE_URL/api/v1/messages" >> "$RESULT_FILE"
|
||||
echo ",$(( ($(date +%s%N) - start_time) / 1000000 ))" >> "$RESULT_FILE"
|
||||
done
|
||||
} &
|
||||
done
|
||||
|
||||
# Wait for all background processes
|
||||
wait
|
||||
|
||||
# Analyze results
|
||||
echo "Load test completed. Analyzing results..."
|
||||
awk -F',' '
|
||||
{
|
||||
status[$1]++
|
||||
if ($1 == "200") {
|
||||
sum_time += $2
|
||||
sum_response += $3
|
||||
count++
|
||||
}
|
||||
}
|
||||
END {
|
||||
printf "Success Rate: %.2f%%\n", (status["200"]/NR)*100
|
||||
printf "Average Response Time: %.3f ms\n", sum_time/count*1000
|
||||
printf "Average Server Response Time: %.3f ms\n", sum_response/count
|
||||
for (code in status) {
|
||||
printf "HTTP %s: %d requests\n", code, status[code]
|
||||
}
|
||||
}' "$RESULT_FILE"
|
||||
|
||||
rm "$RESULT_FILE"
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Common HTTP Status Codes
|
||||
|
||||
- **200 OK**: Request successful
|
||||
- **400 Bad Request**: Invalid request parameters
|
||||
- **401 Unauthorized**: Authentication required
|
||||
- **404 Not Found**: Message or endpoint not found
|
||||
- **500 Internal Server Error**: Server error
|
||||
|
||||
### Error Response Format
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "Error message description",
|
||||
"code": "ERROR_CODE"
|
||||
}
|
||||
```
|
||||
|
||||
### Retry Logic Example
|
||||
|
||||
```javascript
|
||||
async function robustMailHogRequest(client, endpoint, options = {}) {
|
||||
const maxRetries = 3;
|
||||
const retryDelay = 1000;
|
||||
|
||||
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
||||
try {
|
||||
return await client.request(endpoint, options);
|
||||
} catch (error) {
|
||||
if (attempt === maxRetries) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (error.status >= 500 || error.status === 0) {
|
||||
// Retry on server errors or connection issues
|
||||
console.log(`Attempt ${attempt} failed, retrying in ${retryDelay}ms...`);
|
||||
await new Promise(resolve => setTimeout(resolve, retryDelay));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Don't retry on client errors
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user