This commit is contained in:
iTakinn
2025-10-12 18:05:12 -03:00
parent cbf771ab23
commit aa337f9b29
4 changed files with 429 additions and 61 deletions
View File
+12 -9
View File
@@ -12,36 +12,34 @@ WHMCS module for integration with [WGDashboard](https://github.com/donaldzou/WGD
## 🚀 Installation ## 🚀 Installation
1. Upload the module files to the WHMCS directory: 1. Upload the module files to the WHMCS directory:
``` ```
/modules/servers/wgdashboard/ /modules/servers/wgdashboard/
``` ```
2. File structure: 2. File structure:
``` ```
modules/server/wgdashboard/ modules/server/wgdashboard/
├── wgdashboard.php ├── wgdashboard.php
└── clientarea.tpl └── clientarea.tpl
``` ```
3. In the WHMCS admin panel, navigate to: 3. In the WHMCS admin panel, navigate to:
**Setup → Products/Services → Servers → Add New Server** **Setup → Products/Services → Servers → Add New Server**
4. Configure the server: 4. Configure the server:
- **Hostname**: IP or domain of WGDashboard (e.g., `10.0.238.222` or `vpn.example.com`) - **Hostname**: IP or domain of WGDashboard (e.g., `10.0.238.222` or `vpn.example.com`)
- **Module**: WGDashboard - **Module**: WGDashboard
- **Port**: API port (e.g., `10086`) - **Port**: API port (e.g., `10086`)
- **Password**: WGDashboard API Key - **Password**: WGDashboard API Key
5. Click **Test Connection** to validate the configuration. 5. Click **Test Connection** to validate the configuration.
## ⚙️ Product Configuration ## ⚙️ Product Configuration
1. Create or edit a product in **Setup → Products/Services** 1. Create or edit a product in **Setup → Products/Services**
2. In the **Module Settings** tab: 2. In the **Module Settings** tab:
- **Module Name**: WGDashboard - **Module Name**: WGDashboard
- **Server Group**: Select the configured server - **Server Group**: Select the configured server
3. **Configuration Options**: 3. **Configuration Options**:
- **Allowed IPs**: Allowed IPs for the peer (default: `0.0.0.0/0`) - **Allowed IPs**: Allowed IPs for the peer (default: `0.0.0.0/0`)
@@ -59,6 +57,7 @@ WHMCS module for integration with [WGDashboard](https://github.com/donaldzou/WGD
``` ```
Available placeholders: Available placeholders:
- `{{client_email}}` - Client's email - `{{client_email}}` - Client's email
- `{{domain}}` - Service domain - `{{domain}}` - Service domain
- `{{serviceid}}` - Service ID in WHMCS - `{{serviceid}}` - Service ID in WHMCS
@@ -82,7 +81,6 @@ Client Area:
- 📄 **Formatted Configuration**: Ready-to-use WireGuard configuration file - 📄 **Formatted Configuration**: Ready-to-use WireGuard configuration file
- 📋 **Download**: Button to download `.conf` file - 📋 **Download**: Button to download `.conf` file
### Admin Panel ### Admin Panel
In the client's service tab, the administrator views: In the client's service tab, the administrator views:
@@ -95,7 +93,6 @@ In the client's service tab, the administrator views:
- **Data Received**: Volume of data received From peer to server - **Data Received**: Volume of data received From peer to server
- **Data Sent**: Volume of data sent From server to peer - **Data Sent**: Volume of data sent From server to peer
## 🌐 WGDashboard Configuration ## 🌐 WGDashboard Configuration
To use this module, ensure the WGDashboard API is enabled: To use this module, ensure the WGDashboard API is enabled:
@@ -110,16 +107,19 @@ To use this module, ensure the WGDashboard API is enabled:
### Connection Test Fails ### Connection Test Fails
**Error 401/403**: Verify API Key **Error 401/403**: Verify API Key
``` ```
Solution: Confirm that the API Key is correct in the server's Password field Solution: Confirm that the API Key is correct in the server's Password field
``` ```
**Error 404**: Endpoint not found **Error 404**: Endpoint not found
``` ```
Solution: Check if your WGDashboard version supports the API Solution: Check if your WGDashboard version supports the API
``` ```
**Error 500**: WGDashboard internal error **Error 500**: WGDashboard internal error
``` ```
Solution: Check WGDashboard logs on the server Solution: Check WGDashboard logs on the server
``` ```
@@ -134,6 +134,7 @@ Solution: Check WGDashboard logs on the server
### QR Code Doesn't Appear ### QR Code Doesn't Appear
Check: Check:
- If JavaScript is enabled in the browser - If JavaScript is enabled in the browser
- Browser console for errors (F12) - Browser console for errors (F12)
- If QRCode.js library is loading correctly - If QRCode.js library is loading correctly
@@ -144,14 +145,15 @@ The module uses WHMCS's **Server Port** field to define the API port.
Do not include the port in the hostname, only in the dedicated field. Do not include the port in the hostname, only in the dedicated field.
✅ **Correct**: ✅ **Correct**:
- Hostname: `10.0.0.55` - Hostname: `10.0.0.55`
- Port: `10086` - Port: `10086`
❌ **Incorrect**: ❌ **Incorrect**:
- Hostname: `10.0.0.55:10086` - Hostname: `10.0.0.55:10086`
- Port: (empty) - Port: (empty)
## 🔄 Compatibility ## 🔄 Compatibility
- **WHMCS**: 7.0+ - **WHMCS**: 7.0+
@@ -182,6 +184,7 @@ Contributions are welcome! To contribute:
## 📋 Changelog ## 📋 Changelog
### v1.0.0 ### v1.0.0
- ✨ Initial release - ✨ Initial release
- ✅ Peer creation, suspension, reactivation, and termination - ✅ Peer creation, suspension, reactivation, and termination
- 📱 Client area with QR code and formatted configuration - 📱 Client area with QR code and formatted configuration
+47 -51
View File
@@ -1,6 +1,6 @@
{if $error} {if $error}
<div class="alert alert-danger"> <div class="alert alert-danger">
<strong>Erro:</strong> {$error} <strong>Error:</strong> {$error}
</div> </div>
{else} {else}
<div class="row"> <div class="row">
@@ -9,15 +9,15 @@
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<h3 class="panel-title"> <h3 class="panel-title">
<i class="fa fa-qrcode"></i> Código QR <i class="fa fa-qrcode"></i> QR Code
</h3> </h3>
</div> </div>
<div class="panel-body text-center"> <div class="panel-body text-center">
<div id="qrcode" style="display: inline-block; margin-bottom: 10px;"></div> <div id="qrcode" style="display: inline-block; margin-bottom: 10px;"></div>
<p class="text-muted">Escaneie este código QR com seu cliente WireGuard</p> <p class="text-muted">Scan this QR code with your WireGuard client</p>
<div class="text-center" style="margin-top: 15px;"> <div class="text-center" style="margin-top: 15px;">
<button class="btn btn-info btn-sm" onclick="showInstructions()"> <button class="btn btn-info btn-sm" onclick="showInstructions()">
<i class="fa fa-info-circle"></i> Instruções de Instalação <i class="fa fa-info-circle"></i> Installation Instructions
</button> </button>
</div> </div>
</div> </div>
@@ -26,11 +26,11 @@
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<h3 class="panel-title"> <h3 class="panel-title">
<i class="fa fa-qrcode"></i> Código QR <i class="fa fa-qrcode"></i> QR Code
</h3> </h3>
</div> </div>
<div class="panel-body text-center"> <div class="panel-body text-center">
<p class="text-muted">Configuração não disponível</p> <p class="text-muted">Configuration not available</p>
</div> </div>
</div> </div>
{/if} {/if}
@@ -40,25 +40,25 @@
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<h3 class="panel-title"> <h3 class="panel-title">
<i class="fa fa-cog"></i> Configuração WireGuard <i class="fa fa-cog"></i> WireGuard Configuration
</h3> </h3>
</div> </div>
<div class="panel-body"> <div class="panel-body">
{if $formatted_config} {if $formatted_config}
<div class="form-group"> <div class="form-group">
<label>Arquivo de Configuração:</label> <label>Configuration File:</label>
<pre class="wireguard-config" style="background: #f8f9fa; border: 1px solid #dee2e6; border-radius: 4px; padding: 15px; font-family: 'Courier New', monospace; font-size: 13px; line-height: 1.4; color: #333; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word; max-height: 350px; overflow-y: auto;">{$formatted_config|escape}</pre> <pre class="wireguard-config" style="background: #f8f9fa; border: 1px solid #dee2e6; border-radius: 4px; padding: 15px; font-family: 'Courier New', monospace; font-size: 13px; line-height: 1.4; color: #333; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word; max-height: 350px; overflow-y: auto;">{$formatted_config|escape}</pre>
</div> </div>
<div class="text-center"> <div class="text-center">
<button class="btn btn-primary" onclick="downloadFormattedConfig()"> <button class="btn btn-primary" onclick="downloadFormattedConfig()">
<i class="fa fa-download"></i> Baixar Configuração <i class="fa fa-download"></i> Download Configuration
</button> </button>
<button class="btn btn-default" onclick="copyFormattedConfig()"> <button class="btn btn-default" onclick="copyFormattedConfig()">
<i class="fa fa-copy"></i> Copiar para Área de Transferência <i class="fa fa-copy"></i> Copy to Clipboard
</button> </button>
</div> </div>
{else} {else}
<p class="text-muted">Configuração não disponível</p> <p class="text-muted">Configuration not available</p>
{/if} {/if}
</div> </div>
</div> </div>
@@ -66,41 +66,41 @@
</div> </div>
{/if} {/if}
{* Modal de Instruções *} {* Instructions Modal *}
<div class="modal fade" id="instructionsModal" tabindex="-1" role="dialog"> <div class="modal fade" id="instructionsModal" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg" role="document"> <div class="modal-dialog modal-lg" role="document">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h4 class="modal-title">Instruções de Configuração WireGuard</h4> <h4 class="modal-title">WireGuard Setup Instructions</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Fechar"><span aria-hidden="true">&times;</span></button> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<h5>Dispositivos Móveis (Android/iOS):</h5> <h5>Mobile Devices (Android/iOS):</h5>
<ol> <ol>
<li>Instale o aplicativo WireGuard da Google Play Store ou App Store</li> <li>Install the WireGuard app from Google Play Store or App Store</li>
<li>Abra o aplicativo e toque no botão "+"</li> <li>Open the app and tap the "+" button</li>
<li>Selecione "Escanear código QR"</li> <li>Select "Scan from QR code"</li>
<li>Escaneie o código QR exibido acima</li> <li>Scan the QR code displayed above</li>
<li>Dê um nome ao seu túnel e salve</li> <li>Give your tunnel a name and save</li>
</ol> </ol>
<h5>Desktop (Windows/macOS/Linux):</h5> <h5>Desktop (Windows/macOS/Linux):</h5>
<ol> <ol>
<li>Baixe e instale o WireGuard de <a href="https://www.wireguard.com/install/" target="_blank">wireguard.com</a></li> <li>Download and install WireGuard from <a href="https://www.wireguard.com/install/" target="_blank">wireguard.com</a></li>
<li>Clique em "Adicionar Túnel" → "Adicionar túnel vazio..."</li> <li>Click "Add Tunnel" → "Add empty tunnel..."</li>
<li>Copie a configuração acima e cole</li> <li>Copy the configuration above and paste it</li>
<li>Salve e ative o túnel</li> <li>Save and activate the tunnel</li>
</ol> </ol>
<h5>Linha de Comando (Linux):</h5> <h5>Command Line (Linux):</h5>
<ol> <ol>
<li>Salve a configuração em um arquivo (ex: <code>wg0.conf</code>)</li> <li>Save the configuration to a file (e.g., <code>wg0.conf</code>)</li>
<li>Execute: <code>sudo wg-quick up wg0</code></li> <li>Run: <code>sudo wg-quick up wg0</code></li>
<li>Para parar: <code>sudo wg-quick down wg0</code></li> <li>To stop: <code>sudo wg-quick down wg0</code></li>
</ol> </ol>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Fechar</button> <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div> </div>
</div> </div>
</div> </div>
@@ -147,9 +147,8 @@ function generateQRCode(configText) {
console.log('Config text length:', configText.length); console.log('Config text length:', configText.length);
if (qrCodeElement && configText && configText.trim() !== '') { if (qrCodeElement && configText && configText.trim() !== '') {
// Check if QRCode library is loaded
if (typeof QRCode === 'undefined') { if (typeof QRCode === 'undefined') {
console.error('Biblioteca QRCode não disponível, tentando fallback'); console.error('QRCode library not available, trying fallback');
generateQRCodeFallback(configText); generateQRCodeFallback(configText);
return; return;
} }
@@ -164,20 +163,20 @@ function generateQRCode(configText) {
} }
}, function(error) { }, function(error) {
if (error) { if (error) {
console.error('Falha na geração do QR Code:', error); console.error('QR Code generation failed:', error);
qrCodeElement.innerHTML = '<p class="text-danger">Falha na geração do QR Code: ' + error.message + '</p>'; qrCodeElement.innerHTML = '<p class="text-danger">QR Code generation failed: ' + error.message + '</p>';
} else { } else {
console.log('QR Code gerado com sucesso'); console.log('QR Code generated successfully');
} }
}); });
} catch (e) { } catch (e) {
console.error('Exceção durante a geração do QR code:', e); console.error('Exception during QR code generation:', e);
qrCodeElement.innerHTML = '<p class="text-danger">Erro na geração do QR Code: ' + e.message + '</p>'; qrCodeElement.innerHTML = '<p class="text-danger">QR Code generation error: ' + e.message + '</p>';
} }
} else { } else {
console.error('Falha na geração do QR Code: elemento ou configuração ausente'); console.error('QR Code generation failed: element or config missing');
if (qrCodeElement) { if (qrCodeElement) {
qrCodeElement.innerHTML = '<p class="text-warning">Configuração não disponível para QR code</p>'; qrCodeElement.innerHTML = '<p class="text-warning">Configuration not available for QR code</p>';
} }
} }
} }
@@ -188,7 +187,6 @@ function generateQRCodeFallback(configText) {
if (qrCodeElement && configText) { if (qrCodeElement && configText) {
try { try {
// Create QR code using online service as fallback
var qrUrl = 'https://api.qrserver.com/v1/create-qr-code/?size=250x250&data=' + encodeURIComponent(configText); var qrUrl = 'https://api.qrserver.com/v1/create-qr-code/?size=250x250&data=' + encodeURIComponent(configText);
var img = document.createElement('img'); var img = document.createElement('img');
img.src = qrUrl; img.src = qrUrl;
@@ -199,18 +197,17 @@ function generateQRCodeFallback(configText) {
console.log('Fallback QR code loaded successfully'); console.log('Fallback QR code loaded successfully');
}; };
img.onerror = function() { img.onerror = function() {
qrCodeElement.innerHTML = '<p class="text-danger">Falha ao gerar código QR</p>'; qrCodeElement.innerHTML = '<p class="text-danger">Failed to generate QR code</p>';
}; };
qrCodeElement.innerHTML = ''; qrCodeElement.innerHTML = '';
qrCodeElement.appendChild(img); qrCodeElement.appendChild(img);
} catch (e) { } catch (e) {
console.error('Falha na geração do QR code de fallback:', e); console.error('Fallback QR code generation failed:', e);
qrCodeElement.innerHTML = '<p class="text-danger">Falha na geração do código QR</p>'; qrCodeElement.innerHTML = '<p class="text-danger">Failed to generate QR code</p>';
} }
} }
} }
// Generate QR Code if formatted config is available
{if $formatted_config} {if $formatted_config}
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
configForQR = {$formatted_config|json_encode}; configForQR = {$formatted_config|json_encode};
@@ -219,7 +216,6 @@ document.addEventListener('DOMContentLoaded', function() {
generateQRCode(configForQR); generateQRCode(configForQR);
} else { } else {
console.log('Waiting for QRCode library to load...'); console.log('Waiting for QRCode library to load...');
// Timeout fallback
setTimeout(function() { setTimeout(function() {
if (!qrCodeLibraryLoaded && configForQR) { if (!qrCodeLibraryLoaded && configForQR) {
console.log('Timeout reached, using fallback QR generation'); console.log('Timeout reached, using fallback QR generation');
@@ -229,7 +225,7 @@ document.addEventListener('DOMContentLoaded', function() {
} }
}); });
{else} {else}
console.log('No formatted config available for QR code'); console.log('No configuration available for QR code');
{/if} {/if}
function downloadFormattedConfig() { function downloadFormattedConfig() {
@@ -250,7 +246,7 @@ function copyFormattedConfig() {
var config = {$formatted_config|json_encode}; var config = {$formatted_config|json_encode};
if (navigator.clipboard && window.isSecureContext) { if (navigator.clipboard && window.isSecureContext) {
navigator.clipboard.writeText(config).then(function() { navigator.clipboard.writeText(config).then(function() {
alert('Configuração copiada para a área de transferência!'); alert('Configuration copied to clipboard!');
}).catch(function(err) { }).catch(function(err) {
fallbackCopyTextToClipboard(config); fallbackCopyTextToClipboard(config);
}); });
@@ -272,12 +268,12 @@ function fallbackCopyTextToClipboard(text) {
try { try {
var successful = document.execCommand('copy'); var successful = document.execCommand('copy');
if (successful) { if (successful) {
alert('Configuração copiada para a área de transferência!'); alert('Configuration copied to clipboard!');
} else { } else {
alert('Falha ao copiar configuração. Por favor, copie manualmente.'); alert('Failed to copy configuration. Please copy manually.');
} }
} catch (err) { } catch (err) {
alert('Falha ao copiar configuração. Por favor, copie manualmente.'); alert('Failed to copy configuration. Please copy manually.');
} }
document.body.removeChild(textArea); document.body.removeChild(textArea);
} }
@@ -303,9 +299,9 @@ function copyConfig() {
textarea.select(); textarea.select();
try { try {
document.execCommand('copy'); document.execCommand('copy');
alert('Configuração copiada para a área de transferência!'); alert('Configuration copied to clipboard!');
} catch (err) { } catch (err) {
alert('Falha ao copiar configuração. Por favor, selecione e copie manualmente.'); alert('Failed to copy configuration. Please select and copy manually.');
} }
} }
} }
+369
View File
@@ -0,0 +1,369 @@
{if $error}
<div class="alert alert-danger">
<strong>Erro:</strong> {$error}
</div>
{else}
<div class="row">
<div class="col-md-6">
{if $formatted_config}
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">
<i class="fa fa-qrcode"></i> Código QR
</h3>
</div>
<div class="panel-body text-center">
<div id="qrcode" style="display: inline-block; margin-bottom: 10px;"></div>
<p class="text-muted">Escaneie este código QR com seu cliente WireGuard</p>
<div class="text-center" style="margin-top: 15px;">
<button class="btn btn-info btn-sm" onclick="showInstructions()">
<i class="fa fa-info-circle"></i> Instruções de Instalação
</button>
</div>
</div>
</div>
{else}
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">
<i class="fa fa-qrcode"></i> Código QR
</h3>
</div>
<div class="panel-body text-center">
<p class="text-muted">Configuração não disponível</p>
</div>
</div>
{/if}
</div>
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">
<i class="fa fa-cog"></i> Configuração WireGuard
</h3>
</div>
<div class="panel-body">
{if $formatted_config}
<div class="form-group">
<label>Arquivo de Configuração:</label>
<pre class="wireguard-config" style="background: #f8f9fa; border: 1px solid #dee2e6; border-radius: 4px; padding: 15px; font-family: 'Courier New', monospace; font-size: 13px; line-height: 1.4; color: #333; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word; max-height: 350px; overflow-y: auto;">{$formatted_config|escape}</pre>
</div>
<div class="text-center">
<button class="btn btn-primary" onclick="downloadFormattedConfig()">
<i class="fa fa-download"></i> Baixar Configuração
</button>
<button class="btn btn-default" onclick="copyFormattedConfig()">
<i class="fa fa-copy"></i> Copiar para Área de Transferência
</button>
</div>
{else}
<p class="text-muted">Configuração não disponível</p>
{/if}
</div>
</div>
</div>
</div>
{/if}
{* Modal de Instruções *}
<div class="modal fade" id="instructionsModal" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Instruções de Configuração WireGuard</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Fechar"><span aria-hidden="true">&times;</span></button>
</div>
<div class="modal-body">
<h5>Dispositivos Móveis (Android/iOS):</h5>
<ol>
<li>Instale o aplicativo WireGuard da Google Play Store ou App Store</li>
<li>Abra o aplicativo e toque no botão "+"</li>
<li>Selecione "Escanear código QR"</li>
<li>Escaneie o código QR exibido acima</li>
<li>Dê um nome ao seu túnel e salve</li>
</ol>
<h5>Desktop (Windows/macOS/Linux):</h5>
<ol>
<li>Baixe e instale o WireGuard de <a href="https://www.wireguard.com/install/" target="_blank">wireguard.com</a></li>
<li>Clique em "Adicionar Túnel" → "Adicionar túnel vazio..."</li>
<li>Copie a configuração acima e cole</li>
<li>Salve e ative o túnel</li>
</ol>
<h5>Linha de Comando (Linux):</h5>
<ol>
<li>Salve a configuração em um arquivo (ex: <code>wg0.conf</code>)</li>
<li>Execute: <code>sudo wg-quick up wg0</code></li>
<li>Para parar: <code>sudo wg-quick down wg0</code></li>
</ol>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Fechar</button>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/qrcode@1.5.3/build/qrcode.min.js" onload="initQRCode()" onerror="loadQRCodeFallback()"></script>
<script>
var qrCodeLibraryLoaded = false;
var configForQR = null;
function initQRCode() {
qrCodeLibraryLoaded = true;
console.log('QRCode library loaded successfully');
if (configForQR) {
generateQRCode(configForQR);
}
}
function loadQRCodeFallback() {
console.log('Primary QRCode library failed, trying fallback...');
var script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/qrcode-generator@1.4.4/qrcode.min.js';
script.onload = function() {
console.log('Fallback QRCode library loaded');
qrCodeLibraryLoaded = true;
if (configForQR) {
generateQRCodeFallback(configForQR);
}
};
script.onerror = function() {
console.error('Both QRCode libraries failed to load');
var qrCodeElement = document.getElementById('qrcode');
if (qrCodeElement) {
qrCodeElement.innerHTML = '<p class="text-danger">Unable to load QR Code library</p>';
}
};
document.head.appendChild(script);
}
function generateQRCode(configText) {
var qrCodeElement = document.getElementById('qrcode');
console.log('Generating QR Code with qrcode.js...');
console.log('Config text length:', configText.length);
if (qrCodeElement && configText && configText.trim() !== '') {
// Check if QRCode library is loaded
if (typeof QRCode === 'undefined') {
console.error('Biblioteca QRCode não disponível, tentando fallback');
generateQRCodeFallback(configText);
return;
}
try {
QRCode.toCanvas(qrCodeElement, configText, {
width: 250,
margin: 3,
color: {
dark: '#000000',
light: '#FFFFFF'
}
}, function(error) {
if (error) {
console.error('Falha na geração do QR Code:', error);
qrCodeElement.innerHTML = '<p class="text-danger">Falha na geração do QR Code: ' + error.message + '</p>';
} else {
console.log('QR Code gerado com sucesso');
}
});
} catch (e) {
console.error('Exceção durante a geração do QR code:', e);
qrCodeElement.innerHTML = '<p class="text-danger">Erro na geração do QR Code: ' + e.message + '</p>';
}
} else {
console.error('Falha na geração do QR Code: elemento ou configuração ausente');
if (qrCodeElement) {
qrCodeElement.innerHTML = '<p class="text-warning">Configuração não disponível para QR code</p>';
}
}
}
function generateQRCodeFallback(configText) {
var qrCodeElement = document.getElementById('qrcode');
console.log('Trying fallback QR code generation...');
if (qrCodeElement && configText) {
try {
// Create QR code using online service as fallback
var qrUrl = 'https://api.qrserver.com/v1/create-qr-code/?size=250x250&data=' + encodeURIComponent(configText);
var img = document.createElement('img');
img.src = qrUrl;
img.alt = 'WireGuard QR Code';
img.style.border = '3px solid #ddd';
img.style.borderRadius = '4px';
img.onload = function() {
console.log('Fallback QR code loaded successfully');
};
img.onerror = function() {
qrCodeElement.innerHTML = '<p class="text-danger">Falha ao gerar código QR</p>';
};
qrCodeElement.innerHTML = '';
qrCodeElement.appendChild(img);
} catch (e) {
console.error('Falha na geração do QR code de fallback:', e);
qrCodeElement.innerHTML = '<p class="text-danger">Falha na geração do código QR</p>';
}
}
}
// Generate QR Code if formatted config is available
{if $formatted_config}
document.addEventListener('DOMContentLoaded', function() {
configForQR = {$formatted_config|json_encode};
if (qrCodeLibraryLoaded) {
generateQRCode(configForQR);
} else {
console.log('Waiting for QRCode library to load...');
// Timeout fallback
setTimeout(function() {
if (!qrCodeLibraryLoaded && configForQR) {
console.log('Timeout reached, using fallback QR generation');
generateQRCodeFallback(configForQR);
}
}, 3000);
}
});
{else}
console.log('No formatted config available for QR code');
{/if}
function downloadFormattedConfig() {
{if $formatted_config}
var config = {$formatted_config|json_encode};
var element = document.createElement('a');
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(config));
element.setAttribute('download', '{$peer_name|default:"wireguard"}.conf');
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
{/if}
}
function copyFormattedConfig() {
{if $formatted_config}
var config = {$formatted_config|json_encode};
if (navigator.clipboard && window.isSecureContext) {
navigator.clipboard.writeText(config).then(function() {
alert('Configuração copiada para a área de transferência!');
}).catch(function(err) {
fallbackCopyTextToClipboard(config);
});
} else {
fallbackCopyTextToClipboard(config);
}
{/if}
}
function fallbackCopyTextToClipboard(text) {
var textArea = document.createElement("textarea");
textArea.value = text;
textArea.style.top = "0";
textArea.style.left = "0";
textArea.style.position = "fixed";
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
var successful = document.execCommand('copy');
if (successful) {
alert('Configuração copiada para a área de transferência!');
} else {
alert('Falha ao copiar configuração. Por favor, copie manualmente.');
}
} catch (err) {
alert('Falha ao copiar configuração. Por favor, copie manualmente.');
}
document.body.removeChild(textArea);
}
// Legacy functions for backward compatibility
function downloadConfig() {
var textarea = document.querySelector('textarea');
if (textarea) {
var config = textarea.value;
var element = document.createElement('a');
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(config));
element.setAttribute('download', '{$peer_name|default:"wireguard"}.conf');
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
}
function copyConfig() {
var textarea = document.querySelector('textarea');
if (textarea) {
textarea.select();
try {
document.execCommand('copy');
alert('Configuração copiada para a área de transferência!');
} catch (err) {
alert('Falha ao copiar configuração. Por favor, selecione e copie manualmente.');
}
}
}
function showInstructions() {
$('#instructionsModal').modal('show');
}
</script>
<style>
.panel-heading .fa {
margin-right: 5px;
}
.center-block {
display: block;
margin-left: auto;
margin-right: auto;
}
code {
word-break: break-all;
}
.table-condensed td {
padding: 5px;
}
.modal-body h5 {
color: #337ab7;
margin-top: 20px;
margin-bottom: 10px;
}
.modal-body h5:first-child {
margin-top: 0;
}
/***** Modal close button alignment *****/
.modal-header {
position: relative;
}
.modal-header .close {
position: absolute;
right: 15px;
top: 50%;
transform: translateY(-50%);
font-size: 28px;
line-height: 1;
}
.wireguard-config {
max-height: 350px;
overflow-y: auto;
}
#qrcode canvas {
border: 1px solid #ddd;
border-radius: 4px;
}
</style>