completed SMS gateway project
This commit is contained in:
614
GATEWAY_TESTING.md
Normal file
614
GATEWAY_TESTING.md
Normal file
@@ -0,0 +1,614 @@
|
||||
# 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
|
||||
1. Navigate to `/admin/gateways`
|
||||
2. Find the gateway you want to test
|
||||
3. Click the blue **Test** button
|
||||
|
||||
**Option B**: From Gateway Details
|
||||
1. Navigate to `/admin/gateways/:id`
|
||||
2. Scroll to bottom
|
||||
3. Click **Test Gateway** button
|
||||
|
||||
### Step 2: Check Connection Status
|
||||
|
||||
The page loads with automatic status check:
|
||||
|
||||
1. **Wait for status**: Shows loading spinner
|
||||
2. **View result**: Green (online) or Red (offline)
|
||||
3. **Refresh if needed**: Click **Refresh Status** button
|
||||
|
||||
**Connection Check Details**:
|
||||
- Verifies `last_heartbeat_at` timestamp
|
||||
- Must be within 2 minutes to be "online"
|
||||
- Shows exact time of last heartbeat
|
||||
- Displays human-readable time ago
|
||||
|
||||
### Step 3: Send Test SMS
|
||||
|
||||
1. **Enter phone number**:
|
||||
- Include country code (e.g., `+959123456789`)
|
||||
- Required field
|
||||
- Validated on submission
|
||||
|
||||
2. **Enter message**:
|
||||
- Default test message provided
|
||||
- Customizable content
|
||||
- Character counter updates live
|
||||
- Warns if over 160 chars
|
||||
|
||||
3. **Click "Send Test SMS"**:
|
||||
- Button shows spinner: "Sending..."
|
||||
- Waits for response
|
||||
- Displays result
|
||||
|
||||
4. **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
|
||||
|
||||
1. Navigate to `/admin/logs`
|
||||
2. Look for test message:
|
||||
- Message ID from success response
|
||||
- Phone number you entered
|
||||
- Status: pending → sent → delivered
|
||||
3. Filter by gateway to see only this gateway's messages
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Check Connection
|
||||
|
||||
**Endpoint**: `POST /admin/gateways/:id/check_connection`
|
||||
|
||||
**Response (Online)**:
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"message": "Gateway is online",
|
||||
"last_heartbeat": "2025-10-20T13:45:30.000Z",
|
||||
"time_ago": "1 minute"
|
||||
}
|
||||
```
|
||||
|
||||
**Response (Offline)**:
|
||||
```json
|
||||
{
|
||||
"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**:
|
||||
```json
|
||||
{
|
||||
"phone_number": "+959123456789",
|
||||
"message_body": "This is a test message"
|
||||
}
|
||||
```
|
||||
|
||||
**Response (Success)**:
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"message": "Test SMS queued for sending",
|
||||
"message_id": "msg_abc123def456...",
|
||||
"sms_status": "pending"
|
||||
}
|
||||
```
|
||||
|
||||
**Response (Error)**:
|
||||
```json
|
||||
{
|
||||
"status": "error",
|
||||
"message": "Phone number and message are required"
|
||||
}
|
||||
```
|
||||
|
||||
## Routes Added
|
||||
|
||||
```ruby
|
||||
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 page
|
||||
- `POST /admin/gateways/:id/check_connection` - Check status
|
||||
- `POST /admin/gateways/:id/send_test_sms` - Send test SMS
|
||||
|
||||
## Controller Actions
|
||||
|
||||
### `test`
|
||||
```ruby
|
||||
def test
|
||||
@gateway = Gateway.find(params[:id])
|
||||
end
|
||||
```
|
||||
Renders the testing page.
|
||||
|
||||
### `check_connection`
|
||||
```ruby
|
||||
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`
|
||||
```ruby
|
||||
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
|
||||
|
||||
```javascript
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
checkConnection(); // Check on page load
|
||||
});
|
||||
```
|
||||
|
||||
### Refresh Button
|
||||
|
||||
```javascript
|
||||
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
|
||||
|
||||
```javascript
|
||||
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
|
||||
|
||||
```javascript
|
||||
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:
|
||||
|
||||
```ruby
|
||||
{
|
||||
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**:
|
||||
1. Database connection issue
|
||||
2. Gateway record not found
|
||||
3. JavaScript error
|
||||
|
||||
**Solutions**:
|
||||
```bash
|
||||
# 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**:
|
||||
1. Gateway offline
|
||||
2. Sidekiq not running
|
||||
3. Redis not running
|
||||
4. Queue backed up
|
||||
|
||||
**Solutions**:
|
||||
```bash
|
||||
# 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**:
|
||||
1. Missing country code
|
||||
2. Invalid format
|
||||
3. Phonelib validation failed
|
||||
|
||||
**Solutions**:
|
||||
- Always include country code: `+959123456789`
|
||||
- Check number format for your country
|
||||
- Test number in console:
|
||||
```ruby
|
||||
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:
|
||||
|
||||
```ruby
|
||||
# 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
|
||||
|
||||
1. Navigate to `/admin/logs`
|
||||
2. Filter by:
|
||||
- Gateway name
|
||||
- Phone number
|
||||
- Date range
|
||||
3. Look for status progression:
|
||||
- `pending` → `sent` → `delivered`
|
||||
4. Check error messages if failed
|
||||
|
||||
### Identify Test Messages
|
||||
|
||||
Test messages have:
|
||||
- `metadata.test = true`
|
||||
- `metadata.sent_from = "admin_interface"`
|
||||
|
||||
**Query in console**:
|
||||
```ruby
|
||||
# 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**:
|
||||
1. Click "Test" button
|
||||
2. Check status (auto-loads)
|
||||
3. Send test SMS
|
||||
4. View in logs
|
||||
|
||||
Perfect for debugging, verification, and training! 🚀
|
||||
Reference in New Issue
Block a user