15 KiB
Gateway Testing via Admin Interface
Overview
The admin interface now includes a comprehensive gateway testing module that allows you to:
- Check gateway connection status in real-time
- Send test SMS messages through specific gateways
- Verify gateway functionality without external tools
- Debug connection issues
Features
1. Connection Status Check
Real-time Gateway Status:
- ✅ Online/Offline detection
- ⏰ Last heartbeat timestamp
- 🕐 Time since last connection
- 🔄 One-click refresh
How It Works:
- Checks if gateway sent heartbeat within last 2 minutes
- Displays exact last heartbeat time
- Shows human-readable "time ago" format
- Updates with AJAX (no page reload)
2. Send Test SMS
Test Message Features:
- 📱 Phone number validation
- ✉️ Custom message composition
- 📊 Character counter (160 char SMS limit)
- 📝 Multi-part SMS detection
- ✅ Success/failure feedback
- 🔍 Message ID tracking
Message Tracking:
- Test messages marked with
metadata: { test: true } - Identifies sender as "admin_interface"
- Full message history in logs
- Same queue as regular messages
Access Points
From Gateway List
URL: /admin/gateways
Each gateway has a Test button in the Actions column:
- Blue button with flask icon
- Located next to Activate/Deactivate button
- Available for all gateways (online or offline)
From Gateway Details
URL: /admin/gateways/:id
Test Gateway button at the bottom:
- Blue button with flask icon
- Located above Activate/Deactivate button
- Opens dedicated testing page
Direct Testing Page
URL: /admin/gateways/:id/test
Full testing interface with:
- Connection status card
- Gateway information
- Test SMS form
- Real-time updates
Testing Interface
Page Layout
┌─────────────────────────────────────────┐
│ Test Gateway: [Gateway Name] │
│ [Back to Gateway Details] │
├─────────────────────────────────────────┤
│ Gateway Status │
│ ┌───────────────────────┐ │
│ │ ✅ Gateway is Online │ [Refresh] │
│ │ Last heartbeat: 30s │ │
│ └───────────────────────┘ │
├─────────────────────────────────────────┤
│ Connection Information │
│ Device ID: android-001 │
│ Name: Office Phone │
│ Priority: 5 │
│ Active: Yes │
├─────────────────────────────────────────┤
│ Send Test SMS │
│ Phone Number: [+959123456789____] │
│ Message: [This is a test___________] │
│ [________________________] │
│ 160 characters remaining │
│ │
│ [Send Test SMS] [Reset Form] │
└─────────────────────────────────────────┘
Status Indicators
Online (Green):
┌──────────────────────────────┐
│ ✅ Gateway is Online │
│ Last heartbeat: 1 minute ago │
│ 2025-10-20 13:45:30 │
└──────────────────────────────┘
Offline (Red):
┌──────────────────────────────┐
│ ❌ Gateway is Offline │
│ Gateway is offline │
│ Last seen: 5 hours ago │
└──────────────────────────────┘
Never Connected (Red):
┌──────────────────────────────┐
│ ❌ Gateway is Offline │
│ Gateway is offline │
│ Never connected │
└──────────────────────────────┘
Using the Test Feature
Step 1: Access Testing Page
Option A: From Gateway List
- Navigate to
/admin/gateways - Find the gateway you want to test
- Click the blue Test button
Option B: From Gateway Details
- Navigate to
/admin/gateways/:id - Scroll to bottom
- Click Test Gateway button
Step 2: Check Connection Status
The page loads with automatic status check:
- Wait for status: Shows loading spinner
- View result: Green (online) or Red (offline)
- Refresh if needed: Click Refresh Status button
Connection Check Details:
- Verifies
last_heartbeat_attimestamp - Must be within 2 minutes to be "online"
- Shows exact time of last heartbeat
- Displays human-readable time ago
Step 3: Send Test SMS
-
Enter phone number:
- Include country code (e.g.,
+959123456789) - Required field
- Validated on submission
- Include country code (e.g.,
-
Enter message:
- Default test message provided
- Customizable content
- Character counter updates live
- Warns if over 160 chars
-
Click "Send Test SMS":
- Button shows spinner: "Sending..."
- Waits for response
- Displays result
-
View result:
Success (Green):
┌──────────────────────────────────────┐
│ ✅ Test SMS Sent Successfully! │
│ Test SMS queued for sending │
│ Message ID: msg_abc123... │
│ Status: pending │
└──────────────────────────────────────┘
Error (Red):
┌──────────────────────────────────────┐
│ ❌ Failed to Send Test SMS │
│ Phone number is not valid │
└──────────────────────────────────────┘
Step 4: Verify in Logs
- Navigate to
/admin/logs - Look for test message:
- Message ID from success response
- Phone number you entered
- Status: pending → sent → delivered
- Filter by gateway to see only this gateway's messages
API Endpoints
Check Connection
Endpoint: POST /admin/gateways/:id/check_connection
Response (Online):
{
"status": "success",
"message": "Gateway is online",
"last_heartbeat": "2025-10-20T13:45:30.000Z",
"time_ago": "1 minute"
}
Response (Offline):
{
"status": "error",
"message": "Gateway is offline",
"last_heartbeat": "2025-10-20T08:30:15.000Z",
"time_ago": "5 hours"
}
Send Test SMS
Endpoint: POST /admin/gateways/:id/send_test_sms
Request:
{
"phone_number": "+959123456789",
"message_body": "This is a test message"
}
Response (Success):
{
"status": "success",
"message": "Test SMS queued for sending",
"message_id": "msg_abc123def456...",
"sms_status": "pending"
}
Response (Error):
{
"status": "error",
"message": "Phone number and message are required"
}
Routes Added
resources :gateways do
member do
get :test # Testing page
post :check_connection # AJAX status check
post :send_test_sms # AJAX send test
post :toggle # Activate/deactivate (existing)
end
end
New Routes:
GET /admin/gateways/:id/test- Testing pagePOST /admin/gateways/:id/check_connection- Check statusPOST /admin/gateways/:id/send_test_sms- Send test SMS
Controller Actions
test
def test
@gateway = Gateway.find(params[:id])
end
Renders the testing page.
check_connection
def check_connection
@gateway = Gateway.find(params[:id])
if @gateway.online?
render json: {
status: "success",
message: "Gateway is online",
last_heartbeat: @gateway.last_heartbeat_at,
time_ago: helpers.time_ago_in_words(@gateway.last_heartbeat_at)
}
else
render json: {
status: "error",
message: "Gateway is offline",
last_heartbeat: @gateway.last_heartbeat_at,
time_ago: @gateway.last_heartbeat_at ? helpers.time_ago_in_words(@gateway.last_heartbeat_at) : "never"
}
end
end
send_test_sms
def send_test_sms
@gateway = Gateway.find(params[:id])
phone_number = params[:phone_number]
message_body = params[:message_body]
sms = SmsMessage.create!(
direction: "outbound",
phone_number: phone_number,
message_body: message_body,
gateway: @gateway,
metadata: { test: true, sent_from: "admin_interface" }
)
render json: {
status: "success",
message: "Test SMS queued for sending",
message_id: sms.message_id,
sms_status: sms.status
}
end
JavaScript Features
Auto-load Status
document.addEventListener('DOMContentLoaded', function() {
checkConnection(); // Check on page load
});
Refresh Button
async function checkConnection() {
// Show loading
container.innerHTML = '<div class="spinner">...</div>';
// Fetch status
const response = await fetch('/admin/gateways/:id/check_connection', {
method: 'POST',
headers: {
'X-CSRF-Token': csrfToken
}
});
const data = await response.json();
// Display result
}
Character Counter
messageBody.addEventListener('input', updateCharCount);
function updateCharCount() {
const length = messageBody.value.length;
const remaining = 160 - length;
if (remaining < 0) {
const parts = Math.ceil(length / 160);
charCount.textContent = `${Math.abs(remaining)} characters over (${parts} parts)`;
charCount.classList.add('text-red-600');
} else {
charCount.textContent = `${remaining} characters remaining`;
}
}
Form Submission
form.addEventListener('submit', async function(e) {
e.preventDefault();
// Disable button
submitButton.disabled = true;
submitText.innerHTML = 'Sending...';
// Send request
const response = await fetch('/admin/gateways/:id/send_test_sms', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': csrfToken
},
body: JSON.stringify({
phone_number: phoneNumber.value,
message_body: messageBody.value
})
});
const data = await response.json();
// Display result and re-enable button
});
Test Message Metadata
All test messages include metadata for identification:
{
test: true,
sent_from: "admin_interface"
}
Benefits:
- Easy to filter test messages in logs
- Distinguish from production messages
- Audit trail of admin testing
- Can be excluded from analytics
Troubleshooting
Connection Check Fails
Symptom: Can't check gateway status
Causes:
- Database connection issue
- Gateway record not found
- JavaScript error
Solutions:
# Check Rails logs
tail -f log/development.log
# Verify gateway exists
bin/rails runner "puts Gateway.find(1).inspect"
# Check browser console for JavaScript errors
Test SMS Not Sending
Symptom: SMS queued but never sent
Causes:
- Gateway offline
- Sidekiq not running
- Redis not running
- Queue backed up
Solutions:
# Check gateway status
bin/rails console
> Gateway.find(1).online?
# Check Sidekiq
ps aux | grep sidekiq
# Start Sidekiq if needed
bundle exec sidekiq
# Check Redis
redis-cli ping
Invalid Phone Number
Symptom: "Phone number is not valid" error
Causes:
- Missing country code
- Invalid format
- Phonelib validation failed
Solutions:
- Always include country code:
+959123456789 - Check number format for your country
- Test number in console:
Phonelib.parse("+959123456789").valid?
Security Considerations
Admin Authentication Required
- All testing endpoints require admin login
- CSRF protection enabled
- Session validation
- No public access
Rate Limiting (Recommended)
Consider adding rate limiting:
# config/initializers/rack_attack.rb
Rack::Attack.throttle('test_sms_per_admin', limit: 10, period: 1.hour) do |req|
if req.path == '/admin/gateways/*/send_test_sms' && req.post?
req.session[:admin_id]
end
end
Test Message Limits
Best Practices:
- Limit test messages to prevent abuse
- Log all test SMS sends
- Monitor for unusual patterns
- Alert on excessive testing
Phone Number Privacy
Considerations:
- Test messages go to real phone numbers
- Recipients will receive actual SMS
- Use dedicated test numbers
- Don't test with customer numbers
Best Practices
When to Use Testing
✅ Good Use Cases:
- After gateway registration (verify it works)
- After configuration changes
- Diagnosing offline issues
- Verifying app updates
- Training new staff
❌ Avoid:
- Testing with production phone numbers
- Excessive testing (generates costs)
- Testing offline gateways repeatedly
- Using for regular message sending
Test Message Guidelines
Recommended Content:
This is a test message from MySMSAPio admin interface.
Gateway: [Gateway Name]
Date: [Date/Time]
Ignore this message.
Avoid:
- Long messages (keep under 160 chars)
- Multiple consecutive tests
- Testing during peak hours
- Sensitive information in tests
Monitoring Test Messages
View in Logs
- Navigate to
/admin/logs - Filter by:
- Gateway name
- Phone number
- Date range
- Look for status progression:
pending→sent→delivered
- Check error messages if failed
Identify Test Messages
Test messages have:
metadata.test = truemetadata.sent_from = "admin_interface"
Query in console:
# Find all test messages
SmsMessage.where("metadata->>'test' = 'true'").count
# Find recent test messages
SmsMessage.where("metadata->>'test' = 'true'")
.where("created_at > ?", 1.day.ago)
.order(created_at: :desc)
Summary
✅ Implemented: Gateway testing via admin interface ✅ Features: Connection check + Test SMS sending ✅ Access: From gateway list or details page ✅ Real-time: AJAX status updates ✅ Tracking: Full metadata and logging ✅ Security: Admin authentication required
Test any gateway easily:
- Click "Test" button
- Check status (auto-loads)
- Send test SMS
- View in logs
Perfect for debugging, verification, and training! 🚀