Add QR code support in API endpoints
- Updated /api/clients/create to return config and qr_code
- Updated /api/clients/{id}/details to include config and qr_code
- Added new endpoint /api/clients/{id}/qr for getting QR code only
- Added API_EXAMPLES.md with usage examples and integration code
- Updated README.md API documentation
This commit is contained in:
+276
@@ -0,0 +1,276 @@
|
|||||||
|
# API Usage Examples
|
||||||
|
|
||||||
|
## Authentication
|
||||||
|
|
||||||
|
### Get JWT Token
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:8082/api/auth/token \
|
||||||
|
-d "email=admin@amnez.ia&password=admin123"
|
||||||
|
```
|
||||||
|
|
||||||
|
Response:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
|
||||||
|
"type": "Bearer",
|
||||||
|
"expires_in": 2592000
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Clients
|
||||||
|
|
||||||
|
### Create Client with QR Code
|
||||||
|
```bash
|
||||||
|
TOKEN="your-jwt-token"
|
||||||
|
|
||||||
|
curl -X POST http://localhost:8082/api/clients/create \
|
||||||
|
-H "Authorization: Bearer $TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"server_id": 1,
|
||||||
|
"name": "My Phone"
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
Response:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"client": {
|
||||||
|
"id": 1,
|
||||||
|
"name": "My Phone",
|
||||||
|
"server_id": 1,
|
||||||
|
"client_ip": "10.8.1.1",
|
||||||
|
"status": "active",
|
||||||
|
"created_at": "2025-11-07 12:00:00",
|
||||||
|
"config": "[Interface]\nPrivateKey = ...\n...",
|
||||||
|
"qr_code": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `qr_code` field contains a data URI that can be used directly in HTML:
|
||||||
|
```html
|
||||||
|
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..." alt="QR Code" />
|
||||||
|
```
|
||||||
|
|
||||||
|
### Get Client QR Code
|
||||||
|
```bash
|
||||||
|
curl -X GET http://localhost:8082/api/clients/1/qr \
|
||||||
|
-H "Authorization: Bearer $TOKEN"
|
||||||
|
```
|
||||||
|
|
||||||
|
Response:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"qr_code": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...",
|
||||||
|
"client_name": "My Phone"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Get Client Details with Stats, Config and QR
|
||||||
|
```bash
|
||||||
|
curl -X GET http://localhost:8082/api/clients/1/details \
|
||||||
|
-H "Authorization: Bearer $TOKEN"
|
||||||
|
```
|
||||||
|
|
||||||
|
Response:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"client": {
|
||||||
|
"id": 1,
|
||||||
|
"name": "My Phone",
|
||||||
|
"server_id": 1,
|
||||||
|
"client_ip": "10.8.1.1",
|
||||||
|
"status": "active",
|
||||||
|
"created_at": "2025-11-07 12:00:00",
|
||||||
|
"stats": {
|
||||||
|
"sent": "1.23 GB",
|
||||||
|
"received": "456.78 MB",
|
||||||
|
"total": "1.68 GB",
|
||||||
|
"last_seen": "Online",
|
||||||
|
"is_online": true
|
||||||
|
},
|
||||||
|
"bytes_sent": 1320000000,
|
||||||
|
"bytes_received": 478800000,
|
||||||
|
"last_handshake": "2025-11-07 12:30:00",
|
||||||
|
"config": "[Interface]\nPrivateKey = ...\n...",
|
||||||
|
"qr_code": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Servers
|
||||||
|
|
||||||
|
### List Servers
|
||||||
|
```bash
|
||||||
|
curl -X GET http://localhost:8082/api/servers \
|
||||||
|
-H "Authorization: Bearer $TOKEN"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create Server
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:8082/api/servers/create \
|
||||||
|
-H "Authorization: Bearer $TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"name": "US Server",
|
||||||
|
"host": "192.168.1.100",
|
||||||
|
"port": 22,
|
||||||
|
"username": "root",
|
||||||
|
"password": "your-password"
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Get Server Clients
|
||||||
|
```bash
|
||||||
|
curl -X GET http://localhost:8082/api/servers/1/clients \
|
||||||
|
-H "Authorization: Bearer $TOKEN"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Client Management
|
||||||
|
|
||||||
|
### Revoke Client
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:8082/api/clients/1/revoke \
|
||||||
|
-H "Authorization: Bearer $TOKEN"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Restore Client
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:8082/api/clients/1/restore \
|
||||||
|
-H "Authorization: Bearer $TOKEN"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Delete Client
|
||||||
|
```bash
|
||||||
|
curl -X DELETE http://localhost:8082/api/clients/1/delete \
|
||||||
|
-H "Authorization: Bearer $TOKEN"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Integration Examples
|
||||||
|
|
||||||
|
### Python Example
|
||||||
|
```python
|
||||||
|
import requests
|
||||||
|
import base64
|
||||||
|
from io import BytesIO
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
# Get token
|
||||||
|
response = requests.post('http://localhost:8082/api/auth/token',
|
||||||
|
data={'email': 'admin@amnez.ia', 'password': 'admin123'})
|
||||||
|
token = response.json()['token']
|
||||||
|
|
||||||
|
headers = {'Authorization': f'Bearer {token}'}
|
||||||
|
|
||||||
|
# Create client
|
||||||
|
client_data = {
|
||||||
|
'server_id': 1,
|
||||||
|
'name': 'My Phone'
|
||||||
|
}
|
||||||
|
response = requests.post('http://localhost:8082/api/clients/create',
|
||||||
|
json=client_data, headers=headers)
|
||||||
|
|
||||||
|
result = response.json()
|
||||||
|
qr_code_data_uri = result['client']['qr_code']
|
||||||
|
|
||||||
|
# Save QR code as image
|
||||||
|
qr_base64 = qr_code_data_uri.split(',')[1]
|
||||||
|
qr_bytes = base64.b64decode(qr_base64)
|
||||||
|
image = Image.open(BytesIO(qr_bytes))
|
||||||
|
image.save('qr_code.png')
|
||||||
|
|
||||||
|
print(f"Client created: {result['client']['name']}")
|
||||||
|
print(f"QR code saved to qr_code.png")
|
||||||
|
```
|
||||||
|
|
||||||
|
### JavaScript/Node.js Example
|
||||||
|
```javascript
|
||||||
|
const axios = require('axios');
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
// Get token
|
||||||
|
const authResponse = await axios.post('http://localhost:8082/api/auth/token',
|
||||||
|
'email=admin@amnez.ia&password=admin123');
|
||||||
|
const token = authResponse.data.token;
|
||||||
|
|
||||||
|
const headers = { 'Authorization': `Bearer ${token}` };
|
||||||
|
|
||||||
|
// Create client
|
||||||
|
const clientData = {
|
||||||
|
server_id: 1,
|
||||||
|
name: 'My Phone'
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await axios.post('http://localhost:8082/api/clients/create',
|
||||||
|
clientData, { headers });
|
||||||
|
|
||||||
|
const qrCodeDataUri = response.data.client.qr_code;
|
||||||
|
|
||||||
|
// Save QR code as image
|
||||||
|
const base64Data = qrCodeDataUri.split(',')[1];
|
||||||
|
fs.writeFileSync('qr_code.png', base64Data, 'base64');
|
||||||
|
|
||||||
|
console.log(`Client created: ${response.data.client.name}`);
|
||||||
|
console.log('QR code saved to qr_code.png');
|
||||||
|
```
|
||||||
|
|
||||||
|
### Display QR Code in Web Page
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>VPN Client QR Code</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Scan this QR code with Amnezia VPN app</h1>
|
||||||
|
<div id="qr-container"></div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
async function loadQRCode() {
|
||||||
|
// Get token
|
||||||
|
const formData = new URLSearchParams();
|
||||||
|
formData.append('email', 'admin@amnez.ia');
|
||||||
|
formData.append('password', 'admin123');
|
||||||
|
|
||||||
|
const authResponse = await fetch('http://localhost:8082/api/auth/token', {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData
|
||||||
|
});
|
||||||
|
const authData = await authResponse.json();
|
||||||
|
const token = authData.token;
|
||||||
|
|
||||||
|
// Create client
|
||||||
|
const response = await fetch('http://localhost:8082/api/clients/create', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${token}`,
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
server_id: 1,
|
||||||
|
name: 'Web Client'
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
// Display QR code
|
||||||
|
const img = document.createElement('img');
|
||||||
|
img.src = data.client.qr_code;
|
||||||
|
img.alt = 'VPN Client QR Code';
|
||||||
|
img.style.width = '300px';
|
||||||
|
img.style.height = '300px';
|
||||||
|
|
||||||
|
document.getElementById('qr-container').appendChild(img);
|
||||||
|
}
|
||||||
|
|
||||||
|
loadQRCode();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
@@ -99,8 +99,9 @@ GET /api/servers/{id}/clients - List clients on server
|
|||||||
### Clients
|
### Clients
|
||||||
```
|
```
|
||||||
GET /api/clients - List all clients
|
GET /api/clients - List all clients
|
||||||
GET /api/clients/{id}/details - Get client details with stats
|
GET /api/clients/{id}/details - Get client details with stats, config and QR code
|
||||||
POST /api/clients/create - Create new client
|
GET /api/clients/{id}/qr - Get client QR code
|
||||||
|
POST /api/clients/create - Create new client (returns config and QR code)
|
||||||
Parameters: server_id, name
|
Parameters: server_id, name
|
||||||
POST /api/clients/{id}/revoke - Revoke client access
|
POST /api/clients/{id}/revoke - Revoke client access
|
||||||
POST /api/clients/{id}/restore - Restore client access
|
POST /api/clients/{id}/restore - Restore client access
|
||||||
|
|||||||
+47
-1
@@ -820,6 +820,8 @@ Router::get('/api/clients/{id}/details', function ($params) {
|
|||||||
'bytes_sent' => $clientData['bytes_sent'],
|
'bytes_sent' => $clientData['bytes_sent'],
|
||||||
'bytes_received' => $clientData['bytes_received'],
|
'bytes_received' => $clientData['bytes_received'],
|
||||||
'last_handshake' => $clientData['last_handshake'],
|
'last_handshake' => $clientData['last_handshake'],
|
||||||
|
'config' => $clientData['config'],
|
||||||
|
'qr_code' => $clientData['qr_code'],
|
||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
@@ -828,6 +830,37 @@ Router::get('/api/clients/{id}/details', function ($params) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// API: Get client QR code
|
||||||
|
Router::get('/api/clients/{id}/qr', function ($params) {
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
$user = JWT::requireAuth();
|
||||||
|
if (!$user) return;
|
||||||
|
|
||||||
|
$clientId = (int)$params['id'];
|
||||||
|
|
||||||
|
try {
|
||||||
|
$client = new VpnClient($clientId);
|
||||||
|
$clientData = $client->getData();
|
||||||
|
|
||||||
|
// Check ownership
|
||||||
|
if ($clientData['user_id'] != $user['id']) {
|
||||||
|
http_response_code(403);
|
||||||
|
echo json_encode(['error' => 'Forbidden']);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'qr_code' => $clientData['qr_code'],
|
||||||
|
'client_name' => $clientData['name']
|
||||||
|
]);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
http_response_code(404);
|
||||||
|
echo json_encode(['error' => 'Client not found']);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// API: Revoke client
|
// API: Revoke client
|
||||||
Router::post('/api/clients/{id}/revoke', function ($params) {
|
Router::post('/api/clients/{id}/revoke', function ($params) {
|
||||||
header('Content-Type: application/json');
|
header('Content-Type: application/json');
|
||||||
@@ -968,7 +1001,20 @@ Router::post('/api/clients/create', function () {
|
|||||||
$client = new VpnClient($clientId);
|
$client = new VpnClient($clientId);
|
||||||
$clientData = $client->getData();
|
$clientData = $client->getData();
|
||||||
|
|
||||||
echo json_encode(['client' => $clientData]);
|
// Return client data with config and QR code
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'client' => [
|
||||||
|
'id' => $clientData['id'],
|
||||||
|
'name' => $clientData['name'],
|
||||||
|
'server_id' => $clientData['server_id'],
|
||||||
|
'client_ip' => $clientData['client_ip'],
|
||||||
|
'status' => $clientData['status'],
|
||||||
|
'created_at' => $clientData['created_at'],
|
||||||
|
'config' => $clientData['config'],
|
||||||
|
'qr_code' => $clientData['qr_code'],
|
||||||
|
]
|
||||||
|
]);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
http_response_code(500);
|
http_response_code(500);
|
||||||
echo json_encode(['error' => $e->getMessage()]);
|
echo json_encode(['error' => $e->getMessage()]);
|
||||||
|
|||||||
Reference in New Issue
Block a user