Files
gh-whamp-whamp-claude-tools…/skills/mailhog/scripts/mailhog_manager.sh
2025-11-30 09:05:59 +08:00

861 lines
23 KiB
Bash
Executable File

#!/bin/bash
# mailhog_manager.sh - Comprehensive MailHog server management utility
# Usage: ./mailhog_manager.sh [COMMAND] [OPTIONS]
set -e
# Default configuration
DEFAULT_SMTP_PORT=1025
DEFAULT_UI_PORT=8025
DEFAULT_API_PORT=8025
DEFAULT_HOSTNAME="mailhog.local"
DEFAULT_STORAGE="memory"
PID_FILE="/tmp/mailhog.pid"
LOG_FILE="/tmp/mailhog.log"
CONFIG_FILE="$HOME/.mailhog_config"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# Print colored output
print_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
print_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
print_header() {
echo -e "${CYAN}=== $1 ===${NC}"
}
# Show usage information
show_usage() {
cat << EOF
MailHog Server Manager
Usage: $0 COMMAND [OPTIONS]
COMMANDS:
start Start MailHog server
stop Stop MailHog server
restart Restart MailHog server
status Show MailHog server status
logs Show MailHog server logs
config Show current configuration
test Test MailHog functionality
cleanup Clean up MailHog data
install Install MailHog (if not installed)
update Update MailHog to latest version
START OPTIONS:
-p, --smtp-port PORT SMTP port (default: $DEFAULT_SMTP_PORT)
-u, --ui-port PORT UI/API port (default: $DEFAULT_UI_PORT)
-h, --hostname HOST Hostname (default: $DEFAULT_HOSTNAME)
-s, --storage TYPE Storage type: memory, mongodb, maildir (default: $DEFAULT_STORAGE)
--mongo-uri URI MongoDB connection URI (for MongoDB storage)
--mongo-db DB MongoDB database name (default: mailhog)
--mongo-coll COLL MongoDB collection name (default: messages)
--maildir-path PATH Maildir storage path
--auth-file FILE Authentication file path
--outgoing-smtp FILE Outgoing SMTP configuration file
--cors-origin ORIGIN CORS origin
--invite-jim Enable Jim network simulation
-d, --daemon Run in daemon mode
-v, --verbose Enable verbose logging
CONFIGURATION:
--save Save current configuration as default
--load Load saved configuration
--reset Reset configuration to defaults
TESTING OPTIONS:
--send-email Send test email
--check-connection Check SMTP connection
--check-api Check API connection
--cleanup-test Clean up test emails
EXAMPLES:
# Start MailHog with default settings
$0 start
# Start with custom ports and MongoDB storage
$0 start --smtp-port 1026 --ui-port 8026 --storage mongodb --mongo-uri mongodb://localhost:27017
# Start in daemon mode with authentication
$0 start --auth-file /path/to/auth.txt --daemon
# Check server status
$0 status
# Send test email
$0 test --send-email --to test@recipient.local
# View logs
$0 logs
# Save current configuration
$0 config --save
INSTALLATION:
# Install MailHog (Linux/macOS)
$0 install
# Update to latest version
$0 update
EOF
}
# Load configuration from file
load_config() {
if [[ -f "$CONFIG_FILE" ]]; then
print_info "Loading configuration from $CONFIG_FILE"
# Source the configuration file
source "$CONFIG_FILE"
fi
}
# Save current configuration
save_config() {
cat > "$CONFIG_FILE" << EOF
# MailHog Configuration
SMTP_PORT="$SMTP_PORT"
UI_PORT="$UI_PORT"
API_PORT="$API_PORT"
HOSTNAME="$HOSTNAME"
STORAGE="$STORAGE"
MONGO_URI="$MONGO_URI"
MONGO_DB="$MONGO_DB"
MONGO_COLL="$MONGO_COLL"
MAILDIR_PATH="$MAILDIR_PATH"
AUTH_FILE="$AUTH_FILE"
OUTGOING_SMTP="$OUTGOING_SMTP"
CORS_ORIGIN="$CORS_ORIGIN"
INVITE_JIM="$INVITE_JIM"
EOF
print_success "Configuration saved to $CONFIG_FILE"
}
# Reset configuration to defaults
reset_config() {
SMTP_PORT="$DEFAULT_SMTP_PORT"
UI_PORT="$DEFAULT_UI_PORT"
API_PORT="$DEFAULT_API_PORT"
HOSTNAME="$DEFAULT_HOSTNAME"
STORAGE="$DEFAULT_STORAGE"
MONGO_URI=""
MONGO_DB="mailhog"
MONGO_COLL="messages"
MAILDIR_PATH=""
AUTH_FILE=""
OUTGOING_SMTP=""
CORS_ORIGIN=""
INVITE_JIM=""
print_success "Configuration reset to defaults"
}
# Check if MailHog is installed
check_mailhog_installed() {
if command -v mailhog >/dev/null 2>&1; then
return 0
else
return 1
fi
}
# Install MailHog
install_mailhog() {
print_header "Installing MailHog"
if check_mailhog_installed; then
print_warning "MailHog is already installed"
return 0
fi
local os=$(uname -s | tr '[:upper:]' '[:lower:]')
local arch=$(uname -m)
# Determine architecture
case "$arch" in
x86_64)
arch="amd64"
;;
aarch64|arm64)
arch="arm64"
;;
armv7l)
arch="arm7"
;;
*)
print_error "Unsupported architecture: $arch"
return 1
;;
esac
# Get latest version
local latest_version=$(curl -s "https://api.github.com/repos/mailhog/MailHog/releases/latest" | grep -o '"tag_name": "[^"]*' | cut -d'"' -f2 | sed 's/v//')
if [[ -z "$latest_version" ]]; then
print_error "Failed to fetch latest MailHog version"
return 1
fi
local download_url="https://github.com/mailhog/MailHog/releases/download/v${latest_version}/MailHog_${os}_${arch}.zip"
print_info "Downloading MailHog v${latest_version} for ${os}/${arch}..."
# Download and extract
local temp_dir=$(mktemp -d)
cd "$temp_dir"
if curl -L -o mailhog.zip "$download_url"; then
unzip mailhog.zip
chmod +x MailHog
# Move to binary directory
if [[ -w "/usr/local/bin" ]]; then
sudo mv MailHog /usr/local/bin/
elif [[ -w "$HOME/.local/bin" ]]; then
mkdir -p "$HOME/.local/bin"
mv MailHog "$HOME/.local/bin/"
# Add to PATH if not already there
if ! echo "$PATH" | grep -q "$HOME/.local/bin"; then
echo 'export PATH="$PATH:$HOME/.local/bin"' >> "$HOME/.bashrc"
export PATH="$PATH:$HOME/.local/bin"
fi
else
print_error "No writable binary directory found. Please install manually."
cd /
rm -rf "$temp_dir"
return 1
fi
cd /
rm -rf "$temp_dir"
print_success "MailHog v${latest_version} installed successfully"
return 0
else
print_error "Failed to download MailHog"
cd /
rm -rf "$temp_dir"
return 1
fi
}
# Update MailHog
update_mailhog() {
print_header "Updating MailHog"
if ! check_mailhog_installed; then
print_warning "MailHog not installed. Installing..."
install_mailhog
return $?
fi
# Stop MailHog if running
if is_mailhog_running; then
stop_mailhog
fi
# Install latest version
install_mailhog
}
# Check if MailHog is running
is_mailhog_running() {
if [[ -f "$PID_FILE" ]]; then
local pid=$(cat "$PID_FILE")
if ps -p "$pid" > /dev/null 2>&1; then
return 0
else
# PID file exists but process is not running
rm -f "$PID_FILE"
return 1
fi
fi
# Check by looking for mailhog process
if pgrep -f "mailhog" > /dev/null; then
return 0
fi
return 1
}
# Build MailHog command with current configuration
build_mailhog_command() {
local cmd="mailhog"
# Basic configuration
cmd+=" -hostname $HOSTNAME"
cmd+=" -smtp-bind-addr 0.0.0.0:$SMTP_PORT"
cmd+=" -ui-bind-addr 0.0.0.0:$UI_PORT"
cmd+=" -api-bind-addr 0.0.0.0:$API_PORT"
# Storage configuration
case "$STORAGE" in
"mongodb")
if [[ -n "$MONGO_URI" ]]; then
cmd+=" -storage mongodb -mongo-uri $MONGO_URI"
else
cmd+=" -storage mongodb -mongo-uri 127.0.0.1:27017"
fi
if [[ -n "$MONGO_DB" ]]; then
cmd+=" -mongo-db $MONGO_DB"
fi
if [[ -n "$MONGO_COLL" ]]; then
cmd+=" -mongo-coll $MONGO_COLL"
fi
;;
"maildir")
if [[ -n "$MAILDIR_PATH" ]]; then
cmd+=" -storage maildir -maildir-path $MAILDIR_PATH"
else
cmd+=" -storage maildir -maildir-path ./maildir"
fi
;;
*)
cmd+=" -storage memory"
;;
esac
# Optional configurations
if [[ -n "$AUTH_FILE" ]]; then
cmd+=" -auth-file $AUTH_FILE"
fi
if [[ -n "$OUTGOING_SMTP" ]]; then
cmd+=" -outgoing-smtp $OUTGOING_SMTP"
fi
if [[ -n "$CORS_ORIGIN" ]]; then
cmd+=" -cors-origin \"$CORS_ORIGIN\""
fi
if [[ "$INVITE_JIM" == "true" ]]; then
cmd+=" -invite-jim"
fi
echo "$cmd"
}
# Start MailHog
start_mailhog() {
print_header "Starting MailHog"
if is_mailhog_running; then
print_warning "MailHog is already running"
return 0
fi
if ! check_mailhog_installed; then
print_error "MailHog is not installed. Run '$0 install' first."
return 1
fi
# Check if ports are available
if netstat -tlnp 2>/dev/null | grep -q ":$SMTP_PORT "; then
print_error "Port $SMTP_PORT is already in use"
return 1
fi
if netstat -tlnp 2>/dev/null | grep -q ":$UI_PORT "; then
print_error "Port $UI_PORT is already in use"
return 1
fi
local mailhog_cmd=$(build_mailhog_command)
print_info "Starting MailHog with the following command:"
print_info "$mailhog_cmd"
if [[ "$DAEMON_MODE" == true ]]; then
# Start in daemon mode
nohup $mailhog_cmd > "$LOG_FILE" 2>&1 &
local pid=$!
echo "$pid" > "$PID_FILE"
print_success "MailHog started in daemon mode (PID: $pid)"
print_info "Logs are being written to: $LOG_FILE"
else
# Start in foreground
$mailhog_cmd 2>&1 | tee "$LOG_FILE" &
local pid=$!
echo "$pid" > "$PID_FILE"
print_success "MailHog started (PID: $pid)"
fi
# Wait a moment and check if it started successfully
sleep 2
if is_mailhog_running; then
print_success "MailHog started successfully"
print_info "SMTP server: localhost:$SMTP_PORT"
print_info "Web UI: http://localhost:$UI_PORT"
print_info "API: http://localhost:$API_PORT/api/v1"
else
print_error "MailHog failed to start"
return 1
fi
}
# Stop MailHog
stop_mailhog() {
print_header "Stopping MailHog"
if ! is_mailhog_running; then
print_warning "MailHog is not running"
return 0
fi
local pid=""
if [[ -f "$PID_FILE" ]]; then
pid=$(cat "$PID_FILE")
else
# Try to find PID by process name
pid=$(pgrep -f "mailhog" | head -1)
fi
if [[ -n "$pid" ]]; then
print_info "Stopping MailHog (PID: $pid)"
kill "$pid"
# Wait for process to stop
local count=0
while ps -p "$pid" > /dev/null 2>&1 && [[ $count -lt 10 ]]; do
sleep 1
((count++))
done
# Force kill if still running
if ps -p "$pid" > /dev/null 2>&1; then
print_warning "Force killing MailHog process"
kill -9 "$pid"
fi
rm -f "$PID_FILE"
print_success "MailHog stopped"
else
print_warning "Could not find MailHog process to stop"
fi
}
# Restart MailHog
restart_mailhog() {
print_header "Restarting MailHog"
stop_mailhog
sleep 2
start_mailhog
}
# Show MailHog status
show_status() {
print_header "MailHog Status"
if is_mailhog_running; then
print_success "MailHog is running"
# Show process information
if [[ -f "$PID_FILE" ]]; then
local pid=$(cat "$PID_FILE")
print_info "PID: $pid"
print_info "Command: $(ps -p "$pid" -o cmd=)"
fi
# Show configuration
echo ""
print_info "Configuration:"
print_info " SMTP Port: $SMTP_PORT"
print_info " UI Port: $UI_PORT"
print_info " API Port: $API_PORT"
print_info " Hostname: $HOSTNAME"
print_info " Storage: $STORAGE"
# Test API connection
echo ""
print_info "Testing API connection..."
if curl -s --max-time 3 "http://localhost:$API_PORT/api/v1/status" > /dev/null 2>&1; then
print_success "API is accessible"
local messages=$(curl -s "http://localhost:$API_PORT/api/v1/messages" 2>/dev/null | jq -r '.total // 0' 2>/dev/null || echo "unknown")
print_info "Messages stored: $messages"
else
print_error "API is not accessible"
fi
# Show URLs
echo ""
print_info "Access URLs:"
print_info " Web UI: http://localhost:$UI_PORT"
print_info " API: http://localhost:$API_PORT/api/v1"
print_info " SMTP: localhost:$SMTP_PORT"
else
print_error "MailHog is not running"
fi
}
# Show logs
show_logs() {
print_header "MailHog Logs"
if [[ -f "$LOG_FILE" ]]; then
tail -f "$LOG_FILE"
else
print_warning "Log file not found: $LOG_FILE"
fi
}
# Show configuration
show_config() {
print_header "Current Configuration"
echo "SMTP Port: $SMTP_PORT"
echo "UI Port: $UI_PORT"
echo "API Port: $API_PORT"
echo "Hostname: $HOSTNAME"
echo "Storage: $STORAGE"
if [[ "$STORAGE" == "mongodb" ]]; then
echo "MongoDB URI: ${MONGO_URI:-"127.0.0.1:27017"}"
echo "MongoDB DB: $MONGO_DB"
echo "MongoDB Collection: $MONGO_COLL"
fi
if [[ "$STORAGE" == "maildir" ]]; then
echo "Maildir Path: ${MAILDIR_PATH:-"./maildir"}"
fi
echo "Auth File: ${AUTH_FILE:-"none"}"
echo "Outgoing SMTP: ${OUTGOING_SMTP:-"none"}"
echo "CORS Origin: ${CORS_ORIGIN:-"none"}"
echo "Jim Simulation: ${INVITE_JIM:-"false"}"
echo ""
echo "MailHog Command:"
echo "$(build_mailhog_command)"
}
# Test MailHog functionality
test_mailhog() {
print_header "Testing MailHog"
if ! is_mailhog_running; then
print_error "MailHog is not running"
return 1
fi
# Test API connection
if [[ "$CHECK_API" == true ]]; then
print_info "Testing API connection..."
local response=$(curl -s "http://localhost:$API_PORT/api/v1/status" 2>/dev/null)
if [[ $? -eq 0 ]]; then
print_success "API connection successful"
echo "$response" | jq . 2>/dev/null || echo "$response"
else
print_error "API connection failed"
fi
fi
# Test SMTP connection
if [[ "$CHECK_CONNECTION" == true ]]; then
print_info "Testing SMTP connection..."
if echo "" | nc -w 3 localhost "$SMTP_PORT" 2>/dev/null | grep -q "220"; then
print_success "SMTP connection successful"
else
print_error "SMTP connection failed"
fi
fi
# Send test email
if [[ "$SEND_EMAIL" == true ]]; then
print_info "Sending test email..."
local test_to="${TEST_TO:-test@recipient.local}"
local test_from="${TEST_FROM:-test@sender.local}"
local test_subject="MailHog Test Email $(date +%s)"
local test_body="This is a test email sent at $(date)"
# Send email using netcat
(
echo "EHLO localhost"
echo "MAIL FROM:<$test_from>"
echo "RCPT TO:<$test_to>"
echo "DATA"
echo "From: $test_from"
echo "To: $test_to"
echo "Subject: $test_subject"
echo "Date: $(date -R)"
echo ""
echo "$test_body"
echo "."
echo "QUIT"
) | nc localhost "$SMTP_PORT" 2>/dev/null
if [[ $? -eq 0 ]]; then
print_success "Test email sent to $test_to"
# Wait and check if received
sleep 2
local messages=$(curl -s "http://localhost:$API_PORT/api/v1/messages" 2>/dev/null | jq -r '.total // 0' 2>/dev/null)
if [[ "$messages" -gt 0 ]]; then
print_success "Test email received by MailHog ($messages total messages)"
else
print_warning "Test email not found in MailHog (may need more time)"
fi
else
print_error "Failed to send test email"
fi
fi
# Clean up test emails
if [[ "$CLEANUP_TEST" == true ]]; then
print_info "Cleaning up test emails..."
local response=$(curl -s -X DELETE "http://localhost:$API_PORT/api/v1/messages" 2>/dev/null)
if [[ $? -eq 0 ]]; then
print_success "Test emails cleaned up"
else
print_warning "Failed to clean up test emails"
fi
fi
}
# Clean up MailHog data
cleanup_mailhog() {
print_header "Cleaning up MailHog"
if is_mailhog_running; then
print_info "Clearing all messages..."
local response=$(curl -s -X DELETE "http://localhost:$API_PORT/api/v1/messages" 2>/dev/null)
if [[ $? -eq 0 ]]; then
print_success "All messages cleared"
else
print_warning "Failed to clear messages"
fi
else
print_warning "MailHog is not running"
fi
# Clean up temporary files
print_info "Cleaning up temporary files..."
rm -f "$PID_FILE" "$LOG_FILE"
# Optional: Clean up maildir if used
if [[ "$STORAGE" == "maildir" ]] && [[ -n "$MAILDIR_PATH" ]]; then
if [[ -d "$MAILDIR_PATH" ]]; then
print_info "Cleaning up Maildir storage..."
rm -rf "$MAILDIR_PATH"/{cur,new,tmp}/*
print_success "Maildir storage cleaned"
fi
fi
print_success "Cleanup completed"
}
# Main function
main() {
# Load default configuration
load_config
# Set defaults if not loaded
SMTP_PORT="${SMTP_PORT:-$DEFAULT_SMTP_PORT}"
UI_PORT="${UI_PORT:-$DEFAULT_UI_PORT}"
API_PORT="${API_PORT:-$DEFAULT_API_PORT}"
HOSTNAME="${HOSTNAME:-$DEFAULT_HOSTNAME}"
STORAGE="${STORAGE:-$DEFAULT_STORAGE}"
MONGO_DB="${MONGO_DB:-mailhog}"
MONGO_COLL="${MONGO_COLL:-messages}"
DAEMON_MODE=false
# Parse command line arguments
case "${1:-}" in
"start")
shift
while [[ $# -gt 0 ]]; do
case $1 in
-p|--smtp-port)
SMTP_PORT="$2"
shift 2
;;
-u|--ui-port)
UI_PORT="$2"
API_PORT="$2"
shift 2
;;
-h|--hostname)
HOSTNAME="$2"
shift 2
;;
-s|--storage)
STORAGE="$2"
shift 2
;;
--mongo-uri)
MONGO_URI="$2"
shift 2
;;
--mongo-db)
MONGO_DB="$2"
shift 2
;;
--mongo-coll)
MONGO_COLL="$2"
shift 2
;;
--maildir-path)
MAILDIR_PATH="$2"
shift 2
;;
--auth-file)
AUTH_FILE="$2"
shift 2
;;
--outgoing-smtp)
OUTGOING_SMTP="$2"
shift 2
;;
--cors-origin)
CORS_ORIGIN="$2"
shift 2
;;
--invite-jim)
INVITE_JIM="true"
shift
;;
-d|--daemon)
DAEMON_MODE=true
shift
;;
-v|--verbose)
VERBOSE=true
shift
;;
--save)
save_config
exit 0
;;
*)
print_error "Unknown option: $1"
show_usage
exit 1
;;
esac
done
start_mailhog
;;
"stop")
stop_mailhog
;;
"restart")
restart_mailhog
;;
"status")
show_status
;;
"logs")
show_logs
;;
"config")
case "${2:-}" in
"--save")
save_config
;;
"--load")
load_config
print_success "Configuration loaded"
;;
"--reset")
reset_config
;;
*)
show_config
;;
esac
;;
"test")
shift
while [[ $# -gt 0 ]]; do
case $1 in
--send-email)
SEND_EMAIL=true
shift
;;
--check-connection)
CHECK_CONNECTION=true
shift
;;
--check-api)
CHECK_API=true
shift
;;
--cleanup-test)
CLEANUP_TEST=true
shift
;;
--to)
TEST_TO="$2"
shift 2
;;
--from)
TEST_FROM="$2"
shift 2
;;
*)
print_error "Unknown test option: $1"
show_usage
exit 1
;;
esac
done
# If no specific test options, run all tests
if [[ -z "$SEND_EMAIL" && -z "$CHECK_CONNECTION" && -z "$CHECK_API" && -z "$CLEANUP_TEST" ]]; then
SEND_EMAIL=true
CHECK_CONNECTION=true
CHECK_API=true
CLEANUP_TEST=true
fi
test_mailhog
;;
"cleanup")
cleanup_mailhog
;;
"install")
install_mailhog
;;
"update")
update_mailhog
;;
"help"|"-h"|"--help")
show_usage
;;
*)
print_error "Unknown command: ${1:-}"
show_usage
exit 1
;;
esac
}
# Run main function
main "$@"