completed SMS gateway project
This commit is contained in:
449
QR_CODE_SETUP.md
Normal file
449
QR_CODE_SETUP.md
Normal file
@@ -0,0 +1,449 @@
|
||||
# QR Code Gateway Setup
|
||||
|
||||
## Overview
|
||||
|
||||
The gateway registration now includes QR code generation for quick and easy Android app configuration. When a new gateway is created, the system automatically generates a QR code containing all necessary configuration data.
|
||||
|
||||
## What's Included in the QR Code
|
||||
|
||||
The QR code contains a JSON payload with:
|
||||
|
||||
```json
|
||||
{
|
||||
"api_key": "gw_live_a6e2b250dade8f6501256a8717723fc3f8ab7d4e7cb26aad470d65ee8478a82c",
|
||||
"api_base_url": "http://localhost:3000",
|
||||
"websocket_url": "ws://localhost:3000/cable",
|
||||
"version": "1.0"
|
||||
}
|
||||
```
|
||||
|
||||
### Fields Explained
|
||||
|
||||
| Field | Description | Example |
|
||||
|-------|-------------|---------|
|
||||
| `api_key` | Gateway authentication key | `gw_live_...` (64 chars) |
|
||||
| `api_base_url` | Base URL for API endpoints | `http://localhost:3000` or `https://api.example.com` |
|
||||
| `websocket_url` | WebSocket connection URL | `ws://localhost:3000/cable` or `wss://api.example.com/cable` |
|
||||
| `version` | Configuration format version | `1.0` |
|
||||
|
||||
## Features
|
||||
|
||||
### 1. QR Code Display
|
||||
|
||||
**Location**: Gateway creation success page (`/admin/gateways/:id` after creation)
|
||||
|
||||
**Visual Features**:
|
||||
- High-quality SVG QR code
|
||||
- Error correction level: High (L=H)
|
||||
- White background with border
|
||||
- Centered display
|
||||
- Info badge explaining contents
|
||||
|
||||
### 2. Manual Configuration Fallback
|
||||
|
||||
If QR scanning is unavailable, the page also displays:
|
||||
- API Base URL (with individual copy button)
|
||||
- WebSocket URL (with individual copy button)
|
||||
- API Key (with individual copy button)
|
||||
- "Copy All" button to copy all three fields at once
|
||||
|
||||
### 3. Copy to Clipboard Functions
|
||||
|
||||
**Individual Field Copy**:
|
||||
```javascript
|
||||
copyField('api-base-url') // Copies just the API base URL
|
||||
copyField('ws-url') // Copies just the WebSocket URL
|
||||
copyField('api-key') // Copies just the API key
|
||||
```
|
||||
|
||||
**Copy All Configuration**:
|
||||
```javascript
|
||||
copyAllConfig() // Copies all fields as formatted text
|
||||
```
|
||||
|
||||
Output format:
|
||||
```
|
||||
API Base URL: http://localhost:3000
|
||||
WebSocket URL: ws://localhost:3000/cable
|
||||
API Key: gw_live_a6e2b250dade8f6501256a8717723fc3f8ab7d4e7cb26aad470d65ee8478a82c
|
||||
```
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### Gem Used
|
||||
|
||||
**rqrcode** v2.0+
|
||||
- Pure Ruby QR code generator
|
||||
- No external dependencies
|
||||
- SVG output support
|
||||
- High error correction
|
||||
|
||||
Added to `Gemfile`:
|
||||
```ruby
|
||||
gem "rqrcode", "~> 2.0"
|
||||
```
|
||||
|
||||
### Controller Method
|
||||
|
||||
**File**: `app/controllers/admin/gateways_controller.rb`
|
||||
|
||||
```ruby
|
||||
def generate_qr_code_data(api_key)
|
||||
require "rqrcode"
|
||||
|
||||
# Determine URLs based on request
|
||||
base_url = request.base_url
|
||||
ws_url = request.base_url.sub(/^http/, "ws") + "/cable"
|
||||
|
||||
# Create JSON configuration
|
||||
config_data = {
|
||||
api_key: api_key,
|
||||
api_base_url: base_url,
|
||||
websocket_url: ws_url,
|
||||
version: "1.0"
|
||||
}.to_json
|
||||
|
||||
# Generate QR code with high error correction
|
||||
qr = RQRCode::QRCode.new(config_data, level: :h)
|
||||
|
||||
# Return as SVG string
|
||||
qr.as_svg(
|
||||
offset: 0,
|
||||
color: "000",
|
||||
shape_rendering: "crispEdges",
|
||||
module_size: 4,
|
||||
standalone: true,
|
||||
use_path: true
|
||||
)
|
||||
end
|
||||
```
|
||||
|
||||
### View Integration
|
||||
|
||||
**File**: `app/views/admin/gateways/show.html.erb`
|
||||
|
||||
The QR code is displayed using:
|
||||
```erb
|
||||
<div class="bg-white p-4 rounded-lg shadow-inner border-2 border-gray-200">
|
||||
<%= @qr_code_data.html_safe %>
|
||||
</div>
|
||||
```
|
||||
|
||||
## URL Detection
|
||||
|
||||
The system automatically detects the correct URLs based on the request:
|
||||
|
||||
### Development
|
||||
- Base URL: `http://localhost:3000`
|
||||
- WebSocket URL: `ws://localhost:3000/cable`
|
||||
|
||||
### Production (HTTP)
|
||||
- Base URL: `http://api.example.com`
|
||||
- WebSocket URL: `ws://api.example.com/cable`
|
||||
|
||||
### Production (HTTPS) - Recommended
|
||||
- Base URL: `https://api.example.com`
|
||||
- WebSocket URL: `wss://api.example.com/cable`
|
||||
|
||||
**Note**: WebSocket URL automatically changes from `http` to `ws` and `https` to `wss`.
|
||||
|
||||
## Android App Integration
|
||||
|
||||
### QR Code Scanning Flow
|
||||
|
||||
1. **User opens Android SMS Gateway app**
|
||||
2. **Taps "Scan QR Code" or similar option**
|
||||
3. **Camera opens with QR scanner**
|
||||
4. **Scans the QR code from admin interface**
|
||||
5. **App parses JSON configuration**
|
||||
6. **All fields auto-populated**:
|
||||
- API Base URL field
|
||||
- WebSocket URL field
|
||||
- API Key field
|
||||
7. **User taps "Save" or "Connect"**
|
||||
8. **App connects to server**
|
||||
9. **Gateway appears as "Online" in admin interface**
|
||||
|
||||
### Expected Android App Code
|
||||
|
||||
The Android app should:
|
||||
|
||||
1. **Scan QR Code**:
|
||||
```kotlin
|
||||
// Using ML Kit or ZXing library
|
||||
val result = qrCodeScanner.scan()
|
||||
val jsonString = result.text
|
||||
```
|
||||
|
||||
2. **Parse JSON**:
|
||||
```kotlin
|
||||
val config = JSONObject(jsonString)
|
||||
val apiKey = config.getString("api_key")
|
||||
val apiBaseUrl = config.getString("api_base_url")
|
||||
val websocketUrl = config.getString("websocket_url")
|
||||
val version = config.getString("version")
|
||||
```
|
||||
|
||||
3. **Validate Version**:
|
||||
```kotlin
|
||||
if (version != "1.0") {
|
||||
showError("Unsupported configuration version")
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
4. **Save Configuration**:
|
||||
```kotlin
|
||||
sharedPreferences.edit().apply {
|
||||
putString("api_key", apiKey)
|
||||
putString("api_base_url", apiBaseUrl)
|
||||
putString("websocket_url", websocketUrl)
|
||||
apply()
|
||||
}
|
||||
```
|
||||
|
||||
5. **Connect to Server**:
|
||||
```kotlin
|
||||
// Connect to WebSocket
|
||||
webSocketClient.connect(websocketUrl, apiKey)
|
||||
|
||||
// Test API connection
|
||||
apiClient.setBaseUrl(apiBaseUrl)
|
||||
apiClient.setAuthToken(apiKey)
|
||||
apiClient.sendHeartbeat()
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### QR Code Security
|
||||
|
||||
✅ **Secure**:
|
||||
- QR code displayed only once after creation
|
||||
- Requires admin authentication to view
|
||||
- Session-based display (not in URL)
|
||||
- Page cannot be refreshed to see QR code again
|
||||
|
||||
⚠️ **Warning**:
|
||||
- Anyone with camera access to the screen can scan the QR code
|
||||
- QR code contains full API key in plaintext JSON
|
||||
- Suitable for secure environments only
|
||||
|
||||
### Best Practices
|
||||
|
||||
1. **Display Environment**:
|
||||
- Only display QR code in secure locations
|
||||
- Ensure no cameras/recording devices nearby
|
||||
- Clear screen after scanning
|
||||
|
||||
2. **Network Security**:
|
||||
- Use HTTPS/WSS in production (`config.force_ssl = true`)
|
||||
- Never use HTTP/WS in production
|
||||
- Implement rate limiting on WebSocket connections
|
||||
|
||||
3. **Key Management**:
|
||||
- QR code shown only once during gateway creation
|
||||
- If compromised, deactivate gateway and create new one
|
||||
- Regularly audit active gateways
|
||||
|
||||
4. **Mobile App Security**:
|
||||
- Store configuration in encrypted SharedPreferences
|
||||
- Use Android Keystore for API key storage
|
||||
- Implement certificate pinning for API calls
|
||||
- Validate SSL certificates for WebSocket connections
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### QR Code Not Displaying
|
||||
|
||||
**Check**:
|
||||
1. `rqrcode` gem installed: `bundle show rqrcode`
|
||||
2. Controller generates QR code: Check `@qr_code_data` in view
|
||||
3. Browser console for JavaScript errors
|
||||
4. View source - SVG should be present
|
||||
|
||||
**Fix**:
|
||||
```bash
|
||||
bundle install
|
||||
bin/rails restart
|
||||
```
|
||||
|
||||
### QR Code Too Complex to Scan
|
||||
|
||||
**Symptom**: QR scanner can't read the code
|
||||
|
||||
**Cause**: JSON payload too long (rare, but possible with very long URLs)
|
||||
|
||||
**Solution**:
|
||||
- Use shorter domain names
|
||||
- Reduce module_size in controller (current: 4)
|
||||
- Lower error correction level (current: :h, try :m or :l)
|
||||
|
||||
### Wrong URLs in QR Code
|
||||
|
||||
**Symptom**: QR code contains `localhost` in production
|
||||
|
||||
**Cause**: `request.base_url` not detecting correctly
|
||||
|
||||
**Fix**: Set environment variables in production
|
||||
```bash
|
||||
# .env or config
|
||||
RAILS_FORCE_SSL=true
|
||||
RAILS_RELATIVE_URL_ROOT=https://api.example.com
|
||||
```
|
||||
|
||||
Or override in controller:
|
||||
```ruby
|
||||
base_url = ENV['API_BASE_URL'] || request.base_url
|
||||
```
|
||||
|
||||
### Android App Can't Parse QR Code
|
||||
|
||||
**Symptom**: App shows "Invalid QR code" error
|
||||
|
||||
**Causes**:
|
||||
1. QR code scanner library issue
|
||||
2. JSON parsing error
|
||||
3. Wrong configuration version
|
||||
|
||||
**Debug**:
|
||||
```kotlin
|
||||
try {
|
||||
val json = JSONObject(qrCodeText)
|
||||
Log.d("QR", "API Key: ${json.getString("api_key")}")
|
||||
Log.d("QR", "Base URL: ${json.getString("api_base_url")}")
|
||||
Log.d("QR", "WS URL: ${json.getString("websocket_url")}")
|
||||
} catch (e: Exception) {
|
||||
Log.e("QR", "Parse error: ${e.message}")
|
||||
}
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
### Manual QR Code Test
|
||||
|
||||
1. **Create Test Gateway**:
|
||||
```bash
|
||||
bin/rails console
|
||||
```
|
||||
|
||||
```ruby
|
||||
gateway = Gateway.new(
|
||||
device_id: "test-qr-001",
|
||||
name: "QR Test Gateway",
|
||||
priority: 1,
|
||||
status: "offline"
|
||||
)
|
||||
raw_key = gateway.generate_api_key!
|
||||
puts "Gateway ID: #{gateway.id}"
|
||||
puts "Raw Key: #{raw_key}"
|
||||
```
|
||||
|
||||
2. **Navigate to Success Page**:
|
||||
- Visit: `http://localhost:3000/admin/gateways/new`
|
||||
- Fill form and submit
|
||||
- Should redirect to gateway show page with QR code
|
||||
|
||||
3. **Test QR Code**:
|
||||
- Open QR code scanner app on phone
|
||||
- Scan the displayed QR code
|
||||
- Verify JSON payload contains all fields
|
||||
|
||||
4. **Test Copy Buttons**:
|
||||
- Click individual copy buttons (API URL, WS URL, API Key)
|
||||
- Verify green checkmark feedback
|
||||
- Click "Copy All" button
|
||||
- Paste in text editor - verify format
|
||||
|
||||
### Automated Test
|
||||
|
||||
```ruby
|
||||
# test/controllers/admin/gateways_controller_test.rb
|
||||
test "should generate QR code on gateway creation" do
|
||||
post admin_gateways_url, params: {
|
||||
gateway: {
|
||||
device_id: "test-001",
|
||||
name: "Test Gateway",
|
||||
priority: 5
|
||||
}
|
||||
}
|
||||
|
||||
assert_response :redirect
|
||||
|
||||
gateway = Gateway.last
|
||||
get admin_gateway_url(gateway)
|
||||
|
||||
assert_response :success
|
||||
assert_select 'svg' # QR code should be present as SVG
|
||||
end
|
||||
```
|
||||
|
||||
## Configuration Examples
|
||||
|
||||
### Local Development
|
||||
```json
|
||||
{
|
||||
"api_key": "gw_live_abc123...",
|
||||
"api_base_url": "http://localhost:3000",
|
||||
"websocket_url": "ws://localhost:3000/cable",
|
||||
"version": "1.0"
|
||||
}
|
||||
```
|
||||
|
||||
### Staging Environment
|
||||
```json
|
||||
{
|
||||
"api_key": "gw_live_def456...",
|
||||
"api_base_url": "https://staging-api.example.com",
|
||||
"websocket_url": "wss://staging-api.example.com/cable",
|
||||
"version": "1.0"
|
||||
}
|
||||
```
|
||||
|
||||
### Production Environment
|
||||
```json
|
||||
{
|
||||
"api_key": "gw_live_ghi789...",
|
||||
"api_base_url": "https://api.example.com",
|
||||
"websocket_url": "wss://api.example.com/cable",
|
||||
"version": "1.0"
|
||||
}
|
||||
```
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
### Possible Improvements
|
||||
|
||||
1. **Download QR Code**:
|
||||
- Add "Download QR Code" button
|
||||
- Generate PNG image
|
||||
- Allow saving for later
|
||||
|
||||
2. **Email QR Code**:
|
||||
- Send QR code via email
|
||||
- Secure time-limited link
|
||||
- Auto-expires after 1 hour
|
||||
|
||||
3. **Multiple QR Code Formats**:
|
||||
- Different sizes (small, medium, large)
|
||||
- Different error correction levels
|
||||
- PNG, SVG, PDF options
|
||||
|
||||
4. **Configuration Presets**:
|
||||
- Save common configurations
|
||||
- Apply preset to multiple gateways
|
||||
- Template system
|
||||
|
||||
5. **Advanced Security**:
|
||||
- Encrypted QR code payload
|
||||
- Time-limited configuration URLs
|
||||
- Two-factor gateway activation
|
||||
|
||||
## Summary
|
||||
|
||||
✅ **Implemented**: QR code generation with all gateway configuration
|
||||
✅ **Features**: Scan QR code OR manual copy/paste
|
||||
✅ **Security**: One-time display, session-based, admin-only
|
||||
✅ **UX**: Copy buttons with visual feedback, clear instructions
|
||||
✅ **Production Ready**: Automatic URL detection (HTTP/HTTPS/WS/WSS)
|
||||
|
||||
The QR code feature makes gateway setup much faster and reduces configuration errors by eliminating manual typing of long API keys!
|
||||
Reference in New Issue
Block a user