2024-02-14 16:36:01 -03:00
{% extends "base.html" %}
{% block content %}
2025-02-24 09:55:52 -03:00
{% if wireguard_instances %}
< div class = "card card-primary card-outline" >
< div class = "card-body" >
< ul class = "nav nav-tabs" role = "tablist" >
{% for wgconf in wireguard_instances %}
< li class = "nav-item" >
< a class = "nav-link {% if wgconf == current_instance %}active{% endif %}" href = "/peer/list/?uuid={{ wgconf.uuid }}" role = "tab" >
wg{{ wgconf.instance_id }} {% if wgconf.name %}({{ wgconf.name }}){% endif %}
< / a >
< / li >
{% endfor %}
< / ul >
< div class = "tab-content" id = "custom-content-below-tabContent" >
< div class = "tab-pane fade show active" id = "custom-content-below-home" role = "tabpanel" aria-labelledby = "custom-content-below-home-tab" >
< div class = "row" >
{% for peer in peer_list %}
< div class = "col-md-6" id = "peer-{{ peer.public_key }}" >
< div class = "callout" >
< div class = "d-flex justify-content-between align-items-start" >
< h5 > < a href = "#" onclick = "openPeerModal('{{ peer.uuid }}');" style = "text-decoration: none" > {{ peer }}< / a > < / h5 >
< span >
2025-01-22 11:53:56 -03:00
{% if user_acl.user_level >= 30 %}
2025-02-24 09:55:52 -03:00
< div class = "d-inline-flex flex-column" >
2025-02-24 09:28:42 -03:00
< a href = "/peer/sort/?peer={{ peer.uuid }}&direction=up" style = "line-height:0px" >
< i class = "fas fa-sort-up" > < / i >
< / a >
2025-01-22 11:53:56 -03:00
< div style = "overflow:hidden;margin-top: -9px" >
2025-02-24 09:28:42 -03:00
< a href = "/peer/sort/?peer={{ peer.uuid }}&direction=down" style = "position:relative;top:-11px" >
< i class = "fas fa-sort-down" > < / i >
< / a >
2025-01-22 11:53:56 -03:00
< / div >
< / div >
{% endif %}
2025-02-24 09:55:52 -03:00
< a href = "javascript:void(0);" onclick = "openImageLightbox('/tools/download_peer_config/?uuid={{ peer.uuid }}&format=qrcode');" >
2025-02-24 09:28:42 -03:00
< i class = "fas fa-qrcode" > < / i >
< / a >
< a href = "/tools/download_peer_config/?uuid={{ peer.uuid }}" >
< i class = "fas fa-download" > < / i >
< / a >
< a href = "/peer/manage/?peer={{ peer.uuid }}" >
< i class = "far fa-edit" > < / i >
< / a >
2025-01-22 11:53:56 -03:00
< / span >
2025-02-24 09:55:52 -03:00
< / div >
< p >
< b > Transfer:< / b > < span id = "peer-transfer-{{ peer.public_key }}" > < / span > < br >
< b > Latest Handshake:< / b > < span id = "peer-latest-handshake-{{ peer.public_key }}" > < / span >
< span style = "display: none;" id = "peer-stored-latest-handshake-{{ peer.public_key }}" >
2025-02-24 09:28:42 -03:00
{% if peer.peerstatus.last_handshake %}{{ peer.peerstatus.last_handshake|date:"U" }}{% else %}0{% endif %}
< / span > < br >
2025-02-24 09:55:52 -03:00
< b > Endpoints:< / b > < span id = "peer-endpoints-{{ peer.public_key }}" > < / span > < br >
< b > Allowed IPs:< / b >
< span id = "peer-allowed-ips-{{ peer.public_key }}" >
2024-03-09 16:02:48 -03:00
{% for address in peer.peerallowedip_set.all %}
2025-02-24 09:28:42 -03:00
{% if address.priority == 0 and address.config_file == 'server' %}
{{ address }}
{% endif %}
2024-03-09 16:02:48 -03:00
{% endfor %}
2025-02-24 09:55:52 -03:00
{% for address in peer.peerallowedip_set.all %}
{% if address.priority >= 1 and address.config_file == 'server' %}
{{ address }}
{% endif %}
{% endfor %}
2024-02-17 11:53:51 -03:00
< / span >
2025-02-24 09:55:52 -03:00
< / p >
< / div >
< / div >
{% endfor %}
2024-02-14 16:36:01 -03:00
< / div >
2025-02-24 09:55:52 -03:00
{% if add_peer_enabled %}
< a class = "btn btn-primary" href = "/peer/manage/?instance={{ current_instance.uuid }}" > Create Peer< / a >
{% else %}
< a class = "btn btn-primary disabled" href = "" > Create Peer< / a >
{% endif %}
2024-02-14 16:36:01 -03:00
< / div >
< / div >
2025-02-24 09:28:42 -03:00
< / div >
2024-02-14 16:36:01 -03:00
< / div >
2025-02-24 09:55:52 -03:00
<!-- Peer Preview Modal -->
< div class = "modal fade" id = "peerPreviewModal" tabindex = "-1" aria-labelledby = "peerPreviewModalLabel" aria-hidden = "true" >
< div class = "modal-dialog modal-lg" >
< div class = "modal-content" >
< div class = "modal-header" >
< h5 class = "modal-title" id = "peerPreviewModalLabel" > Peer Preview< / h5 >
< button type = "button" class = "close" data-dismiss = "modal" aria-label = "Close" >
< span aria-hidden = "true" > × < / span >
< / button >
< / div >
< div class = "modal-body" >
<!-- Peer Information -->
< h3 id = "peerName" > Peer Name Placeholder< / h3 >
< p > < b > Transfer:< / b > < span id = "peerTransfer" > --< / span > < / p >
< p > < b > Latest Handshake:< / b > < span id = "peerHandshake" > --< / span > < / p >
< p > < b > Endpoints:< / b > < span id = "peerEndpoints" > --< / span > < / p >
< p > < b > Allowed IPs:< / b > < span id = "peerAllowedIPs" > --< / span > < / p >
<!-- Consumption graph placeholder -->
< div >
< canvas id = "peerGraph" width = "400" height = "200" > < / canvas >
< / div >
< / div >
< div class = "modal-footer" >
< button type = "button" class = "btn btn-secondary" data-dismiss = "modal" > Close< / button >
< a href = "#" class = "btn btn-primary" id = "editPeerButton" > Edit< / a >
< / div >
< / div >
< / div >
< / div >
{% else %}
< div class = "alert alert-warning" role = "alert" >
< h4 class = "alert-heading" > No WireGuard Instances Found< / h4 >
< p > There are no WireGuard instances configured. You can add a new instance by clicking the button below.< / p >
< / div >
< p >
< a href = "/server/manage/" class = "btn btn-primary" > Add WireGuard Instance< / a >
< / p >
{% endif %}
2025-02-24 09:28:42 -03:00
{% endblock %}
2024-02-14 16:36:01 -03:00
2025-02-24 09:28:42 -03:00
{% block custom_page_scripts %}
2024-02-14 16:36:01 -03:00
2025-02-24 09:55:52 -03:00
< script >
// Function to open the peer preview modal and fetch its details
function openPeerModal(uuid) {
$.ajax({
url: '/api/peer_info/',
data: { uuid: uuid },
type: 'GET',
dataType: 'json',
success: function(data) {
// Update modal placeholders with fetched data
$('#peerName').text(data.name || 'Unnamed Peer');
if (data.transfer) {
$('#peerTransfer').text(convertBytes(data.transfer.tx) + ' TX, ' + convertBytes(data.transfer.rx) + ' RX');
} else {
$('#peerTransfer').text('--');
}
$('#peerHandshake').text(data.latest_handshake ? new Date(parseInt(data.latest_handshake) * 1000).toLocaleString() : '--');
$('#peerEndpoints').text(data.endpoints || '--');
$('#peerAllowedIPs').text(data.allowed_ips || '--');
2024-02-14 16:36:01 -03:00
2025-02-24 09:55:52 -03:00
// Update the Edit button URL
$('#editPeerButton').attr('href', '/peer/manage/?peer=' + uuid);
2024-02-14 16:36:01 -03:00
2025-02-24 09:55:52 -03:00
// Open the modal
$('#peerPreviewModal').modal('show');
},
error: function(xhr, status, error) {
console.error("Error fetching peer info:", error);
}
});
}
< / script >
2024-02-17 11:53:51 -03:00
2025-02-24 09:55:52 -03:00
< script >
document.addEventListener('DOMContentLoaded', function() {
//const fetchWireguardStatus = async () => {
// try {
// const response = await fetch('/api/wireguard_status/');
// const data = await response.json();
// updateUI(data);
// } catch (error) {
// console.error('Error fetching Wireguard status:', error);
// }
//};
const fetchWireguardStatus = async () => {
try {
const response = await fetch('/api/wireguard_status/');
let data = await response.json();
2024-02-23 18:18:52 -03:00
2025-02-24 09:55:52 -03:00
// if latest-handshakes is 0, use the stored value
for (const [interfaceName, peers] of Object.entries(data)) {
for (const [peerId, peerInfo] of Object.entries(peers)) {
const peerElementId = `peer-stored-latest-handshake-${peerId}`;
const storedHandshakeElement = document.getElementById(peerElementId);
if (peerInfo['latest-handshakes'] === '0' & & storedHandshakeElement) {
peerInfo['latest-handshakes'] = storedHandshakeElement.textContent;
}
2024-02-23 18:18:52 -03:00
}
}
2025-02-24 09:55:52 -03:00
updateUI(data);
} catch (error) {
console.error('Error fetching Wireguard status:', error);
2024-02-23 18:18:52 -03:00
}
2025-02-24 09:55:52 -03:00
};
fetchWireguardStatus();
setInterval(fetchWireguardStatus, {{ current_instance.peer_list_refresh_interval }} * 1000);
});
const updateUI = (data) => {
for (const [interfaceName, peers] of Object.entries(data)) {
for (const [peerId, peerInfo] of Object.entries(peers)) {
const peerDiv = document.getElementById(`peer-${peerId}`);
if (peerDiv) {
updatePeerInfo(peerDiv, peerId, peerInfo);
updateCalloutClass(peerDiv, peerInfo['latest-handshakes']);
}
2024-02-17 11:53:51 -03:00
}
}
2025-02-24 09:55:52 -03:00
};
2024-02-17 11:53:51 -03:00
2025-02-24 09:55:52 -03:00
const updatePeerInfo = (peerDiv, peerId, peerInfo) => {
const escapedPeerId = peerId.replace(/([!"#$%&'()*+,.\/:; < =>?@[\]^`{|}~])/g, '\\$1');
const transfer = peerDiv.querySelector(`#peer-transfer-${escapedPeerId}`);
const latestHandshake = peerDiv.querySelector(`#peer-latest-handshake-${escapedPeerId}`);
const endpoints = peerDiv.querySelector(`#peer-endpoints-${escapedPeerId}`);
const allowedIps = peerDiv.querySelector(`#peer-allowed-ips-${escapedPeerId}`);
2024-02-17 11:53:51 -03:00
2025-02-24 09:55:52 -03:00
transfer.textContent = `${convertBytes(peerInfo.transfer.tx)} TX, ${convertBytes(peerInfo.transfer.rx)} RX`;
latestHandshake.textContent = `${peerInfo['latest-handshakes'] !== '0' ? new Date(parseInt(peerInfo['latest-handshakes']) * 1000).toLocaleString() : '0'}`;
endpoints.textContent = `${peerInfo.endpoints}`;
checkAllowedIps(allowedIps, peerInfo['allowed-ips']);
};
const convertBytes = (bytes) => {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
};
2024-02-17 11:53:51 -03:00
2025-02-24 09:55:52 -03:00
const checkAllowedIps = (allowedIpsElement, allowedIpsApiResponse) => {
const apiIps = allowedIpsApiResponse[0].split(' ');
const htmlIpsText = allowedIpsElement.textContent.trim();
const htmlIpsArray = htmlIpsText.match(/\b(?:\d{1,3}\.){3}\d{1,3}\/\d{1,2}\b/g);
2024-02-15 18:15:15 -03:00
2025-02-24 09:55:52 -03:00
allowedIpsElement.innerHTML = '';
htmlIpsArray.forEach((ip, index, array) => {
const ipSpan = document.createElement('span');
ipSpan.textContent = ip;
allowedIpsElement.appendChild(ipSpan);
2024-02-17 11:53:51 -03:00
2025-02-24 09:55:52 -03:00
if (!apiIps.includes(ip)) {
ipSpan.style.color = 'red';
ipSpan.style.textDecoration = 'underline';
ipSpan.title = 'This address does not appear in the wg show command output, likely indicating that another peer has an IP overlapping this network or that the configuration file is outdated.';
}
2024-02-17 11:53:51 -03:00
2025-02-24 09:55:52 -03:00
if (index < array.length - 1 ) {
allowedIpsElement.appendChild(document.createTextNode(', '));
}
});
};
const updateCalloutClass = (peerDiv, latestHandshake) => {
const calloutDiv = peerDiv.querySelector('.callout');
calloutDiv.classList.remove('callout-success', 'callout-info', 'callout-warning', 'callout-danger');
const handshakeAge = Date.now() / 1000 - parseInt(latestHandshake);
if (latestHandshake === '0') {
calloutDiv.classList.add('callout-danger');
} else if (handshakeAge < 600 ) {
calloutDiv.classList.add('callout-success');
} else if (handshakeAge < 1800 ) {
calloutDiv.classList.add('callout-info');
} else if (handshakeAge < 21600 ) {
calloutDiv.classList.add('callout-warning');
}
calloutDiv.style.transition = 'all 5s';
};
< / script >
< script >
function openImageLightbox(url) {
window.open(url, 'Image', 'width=500,height=500,toolbar=0,location=0,menubar=0');
}
< / script >
2024-02-17 11:53:51 -03:00
2024-02-14 16:36:01 -03:00
{% endblock %}