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 %}
2024-02-14 16:36:01 -03:00
{% endblock %}
{% block custom_page_scripts %}
2024-02-17 11:53:51 -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 ( '--' );
2024-02-23 18:18:52 -03:00
}
2025-02-24 09:55:52 -03:00
$ ( '#peerHandshake' ). text ( data . latest_handshake ? new Date ( parseInt ( data . latest_handshake ) * 1000 ). toLocaleString () : '--' );
$ ( '#peerEndpoints' ). text ( data . endpoints || '--' );
$ ( '#peerAllowedIPs' ). text ( data . allowed_ips || '--' );
2024-02-17 11:53:51 -03:00
2025-02-24 09:55:52 -03:00
// Update the Edit button URL
$ ( '#editPeerButton' ). attr ( 'href' , '/peer/manage/?peer=' + uuid );
2024-02-17 11:53:51 -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 );
}
});
2024-02-17 11:53:51 -03:00
}
</ script >
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-15 18:15:15 -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-17 11:53:51 -03:00
2025-02-24 09:55:52 -03:00
updateUI ( data );
} catch ( error ) {
console . error ( 'Error fetching Wireguard status:' , error );
}
};
2024-02-17 11:53:51 -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' ]);
}
}
}
};
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 } ` );
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 ];
};
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 );
allowedIpsElement . innerHTML = '' ;
htmlIpsArray . forEach (( ip , index , array ) => {
const ipSpan = document . createElement ( 'span' );
ipSpan . textContent = ip ;
allowedIpsElement . appendChild ( ipSpan );
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.' ;
}
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 %}