Toby Chui 136989f2ea Added plugin dir parameter
- Added plugin dir parameter
- Fixed critical architectural bug that effects plugin UI in production mode
- Updated implementation of embed FS routing
- Minor dark theme update
- Fixed ztnc UI bug for msgbox and confirm box
2025-03-28 21:24:18 +08:00

267 lines
11 KiB
HTML

<html>
<head>
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1"/>
<meta charset="UTF-8">
<meta name="theme-color" content="#4b75ff">
<meta name="zoraxy.csrf.Token" content="{{.csrfToken}}">
<link rel="icon" type="image/png" href="/favicon.png" />
<title>Global Area Network | Zoraxy</title>
<link rel="stylesheet" href="/script/semantic/semantic.min.css">
<script src="/script/jquery-3.6.0.min.js"></script>
<script src="/script/semantic/semantic.min.js"></script>
<script src="/script/tablesort.js"></script>
<script src="/script/countryCode.js"></script>
<script src="/script/chart.js"></script>
<script src="/script/utils.js"></script>
<link rel="stylesheet" href="/main.css">
<style>
body{
background:none;
}
</style>
</head>
<body>
<!-- Dark theme script must be included after body tag-->
<link rel="stylesheet" href="/darktheme.css">
<script src="/script/darktheme.js"></script>
<div id="ganetWindow" class="standardContainer">
<div class="ui basic segment">
<h2>Global Area Network</h2>
<p>Virtual Network Hub that allows all networked devices to communicate as if they all reside in the same physical data center or cloud region</p>
</div>
<div class="gansnetworks">
<div class="ganstats ui basic segment">
<div style="float: right; max-width: 300px; margin-top: 0.4em;">
<h1 class="ui header" style="text-align: right;">
<span class="ganControllerID"></span>
<div class="sub header">Network Controller ID</div>
</h1>
</div>
<div class="ui list">
<div class="item">
<i class="exchange icon"></i>
<div class="content">
<div class="header" style="font-size: 1.2em;" id="ganetCount">0</div>
<div class="description">Networks</div>
</div>
</div>
<div class="item">
<i class="desktop icon"></i>
<div class="content">
<div class="header" style="font-size: 1.2em;" id="ganodeCount">0</div>
<div class="description" id="connectedNodes" count="0">Connected Nodes</div>
</div>
</div>
</div>
</div>
<div class="ganlist">
<button class="ui basic orange button" onclick="addGANet();">Create New Network</button>
<div class="ui divider"></div>
<!--
<div class="ui icon input" style="margin-bottom: 1em;">
<input type="text" placeholder="Search a Network">
<i class="circular search link icon"></i>
</div>-->
<div style="width: 100%; overflow-x: auto;">
<table class="ui celled basic unstackable striped table">
<thead>
<tr>
<th>Network ID</th>
<th>Name</th>
<th>Description</th>
<th>Subnet (Assign Range)</th>
<th>Nodes</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="GANetList">
<tr>
<td colspan="6"><i class="ui green circle check icon"></i> No Global Area Network Found on this host</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<script>
/*
Network Management Functions
*/
function handleAddNetwork(){
let networkName = $("#networkName").val().trim();
if (networkName == ""){
parent.msgbox("Network name cannot be empty", false, 5000);
return;
}
//Add network with default settings
addGANet(networkName, "192.168.196.0/24");
$("#networkName").val("");
}
function initGANetID(){
$.get("./api/gan/network/info", function(data){
if (data.error !== undefined){
parent.msgbox(data.error, false, 5000)
}else{
if (data != ""){
$(".ganControllerID").text(data);
}
}
})
}
function addGANet() {
$.cjax({
url: "./api/gan/network/add",
type: "POST",
dataType: "json",
data: {},
success: function(response) {
if (response.error != undefined){
parent.msgbox(response.error, false, 5000);
}else{
parent.msgbox("Network added successfully");
}
console.log("Network added successfully:", response);
listGANet();
},
error: function(xhr, status, error) {
console.log("Error adding network:", error);
}
});
}
function listGANet(){
$("#connectedNodes").attr("count", "0");
$.get("./api/gan/network/list", function(data){
$("#GANetList").empty();
if (data.error != undefined){
console.log(data.error);
parent.msgbox("Unable to load auth token for GANet", false, 5000);
//token error or no zerotier found
$(".gansnetworks").addClass("disabled");
$("#GANetList").append(`<tr>
<td colspan="6"><i class="red times circle icon"></i> Auth token access error or not found</td>
</tr>`);
$(".ganControllerID").text('Access Denied');
}else{
var nodeCount = 0;
data.forEach(function(gan){
$("#GANetList").append(`<tr class="ganetEntry" addr="${gan.nwid}">
<td><a href="#" onclick="event.preventDefault(); openGANetDetails('${gan.nwid}');">${gan.nwid}</a></td>
<td>${gan.name}</td>
<td class="gandesc" addr="${gan.nwid}"></td>
<td class="ganetSubnet"></td>
<td class="ganetNodes"></td>
<td>
<button onclick="openGANetDetails('${gan.nwid}');" class="ui tiny basic icon button" title="Edit Network"><i class="edit icon"></i></button>
<button onclick="removeGANet('${gan.nwid}');" class="ui tiny basic icon button" title="Remove Network"><i class="red remove icon"></i></button>
</td>
</tr>`);
nodeCount += 0;
});
if (data.length == 0){
$("#GANetList").append(`<tr>
<td colspan="6"><i class="ui green circle check icon"></i> No Global Area Network Found on this host</td>
</tr>`);
}
$("#ganodeCount").text(nodeCount);
$("#ganetCount").text(data.length);
//Load description
$(".gandesc").each(function(){
let addr = $(this).attr("addr");
let domEle = $(this);
$.get("./api/gan/network/name?netid=" + addr, function(data){
$(domEle).text(data[1]);
});
});
$(".ganetEntry").each(function(){
let addr = $(this).attr("addr");
let subnetEle = $(this).find(".ganetSubnet");
let nodeEle = $(this).find(".ganetNodes");
$.get("./api/gan/network/list?netid=" + addr, function(data){
if (data.routes != undefined && data.routes.length > 0){
if (data.ipAssignmentPools != undefined && data.ipAssignmentPools.length > 0){
$(subnetEle).html(`${data.routes[0].target} <br> (${data.ipAssignmentPools[0].ipRangeStart} - ${data.ipAssignmentPools[0].ipRangeEnd})`);
}else{
$(subnetEle).html(`${data.routes[0].target}<br>(Unassigned Range)`);
}
}else{
$(subnetEle).text("Unassigned");
}
//console.log(data);
});
$.get("./api/gan/members/list?netid=" + addr, function(data){
$(nodeEle).text(data.length);
let currentNodesCount = parseInt($("#connectedNodes").attr("count"));
currentNodesCount += data.length;
$("#connectedNodes").attr("count", currentNodesCount);
$("#ganodeCount").text($("#connectedNodes").attr("count"));
})
});
}
})
}
//Remove the given GANet
function removeGANet(netid){
//Reusing Zoraxy confirm box
parent.confirmBox("Confirm remove " + netid + "?", function(choice){
if (choice == true){
$.cjax({
url: "./api/gan/network/remove",
type: "POST",
dataType: "json",
data: {
id: netid,
},
success: function(data){
if (data.error != undefined){
parent.msgbox(data.error, false, 5000);
}else{
parent.msgbox("Net " + netid + " removed");
}
listGANet();
}
});
}
});
}
function openGANetDetails(netid){
$("#ganetWindow").load("./details.html", function(){
setTimeout(function(){
initGanetDetails(netid);
});
});
}
$(document).ready(function(){
listGANet();
initGANetID();
});
if (typeof(msgbox) == "undefined"){
msgbox = function(msg, error=false, timeout=3000){
console.log(msg);
}
}
</script>
</body>
</html>