A full-featured mail administration application built with Next.js (Pages Router) that allows you to manage SMTP settings, create email templates, and send emails through a secure JWT-authenticated interface.
- JWT Authentication: Secure login with bcrypt password hashing
- SMTP Configuration: Configure and test SMTP server settings
- Email Templates: Create, manage, and reuse email templates
- Email Composer: Send emails using saved SMTP configuration and templates
- File-Based Storage: Simple JSON file storage (easily replaceable with database)
- Health Check: Basic API health monitoring endpoint
bolt-mail-admin-next/
├── pages/
│ ├── api/
│ │ ├── admin/
│ │ │ └── change-password.ts # Change admin password (protected)
│ │ ├── smtp/
│ │ │ ├── index.ts # Get/Save SMTP config (protected)
│ │ │ └── test.ts # Test SMTP connection (protected)
│ │ ├── health.ts # Health check endpoint
│ │ ├── login.ts # User login
│ │ ├── send.ts # Send email (protected)
│ │ └── templates.ts # CRUD templates (protected)
│ ├── index.tsx # Login page
│ ├── smtp-settings.tsx # SMTP & template management
│ └── compose.tsx # Compose & send emails
├── lib/
│ ├── auth.ts # JWT utilities & middleware
│ └── store.ts # File-based JSON storage
├── data/
│ └── store.json # Auto-generated data store
└── .env # Environment variables (create from .env.example)
npm install
Create a .env
file from the example:
cp .env.example .env
Edit .env
and set your values:
# JWT Secret (use a strong random string in production)
JWT_SECRET=your-secret-key-change-this-in-production
# Default Admin Credentials (used on first run)
ADMIN_EMAIL=admin@example.com
ADMIN_PASSWORD=changeme123
# Node Environment
NODE_ENV=development
IMPORTANT: Change JWT_SECRET
and ADMIN_PASSWORD
before deploying to production!
npm run dev
Visit http://localhost:3000
Use the credentials from your .env
file:
- Email:
admin@example.com
(or your configured email) - Password:
changeme123
(or your configured password)
- Navigate to "SMTP Settings"
- Enter your SMTP server details (host, port, credentials)
- Test the connection
- Save configuration
- Create templates (optional) in the Templates tab
- Go to "Compose" to send emails
- Select a template or write from scratch
POST /api/login
- Authenticate and receive JWTGET /api/health
- Check API status
POST /api/admin/change-password
- Change admin passwordGET /api/smtp
- Get SMTP configuration (password masked)POST /api/smtp
- Save SMTP configurationPOST /api/smtp/test
- Test SMTP connectionPOST /api/send
- Send emailGET /api/templates
- List all templatesPOST /api/templates
- Create new templateDELETE /api/templates?id={id}
- Delete template
# Login
curl -X POST http://localhost:3000/api/login \
-H "Content-Type: application/json" \
-d '{"email":"admin@example.com","password":"changeme123"}'
# Send email (with token)
curl -X POST http://localhost:3000/api/send \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-d '{
"to": "recipient@example.com",
"subject": "Test Email",
"body": "<p>Hello World!</p>"
}'
- Never commit
.env
file - It contains sensitive credentials - Change default credentials - Update
JWT_SECRET
andADMIN_PASSWORD
in production - Use strong passwords - Minimum 8 characters, mix of letters, numbers, symbols
- HTTPS in production - Always use SSL/TLS in production environments
- Secure JWT_SECRET - Use a cryptographically random string (at least 32 characters)
- SMTP credentials - Stored in plain text in JSON file (see migration notes below)
- Rate limiting - Consider adding rate limiting to API routes in production
- CORS - Configure CORS headers appropriately for your frontend domain
# Generate a secure JWT secret
JWT_SECRET=$(openssl rand -base64 32)
# Use environment variables for sensitive data
ADMIN_EMAIL=secure-admin@yourdomain.com
ADMIN_PASSWORD=$(openssl rand -base64 16)
NODE_ENV=production
The application currently uses a file-based JSON store (./data/store.json
). To migrate to a database:
Current file operations can be swapped with database calls:
// BEFORE: File-based
export async function getStore(): Promise<Store> {
const data = fs.readFileSync(STORE_FILE, 'utf-8');
return JSON.parse(data);
}
// AFTER: Database (example with Supabase)
export async function getStore(): Promise<Store> {
const { data: user } = await supabase.from('users').select('*').single();
const { data: smtp } = await supabase.from('smtp_config').select('*').maybeSingle();
const { data: templates } = await supabase.from('templates').select('*');
return { user, smtp, templates };
}
Instead of storing SMTP passwords in the database:
// Example with AWS Secrets Manager, HashiCorp Vault, etc.
import { SecretsManager } from '@aws-sdk/client-secrets-manager';
const secretsManager = new SecretsManager({ region: 'us-east-1' });
async function getSMTPPassword() {
const secret = await secretsManager.getSecretValue({ SecretId: 'smtp-password' });
return secret.SecretString;
}
-- Users table
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- SMTP configuration table
CREATE TABLE smtp_config (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
host TEXT NOT NULL,
port INTEGER NOT NULL,
secure BOOLEAN DEFAULT false,
user TEXT NOT NULL,
password_encrypted TEXT NOT NULL, -- Encrypt before storing
from_name TEXT,
from_email TEXT NOT NULL,
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- Templates table
CREATE TABLE templates (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
subject TEXT NOT NULL,
body TEXT NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);
Host: smtp.gmail.com
Port: 587 (or 465 for SSL)
Secure: Yes (for 465) / No (for 587 with STARTTLS)
User: your-email@gmail.com
Password: App Password (not your Gmail password)
Note: You need to create an App Password for Gmail.
Host: smtp.sendgrid.net
Port: 587
Secure: No
User: apikey
Password: Your SendGrid API Key
Host: smtp.mailgun.org
Port: 587
Secure: No
User: postmaster@your-domain.mailgun.org
Password: Your Mailgun SMTP Password
# Run development server
npm run dev
# Build for production
npm run build
# Start production server
npm start
# Type check
npm run typecheck
# Lint code
npm run lint
- Verify your SMTP credentials are correct
- Check if your email provider requires app-specific passwords
- Ensure firewall allows outbound connections on SMTP port
- Try toggling the "Use SSL/TLS" option
- Your JWT token may have expired (7-day expiration)
- Log out and log back in to get a new token
- Test SMTP connection first
- Check SMTP configuration is saved
- Verify recipient email address is valid
- Check your SMTP provider's sending limits
MIT
This is a demonstration project. Feel free to fork and modify for your needs.
If you discover a security vulnerability, please email security@example.com instead of using the issue tracker.