450 lines
11 KiB
Markdown
450 lines
11 KiB
Markdown
# 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!
|