completed SMS gateway project

This commit is contained in:
Min Zeya Phyo
2025-10-22 17:22:17 +08:00
commit c883fa7128
190 changed files with 16294 additions and 0 deletions

614
GATEWAY_TESTING.md Normal file
View 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! 🚀