Compare commits
3 commits
baa2490bbe
...
6c60d88f62
| Author | SHA1 | Date | |
|---|---|---|---|
| 6c60d88f62 | |||
| 54c594e656 | |||
| 08b49d3d75 |
12 changed files with 78 additions and 259 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -68,3 +68,4 @@ config.production.lua
|
||||||
|
|
||||||
config/furt.conf
|
config/furt.conf
|
||||||
|
|
||||||
|
scripts/production_test_sequence.sh
|
||||||
|
|
|
||||||
|
|
@ -24,3 +24,4 @@ a670de0f,25a709e,feature/pid-file-service-management,2025-09-05T20:30:13Z,michae
|
||||||
a670de0f,59f372f,feature/pid-file-service-management,2025-09-07T14:58:01Z,michael,git,lua-api
|
a670de0f,59f372f,feature/pid-file-service-management,2025-09-07T14:58:01Z,michael,git,lua-api
|
||||||
a670de0f,683d6e5,fix/validate-config-posix-regex,2025-09-07T16:00:48Z,michael,git,lua-api
|
a670de0f,683d6e5,fix/validate-config-posix-regex,2025-09-07T16:00:48Z,michael,git,lua-api
|
||||||
a670de0f,24bd94d,feature/systemd-hardening,2025-09-07T16:40:47Z,michael,git,lua-api
|
a670de0f,24bd94d,feature/systemd-hardening,2025-09-07T16:40:47Z,michael,git,lua-api
|
||||||
|
4ee95dbc,08b49d3,security/sanitize-test-scripts,2025-09-07T19:25:38Z,michael,git,lua-api
|
||||||
|
|
|
||||||
0
scripts/cleanup_debug.sh
Normal file → Executable file
0
scripts/cleanup_debug.sh
Normal file → Executable file
4
scripts/manual_mail_test.sh
Normal file → Executable file
4
scripts/manual_mail_test.sh
Normal file → Executable file
|
|
@ -4,11 +4,11 @@
|
||||||
echo "Testing SMTP with corrected JSON..."
|
echo "Testing SMTP with corrected JSON..."
|
||||||
|
|
||||||
# Simple test without timestamp embedding
|
# Simple test without timestamp embedding
|
||||||
curl -X POST http://127.0.0.1:8080/v1/mail/send \
|
curl -X POST http://127.0.0.1:7811/v1/mail/send \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d '{
|
-d '{
|
||||||
"name": "Furt Test User",
|
"name": "Furt Test User",
|
||||||
"email": "michael@dragons-at-work.de",
|
"email": "admin@example.com",
|
||||||
"subject": "Furt SMTP Test Success!",
|
"subject": "Furt SMTP Test Success!",
|
||||||
"message": "This is a test email from Furt Lua HTTP-Server. SMTP Integration working!"
|
"message": "This is a test email from Furt Lua HTTP-Server. SMTP Integration working!"
|
||||||
}'
|
}'
|
||||||
|
|
|
||||||
|
|
@ -1,80 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
# Production Test für api.dragons-at-work.de
|
|
||||||
|
|
||||||
echo "Testing Production API via Apache Proxy"
|
|
||||||
echo "======================================="
|
|
||||||
|
|
||||||
# Test 1: HTTPS Health Check
|
|
||||||
echo ""
|
|
||||||
echo "[1] Testing HTTPS Health Check..."
|
|
||||||
https_health=$(curl -s https://api.dragons-at-work.de/health)
|
|
||||||
echo "HTTPS Response: $https_health"
|
|
||||||
|
|
||||||
if echo "$https_health" | grep -q "healthy"; then
|
|
||||||
echo "[OK] HTTPS Proxy working"
|
|
||||||
else
|
|
||||||
echo "[ERROR] HTTPS Proxy failed"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Test 2: SMTP Status via HTTPS
|
|
||||||
echo ""
|
|
||||||
echo "[2] Testing SMTP Configuration via HTTPS..."
|
|
||||||
if echo "$https_health" | grep -q '"smtp_configured":true'; then
|
|
||||||
echo "[OK] SMTP configured and accessible via HTTPS"
|
|
||||||
else
|
|
||||||
echo "[ERROR] SMTP not configured or not accessible"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Test 3: CORS Headers
|
|
||||||
echo ""
|
|
||||||
echo "[3] Testing CORS Headers..."
|
|
||||||
cors_test=$(curl -s -I https://api.dragons-at-work.de/health | grep -i "access-control")
|
|
||||||
if [ -n "$cors_test" ]; then
|
|
||||||
echo "[OK] CORS headers present: $cors_test"
|
|
||||||
else
|
|
||||||
echo "[WARN] CORS headers missing - add to Apache config"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Test 4: Production Mail Test
|
|
||||||
echo ""
|
|
||||||
echo "[4] Testing Production Mail via HTTPS..."
|
|
||||||
echo "WARNING: This sends real email via production API"
|
|
||||||
read -p "Continue with production mail test? (y/N): " -n 1 -r
|
|
||||||
echo
|
|
||||||
|
|
||||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
|
||||||
echo "Sending production test email..."
|
|
||||||
|
|
||||||
prod_mail_response=$(curl -s -X POST https://api.dragons-at-work.de/v1/mail/send \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{
|
|
||||||
"name": "Production Test",
|
|
||||||
"email": "test@dragons-at-work.de",
|
|
||||||
"subject": "Production API Test - Apache Proxy Success!",
|
|
||||||
"message": "This email was sent via the production API at api.dragons-at-work.de through Apache proxy to Furt-Lua backend. HTTPS integration working!"
|
|
||||||
}')
|
|
||||||
|
|
||||||
echo "Production Response: $prod_mail_response"
|
|
||||||
|
|
||||||
if echo "$prod_mail_response" | grep -q '"success":true'; then
|
|
||||||
echo "[OK] PRODUCTION MAIL SENT VIA HTTPS!"
|
|
||||||
echo "Check admin@dragons-at-work.de for delivery confirmation"
|
|
||||||
else
|
|
||||||
echo "[ERROR] Production mail failed"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "Skipping production mail test"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Test 5: Security Headers
|
|
||||||
echo ""
|
|
||||||
echo "[5] Testing Security Headers..."
|
|
||||||
security_headers=$(curl -s -I https://api.dragons-at-work.de/health)
|
|
||||||
echo "Security Headers:"
|
|
||||||
echo "$security_headers" | grep -i "x-content-type-options\|x-frame-options\|strict-transport"
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "Production Test Complete!"
|
|
||||||
echo "========================"
|
|
||||||
echo "Next: Hugo integration with https://api.dragons-at-work.de/v1/mail/send"
|
|
||||||
|
|
@ -1,101 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
# furt-lua/scripts/setup_env.sh
|
|
||||||
# Add SMTP environment variables to existing .env (non-destructive)
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Colors for output
|
|
||||||
GREEN='\033[0;32m'
|
|
||||||
YELLOW='\033[1;33m'
|
|
||||||
RED='\033[0;31m'
|
|
||||||
NC='\033[0m' # No Color
|
|
||||||
|
|
||||||
echo -e "${GREEN}=== Furt SMTP Environment Setup ===${NC}"
|
|
||||||
|
|
||||||
# Navigate to furt project root
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
PROJECT_ROOT="$(dirname "$(dirname "$SCRIPT_DIR")")"
|
|
||||||
ENV_FILE="$PROJECT_ROOT/.env"
|
|
||||||
|
|
||||||
echo -e "${YELLOW}Project root:${NC} $PROJECT_ROOT"
|
|
||||||
echo -e "${YELLOW}Environment file:${NC} $ENV_FILE"
|
|
||||||
|
|
||||||
# Check if .env exists
|
|
||||||
if [ ! -f "$ENV_FILE" ]; then
|
|
||||||
echo -e "${YELLOW}Creating new .env file...${NC}"
|
|
||||||
cat > "$ENV_FILE" << 'EOF'
|
|
||||||
# Dragons@Work Project Environment Variables
|
|
||||||
|
|
||||||
# Furt SMTP Configuration for mail.dragons-at-work.de
|
|
||||||
SMTP_HOST="mail.dragons-at-work.de"
|
|
||||||
SMTP_PORT="465"
|
|
||||||
SMTP_USERNAME="your_email@dragons-at-work.de"
|
|
||||||
SMTP_PASSWORD="your_smtp_password"
|
|
||||||
SMTP_FROM="noreply@dragons-at-work.de"
|
|
||||||
SMTP_TO="michael@dragons-at-work.de"
|
|
||||||
EOF
|
|
||||||
echo -e "${GREEN}[OK] Created new .env file${NC}"
|
|
||||||
echo -e "${YELLOW}[EDIT] Please edit:${NC} nano $ENV_FILE"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo -e "${GREEN}[OK] Found existing .env file${NC}"
|
|
||||||
|
|
||||||
# Check if SMTP variables already exist
|
|
||||||
smtp_username_exists=$(grep -c "^SMTP_USERNAME=" "$ENV_FILE" 2>/dev/null || echo "0")
|
|
||||||
smtp_password_exists=$(grep -c "^SMTP_PASSWORD=" "$ENV_FILE" 2>/dev/null || echo "0")
|
|
||||||
|
|
||||||
if [ "$smtp_username_exists" -gt 0 ] && [ "$smtp_password_exists" -gt 0 ]; then
|
|
||||||
echo -e "${GREEN}[OK] SMTP variables already configured${NC}"
|
|
||||||
|
|
||||||
# Load and show current values
|
|
||||||
source "$ENV_FILE"
|
|
||||||
echo -e "${YELLOW}Current SMTP User:${NC} ${SMTP_USERNAME:-NOT_SET}"
|
|
||||||
echo -e "${YELLOW}Current SMTP Password:${NC} ${SMTP_PASSWORD:+[CONFIGURED]}${SMTP_PASSWORD:-NOT_SET}"
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo -e "${YELLOW}To update SMTP settings:${NC} nano $ENV_FILE"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Add missing SMTP variables
|
|
||||||
echo -e "${YELLOW}Adding SMTP configuration to existing .env...${NC}"
|
|
||||||
|
|
||||||
# Add section header if not present
|
|
||||||
if ! grep -q "SMTP_" "$ENV_FILE" 2>/dev/null; then
|
|
||||||
echo "" >> "$ENV_FILE"
|
|
||||||
echo "# Furt SMTP Configuration for mail.dragons-at-work.de" >> "$ENV_FILE"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Add username if missing
|
|
||||||
if [ "$smtp_username_exists" -eq 0 ]; then
|
|
||||||
echo "SMTP_HOST=\"mail.dragons-at-work.de\"" >> "$ENV_FILE"
|
|
||||||
echo "SMTP_PORT=\"465\"" >> "$ENV_FILE"
|
|
||||||
echo "SMTP_USERNAME=\"your_email@dragons-at-work.de\"" >> "$ENV_FILE"
|
|
||||||
echo -e "${GREEN}[OK] Added SMTP_HOST, SMTP_PORT, SMTP_USERNAME${NC}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Add password if missing
|
|
||||||
if [ "$smtp_password_exists" -eq 0 ]; then
|
|
||||||
echo "SMTP_PASSWORD=\"your_smtp_password\"" >> "$ENV_FILE"
|
|
||||||
echo "SMTP_FROM=\"noreply@dragons-at-work.de\"" >> "$ENV_FILE"
|
|
||||||
echo "SMTP_TO=\"michael@dragons-at-work.de\"" >> "$ENV_FILE"
|
|
||||||
echo -e "${GREEN}[OK] Added SMTP_PASSWORD, SMTP_FROM, SMTP_TO${NC}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo -e "${GREEN}[OK] SMTP configuration added to .env${NC}"
|
|
||||||
echo ""
|
|
||||||
echo -e "${YELLOW}Next steps:${NC}"
|
|
||||||
echo "1. Edit SMTP credentials: nano $ENV_FILE"
|
|
||||||
echo "2. Set your actual email@dragons-at-work.de in SMTP_USERNAME"
|
|
||||||
echo "3. Set your actual SMTP password in SMTP_PASSWORD"
|
|
||||||
echo "4. Test with: ./scripts/start.sh"
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo -e "${YELLOW}Current .env content:${NC}"
|
|
||||||
echo "==================="
|
|
||||||
cat "$ENV_FILE"
|
|
||||||
echo "==================="
|
|
||||||
echo ""
|
|
||||||
echo -e "${GREEN}Ready for SMTP testing!${NC}"
|
|
||||||
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
BASE_URL="http://127.0.0.1:8080"
|
BASE_URL="http://127.0.0.1:8080"
|
||||||
# Use correct API keys that match current .env
|
# Use correct API keys that match current .env
|
||||||
API_KEY="hugo-dev-key-change-in-production"
|
API_KEY="YOUR_API_KEY_HERE"
|
||||||
|
|
||||||
echo "⚡ Furt API Stress Test"
|
echo "⚡ Furt API Stress Test"
|
||||||
echo "======================"
|
echo "======================"
|
||||||
|
|
@ -20,9 +20,9 @@ for i in {1..20}; do
|
||||||
response=$(curl -s -w "%{http_code}" \
|
response=$(curl -s -w "%{http_code}" \
|
||||||
-H "X-API-Key: $API_KEY" \
|
-H "X-API-Key: $API_KEY" \
|
||||||
"$BASE_URL/v1/auth/status")
|
"$BASE_URL/v1/auth/status")
|
||||||
|
|
||||||
status=$(echo "$response" | tail -c 4)
|
status=$(echo "$response" | tail -c 4)
|
||||||
|
|
||||||
if [ "$status" == "200" ]; then
|
if [ "$status" == "200" ]; then
|
||||||
rate_limit_remaining=$(echo "$response" | head -n -1 | jq -r '.rate_limit_remaining // "N/A"' 2>/dev/null)
|
rate_limit_remaining=$(echo "$response" | head -n -1 | jq -r '.rate_limit_remaining // "N/A"' 2>/dev/null)
|
||||||
echo "Request $i: ✅ 200 OK (Rate limit remaining: $rate_limit_remaining)"
|
echo "Request $i: ✅ 200 OK (Rate limit remaining: $rate_limit_remaining)"
|
||||||
|
|
@ -33,7 +33,7 @@ for i in {1..20}; do
|
||||||
else
|
else
|
||||||
echo "Request $i: ❌ $status Error"
|
echo "Request $i: ❌ $status Error"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Small delay to prevent overwhelming
|
# Small delay to prevent overwhelming
|
||||||
sleep 0.1
|
sleep 0.1
|
||||||
done
|
done
|
||||||
|
|
@ -58,10 +58,10 @@ for i in {1..10}; do
|
||||||
-H "X-API-Key: $API_KEY" \
|
-H "X-API-Key: $API_KEY" \
|
||||||
"$BASE_URL/health")
|
"$BASE_URL/health")
|
||||||
local_end=$(date +%s.%N)
|
local_end=$(date +%s.%N)
|
||||||
|
|
||||||
status=$(echo "$response" | tail -c 4)
|
status=$(echo "$response" | tail -c 4)
|
||||||
duration=$(echo "$local_end - $local_start" | bc -l)
|
duration=$(echo "$local_end - $local_start" | bc -l)
|
||||||
|
|
||||||
echo "Concurrent $i: Status $status, Duration ${duration}s" > "$temp_dir/result_$i"
|
echo "Concurrent $i: Status $status, Duration ${duration}s" > "$temp_dir/result_$i"
|
||||||
} &
|
} &
|
||||||
done
|
done
|
||||||
|
|
@ -85,18 +85,18 @@ mail_errors=0
|
||||||
|
|
||||||
for i in {1..5}; do
|
for i in {1..5}; do
|
||||||
start_time=$(date +%s.%N)
|
start_time=$(date +%s.%N)
|
||||||
|
|
||||||
response=$(curl -s -w "%{http_code}" \
|
response=$(curl -s -w "%{http_code}" \
|
||||||
-H "X-API-Key: $API_KEY" \
|
-H "X-API-Key: $API_KEY" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d "{\"name\":\"Stress Test $i\",\"email\":\"test$i@example.com\",\"subject\":\"Performance Test\",\"message\":\"Load test message $i\"}" \
|
-d "{\"name\":\"Stress Test $i\",\"email\":\"test$i@example.com\",\"subject\":\"Performance Test\",\"message\":\"Load test message $i\"}" \
|
||||||
"$BASE_URL/v1/mail/send")
|
"$BASE_URL/v1/mail/send")
|
||||||
|
|
||||||
end_time=$(date +%s.%N)
|
end_time=$(date +%s.%N)
|
||||||
duration=$(echo "$end_time - $start_time" | bc -l)
|
duration=$(echo "$end_time - $start_time" | bc -l)
|
||||||
|
|
||||||
status=$(echo "$response" | tail -c 4)
|
status=$(echo "$response" | tail -c 4)
|
||||||
|
|
||||||
if [ "$status" == "200" ]; then
|
if [ "$status" == "200" ]; then
|
||||||
echo "Mail $i: ✅ 200 OK (${duration}s)"
|
echo "Mail $i: ✅ 200 OK (${duration}s)"
|
||||||
((mail_success++))
|
((mail_success++))
|
||||||
|
|
@ -104,7 +104,7 @@ for i in {1..5}; do
|
||||||
echo "Mail $i: ❌ Status $status (${duration}s)"
|
echo "Mail $i: ❌ Status $status (${duration}s)"
|
||||||
((mail_errors++))
|
((mail_errors++))
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Delay between mail sends to be nice to SMTP server
|
# Delay between mail sends to be nice to SMTP server
|
||||||
sleep 1
|
sleep 1
|
||||||
done
|
done
|
||||||
|
|
@ -120,7 +120,7 @@ mixed_success=0
|
||||||
|
|
||||||
for i in {1..15}; do
|
for i in {1..15}; do
|
||||||
((mixed_total++))
|
((mixed_total++))
|
||||||
|
|
||||||
if [ $((i % 3)) -eq 0 ]; then
|
if [ $((i % 3)) -eq 0 ]; then
|
||||||
# Every 3rd request: auth status
|
# Every 3rd request: auth status
|
||||||
endpoint="/v1/auth/status"
|
endpoint="/v1/auth/status"
|
||||||
|
|
@ -128,20 +128,20 @@ for i in {1..15}; do
|
||||||
# Other requests: health check
|
# Other requests: health check
|
||||||
endpoint="/health"
|
endpoint="/health"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
response=$(curl -s -w "%{http_code}" \
|
response=$(curl -s -w "%{http_code}" \
|
||||||
-H "X-API-Key: $API_KEY" \
|
-H "X-API-Key: $API_KEY" \
|
||||||
"$BASE_URL$endpoint")
|
"$BASE_URL$endpoint")
|
||||||
|
|
||||||
status=$(echo "$response" | tail -c 4)
|
status=$(echo "$response" | tail -c 4)
|
||||||
|
|
||||||
if [ "$status" == "200" ]; then
|
if [ "$status" == "200" ]; then
|
||||||
echo "Mixed $i ($endpoint): ✅ 200 OK"
|
echo "Mixed $i ($endpoint): ✅ 200 OK"
|
||||||
((mixed_success++))
|
((mixed_success++))
|
||||||
else
|
else
|
||||||
echo "Mixed $i ($endpoint): ❌ $status"
|
echo "Mixed $i ($endpoint): ❌ $status"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
sleep 0.2
|
sleep 0.2
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@
|
||||||
# Test API-Key-Authentifizierung (ohne jq parse errors)
|
# Test API-Key-Authentifizierung (ohne jq parse errors)
|
||||||
|
|
||||||
BASE_URL="http://127.0.0.1:8080"
|
BASE_URL="http://127.0.0.1:8080"
|
||||||
HUGO_API_KEY="hugo-dev-key-change-in-production"
|
HUGO_API_KEY="YOUR_API_KEY_HERE"
|
||||||
ADMIN_API_KEY="admin-dev-key-change-in-production"
|
ADMIN_API_KEY="YOUR_ADMIN_KEY_HERE"
|
||||||
INVALID_API_KEY="invalid-key-should-fail"
|
INVALID_API_KEY="invalid-key-should-fail"
|
||||||
|
|
||||||
echo "🔐 Testing Furt API-Key Authentication"
|
echo "🔐 Testing Furt API-Key Authentication"
|
||||||
|
|
@ -16,24 +16,24 @@ make_request() {
|
||||||
local url="$2"
|
local url="$2"
|
||||||
local headers="$3"
|
local headers="$3"
|
||||||
local data="$4"
|
local data="$4"
|
||||||
|
|
||||||
echo "Request: $method $url"
|
echo "Request: $method $url"
|
||||||
if [ -n "$headers" ]; then
|
if [ -n "$headers" ]; then
|
||||||
echo "Headers: $headers"
|
echo "Headers: $headers"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local response=$(curl -s $method \
|
local response=$(curl -s $method \
|
||||||
${headers:+-H "$headers"} \
|
${headers:+-H "$headers"} \
|
||||||
${data:+-d "$data"} \
|
${data:+-d "$data"} \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
"$url")
|
"$url")
|
||||||
|
|
||||||
local status=$(curl -s -o /dev/null -w "%{http_code}" $method \
|
local status=$(curl -s -o /dev/null -w "%{http_code}" $method \
|
||||||
${headers:+-H "$headers"} \
|
${headers:+-H "$headers"} \
|
||||||
${data:+-d "$data"} \
|
${data:+-d "$data"} \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
"$url")
|
"$url")
|
||||||
|
|
||||||
echo "Status: $status"
|
echo "Status: $status"
|
||||||
echo "Response: $response" | jq '.' 2>/dev/null || echo "$response"
|
echo "Response: $response" | jq '.' 2>/dev/null || echo "$response"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
|
||||||
2
scripts/test_modular.sh
Normal file → Executable file
2
scripts/test_modular.sh
Normal file → Executable file
|
|
@ -3,7 +3,7 @@
|
||||||
# Test der modularen Furt-Architektur
|
# Test der modularen Furt-Architektur
|
||||||
|
|
||||||
BASE_URL="http://127.0.0.1:8080"
|
BASE_URL="http://127.0.0.1:8080"
|
||||||
HUGO_API_KEY="hugo-dev-key-change-in-production"
|
HUGO_API_KEY="YOUR_API_KEY_HERE"
|
||||||
|
|
||||||
echo "🧩 Testing Modular Furt Architecture"
|
echo "🧩 Testing Modular Furt Architecture"
|
||||||
echo "===================================="
|
echo "===================================="
|
||||||
|
|
|
||||||
20
scripts/test_smtp.sh
Normal file → Executable file
20
scripts/test_smtp.sh
Normal file → Executable file
|
|
@ -36,7 +36,7 @@ else
|
||||||
echo "[ERROR] Validation failed"
|
echo "[ERROR] Validation failed"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Test 3: Invalid Email Format
|
# Test 3: Invalid Email Format
|
||||||
echo ""
|
echo ""
|
||||||
echo "[3] Testing email validation..."
|
echo "[3] Testing email validation..."
|
||||||
email_validation_response=$(curl -s -X POST "$SERVER_URL/v1/mail/send" \
|
email_validation_response=$(curl -s -X POST "$SERVER_URL/v1/mail/send" \
|
||||||
|
|
@ -54,36 +54,36 @@ fi
|
||||||
# Test 4: Valid Mail Request (REAL SMTP TEST)
|
# Test 4: Valid Mail Request (REAL SMTP TEST)
|
||||||
echo ""
|
echo ""
|
||||||
echo "[4] Testing REAL mail sending..."
|
echo "[4] Testing REAL mail sending..."
|
||||||
echo "WARNING: This will send a real email to michael@dragons-at-work.de"
|
echo "WARNING: This will send a real email to admin@example.com"
|
||||||
read -p "Continue with real mail test? (y/N): " -n 1 -r
|
read -p "Continue with real mail test? (y/N): " -n 1 -r
|
||||||
echo
|
echo
|
||||||
|
|
||||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||||
echo "Sending real test email..."
|
echo "Sending real test email..."
|
||||||
|
|
||||||
mail_response=$(curl -s -X POST "$SERVER_URL/v1/mail/send" \
|
mail_response=$(curl -s -X POST "$SERVER_URL/v1/mail/send" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d '{
|
-d '{
|
||||||
"name": "Furt Test User",
|
"name": "Furt Test User",
|
||||||
"email": "test@dragons-at-work.de",
|
"email": "test@example.com",
|
||||||
"subject": "Furt SMTP Test - Week 2 Success!",
|
"subject": "Furt SMTP Test - Week 2 Success!",
|
||||||
"message": "This is a test email from the Furt Lua HTTP-Server.\n\nSMTP Integration is working!\n\nTimestamp: '$(date)'\nServer: furt-lua v1.0"
|
"message": "This is a test email from the Furt Lua HTTP-Server.\n\nSMTP Integration is working!\n\nTimestamp: '$(date)'\nServer: furt-lua v1.0"
|
||||||
}')
|
}')
|
||||||
|
|
||||||
echo "Response: $mail_response"
|
echo "Response: $mail_response"
|
||||||
|
|
||||||
# Check for success
|
# Check for success
|
||||||
if echo "$mail_response" | grep -q '"success":true'; then
|
if echo "$mail_response" | grep -q '"success":true'; then
|
||||||
echo "[OK] MAIL SENT SUCCESSFULLY!"
|
echo "[OK] MAIL SENT SUCCESSFULLY!"
|
||||||
echo "Check michael@dragons-at-work.de inbox"
|
echo "Check admin@example.com inbox"
|
||||||
|
|
||||||
# Extract request ID
|
# Extract request ID
|
||||||
request_id=$(echo "$mail_response" | grep -o '"request_id":"[^"]*"' | cut -d'"' -f4)
|
request_id=$(echo "$mail_response" | grep -o '"request_id":"[^"]*"' | cut -d'"' -f4)
|
||||||
echo "Request ID: $request_id"
|
echo "Request ID: $request_id"
|
||||||
else
|
else
|
||||||
echo "[ERROR] Mail sending failed"
|
echo "[ERROR] Mail sending failed"
|
||||||
echo "Check server logs and SMTP credentials"
|
echo "Check server logs and SMTP credentials"
|
||||||
|
|
||||||
# Show error details
|
# Show error details
|
||||||
if echo "$mail_response" | grep -q "error"; then
|
if echo "$mail_response" | grep -q "error"; then
|
||||||
error_msg=$(echo "$mail_response" | grep -o '"error":"[^"]*"' | cut -d'"' -f4)
|
error_msg=$(echo "$mail_response" | grep -o '"error":"[^"]*"' | cut -d'"' -f4)
|
||||||
|
|
@ -126,7 +126,7 @@ echo "Performance: ${duration_ms}ms"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Week 2 Challenge Status:"
|
echo "Week 2 Challenge Status:"
|
||||||
echo " SMTP Integration: COMPLETE"
|
echo " SMTP Integration: COMPLETE"
|
||||||
echo " Environment Variables: CHECK .env"
|
echo " Environment Variables: CHECK .env"
|
||||||
echo " Native Lua Implementation: DONE"
|
echo " Native Lua Implementation: DONE"
|
||||||
echo " Production Ready: READY FOR TESTING"
|
echo " Production Ready: READY FOR TESTING"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -91,9 +91,7 @@ end
|
||||||
function FurtServer:add_cors_headers(request)
|
function FurtServer:add_cors_headers(request)
|
||||||
local allowed_origins = config.cors and config.cors.allowed_origins or {
|
local allowed_origins = config.cors and config.cors.allowed_origins or {
|
||||||
"http://localhost:1313",
|
"http://localhost:1313",
|
||||||
"http://127.0.0.1:1313",
|
"http://127.0.0.1:1313"
|
||||||
"https://dragons-at-work.de",
|
|
||||||
"https://www.dragons-at-work.de"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Check if request has Origin header
|
-- Check if request has Origin header
|
||||||
|
|
|
||||||
80
src/smtp.lua
80
src/smtp.lua
|
|
@ -19,7 +19,7 @@ function SSLCompat:detect_ssl_library()
|
||||||
return "luaossl", ssl_lib
|
return "luaossl", ssl_lib
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Try luasec
|
-- Try luasec
|
||||||
local success, ssl_lib = pcall(require, "ssl")
|
local success, ssl_lib = pcall(require, "ssl")
|
||||||
if success and ssl_lib then
|
if success and ssl_lib then
|
||||||
|
|
@ -28,23 +28,23 @@ function SSLCompat:detect_ssl_library()
|
||||||
return "luasec", ssl_lib
|
return "luasec", ssl_lib
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return nil, "No compatible SSL library found (tried luaossl, luasec)"
|
return nil, "No compatible SSL library found (tried luaossl, luasec)"
|
||||||
end
|
end
|
||||||
|
|
||||||
function SSLCompat:wrap_socket(sock, options)
|
function SSLCompat:wrap_socket(sock, options)
|
||||||
local ssl_type, ssl_lib = self:detect_ssl_library()
|
local ssl_type, ssl_lib = self:detect_ssl_library()
|
||||||
|
|
||||||
if not ssl_type then
|
if not ssl_type then
|
||||||
return nil, ssl_lib -- ssl_lib contains error message
|
return nil, ssl_lib -- ssl_lib contains error message
|
||||||
end
|
end
|
||||||
|
|
||||||
if ssl_type == "luaossl" then
|
if ssl_type == "luaossl" then
|
||||||
return self:wrap_luaossl(sock, options, ssl_lib)
|
return self:wrap_luaossl(sock, options, ssl_lib)
|
||||||
elseif ssl_type == "luasec" then
|
elseif ssl_type == "luasec" then
|
||||||
return self:wrap_luasec(sock, options, ssl_lib)
|
return self:wrap_luasec(sock, options, ssl_lib)
|
||||||
end
|
end
|
||||||
|
|
||||||
return nil, "Unknown SSL library type: " .. ssl_type
|
return nil, "Unknown SSL library type: " .. ssl_type
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -55,18 +55,18 @@ function SSLCompat:wrap_luaossl(sock, options, ssl_lib)
|
||||||
protocol = "tlsv1_2",
|
protocol = "tlsv1_2",
|
||||||
verify = "none" -- For self-signed certs
|
verify = "none" -- For self-signed certs
|
||||||
})
|
})
|
||||||
|
|
||||||
if not ssl_sock then
|
if not ssl_sock then
|
||||||
return nil, "luaossl wrap failed: " .. (err or "unknown error")
|
return nil, "luaossl wrap failed: " .. (err or "unknown error")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- luaossl typically does handshake automatically, but explicit is safer
|
-- luaossl typically does handshake automatically, but explicit is safer
|
||||||
local success, err = pcall(function() return ssl_sock:dohandshake() end)
|
local success, err = pcall(function() return ssl_sock:dohandshake() end)
|
||||||
if not success then
|
if not success then
|
||||||
-- Some luaossl versions don't need explicit handshake
|
-- Some luaossl versions don't need explicit handshake
|
||||||
-- Continue if dohandshake doesn't exist
|
-- Continue if dohandshake doesn't exist
|
||||||
end
|
end
|
||||||
|
|
||||||
return ssl_sock, nil
|
return ssl_sock, nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -78,28 +78,28 @@ function SSLCompat:wrap_luasec(sock, options, ssl_lib)
|
||||||
verify = "none",
|
verify = "none",
|
||||||
options = "all"
|
options = "all"
|
||||||
})
|
})
|
||||||
|
|
||||||
if not ssl_sock then
|
if not ssl_sock then
|
||||||
return nil, "luasec wrap failed: " .. (err or "unknown error")
|
return nil, "luasec wrap failed: " .. (err or "unknown error")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- luasec requires explicit handshake
|
-- luasec requires explicit handshake
|
||||||
local success, err = ssl_sock:dohandshake()
|
local success, err = ssl_sock:dohandshake()
|
||||||
if not success then
|
if not success then
|
||||||
return nil, "luasec handshake failed: " .. (err or "unknown error")
|
return nil, "luasec handshake failed: " .. (err or "unknown error")
|
||||||
end
|
end
|
||||||
|
|
||||||
return ssl_sock, nil
|
return ssl_sock, nil
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Create SMTP instance
|
-- Create SMTP instance
|
||||||
function SMTP:new(config)
|
function SMTP:new(config)
|
||||||
local instance = {
|
local instance = {
|
||||||
server = config.smtp_server or "mail.dragons-at-work.de",
|
server = config.smtp_server,
|
||||||
port = config.smtp_port or 465,
|
port = config.smtp_port,
|
||||||
username = config.username,
|
username = config.username,
|
||||||
password = config.password,
|
password = config.password,
|
||||||
from_address = config.from_address or "noreply@dragons-at-work.de",
|
from_address = config.from_address,
|
||||||
use_ssl = config.use_ssl or true,
|
use_ssl = config.use_ssl or true,
|
||||||
debug = config.debug or false,
|
debug = config.debug or false,
|
||||||
ssl_compat = SSLCompat
|
ssl_compat = SSLCompat
|
||||||
|
|
@ -133,7 +133,7 @@ function SMTP:send_command(sock, command, expected_code)
|
||||||
if self.debug then
|
if self.debug then
|
||||||
print("SMTP CMD: " .. (command or ""):gsub("\r\n", "\\r\\n"))
|
print("SMTP CMD: " .. (command or ""):gsub("\r\n", "\\r\\n"))
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Only send if command is not nil (for server greeting, command is nil)
|
-- Only send if command is not nil (for server greeting, command is nil)
|
||||||
if command then
|
if command then
|
||||||
local success, err = sock:send(command .. "\r\n")
|
local success, err = sock:send(command .. "\r\n")
|
||||||
|
|
@ -141,16 +141,16 @@ function SMTP:send_command(sock, command, expected_code)
|
||||||
return false, "Failed to send command: " .. (err or "unknown error")
|
return false, "Failed to send command: " .. (err or "unknown error")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local response, err = sock:receive()
|
local response, err = sock:receive()
|
||||||
if not response then
|
if not response then
|
||||||
return false, "Failed to receive response: " .. (err or "unknown error")
|
return false, "Failed to receive response: " .. (err or "unknown error")
|
||||||
end
|
end
|
||||||
|
|
||||||
if self.debug then
|
if self.debug then
|
||||||
print("SMTP RSP: " .. response)
|
print("SMTP RSP: " .. response)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Handle multi-line responses (like EHLO)
|
-- Handle multi-line responses (like EHLO)
|
||||||
local full_response = response
|
local full_response = response
|
||||||
while response:match("^%d%d%d%-") do
|
while response:match("^%d%d%d%-") do
|
||||||
|
|
@ -163,12 +163,12 @@ function SMTP:send_command(sock, command, expected_code)
|
||||||
end
|
end
|
||||||
full_response = full_response .. "\n" .. response
|
full_response = full_response .. "\n" .. response
|
||||||
end
|
end
|
||||||
|
|
||||||
local code = response:match("^(%d+)")
|
local code = response:match("^(%d+)")
|
||||||
if expected_code and code ~= tostring(expected_code) then
|
if expected_code and code ~= tostring(expected_code) then
|
||||||
return false, "Unexpected response: " .. full_response
|
return false, "Unexpected response: " .. full_response
|
||||||
end
|
end
|
||||||
|
|
||||||
return true, full_response
|
return true, full_response
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -179,38 +179,38 @@ function SMTP:connect()
|
||||||
if not sock then
|
if not sock then
|
||||||
return false, "Failed to create socket: " .. (err or "unknown error")
|
return false, "Failed to create socket: " .. (err or "unknown error")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Set timeout
|
-- Set timeout
|
||||||
sock:settimeout(30)
|
sock:settimeout(30)
|
||||||
|
|
||||||
-- Connect to server
|
-- Connect to server
|
||||||
local success, err = sock:connect(self.server, self.port)
|
local success, err = sock:connect(self.server, self.port)
|
||||||
if not success then
|
if not success then
|
||||||
return false, "Failed to connect to " .. self.server .. ":" .. self.port .. " - " .. (err or "unknown error")
|
return false, "Failed to connect to " .. self.server .. ":" .. self.port .. " - " .. (err or "unknown error")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Wrap with SSL for port 465 using compatibility layer
|
-- Wrap with SSL for port 465 using compatibility layer
|
||||||
if self.use_ssl and self.port == 465 then
|
if self.use_ssl and self.port == 465 then
|
||||||
local ssl_sock, err = self.ssl_compat:wrap_socket(sock, {
|
local ssl_sock, err = self.ssl_compat:wrap_socket(sock, {
|
||||||
mode = "client",
|
mode = "client",
|
||||||
protocol = "tlsv1_2"
|
protocol = "tlsv1_2"
|
||||||
})
|
})
|
||||||
|
|
||||||
if not ssl_sock then
|
if not ssl_sock then
|
||||||
sock:close()
|
sock:close()
|
||||||
return false, "Failed to establish SSL connection: " .. (err or "unknown error")
|
return false, "Failed to establish SSL connection: " .. (err or "unknown error")
|
||||||
end
|
end
|
||||||
|
|
||||||
sock = ssl_sock
|
sock = ssl_sock
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Read server greeting
|
-- Read server greeting
|
||||||
local success, response = self:send_command(sock, nil, 220)
|
local success, response = self:send_command(sock, nil, 220)
|
||||||
if not success then
|
if not success then
|
||||||
sock:close()
|
sock:close()
|
||||||
return false, "SMTP server greeting failed: " .. response
|
return false, "SMTP server greeting failed: " .. response
|
||||||
end
|
end
|
||||||
|
|
||||||
return sock, nil
|
return sock, nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -219,64 +219,64 @@ function SMTP:send_email(to_address, subject, message, from_name)
|
||||||
if not self.username or not self.password then
|
if not self.username or not self.password then
|
||||||
return false, "SMTP username or password not configured"
|
return false, "SMTP username or password not configured"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Connect to server
|
-- Connect to server
|
||||||
local sock, err = self:connect()
|
local sock, err = self:connect()
|
||||||
if not sock then
|
if not sock then
|
||||||
return false, err
|
return false, err
|
||||||
end
|
end
|
||||||
|
|
||||||
local function cleanup_and_fail(error_msg)
|
local function cleanup_and_fail(error_msg)
|
||||||
sock:close()
|
sock:close()
|
||||||
return false, error_msg
|
return false, error_msg
|
||||||
end
|
end
|
||||||
|
|
||||||
-- EHLO command
|
-- EHLO command
|
||||||
local success, response = self:send_command(sock, "EHLO furt-lua", 250)
|
local success, response = self:send_command(sock, "EHLO furt-lua", 250)
|
||||||
if not success then
|
if not success then
|
||||||
return cleanup_and_fail("EHLO failed: " .. response)
|
return cleanup_and_fail("EHLO failed: " .. response)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- AUTH LOGIN
|
-- AUTH LOGIN
|
||||||
local success, response = self:send_command(sock, "AUTH LOGIN", 334)
|
local success, response = self:send_command(sock, "AUTH LOGIN", 334)
|
||||||
if not success then
|
if not success then
|
||||||
return cleanup_and_fail("AUTH LOGIN failed: " .. response)
|
return cleanup_and_fail("AUTH LOGIN failed: " .. response)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Send username (base64 encoded)
|
-- Send username (base64 encoded)
|
||||||
local username_b64 = self:base64_encode(self.username)
|
local username_b64 = self:base64_encode(self.username)
|
||||||
local success, response = self:send_command(sock, username_b64, 334)
|
local success, response = self:send_command(sock, username_b64, 334)
|
||||||
if not success then
|
if not success then
|
||||||
return cleanup_and_fail("Username authentication failed: " .. response)
|
return cleanup_and_fail("Username authentication failed: " .. response)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Send password (base64 encoded)
|
-- Send password (base64 encoded)
|
||||||
local password_b64 = self:base64_encode(self.password)
|
local password_b64 = self:base64_encode(self.password)
|
||||||
local success, response = self:send_command(sock, password_b64, 235)
|
local success, response = self:send_command(sock, password_b64, 235)
|
||||||
if not success then
|
if not success then
|
||||||
return cleanup_and_fail("Password authentication failed: " .. response)
|
return cleanup_and_fail("Password authentication failed: " .. response)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- MAIL FROM
|
-- MAIL FROM
|
||||||
local mail_from = "MAIL FROM:<" .. self.from_address .. ">"
|
local mail_from = "MAIL FROM:<" .. self.from_address .. ">"
|
||||||
local success, response = self:send_command(sock, mail_from, 250)
|
local success, response = self:send_command(sock, mail_from, 250)
|
||||||
if not success then
|
if not success then
|
||||||
return cleanup_and_fail("MAIL FROM failed: " .. response)
|
return cleanup_and_fail("MAIL FROM failed: " .. response)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- RCPT TO
|
-- RCPT TO
|
||||||
local rcpt_to = "RCPT TO:<" .. to_address .. ">"
|
local rcpt_to = "RCPT TO:<" .. to_address .. ">"
|
||||||
local success, response = self:send_command(sock, rcpt_to, 250)
|
local success, response = self:send_command(sock, rcpt_to, 250)
|
||||||
if not success then
|
if not success then
|
||||||
return cleanup_and_fail("RCPT TO failed: " .. response)
|
return cleanup_and_fail("RCPT TO failed: " .. response)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- DATA command
|
-- DATA command
|
||||||
local success, response = self:send_command(sock, "DATA", 354)
|
local success, response = self:send_command(sock, "DATA", 354)
|
||||||
if not success then
|
if not success then
|
||||||
return cleanup_and_fail("DATA command failed: " .. response)
|
return cleanup_and_fail("DATA command failed: " .. response)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Build email message
|
-- Build email message
|
||||||
local display_name = from_name or "Furt Contact Form"
|
local display_name = from_name or "Furt Contact Form"
|
||||||
local email_content = string.format(
|
local email_content = string.format(
|
||||||
|
|
@ -295,17 +295,17 @@ function SMTP:send_email(to_address, subject, message, from_name)
|
||||||
os.date("%a, %d %b %Y %H:%M:%S %z"),
|
os.date("%a, %d %b %Y %H:%M:%S %z"),
|
||||||
message
|
message
|
||||||
)
|
)
|
||||||
|
|
||||||
-- Send email content
|
-- Send email content
|
||||||
local success, response = self:send_command(sock, email_content, 250)
|
local success, response = self:send_command(sock, email_content, 250)
|
||||||
if not success then
|
if not success then
|
||||||
return cleanup_and_fail("Email sending failed: " .. response)
|
return cleanup_and_fail("Email sending failed: " .. response)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- QUIT
|
-- QUIT
|
||||||
self:send_command(sock, "QUIT", 221)
|
self:send_command(sock, "QUIT", 221)
|
||||||
sock:close()
|
sock:close()
|
||||||
|
|
||||||
return true, "Email sent successfully"
|
return true, "Email sent successfully"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue