feat: add AWG2 protocol support and enhance API documentation for protocol management
This commit is contained in:
@@ -73,3 +73,4 @@ scripts/bootstrap_awg_container.sh
|
||||
scripts/fix_server_visibility.sh
|
||||
scripts/remote_fix_client_create.sh
|
||||
scripts/retest_client_api.sh
|
||||
scripts/awg2_retest_final.sh
|
||||
|
||||
@@ -18,6 +18,34 @@ Response:
|
||||
}
|
||||
```
|
||||
|
||||
## Protocols
|
||||
|
||||
### List Active Protocols (for JWT API clients)
|
||||
```bash
|
||||
curl -X GET http://localhost:8082/api/protocols/active \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
```
|
||||
|
||||
Example response:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"protocols": [
|
||||
{"id": 11, "slug": "awg2", "name": "AmneziaWG 2.0"},
|
||||
{"id": 13, "slug": "aivpn", "name": "AIVPN"},
|
||||
{"id": 12, "slug": "mtproxy", "name": "MTProxy (Telegram)"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Install Protocol on Server
|
||||
```bash
|
||||
curl -X POST http://localhost:8082/api/servers/1/protocols/install \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"protocol_id":11}'
|
||||
```
|
||||
|
||||
## Clients
|
||||
|
||||
### Create Client with QR Code
|
||||
|
||||
@@ -36,9 +36,13 @@ cp .env.example .env
|
||||
docker compose up -d
|
||||
docker compose exec web composer install
|
||||
|
||||
# Ensure all SQL migrations are applied (safe to run repeatedly)
|
||||
docker compose exec -T db sh -lc 'for f in /docker-entrypoint-initdb.d/*.sql; do mysql -uroot -p"$MYSQL_ROOT_PASSWORD" "$MYSQL_DATABASE" < "$f" || true; done'
|
||||
|
||||
# Or for older Docker Compose V1
|
||||
docker-compose up -d
|
||||
docker-compose exec web composer install
|
||||
docker-compose exec -T db sh -lc 'for f in /docker-entrypoint-initdb.d/*.sql; do mysql -uroot -p"$MYSQL_ROOT_PASSWORD" "$MYSQL_DATABASE" < "$f" || true; done'
|
||||
```
|
||||
|
||||
Access: http://localhost:8082
|
||||
@@ -54,7 +58,7 @@ DB_HOST=db
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=amnezia_panel
|
||||
DB_USERNAME=amnezia
|
||||
DB_PASSWORD=amnezia123
|
||||
DB_PASSWORD=amnezia
|
||||
|
||||
ADMIN_EMAIL=admin@amnez.ia
|
||||
ADMIN_PASSWORD=admin123
|
||||
@@ -255,7 +259,8 @@ GET /api/servers/{id}/clients - List clients on server
|
||||
|
||||
### Protocols
|
||||
```
|
||||
GET /api/protocols/active - List all available protocols (with IDs)
|
||||
GET /api/protocols/active - List all available protocols (JWT-friendly, includes protocol IDs)
|
||||
GET /api/protocols - Protocol management endpoint (requires session admin auth, not JWT)
|
||||
GET /api/servers/{id}/protocols - List installed protocols on server
|
||||
POST /api/servers/{id}/protocols/install - Install protocol
|
||||
```
|
||||
|
||||
@@ -278,25 +278,46 @@ class InstallProtocolManager
|
||||
|
||||
try {
|
||||
Logger::appendInstall($serverId, 'Running scripted install...');
|
||||
$metadata = $protocol['definition']['metadata'] ?? [];
|
||||
// Choose/ensure VPN UDP port for script-driven installs
|
||||
if (($protocol['slug'] ?? '') === 'xray-vless' && (!isset($options['server_port']) || !is_int($options['server_port']) || $options['server_port'] <= 0)) {
|
||||
$options['server_port'] = 443;
|
||||
}
|
||||
if (!isset($options['server_port']) || !is_int($options['server_port'])) {
|
||||
$options['server_port'] = self::chooseServerPort($server, $protocol['definition']['metadata'] ?? []);
|
||||
$options['server_port'] = self::chooseServerPort($server, $metadata);
|
||||
}
|
||||
$result = self::runScript($server, $protocol, 'install', $options);
|
||||
if (!isset($result['success'])) {
|
||||
$result['success'] = true;
|
||||
}
|
||||
Logger::appendInstall($serverId, 'Scripted install finished: ' . json_encode($result));
|
||||
|
||||
$rawPort = $result['vpn_port'] ?? null;
|
||||
$resolvedPort = (is_numeric($rawPort) && (int) $rawPort > 0)
|
||||
? (int) $rawPort
|
||||
: ($options['server_port'] ?? null);
|
||||
|
||||
$awgParams = $result['awg_params'] ?? null;
|
||||
if (!is_array($awgParams)) {
|
||||
$flat = [];
|
||||
foreach (['Jc', 'Jmin', 'Jmax', 'S1', 'S2', 'S3', 'S4', 'H1', 'H2', 'H3', 'H4'] as $k) {
|
||||
if (array_key_exists($k, $result) && $result[$k] !== '' && $result[$k] !== null) {
|
||||
$flat[$k] = $result[$k];
|
||||
}
|
||||
}
|
||||
if (!empty($flat)) {
|
||||
$awgParams = $flat;
|
||||
}
|
||||
}
|
||||
|
||||
$extras = [
|
||||
'vpn_port' => $result['vpn_port'] ?? ($options['server_port'] ?? null),
|
||||
'vpn_port' => $resolvedPort,
|
||||
'server_public_key' => $result['server_public_key'] ?? null,
|
||||
'preshared_key' => $result['preshared_key'] ?? null,
|
||||
'awg_params' => $result['awg_params'] ?? null,
|
||||
'awg_params' => $awgParams,
|
||||
'secret' => $result['secret'] ?? null,
|
||||
'server_host' => $result['server_host'] ?? null,
|
||||
'container_name' => $result['container_name'] ?? ($metadata['container_name'] ?? null),
|
||||
];
|
||||
if (($protocol['slug'] ?? '') === 'xray-vless') {
|
||||
foreach (['client_id', 'container_name', 'server_port', 'xray_port', 'reality_public_key', 'reality_private_key', 'reality_short_id', 'reality_server_name'] as $k) {
|
||||
@@ -563,7 +584,6 @@ class InstallProtocolManager
|
||||
|
||||
$context = self::buildContext($server, $protocol, $options);
|
||||
$script = self::renderTemplate($scripts, $context);
|
||||
$script = preg_replace('/<<\s*EOF\b/', "<<'EOF'", $script);
|
||||
$script = preg_replace('/\n\+\s*/', "\n", $script);
|
||||
$exportLines = self::buildExports($context);
|
||||
$wrapper = "bash <<'EOS'\nset -euo pipefail\n" . $exportLines . $script . "\nEOS";
|
||||
@@ -685,6 +705,10 @@ class InstallProtocolManager
|
||||
$setParts[] = 'preshared_key = ?';
|
||||
$params[] = (string) $extras['preshared_key'];
|
||||
}
|
||||
if (isset($extras['container_name']) && $extras['container_name'] !== null && $extras['container_name'] !== '') {
|
||||
$setParts[] = 'container_name = ?';
|
||||
$params[] = (string) $extras['container_name'];
|
||||
}
|
||||
if (array_key_exists('awg_params', $extras)) {
|
||||
$awgParams = $extras['awg_params'];
|
||||
if (is_array($awgParams)) {
|
||||
|
||||
@@ -93,11 +93,11 @@ S1_VAL=50
|
||||
S2_VAL=100
|
||||
S3_VAL=20
|
||||
S4_VAL=10
|
||||
# H1-H4: header ranges (string format "x-y" per AWG2 spec)
|
||||
H1_VAL="1-4294967295"
|
||||
H2_VAL="1-4294967295"
|
||||
H3_VAL="1-4294967295"
|
||||
H4_VAL="1-4294967295"
|
||||
# H1-H4: keep numeric values for broad awg-tools compatibility.
|
||||
H1_VAL=123456789
|
||||
H2_VAL=223456789
|
||||
H3_VAL=323456789
|
||||
H4_VAL=423456789
|
||||
|
||||
# Write config
|
||||
cat > /opt/amnezia/awg2/wg0.conf << EOF
|
||||
@@ -286,10 +286,10 @@ S1_VAL=50
|
||||
S2_VAL=100
|
||||
S3_VAL=20
|
||||
S4_VAL=10
|
||||
H1_VAL="1-4294967295"
|
||||
H2_VAL="1-4294967295"
|
||||
H3_VAL="1-4294967295"
|
||||
H4_VAL="1-4294967295"
|
||||
H1_VAL=123456789
|
||||
H2_VAL=223456789
|
||||
H3_VAL=323456789
|
||||
H4_VAL=423456789
|
||||
|
||||
cat > /opt/amnezia/awg2/wg0.conf << EOF
|
||||
[Interface]
|
||||
|
||||
@@ -2549,6 +2549,14 @@ Router::post('/api/servers/{id}/protocols/install', function ($params) {
|
||||
}
|
||||
|
||||
$result = InstallProtocolManager::activate($server, $protocol, []);
|
||||
|
||||
// Keep API behavior consistent with UI flow: once protocol activation succeeds,
|
||||
// clear transient error state and mark server as active for client creation.
|
||||
if (is_array($result) && !empty($result['success'])) {
|
||||
$pdo = DB::conn();
|
||||
$stmtUpdate = $pdo->prepare('UPDATE vpn_servers SET status = ?, error_message = NULL WHERE id = ?');
|
||||
$stmtUpdate->execute(['active', $serverId]);
|
||||
}
|
||||
echo json_encode($result, JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_SUBSTITUTE);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
|
||||
Reference in New Issue
Block a user