Files
edgevpn/api/public/index.html
renovate[bot] f0d4e0b46c chore(deps): update dependency font-awesome to v6.6.0 (#518)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-16 18:04:17 +00:00

2931 lines
119 KiB
HTML
Executable File

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>EdgeVPN</title>
<meta name="description" content="Edgevpn dashboard">
<meta name="keywords" content="edgevpn,dashboard">
<script src="/js/apexcharts.min.js"></script>
<script src="/js/alpine-magic-helpers.min.js" defer></script>
<script src="/js/alpine.min.js" defer></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/all.min.css" integrity="sha512-Kc323vGBEqzTmouAECnVceyQqyqdsSiqLQISBL29aUW4U/M7pSPA/gEUZQqv1cwx4OnYxTxve5UMg5GT6L4JJg==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<style>
.bg-black-alt {
background:#191919;
}
.text-black-alt {
color:#191919;
}
.border-black-alt {
border-color: #191919;
}
.string { color: green; }
.number { color: darkorange; }
.boolean { color: blue; }
.null { color: magenta; }
.key { color: red; }
#checkbox:checked + label .switch-ball{
background-color: white;
transform: translateX(24px);
transition: transform 0.3s linear;
}
</style>
<script src="/js/tailwind.min.js"></script>
<script>
tailwind.config = {
darkMode: 'class',
theme: {
extend: {
colors: {
clifford: '#da373d',
}
}
}
}
</script>
</head>
<body class="font-sans leading-normal tracking-normal"
x-data="{page: location.hash, 'darkMode': false }"
@hashchange.window="page = location.hash"
x-init="
darkMode = JSON.parse(localStorage.getItem('darkMode'));
$watch('darkMode', value => localStorage.setItem('darkMode', JSON.stringify(value)))"
x-bind:class="darkMode === true ? 'dark bg-black-alt': 'bg-white'"
>
<nav id="header" class="bg-slate-100 dark:bg-gray-900 fixed w-full z-10 top-0 shadow">
<div class="w-full container mx-auto flex flex-wrap items-center mt-0 pt-3 pb-3 md:pb-0">
<div class="w-1/2 pl-2 md:pl-0 align-text-bottom">
<a class="text-gray-100 text-base xl:text-xl no-underline hover:no-underline font-bold align-top" href="#">
<img src="/images/logo.png" class="object-scale-down float-left h-7 w-7"> <span class="pl-4 pt-1 md:pb-0 text-md text-slate-700 dark:text-slate-100"> EdgeVPN </span>
</a>
</div>
<div class="w-1/2 pr-0">
<div class="flex relative inline-block float-right">
<!-- Dark/Light mode button-->
<div >
<div>
<div class="dark:text-gray-100">
<div class="flex items-center justify-center space-x-2">
<span class="text-sm text-gray-800 dark:text-gray-500 p-2">Light</span>
<label for="toggle"
class="flex items-center h-5 p-1 duration-300 ease-in-out bg-gray-300 rounded-full cursor-pointer w-9 dark:bg-gray-600">
<div
class="w-4 h-4 duration-300 ease-in-out transform bg-white rounded-full shadow-md toggle-dot dark:translate-x-3">
</div>
</label>
<span class="text-sm text-gray-400 dark:text-white p-2">Dark</span>
<input id="toggle" type="checkbox" class="hidden" :value="darkMode" @change="darkMode = !darkMode" />
</div>
</div>
</div>
</div>
<!-- END Dark/Light mode button-->
<div class="block lg:hidden pr-4">
<button id="nav-toggle" class="flex items-center px-3 py-2 border rounded text-gray-500 border-gray-600 hover:text-gray-100 hover:border-teal-500 appearance-none focus:outline-none">
<svg class="fill-current h-3 w-3" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><title>Menu</title><path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z"/></svg>
</button>
</div>
</div>
</div>
<div class="w-full flex-grow lg:flex lg:items-center lg:w-auto hidden lg:block mt-2 lg:mt-0 bg-white-300 dark:bg-gray-900 z-20" id="nav-content">
<ul class="list-reset lg:flex flex-1 items-center px-4 md:px-0">
<li class="mr-6 my-2 md:my-0">
<a href="#"
x-bind:class="page === '' ? 'text-blue-400 border-blue-400 hover:border-blue-400' : 'text-gray-500 dark:hover:text-gray-100 dark:hover:border-violet-900 hover:text-gray-900 border-sky-100 dark:border-sky-900 hover:border-pink-400'"
class="block py-1 md:py-3 pl-1 align-middle no-underline border-b-2">
<i
x-bind:class="page === '' ? 'text-blue-400' : '' "
class="fas fa-home fa-fw mr-3"></i><span class="pb-1 md:pb-0 text-sm">Home</span>
</a>
</li>
<li class="mr-6 my-2 md:my-0">
<a href="#nodes"
x-bind:class="page === '#nodes' ? 'text-blue-400 border-blue-400 hover:border-blue-400' : 'text-gray-500 dark:hover:text-gray-100 dark:hover:border-violet-900 hover:text-gray-900 border-sky-100 dark:border-sky-900 hover:border-pink-400'"
class="block py-1 md:py-3 pl-1 align-middle no-underline border-b-2">
<i
x-bind:class="page === '#nodes' ? 'text-blue-400' : '' "
class="fas fa-server fa-fw mr-3"></i><span class="pb-1 md:pb-0 text-sm">Nodes</span>
</a>
</li>
<li class="mr-6 my-2 md:my-0">
<a href="#dns"
x-bind:class="page === '#dns' ? 'text-blue-400 border-blue-400 hover:border-blue-400' : 'text-gray-500 dark:hover:text-gray-100 dark:hover:border-violet-900 hover:text-gray-900 border-sky-100 dark:border-sky-900 hover:border-pink-400'"
class="block py-1 md:py-3 pl-1 align-middle no-underline border-b-2">
<i
x-bind:class="page === '#dns' ? 'text-blue-400' : '' "
class="fas fa-globe fa-fw mr-3"></i><span class="pb-1 md:pb-0 text-sm">DNS</span>
</a>
</li>
<li class="mr-6 my-2 md:my-0">
<a href="#blockchain"
x-bind:class="page === '#blockchain' ? 'text-blue-400 border-blue-400 hover:border-blue-400' : 'text-gray-500 dark:hover:text-gray-100 dark:hover:border-violet-900 hover:text-gray-900 border-sky-100 dark:border-sky-900 hover:border-pink-400'"
class="block py-1 md:py-3 pl-1 align-middle no-underline border-b-2">
<i
x-bind:class="page === '#blockchain' ? 'text-blue-400' : '' "
class="fas fa-dice-d20 fa-fw mr-3"></i><span class="pb-1 md:pb-0 text-sm">Blockchain</span>
</a>
</li>
<li class="mr-6 my-2 md:my-0">
<a href="#services"
x-bind:class="page === '#services' ? 'text-blue-400 border-blue-400 hover:border-blue-400' : 'text-gray-500 dark:hover:text-gray-100 dark:hover:border-violet-900 hover:text-gray-900 border-sky-100 dark:border-sky-900 hover:border-pink-400'"
class="block py-1 md:py-3 pl-1 align-middle no-underline border-b-2">
<i
x-bind:class="page === '#services' ? 'text-blue-400' : '' "
class="fas fa-ethernet fa-fw mr-3"></i><span class="pb-1 md:pb-0 text-sm">Services</span>
</a>
</li>
<li class="mr-6 my-2 md:my-0">
<a href="#peers"
x-bind:class="page === '#peers' ? 'text-blue-400 border-blue-400 hover:border-blue-400' : 'text-gray-500 dark:hover:text-gray-100 dark:hover:border-violet-900 hover:text-gray-900 border-sky-100 dark:border-sky-900 hover:border-pink-400'"
class="block py-1 md:py-3 pl-1 align-middle no-underline border-b-2">
<i
x-bind:class="page === '#peers' ? 'text-blue-400' : '' "
class="fas fa-users fa-fw mr-3"></i><span class="pb-1 md:pb-0 text-sm">Peers</span>
</a>
</li>
</ul>
<div class="relative pull-right pl-4 pr-4 md:pr-0">
</div>
</div>
</div>
</nav>
<!-- Nodes Container-->
<div class="container w-full mx-auto pt-20" x-show="page === '#nodes'">
<div class="px-4 md:px-0 md:mt-8 mb-20 leading-normal " >
<div class="relative " >
<a class="mr-2 bg-sky-300 dark:bg-sky-600 text-white p-2 rounded leading-none items-center absolute top-10 right-1 " href="https://mudler.github.io/edgevpn/docs/getting-started/api/#apimachines" target=_blank>
<i class="fa-solid fa-book fa-fw pr-1"></i> Machines API Documentation
</a>
</div>
</div>
<div class="w-full px-4 md:px-0 md:mt-8 mb-16 text-gray-800 leading-normal"
x-data="machines()"
x-init="$interval(updateItems, 1500)"
>
<!-- Notification toast -->
<div
x-show="open"
class="w-96 p-4 rounded h-32 fixed bottom-4 right-4 transform-gpu transition-transform duration-400 ease"
x-class="success ? 'bg-green-500' : 'bg-red-500'"
x-transition:enter-start="opacity-0 translate-y-full"
x-transition:enter-end="opacity-100 translate-y-0"
x-transition:leave-start="translate-y-0"
x-transition:leave="transition transform ease-in duration-300"
x-transition:leave-end="opacity-0 translate-y-full"
>
<div class="bg-white border-gray-300 dark:border-slate-800 dark:bg-slate-900 border p-3 flex items-start shadow-lg rounded-md space-x-2">
<svg class="flex-shrink-0 h-6 w-6 text-green-400" stroke="currentColor" viewBox="0 0 20 20">
<path stroke-width="1" d="M10.219,1.688c-4.471,0-8.094,3.623-8.094,8.094s3.623,8.094,8.094,8.094s8.094-3.623,8.094-8.094S14.689,1.688,10.219,1.688 M10.219,17.022c-3.994,0-7.242-3.247-7.242-7.241c0-3.994,3.248-7.242,7.242-7.242c3.994,0,7.241,3.248,7.241,7.242C17.46,13.775,14.213,17.022,10.219,17.022 M15.099,7.03c-0.167-0.167-0.438-0.167-0.604,0.002L9.062,12.48l-2.269-2.277c-0.166-0.167-0.437-0.167-0.603,0c-0.166,0.166-0.168,0.437-0.002,0.603l2.573,2.578c0.079,0.08,0.188,0.125,0.3,0.125s0.222-0.045,0.303-0.125l5.736-5.751C15.268,7.466,15.265,7.196,15.099,7.03"></path>
</svg>
<div class="flex-1 space-y-1">
<p class="text-base leading-6 font-medium text-gray-700 dark:text-gray-400" x-text="title"></p>
<p class="text-sm leading-5 text-gray-600 dark:text-gray-400" x-text="message"></p>
</div>
<svg class="flex-shrink-0 h-5 w-5 text-gray-400 cursor-pointer" x-on:click="isShow = false" stroke="currentColor" viewBox="0 0 20 20">
<path stroke-width="1.2" d="M15.898,4.045c-0.271-0.272-0.713-0.272-0.986,0l-4.71,4.711L5.493,4.045c-0.272-0.272-0.714-0.272-0.986,0s-0.272,0.714,0,0.986l4.709,4.711l-4.71,4.711c-0.272,0.271-0.272,0.713,0,0.986c0.136,0.136,0.314,0.203,0.492,0.203c0.179,0,0.357-0.067,0.493-0.203l4.711-4.711l4.71,4.711c0.137,0.136,0.314,0.203,0.494,0.203c0.178,0,0.355-0.067,0.492-0.203c0.273-0.273,0.273-0.715,0-0.986l-4.711-4.711l4.711-4.711C16.172,4.759,16.172,4.317,15.898,4.045z"></path>
</svg>
</div>
</div>
<div class="w-full mt-12 p-3 dark:bg-gray-900 border dark:border-gray-800 rounded shadow">
<div class="border-b bg-white-200 dark:bg-gray-900 dark:border-gray-800 p-3">
<h5 class="font-bold uppercase dark:text-gray-600">Nodes</h5>
</div>
<br>
<div class="relative mt-1 ">
<div class="absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none">
<svg class="w-5 h-5 text-gray-500 dark:text-gray-400" fill="currentColor" viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd"
d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z"
clip-rule="evenodd"></path>
</svg>
</div>
<input
x-ref="searchField"
x-model="search"
x-on:click="viewPage(0)"
x-on:keydown.window.prevent.slash=" viewPage(0), $refs.searchField.focus()"
placeholder="Search..."
type="search"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full pl-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
/>
</div>
<div class="overflow-auto">
<table class="min-w-full bg-white-200 dark:bg-gray-900">
<thead class="bg-slate-200 dark:bg-gray-800 dark:text-white">
<tr>
<th class="w-1/3 text-left py-3 px-4 uppercase font-semibold text-sm">Address</th>
<th class="w-1/3 text-left py-3 px-4 uppercase font-semibold text-sm">PeerID</th>
<th class="w-1/3 text-left py-3 px-4 uppercase font-semibold text-sm">Hostname</th>
<th class="w-1/3 text-left py-3 px-4 uppercase font-semibold text-sm">OS</th>
<th class="w-1/3 text-left py-3 px-4 uppercase font-semibold text-sm">Architecture</th>
<th class="w-1/3 text-left py-3 px-4 uppercase font-semibold text-sm">Version</th>
<th class="w-1/3 text-left py-3 px-4 uppercase font-semibold text-sm"></th>
</tr>
</thead>
<tbody class="text-black dark:text-white">
<template x-for="(d, index) in filtered" :key="index">
<tr
x-bind:class="d.Online ? 'bg-lime-100 dark:bg-lime-800' : 'bg-stone-200 dark:bg-stone-800'"
>
<td x-text="d.Address" class="text-left py-3 px-4"></td>
<td x-text="d.PeerID" class="text-left py-3 px-4"></td>
<td x-text="d.Hostname" class="text-left py-3 px-4"></td>
<td x-text="d.OS" class="text-left py-3 px-4"></td>
<td x-text="d.Arch" class="text-left py-3 px-4"></td>
<td x-text="d.Version" class="text-left py-3 px-4"></td>
<!-- Action menu -->
<td class="text-left py-3 px-4" >
<button
class="px-3 py-2 rounded"
type="button"
x-on:click="deleteItem(d.Address)"
>
<i class="fa-solid fa-trash-can"></i>
</button>
</td>
</tr>
</template>
</tbody>
</table>
<!--Pagination Buttons-->
<div
class="w-full md:w-1/2 mx-auto py-6 flex justify-between items-center"
x-show="pageCount() > 1"
>
<!--First Button-->
<button
x-on:click="viewPage(0)"
:disabled="pageNumber==0"
:class="{ 'disabled cursor-not-allowed text-gray-600' : pageNumber==0 }"
>
<svg
class="h-8 w-8 text-indigo-600"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polygon points="19 20 9 12 19 4 19 20"></polygon>
<line x1="5" y1="19" x2="5" y2="5"></line>
</svg>
</button>
<!--Previous Button-->
<button
x-on:click="prevPage"
:disabled="pageNumber==0"
:class="{ 'disabled cursor-not-allowed text-gray-600' : pageNumber==0 }"
>
<svg
class="h-8 w-8 text-indigo-600"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="15 18 9 12 15 6"></polyline>
</svg>
</button>
<!-- Display page numbers -->
<template x-for="(page,index) in pages()" :key="page">
<button
class="px-3 py-2 rounded"
:class="{ 'bg-indigo-600 text-white font-bold' : page === pageNumber }"
type="button"
x-on:click="viewPage(page)"
>
<span x-text="page" class="dark:text-white"></span>
</button>
</template>
<!--Next Button-->
<button
x-on:click="nextPage"
:disabled="pageNumber >= pageCount() -1"
:class="{ 'disabled cursor-not-allowed text-gray-600' : pageNumber >= pageCount() -1 }"
>
<svg
class="h-8 w-8 text-indigo-600"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="9 18 15 12 9 6"></polyline>
</svg>
</button>
<!--Last Button-->
<button
x-on:click="viewPage(Math.ceil(total/size)-1)"
:disabled="pageNumber >= pageCount() -1"
:class="{ 'disabled cursor-not-allowed text-gray-600' : pageNumber >= pageCount() -1 }"
>
<svg
class="h-8 w-8 text-indigo-600"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polygon points="5 4 15 12 5 20 5 4"></polygon>
<line x1="19" y1="5" x2="19" y2="19"></line>
</svg>
</button>
</div>
<!-- /Pagination Buttons-->
</div>
</div>
</div>
</div>
<!--END Node Container-->
<!-- Blockchain Container-->
<div class="container w-full mx-auto pt-20" x-show="page === '#blockchain'">
<div class="w-full px-4 md:px-0 md:mt-8 mb-16 text-gray-800 leading-normal">
<div class="w-full mt-12 p-3 dark:bg-gray-900 bg-white-100 border dark:border-gray-800 rounded shadow"
x-data="blockchain()"
x-init="$interval(updateItems, 5500)"
>
<div class="border-b border-gray-800 p-3">
<h5 class="font-bold uppercase text-gray-600">Blockchain </h5>
<h5 class="font-bold uppercase text-gray-600">
<span class="mt-1 text-xs px-2 py-1 font-semibold leading-tight text-slate-700 bg-slate-100 rounded-full dark:bg-slate-700 dark:text-slate-100"><i class="fa-solid fa-hashtag fa-fw mr-3"></i> <span x-text="blockchain.Index"></span> </span>
<span class="mt-1 text-xs px-2 py-1 font-semibold leading-tight text-slate-700 bg-slate-100 rounded-full dark:bg-slate-700 dark:text-slate-100"><i class="fa-solid fa-clock fa-fw mr-3"></i> <span x-text="blockchain.Timestamp"></span> </span>
<span class="mt-1 text-xs px-2 py-1 font-semibold leading-tight text-slate-700 bg-slate-100 rounded-full dark:bg-slate-700 dark:text-slate-100"><i class="fa-solid fa-dice-d20 fa-fw mr-3"></i> <span x-text="blockchain.Hash"></span> </span>
<span class="mt-1 text-xs px-2 py-1 font-semibold leading-tight text-slate-700 bg-slate-100 rounded-full dark:bg-slate-700 dark:text-slate-100"><i class="fa-solid fa-link fa-fw mr-3"></i> <span x-text="blockchain.PrevHash"></span> </span>
</div>
<section class="flex justify-center mt-10">
<div class="bg-white-300 py-1 w-11/12 rounded border-b-4 border-red-400 flex dark:text-white">
<pre class="overflow-auto"><code class="overflow-auto" x-html="syntaxHighlight(JSON.stringify(blockchain, null, 2))"></code></pre>
</div>
</section>
</div>
</div>
</div>
<!--END Blockchain Container-->
<!-- DNS Container-->
<div class="container w-full mx-auto pt-20" x-show="page === '#dns'">
<div class="px-4 md:px-0 md:mt-8 mb-20 leading-normal " >
<div class="relative " >
<a class="mr-2 bg-sky-300 dark:bg-sky-600 text-white p-2 rounded leading-none items-center absolute top-10 right-1 " href="https://mudler.github.io/edgevpn/docs/concepts/overview/dns/" target=_blank>
<i class="fa-solid fa-book fa-fw pr-1"></i> DNS Documentation
</a>
</div>
</div>
<div class="w-full px-4 md:px-0 md:mt-8 mb-16 text-gray-800 leading-normal"
x-data="dns()"
x-init="$interval(updateItems, 1500)"
>
<!-- Notification toast -->
<div
x-show="open"
class="w-96 p-4 rounded h-32 fixed bottom-4 right-4 transform-gpu transition-transform duration-400 ease"
x-class="success ? 'bg-green-500' : 'bg-red-500'"
x-transition:enter-start="opacity-0 translate-y-full"
x-transition:enter-end="opacity-100 translate-y-0"
x-transition:leave-start="translate-y-0"
x-transition:leave="transition transform ease-in duration-300"
x-transition:leave-end="opacity-0 translate-y-full"
>
<div class="bg-white border-gray-300 dark:border-slate-800 dark:bg-slate-900 border p-3 flex items-start shadow-lg rounded-md space-x-2">
<svg class="flex-shrink-0 h-6 w-6 text-green-400" stroke="currentColor" viewBox="0 0 20 20">
<path stroke-width="1" d="M10.219,1.688c-4.471,0-8.094,3.623-8.094,8.094s3.623,8.094,8.094,8.094s8.094-3.623,8.094-8.094S14.689,1.688,10.219,1.688 M10.219,17.022c-3.994,0-7.242-3.247-7.242-7.241c0-3.994,3.248-7.242,7.242-7.242c3.994,0,7.241,3.248,7.241,7.242C17.46,13.775,14.213,17.022,10.219,17.022 M15.099,7.03c-0.167-0.167-0.438-0.167-0.604,0.002L9.062,12.48l-2.269-2.277c-0.166-0.167-0.437-0.167-0.603,0c-0.166,0.166-0.168,0.437-0.002,0.603l2.573,2.578c0.079,0.08,0.188,0.125,0.3,0.125s0.222-0.045,0.303-0.125l5.736-5.751C15.268,7.466,15.265,7.196,15.099,7.03"></path>
</svg>
<div class="flex-1 space-y-1">
<p class="text-base leading-6 font-medium text-gray-700 dark:text-gray-400" x-text="title"></p>
<p class="text-sm leading-5 text-gray-600 dark:text-gray-400" x-text="message"></p>
</div>
<svg class="flex-shrink-0 h-5 w-5 text-gray-400 cursor-pointer" x-on:click="isShow = false" stroke="currentColor" viewBox="0 0 20 20">
<path stroke-width="1.2" d="M15.898,4.045c-0.271-0.272-0.713-0.272-0.986,0l-4.71,4.711L5.493,4.045c-0.272-0.272-0.714-0.272-0.986,0s-0.272,0.714,0,0.986l4.709,4.711l-4.71,4.711c-0.272,0.271-0.272,0.713,0,0.986c0.136,0.136,0.314,0.203,0.492,0.203c0.179,0,0.357-0.067,0.493-0.203l4.711-4.711l4.71,4.711c0.137,0.136,0.314,0.203,0.494,0.203c0.178,0,0.355-0.067,0.492-0.203c0.273-0.273,0.273-0.715,0-0.986l-4.711-4.711l4.711-4.711C16.172,4.759,16.172,4.317,15.898,4.045z"></path>
</svg>
</div>
</div>
<div class="w-full mt-12 p-3 dark:bg-gray-900 border dark:border-gray-800 rounded shadow">
<div class="border-b bg-white-200 dark:bg-gray-900 dark:border-gray-800 p-3">
<h5 class="font-bold uppercase dark:text-gray-600">DNS</h5>
</div>
<br>
<div class="relative mt-1 ">
<div class="absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none">
<svg class="w-5 h-5 text-gray-500 dark:text-gray-400" fill="currentColor" viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd"
d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z"
clip-rule="evenodd"></path>
</svg>
</div>
<input
x-ref="searchField"
x-model="search"
x-on:click="viewPage(0)"
x-on:keydown.window.prevent.slash=" viewPage(0), $refs.searchField.focus()"
placeholder="Search..."
type="search"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full pl-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
/>
</div>
<div class="overflow-auto">
<table class="min-w-full bg-white-200 dark:bg-gray-900">
<thead class="bg-slate-200 dark:bg-gray-800 dark:text-white">
<tr>
<th class="w-1/3 text-left py-3 px-4 uppercase font-semibold text-sm">Regex</th>
<th class="w-1/3 text-left py-3 px-4 uppercase font-semibold text-sm">Records</th>
<th class="w-1/3 text-left py-3 px-4 uppercase font-semibold text-sm"></th>
</tr>
</thead>
<tbody class="text-black dark:text-white">
<template x-for="(d, index) in filtered" :key="index">
<tr
>
<td x-text="d.Regex" class="text-left py-3 px-4"></td>
<td x-text="JSON.stringify(d.Records)" class="text-left py-3 px-4"></td>
<!-- Action menu -->
<td class="text-left py-3 px-4" >
<button
class="px-3 py-2 rounded"
type="button"
x-on:click="deleteItem(d.Regex)"
>
<i class="fa-solid fa-trash-can"></i>
</button>
</td>
</tr>
</template>
</tbody>
</table>
<!--Pagination Buttons-->
<div
class="w-full md:w-1/2 mx-auto py-6 flex justify-between items-center"
x-show="pageCount() > 1"
>
<!--First Button-->
<button
x-on:click="viewPage(0)"
:disabled="pageNumber==0"
:class="{ 'disabled cursor-not-allowed text-gray-600' : pageNumber==0 }"
>
<svg
class="h-8 w-8 text-indigo-600"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polygon points="19 20 9 12 19 4 19 20"></polygon>
<line x1="5" y1="19" x2="5" y2="5"></line>
</svg>
</button>
<!--Previous Button-->
<button
x-on:click="prevPage"
:disabled="pageNumber==0"
:class="{ 'disabled cursor-not-allowed text-gray-600' : pageNumber==0 }"
>
<svg
class="h-8 w-8 text-indigo-600"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="15 18 9 12 15 6"></polyline>
</svg>
</button>
<!-- Display page numbers -->
<template x-for="(page,index) in pages()" :key="page">
<button
class="px-3 py-2 rounded"
:class="{ 'bg-indigo-600 text-white font-bold' : page === pageNumber }"
type="button"
x-on:click="viewPage(page)"
>
<span x-text="page" class="dark:text-white"></span>
</button>
</template>
<!--Next Button-->
<button
x-on:click="nextPage"
:disabled="pageNumber >= pageCount() -1"
:class="{ 'disabled cursor-not-allowed text-gray-600' : pageNumber >= pageCount() -1 }"
>
<svg
class="h-8 w-8 text-indigo-600"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="9 18 15 12 9 6"></polyline>
</svg>
</button>
<!--Last Button-->
<button
x-on:click="viewPage(Math.ceil(total/size)-1)"
:disabled="pageNumber >= pageCount() -1"
:class="{ 'disabled cursor-not-allowed text-gray-600' : pageNumber >= pageCount() -1 }"
>
<svg
class="h-8 w-8 text-indigo-600"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polygon points="5 4 15 12 5 20 5 4"></polygon>
<line x1="19" y1="5" x2="19" y2="19"></line>
</svg>
</button>
</div>
<!-- /Pagination Buttons-->
</div>
</div>
</div>
</div>
<!--END DNS Container-->
<!-- Services Container-->
<div class="container w-full mx-auto pt-20" x-show="page === '#services'">
<div class="px-4 md:px-0 md:mt-8 mb-20 leading-normal " >
<div class="relative " >
<a class="mr-2 bg-sky-300 dark:bg-sky-600 text-white p-2 rounded leading-none items-center absolute top-10 right-1 " href="https://mudler.github.io/edgevpn/docs/concepts/overview/services/" target=_blank>
<i class="fa-solid fa-book fa-fw pr-1"></i> Tunnelling Documentation
</a>
</div>
</div>
<div class="w-full px-4 md:px-0 md:mt-8 mb-16 text-gray-800 leading-normal"
x-data="services()"
x-init="$interval(updateItems, 1500)"
>
<!-- Notification toast -->
<div
x-show="open"
class="w-96 p-4 rounded h-32 fixed bottom-4 right-4 transform-gpu transition-transform duration-400 ease"
x-class="success ? 'bg-green-500' : 'bg-red-500'"
x-transition:enter-start="opacity-0 translate-y-full"
x-transition:enter-end="opacity-100 translate-y-0"
x-transition:leave-start="translate-y-0"
x-transition:leave="transition transform ease-in duration-300"
x-transition:leave-end="opacity-0 translate-y-full"
>
<div class="bg-white border-gray-300 dark:border-slate-800 dark:bg-slate-900 border p-3 flex items-start shadow-lg rounded-md space-x-2">
<svg class="flex-shrink-0 h-6 w-6 text-green-400" stroke="currentColor" viewBox="0 0 20 20">
<path stroke-width="1" d="M10.219,1.688c-4.471,0-8.094,3.623-8.094,8.094s3.623,8.094,8.094,8.094s8.094-3.623,8.094-8.094S14.689,1.688,10.219,1.688 M10.219,17.022c-3.994,0-7.242-3.247-7.242-7.241c0-3.994,3.248-7.242,7.242-7.242c3.994,0,7.241,3.248,7.241,7.242C17.46,13.775,14.213,17.022,10.219,17.022 M15.099,7.03c-0.167-0.167-0.438-0.167-0.604,0.002L9.062,12.48l-2.269-2.277c-0.166-0.167-0.437-0.167-0.603,0c-0.166,0.166-0.168,0.437-0.002,0.603l2.573,2.578c0.079,0.08,0.188,0.125,0.3,0.125s0.222-0.045,0.303-0.125l5.736-5.751C15.268,7.466,15.265,7.196,15.099,7.03"></path>
</svg>
<div class="flex-1 space-y-1">
<p class="text-base leading-6 font-medium text-gray-700 dark:text-gray-400" x-text="title"></p>
<p class="text-sm leading-5 text-gray-600 dark:text-gray-400" x-text="message"></p>
</div>
<svg class="flex-shrink-0 h-5 w-5 text-gray-400 cursor-pointer" x-on:click="isShow = false" stroke="currentColor" viewBox="0 0 20 20">
<path stroke-width="1.2" d="M15.898,4.045c-0.271-0.272-0.713-0.272-0.986,0l-4.71,4.711L5.493,4.045c-0.272-0.272-0.714-0.272-0.986,0s-0.272,0.714,0,0.986l4.709,4.711l-4.71,4.711c-0.272,0.271-0.272,0.713,0,0.986c0.136,0.136,0.314,0.203,0.492,0.203c0.179,0,0.357-0.067,0.493-0.203l4.711-4.711l4.71,4.711c0.137,0.136,0.314,0.203,0.494,0.203c0.178,0,0.355-0.067,0.492-0.203c0.273-0.273,0.273-0.715,0-0.986l-4.711-4.711l4.711-4.711C16.172,4.759,16.172,4.317,15.898,4.045z"></path>
</svg>
</div>
</div>
<div class="w-full mt-12 p-3 dark:bg-gray-900 border dark:border-gray-800 rounded shadow">
<div class="border-b bg-white-200 dark:bg-gray-900 dark:border-gray-800 p-3">
<h5 class="font-bold uppercase dark:text-gray-600">TCP Tunnels</h5>
</div>
<br>
<div class="relative mt-1 ">
<div class="absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none">
<svg class="w-5 h-5 text-gray-500 dark:text-gray-400" fill="currentColor" viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd"
d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z"
clip-rule="evenodd"></path>
</svg>
</div>
<input
x-ref="searchField"
x-model="search"
x-on:click="viewPage(0)"
x-on:keydown.window.prevent.slash=" viewPage(0), $refs.searchField.focus()"
placeholder="Search..."
type="search"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full pl-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
/>
</div>
<div class="overflow-auto">
<table class="min-w-full bg-white-200 dark:bg-gray-900">
<thead class="bg-slate-200 dark:bg-gray-800 dark:text-white">
<tr>
<th class="w-1/3 text-left py-3 px-4 uppercase font-semibold text-sm">Name</th>
<th class="w-1/3 text-left py-3 px-4 uppercase font-semibold text-sm">PeerID</th>
</tr>
</thead>
<tbody class="text-black dark:text-white">
<template x-for="(d, index) in filtered" :key="index">
<tr
>
<td x-text="d.Name" class="text-left py-3 px-4"></td>
<td x-text="d.PeerID" class="text-left py-3 px-4"></td>
</tr>
</template>
</tbody>
</table>
<!--Pagination Buttons-->
<div
class="w-full md:w-1/2 mx-auto py-6 flex justify-between items-center"
x-show="pageCount() > 1"
>
<!--First Button-->
<button
x-on:click="viewPage(0)"
:disabled="pageNumber==0"
:class="{ 'disabled cursor-not-allowed text-gray-600' : pageNumber==0 }"
>
<svg
class="h-8 w-8 text-indigo-600"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polygon points="19 20 9 12 19 4 19 20"></polygon>
<line x1="5" y1="19" x2="5" y2="5"></line>
</svg>
</button>
<!--Previous Button-->
<button
x-on:click="prevPage"
:disabled="pageNumber==0"
:class="{ 'disabled cursor-not-allowed text-gray-600' : pageNumber==0 }"
>
<svg
class="h-8 w-8 text-indigo-600"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="15 18 9 12 15 6"></polyline>
</svg>
</button>
<!-- Display page numbers -->
<template x-for="(page,index) in pages()" :key="page">
<button
class="px-3 py-2 rounded"
:class="{ 'bg-indigo-600 text-white font-bold' : page === pageNumber }"
type="button"
x-on:click="viewPage(page)"
>
<span x-text="page" class="dark:text-white"></span>
</button>
</template>
<!--Next Button-->
<button
x-on:click="nextPage"
:disabled="pageNumber >= pageCount() -1"
:class="{ 'disabled cursor-not-allowed text-gray-600' : pageNumber >= pageCount() -1 }"
>
<svg
class="h-8 w-8 text-indigo-600"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="9 18 15 12 9 6"></polyline>
</svg>
</button>
<!--Last Button-->
<button
x-on:click="viewPage(Math.ceil(total/size)-1)"
:disabled="pageNumber >= pageCount() -1"
:class="{ 'disabled cursor-not-allowed text-gray-600' : pageNumber >= pageCount() -1 }"
>
<svg
class="h-8 w-8 text-indigo-600"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polygon points="5 4 15 12 5 20 5 4"></polygon>
<line x1="19" y1="5" x2="19" y2="19"></line>
</svg>
</button>
</div>
<!-- /Pagination Buttons-->
</div>
</div>
</div>
<hr class="border-b-2 border-gray-600 my-8 mx-4">
<div class="px-4 md:px-0 md:mt-8 mb-20 leading-normal " >
<div class="relative " >
<a class="mr-2 bg-sky-300 dark:bg-sky-600 text-white p-2 rounded leading-none items-center absolute top-10 right-1 " href="https://mudler.github.io/edgevpn/docs/concepts/overview/files/" target=_blank>
<i class="fa-solid fa-book fa-fw pr-1"></i> Files Documentation
</a>
</div>
</div>
<div class="w-full px-4 md:px-0 md:mt-8 mb-16 text-gray-800 leading-normal"
x-data="files()"
x-init="$interval(updateItems, 1500)"
>
<!-- Notification toast -->
<div
x-show="open"
class="w-96 p-4 rounded h-32 fixed bottom-4 right-4 transform-gpu transition-transform duration-400 ease"
x-class="success ? 'bg-green-500' : 'bg-red-500'"
x-transition:enter-start="opacity-0 translate-y-full"
x-transition:enter-end="opacity-100 translate-y-0"
x-transition:leave-start="translate-y-0"
x-transition:leave="transition transform ease-in duration-300"
x-transition:leave-end="opacity-0 translate-y-full"
>
<div class="bg-white border-gray-300 dark:border-slate-800 dark:bg-slate-900 border p-3 flex items-start shadow-lg rounded-md space-x-2">
<svg class="flex-shrink-0 h-6 w-6 text-green-400" stroke="currentColor" viewBox="0 0 20 20">
<path stroke-width="1" d="M10.219,1.688c-4.471,0-8.094,3.623-8.094,8.094s3.623,8.094,8.094,8.094s8.094-3.623,8.094-8.094S14.689,1.688,10.219,1.688 M10.219,17.022c-3.994,0-7.242-3.247-7.242-7.241c0-3.994,3.248-7.242,7.242-7.242c3.994,0,7.241,3.248,7.241,7.242C17.46,13.775,14.213,17.022,10.219,17.022 M15.099,7.03c-0.167-0.167-0.438-0.167-0.604,0.002L9.062,12.48l-2.269-2.277c-0.166-0.167-0.437-0.167-0.603,0c-0.166,0.166-0.168,0.437-0.002,0.603l2.573,2.578c0.079,0.08,0.188,0.125,0.3,0.125s0.222-0.045,0.303-0.125l5.736-5.751C15.268,7.466,15.265,7.196,15.099,7.03"></path>
</svg>
<div class="flex-1 space-y-1">
<p class="text-base leading-6 font-medium text-gray-700 dark:text-gray-400" x-text="title"></p>
<p class="text-sm leading-5 text-gray-600 dark:text-gray-400" x-text="message"></p>
</div>
<svg class="flex-shrink-0 h-5 w-5 text-gray-400 cursor-pointer" x-on:click="isShow = false" stroke="currentColor" viewBox="0 0 20 20">
<path stroke-width="1.2" d="M15.898,4.045c-0.271-0.272-0.713-0.272-0.986,0l-4.71,4.711L5.493,4.045c-0.272-0.272-0.714-0.272-0.986,0s-0.272,0.714,0,0.986l4.709,4.711l-4.71,4.711c-0.272,0.271-0.272,0.713,0,0.986c0.136,0.136,0.314,0.203,0.492,0.203c0.179,0,0.357-0.067,0.493-0.203l4.711-4.711l4.71,4.711c0.137,0.136,0.314,0.203,0.494,0.203c0.178,0,0.355-0.067,0.492-0.203c0.273-0.273,0.273-0.715,0-0.986l-4.711-4.711l4.711-4.711C16.172,4.759,16.172,4.317,15.898,4.045z"></path>
</svg>
</div>
</div>
<div class="w-full mt-12 p-3 dark:bg-gray-900 border dark:border-gray-800 rounded shadow">
<div class="border-b bg-white-200 dark:bg-gray-900 dark:border-gray-800 p-3">
<h5 class="font-bold uppercase dark:text-gray-600">Files</h5>
</div>
<br>
<div class="relative mt-1 ">
<div class="absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none">
<svg class="w-5 h-5 text-gray-500 dark:text-gray-400" fill="currentColor" viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd"
d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z"
clip-rule="evenodd"></path>
</svg>
</div>
<input
x-ref="searchField"
x-model="search"
x-on:click="viewPage(0)"
x-on:keydown.window.prevent.slash=" viewPage(0), $refs.searchField.focus()"
placeholder="Search..."
type="search"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full pl-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
/>
</div>
<div class="overflow-auto">
<table class="min-w-full bg-white-200 dark:bg-gray-900">
<thead class="bg-slate-200 dark:bg-gray-800 dark:text-white">
<tr>
<th class="w-1/3 text-left py-3 px-4 uppercase font-semibold text-sm">Name</th>
<th class="w-1/3 text-left py-3 px-4 uppercase font-semibold text-sm">PeerID</th>
</tr>
</thead>
<tbody class="text-black dark:text-white">
<template x-for="(d, index) in filtered" :key="index">
<tr
>
<td x-text="d.Name" class="text-left py-3 px-4"></td>
<td x-text="d.PeerID" class="text-left py-3 px-4"></td>
</tr>
</template>
</tbody>
</table>
<!--Pagination Buttons-->
<div
class="w-full md:w-1/2 mx-auto py-6 flex justify-between items-center"
x-show="pageCount() > 1"
>
<!--First Button-->
<button
x-on:click="viewPage(0)"
:disabled="pageNumber==0"
:class="{ 'disabled cursor-not-allowed text-gray-600' : pageNumber==0 }"
>
<svg
class="h-8 w-8 text-indigo-600"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polygon points="19 20 9 12 19 4 19 20"></polygon>
<line x1="5" y1="19" x2="5" y2="5"></line>
</svg>
</button>
<!--Previous Button-->
<button
x-on:click="prevPage"
:disabled="pageNumber==0"
:class="{ 'disabled cursor-not-allowed text-gray-600' : pageNumber==0 }"
>
<svg
class="h-8 w-8 text-indigo-600"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="15 18 9 12 15 6"></polyline>
</svg>
</button>
<!-- Display page numbers -->
<template x-for="(page,index) in pages()" :key="page">
<button
class="px-3 py-2 rounded"
:class="{ 'bg-indigo-600 text-white font-bold' : page === pageNumber }"
type="button"
x-on:click="viewPage(page)"
>
<span x-text="page" class="dark:text-white"></span>
</button>
</template>
<!--Next Button-->
<button
x-on:click="nextPage"
:disabled="pageNumber >= pageCount() -1"
:class="{ 'disabled cursor-not-allowed text-gray-600' : pageNumber >= pageCount() -1 }"
>
<svg
class="h-8 w-8 text-indigo-600"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="9 18 15 12 9 6"></polyline>
</svg>
</button>
<!--Last Button-->
<button
x-on:click="viewPage(Math.ceil(total/size)-1)"
:disabled="pageNumber >= pageCount() -1"
:class="{ 'disabled cursor-not-allowed text-gray-600' : pageNumber >= pageCount() -1 }"
>
<svg
class="h-8 w-8 text-indigo-600"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polygon points="5 4 15 12 5 20 5 4"></polygon>
<line x1="19" y1="5" x2="19" y2="19"></line>
</svg>
</button>
</div>
<!-- /Pagination Buttons-->
</div>
</div>
</div>
</div>
<!--END Services Container-->
<!-- Peers Container-->
<div class="container w-full mx-auto pt-20" x-show="page === '#peers'">
<div class="w-full px-4 md:px-0 md:mt-8 mb-16 text-gray-800 leading-normal"
x-data="nodes()"
x-init="$interval(updateItems, 1500)"
>
<!-- Notification toast -->
<div
x-show="open"
class="w-96 p-4 rounded h-32 fixed bottom-4 right-4 transform-gpu transition-transform duration-400 ease"
x-class="success ? 'bg-green-500' : 'bg-red-500'"
x-transition:enter-start="opacity-0 translate-y-full"
x-transition:enter-end="opacity-100 translate-y-0"
x-transition:leave-start="translate-y-0"
x-transition:leave="transition transform ease-in duration-300"
x-transition:leave-end="opacity-0 translate-y-full"
>
<div class="bg-white border-gray-300 dark:border-slate-800 dark:bg-slate-900 border p-3 flex items-start shadow-lg rounded-md space-x-2">
<svg class="flex-shrink-0 h-6 w-6 text-green-400" stroke="currentColor" viewBox="0 0 20 20">
<path stroke-width="1" d="M10.219,1.688c-4.471,0-8.094,3.623-8.094,8.094s3.623,8.094,8.094,8.094s8.094-3.623,8.094-8.094S14.689,1.688,10.219,1.688 M10.219,17.022c-3.994,0-7.242-3.247-7.242-7.241c0-3.994,3.248-7.242,7.242-7.242c3.994,0,7.241,3.248,7.241,7.242C17.46,13.775,14.213,17.022,10.219,17.022 M15.099,7.03c-0.167-0.167-0.438-0.167-0.604,0.002L9.062,12.48l-2.269-2.277c-0.166-0.167-0.437-0.167-0.603,0c-0.166,0.166-0.168,0.437-0.002,0.603l2.573,2.578c0.079,0.08,0.188,0.125,0.3,0.125s0.222-0.045,0.303-0.125l5.736-5.751C15.268,7.466,15.265,7.196,15.099,7.03"></path>
</svg>
<div class="flex-1 space-y-1">
<p class="text-base leading-6 font-medium text-gray-700 dark:text-gray-400" x-text="title"></p>
<p class="text-sm leading-5 text-gray-600 dark:text-gray-400" x-text="message"></p>
</div>
<svg class="flex-shrink-0 h-5 w-5 text-gray-400 cursor-pointer" x-on:click="isShow = false" stroke="currentColor" viewBox="0 0 20 20">
<path stroke-width="1.2" d="M15.898,4.045c-0.271-0.272-0.713-0.272-0.986,0l-4.71,4.711L5.493,4.045c-0.272-0.272-0.714-0.272-0.986,0s-0.272,0.714,0,0.986l4.709,4.711l-4.71,4.711c-0.272,0.271-0.272,0.713,0,0.986c0.136,0.136,0.314,0.203,0.492,0.203c0.179,0,0.357-0.067,0.493-0.203l4.711-4.711l4.71,4.711c0.137,0.136,0.314,0.203,0.494,0.203c0.178,0,0.355-0.067,0.492-0.203c0.273-0.273,0.273-0.715,0-0.986l-4.711-4.711l4.711-4.711C16.172,4.759,16.172,4.317,15.898,4.045z"></path>
</svg>
</div>
</div>
<div class="w-full mt-12 p-3 dark:bg-gray-900 border dark:border-gray-800 rounded shadow">
<div class="border-b bg-white-200 dark:bg-gray-900 dark:border-gray-800 p-3">
<h5 class="font-bold uppercase dark:text-gray-600">Nodes</h5>
</div>
<br>
<div class="relative mt-1 ">
<div class="absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none">
<svg class="w-5 h-5 text-gray-500 dark:text-gray-400" fill="currentColor" viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd"
d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z"
clip-rule="evenodd"></path>
</svg>
</div>
<input
x-ref="searchField"
x-model="search"
x-on:click="viewPage(0)"
x-on:keydown.window.prevent.slash=" viewPage(0), $refs.searchField.focus()"
placeholder="Search..."
type="search"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full pl-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
/>
</div>
<div class="overflow-auto">
<table class="min-w-full bg-white-200 dark:bg-gray-900">
<thead class="bg-slate-200 dark:bg-gray-800 dark:text-white">
<tr>
<th class="w-1/3 text-left py-3 px-4 uppercase font-semibold text-sm">PeerID</th>
<th class="w-1/3 text-left py-3 px-4 uppercase font-semibold text-sm">Online</th>
</tr>
</thead>
<tbody class="text-black dark:text-white">
<template x-for="(d, index) in filtered" :key="index">
<tr
>
<td x-text="d.ID" class="text-left py-3 px-4"></td>
<td x-text="d.Online" class="text-left py-3 px-4"></td>
</tr>
</template>
</tbody>
</table>
<!--Pagination Buttons-->
<div
class="w-full md:w-1/2 mx-auto py-6 flex justify-between items-center"
x-show="pageCount() > 1"
>
<!--First Button-->
<button
x-on:click="viewPage(0)"
:disabled="pageNumber==0"
:class="{ 'disabled cursor-not-allowed text-gray-600' : pageNumber==0 }"
>
<svg
class="h-8 w-8 text-indigo-600"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polygon points="19 20 9 12 19 4 19 20"></polygon>
<line x1="5" y1="19" x2="5" y2="5"></line>
</svg>
</button>
<!--Previous Button-->
<button
x-on:click="prevPage"
:disabled="pageNumber==0"
:class="{ 'disabled cursor-not-allowed text-gray-600' : pageNumber==0 }"
>
<svg
class="h-8 w-8 text-indigo-600"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="15 18 9 12 15 6"></polyline>
</svg>
</button>
<!-- Display page numbers -->
<template x-for="(page,index) in pages()" :key="page">
<button
class="px-3 py-2 rounded"
:class="{ 'bg-indigo-600 text-white font-bold' : page === pageNumber }"
type="button"
x-on:click="viewPage(page)"
>
<span x-text="page" class="dark:text-white"></span>
</button>
</template>
<!--Next Button-->
<button
x-on:click="nextPage"
:disabled="pageNumber >= pageCount() -1"
:class="{ 'disabled cursor-not-allowed text-gray-600' : pageNumber >= pageCount() -1 }"
>
<svg
class="h-8 w-8 text-indigo-600"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="9 18 15 12 9 6"></polyline>
</svg>
</button>
<!--Last Button-->
<button
x-on:click="viewPage(Math.ceil(total/size)-1)"
:disabled="pageNumber >= pageCount() -1"
:class="{ 'disabled cursor-not-allowed text-gray-600' : pageNumber >= pageCount() -1 }"
>
<svg
class="h-8 w-8 text-indigo-600"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polygon points="5 4 15 12 5 20 5 4"></polygon>
<line x1="19" y1="5" x2="19" y2="19"></line>
</svg>
</button>
</div>
<!-- /Pagination Buttons-->
</div>
</div>
</div>
<hr class="border-b-2 border-gray-600 my-8 mx-4">
<div class="w-full px-4 md:px-0 md:mt-8 mb-16 text-gray-800 leading-normal"
x-data="peerstore()"
x-init="$interval(updateItems, 1500)"
>
<!-- Notification toast -->
<div
x-show="open"
class="w-96 p-4 rounded h-32 fixed bottom-4 right-4 transform-gpu transition-transform duration-400 ease"
x-class="success ? 'bg-green-500' : 'bg-red-500'"
x-transition:enter-start="opacity-0 translate-y-full"
x-transition:enter-end="opacity-100 translate-y-0"
x-transition:leave-start="translate-y-0"
x-transition:leave="transition transform ease-in duration-300"
x-transition:leave-end="opacity-0 translate-y-full"
>
<div class="bg-white border-gray-300 dark:border-slate-800 dark:bg-slate-900 border p-3 flex items-start shadow-lg rounded-md space-x-2">
<svg class="flex-shrink-0 h-6 w-6 text-green-400" stroke="currentColor" viewBox="0 0 20 20">
<path stroke-width="1" d="M10.219,1.688c-4.471,0-8.094,3.623-8.094,8.094s3.623,8.094,8.094,8.094s8.094-3.623,8.094-8.094S14.689,1.688,10.219,1.688 M10.219,17.022c-3.994,0-7.242-3.247-7.242-7.241c0-3.994,3.248-7.242,7.242-7.242c3.994,0,7.241,3.248,7.241,7.242C17.46,13.775,14.213,17.022,10.219,17.022 M15.099,7.03c-0.167-0.167-0.438-0.167-0.604,0.002L9.062,12.48l-2.269-2.277c-0.166-0.167-0.437-0.167-0.603,0c-0.166,0.166-0.168,0.437-0.002,0.603l2.573,2.578c0.079,0.08,0.188,0.125,0.3,0.125s0.222-0.045,0.303-0.125l5.736-5.751C15.268,7.466,15.265,7.196,15.099,7.03"></path>
</svg>
<div class="flex-1 space-y-1">
<p class="text-base leading-6 font-medium text-gray-700 dark:text-gray-400" x-text="title"></p>
<p class="text-sm leading-5 text-gray-600 dark:text-gray-400" x-text="message"></p>
</div>
<svg class="flex-shrink-0 h-5 w-5 text-gray-400 cursor-pointer" x-on:click="isShow = false" stroke="currentColor" viewBox="0 0 20 20">
<path stroke-width="1.2" d="M15.898,4.045c-0.271-0.272-0.713-0.272-0.986,0l-4.71,4.711L5.493,4.045c-0.272-0.272-0.714-0.272-0.986,0s-0.272,0.714,0,0.986l4.709,4.711l-4.71,4.711c-0.272,0.271-0.272,0.713,0,0.986c0.136,0.136,0.314,0.203,0.492,0.203c0.179,0,0.357-0.067,0.493-0.203l4.711-4.711l4.71,4.711c0.137,0.136,0.314,0.203,0.494,0.203c0.178,0,0.355-0.067,0.492-0.203c0.273-0.273,0.273-0.715,0-0.986l-4.711-4.711l4.711-4.711C16.172,4.759,16.172,4.317,15.898,4.045z"></path>
</svg>
</div>
</div>
<div class="w-full mt-12 p-3 dark:bg-gray-900 border dark:border-gray-800 rounded shadow">
<div class="border-b bg-white-200 dark:bg-gray-900 dark:border-gray-800 p-3">
<h5 class="font-bold uppercase dark:text-gray-600">Peer store</h5>
</div>
<br>
<div class="relative mt-1 ">
<div class="absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none">
<svg class="w-5 h-5 text-gray-500 dark:text-gray-400" fill="currentColor" viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd"
d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z"
clip-rule="evenodd"></path>
</svg>
</div>
<input
x-ref="searchField"
x-model="search"
x-on:click="viewPage(0)"
x-on:keydown.window.prevent.slash=" viewPage(0), $refs.searchField.focus()"
placeholder="Search..."
type="search"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full pl-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
/>
</div>
<div class="overflow-auto">
<table class="min-w-full bg-white-200 dark:bg-gray-900">
<thead class="bg-slate-200 dark:bg-gray-800 dark:text-white">
<tr>
<th class="w-1/3 text-left py-3 px-4 uppercase font-semibold text-sm">PeerID</th>
</tr>
</thead>
<tbody class="text-black dark:text-white">
<template x-for="(d, index) in filtered" :key="index">
<tr
>
<td x-text="d.ID" class="text-left py-3 px-4"></td>
</tr>
</template>
</tbody>
</table>
<!--Pagination Buttons-->
<div
class="w-full md:w-1/2 mx-auto py-6 flex justify-between items-center"
x-show="pageCount() > 1"
>
<!--First Button-->
<button
x-on:click="viewPage(0)"
:disabled="pageNumber==0"
:class="{ 'disabled cursor-not-allowed text-gray-600' : pageNumber==0 }"
>
<svg
class="h-8 w-8 text-indigo-600"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polygon points="19 20 9 12 19 4 19 20"></polygon>
<line x1="5" y1="19" x2="5" y2="5"></line>
</svg>
</button>
<!--Previous Button-->
<button
x-on:click="prevPage"
:disabled="pageNumber==0"
:class="{ 'disabled cursor-not-allowed text-gray-600' : pageNumber==0 }"
>
<svg
class="h-8 w-8 text-indigo-600"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="15 18 9 12 15 6"></polyline>
</svg>
</button>
<!-- Display page numbers -->
<template x-for="(page,index) in pages()" :key="page">
<button
class="px-3 py-2 rounded"
:class="{ 'bg-indigo-600 text-white font-bold' : page === pageNumber }"
type="button"
x-on:click="viewPage(page)"
>
<span x-text="page" class="dark:text-white"></span>
</button>
</template>
<!--Next Button-->
<button
x-on:click="nextPage"
:disabled="pageNumber >= pageCount() -1"
:class="{ 'disabled cursor-not-allowed text-gray-600' : pageNumber >= pageCount() -1 }"
>
<svg
class="h-8 w-8 text-indigo-600"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="9 18 15 12 9 6"></polyline>
</svg>
</button>
<!--Last Button-->
<button
x-on:click="viewPage(Math.ceil(total/size)-1)"
:disabled="pageNumber >= pageCount() -1"
:class="{ 'disabled cursor-not-allowed text-gray-600' : pageNumber >= pageCount() -1 }"
>
<svg
class="h-8 w-8 text-indigo-600"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polygon points="5 4 15 12 5 20 5 4"></polygon>
<line x1="19" y1="5" x2="19" y2="19"></line>
</svg>
</button>
</div>
<!-- /Pagination Buttons-->
</div>
</div>
</div>
</div>
</div>
<!--END Peers Container-->
<!-- Index Container-->
<div class="container w-full mx-auto pt-20" x-show="page === ''">
<div class="w-full px-4 md:px-0 md:mt-8 mb-16 text-gray-800 leading-normal"
x-data="summary()"
x-init="$interval(updateItems, 1500); initChart()"
>
<!--Summary Content-->
<div class="flex flex-wrap">
<div class="w-full md:w-1/2 xl:w-1/3 p-3">
<!--Metric Card-->
<div class="dark:bg-gray-900 bg-white-100 dark:border-gray-800 dark:border-gray-600 border-b-4 rounded shadow p-2">
<div class="flex flex-row items-center">
<div class="flex-shrink pr-4">
<div class="rounded p-3 bg-emerald-600"><i class="fas fa-network-wired fa-2x fa-fw fa-inverse"></i></div>
</div>
<div class="flex-1 text-right md:text-center">
<h5 class="font-bold uppercase text-gray-400">VPN Nodes</h5>
<h3 class="font-bold text-3xl text-gray-600" x-text="summary.Machines"></h3>
</div>
</div>
</div>
<!--/Metric Card-->
</div>
<div class="w-full md:w-1/2 xl:w-1/3 p-3">
<!--Metric Card-->
<div class="dark:bg-gray-900 bg-white-100 dark:border-gray-800 dark:border-gray-600 border-b-4 rounded shadow p-2">
<div class="flex flex-row items-center">
<div class="flex-shrink pr-4">
<div class="rounded p-3 bg-cyan-600"><i class="fa-solid fa-circle-nodes fa-2x fa-fw fa-inverse"></i></div>
</div>
<div class="flex-1 text-right md:text-center">
<h5 class="font-bold uppercase text-gray-400">P2P peers</h5>
<h3 class="font-bold text-3xl text-gray-600" x-text="summary.Peers"></h3>
</div>
</div>
</div>
<!--/Metric Card-->
</div>
<div class="w-full md:w-1/2 xl:w-1/3 p-3">
<!--Metric Card-->
<div class="dark:bg-gray-900 bg-white-100 dark:border-gray-800 dark:border-gray-600 border-b-4 rounded shadow p-2">
<div class="flex flex-row items-center">
<div class="flex-shrink pr-4">
<div class="rounded p-3 bg-yellow-600"><i class="fa-solid fa-box-archive fa-2x fa-fw fa-inverse"></i></div>
</div>
<div class="flex-1 text-right md:text-center">
<h5 class="font-bold uppercase text-gray-400">Files</h5>
<h3 class="font-bold text-3xl text-gray-600" x-text="summary.Files"></h3>
</div>
</div>
</div>
<!--/Metric Card-->
</div>
<div class="w-full md:w-1/2 xl:w-1/3 p-3">
<!--Metric Card-->
<div class="dark:bg-gray-900 bg-white-100 dark:border-gray-800 dark:border-gray-600 border-b-4 rounded shadow p-2">
<div class="flex flex-row items-center">
<div class="flex-shrink pr-4">
<div class="rounded p-3 bg-pink-600"><i class="fas fa-users fa-2x fa-fw fa-inverse"></i></div>
</div>
<div class="flex-1 text-right md:text-center">
<h5 class="font-bold uppercase text-gray-400">Users</h5>
<h3 class="font-bold text-3xl text-gray-600" x-text="summary.Users"></h3>
</div>
</div>
</div>
<!--/Metric Card-->
</div>
<div class="w-full md:w-1/2 xl:w-1/3 p-3">
<!--Metric Card-->
<div class="dark:bg-gray-900 bg-white-100 dark:border-gray-800 dark:border-gray-600 border-b-4 rounded shadow p-2">
<div class="flex flex-row items-center">
<div class="flex-shrink pr-4">
<div class="rounded p-3 bg-indigo-600"><i class="fas fa-dice-d20 fa-2x fa-fw fa-inverse"></i></div>
</div>
<div class="flex-1 text-right md:text-center">
<h5 class="font-bold uppercase text-gray-400">Blockchain index</h5>
<h3 class="font-bold text-3xl text-gray-600" x-text="summary.BlockChain"></h3>
</div>
</div>
</div>
<!--/Metric Card-->
</div>
<div class="w-full md:w-1/2 xl:w-1/3 p-3">
<!--Metric Card-->
<div class="dark:bg-gray-900 bg-white-100 dark:border-gray-800 dark:border-gray-600 border-b-4 rounded shadow p-2">
<div class="flex flex-row items-center">
<div class="flex-shrink pr-4">
<div class="rounded p-3 bg-sky-600"><i class="fa-solid fa-car-tunnel fa-2x fa-fw fa-inverse"></i></div>
</div>
<div class="flex-1 text-right md:text-center">
<h5 class="font-bold uppercase text-gray-400">Services</h5>
<h3 class="font-bold text-3xl text-gray-600" x-text="summary.Services"></h3>
</div>
</div>
</div>
<!--/Metric Card-->
</div>
<div class="w-full md:w-1/2 xl:w-1/3 p-3">
<!--Metric Card-->
<div class="dark:bg-gray-900 bg-white-100 dark:border-gray-800 dark:border-gray-600 border-b-4 rounded shadow p-2">
<div class="flex flex-row items-center">
<div class="flex-shrink pr-4">
<div class="rounded p-3 bg-cyan-600"><i class="fas fa-download fa-2x fa-fw fa-inverse"></i></div>
</div>
<div class="flex-1 text-right md:text-center">
<h5 class="font-bold uppercase text-gray-400">Total downloaded</h5>
<h3 class="font-bold text-3xl text-gray-600" x-text="bytesToSize(metrics.TotalIn)"></h3>
</div>
</div>
</div>
<!--/Metric Card-->
</div>
<div class="w-full md:w-1/2 xl:w-1/3 p-3 text-gray-800 ">
<div class="dark:bg-gray-900 rounded shadow dark:border-gray-600 border-b-4 ">
<div class="bg-white-200 dark:bg-gray-900 p-3">
<h5 class="font-bold float-left uppercase text-gray-400">
<span class="rounded p-1 bg-teal-600"><i class="fa-duotone fa-right-left fa-fw fa-inverse"></i></span>
Bandwidth
</h5>
<h5 class="font-bold uppercase float-right text-gray-600">
<span class="rounded p-1 bg-cyan-600"><i class="fas fa-arrow-down fa-fw fa-inverse"></i></span>
<span x-text="bytesToSize(metrics.RateIn)"></span>
<span class="rounded p-1 bg-amber-600"><i class="fas fa-arrow-up fa-fw fa-inverse"></i></span>
<span x-text="bytesToSize(metrics.RateOut)"></span>
</h5>
</div>
<br>
<div class=" relative mt-1 ">
<!-- Network stat Card-->
<div class="dark:bg-gray-900 bg-white-100 rounded shadow p-2">
<div class="flex flex-row items-center">
<div class="flex-1 text-right md:text-center">
<div x-ref="chart"></div>
</div>
</div>
</div>
<!--/Network stat Card-->
</div>
</div>
</div>
<div class="w-full md:w-1/2 xl:w-1/3 p-3">
<!--Metric Card-->
<div class="dark:bg-gray-900 bg-white-100 dark:border-gray-800 dark:border-gray-600 border-b-4 rounded shadow p-2">
<div class="flex flex-row items-center">
<div class="flex-shrink pr-4">
<div class="rounded p-3 bg-amber-600"><i class="fas fa-upload fa-2x fa-fw fa-inverse"></i></div>
</div>
<div class="flex-1 text-right md:text-center">
<h5 class="font-bold uppercase text-gray-400">Total uploaded</h5>
<h3 class="font-bold text-3xl text-gray-600" x-text="bytesToSize(metrics.TotalOut)"></h3>
</div>
</div>
</div>
<!--/Metric Card-->
</div>
<!--Divider-->
<hr class="border-b-2 border-gray-600 my-8 mx-4">
<div class="w-full px-4 md:px-0 md:mt-8 mb-16 text-gray-800 leading-normal"
x-data="users()"
x-init="$interval(updateItems, 1500)"
>
<!-- Notification toast -->
<div
x-show="open"
class="w-96 p-4 rounded h-32 fixed bottom-4 right-4 transform-gpu transition-transform duration-400 ease"
x-class="success ? 'bg-green-500' : 'bg-red-500'"
x-transition:enter-start="opacity-0 translate-y-full"
x-transition:enter-end="opacity-100 translate-y-0"
x-transition:leave-start="translate-y-0"
x-transition:leave="transition transform ease-in duration-300"
x-transition:leave-end="opacity-0 translate-y-full"
>
<div class="bg-white border-gray-300 dark:border-slate-800 dark:bg-slate-900 border p-3 flex items-start shadow-lg rounded-md space-x-2">
<svg class="flex-shrink-0 h-6 w-6 text-green-400" stroke="currentColor" viewBox="0 0 20 20">
<path stroke-width="1" d="M10.219,1.688c-4.471,0-8.094,3.623-8.094,8.094s3.623,8.094,8.094,8.094s8.094-3.623,8.094-8.094S14.689,1.688,10.219,1.688 M10.219,17.022c-3.994,0-7.242-3.247-7.242-7.241c0-3.994,3.248-7.242,7.242-7.242c3.994,0,7.241,3.248,7.241,7.242C17.46,13.775,14.213,17.022,10.219,17.022 M15.099,7.03c-0.167-0.167-0.438-0.167-0.604,0.002L9.062,12.48l-2.269-2.277c-0.166-0.167-0.437-0.167-0.603,0c-0.166,0.166-0.168,0.437-0.002,0.603l2.573,2.578c0.079,0.08,0.188,0.125,0.3,0.125s0.222-0.045,0.303-0.125l5.736-5.751C15.268,7.466,15.265,7.196,15.099,7.03"></path>
</svg>
<div class="flex-1 space-y-1">
<p class="text-base leading-6 font-medium text-gray-700 dark:text-gray-400" x-text="title"></p>
<p class="text-sm leading-5 text-gray-600 dark:text-gray-400" x-text="message"></p>
</div>
<svg class="flex-shrink-0 h-5 w-5 text-gray-400 cursor-pointer" x-on:click="isShow = false" stroke="currentColor" viewBox="0 0 20 20">
<path stroke-width="1.2" d="M15.898,4.045c-0.271-0.272-0.713-0.272-0.986,0l-4.71,4.711L5.493,4.045c-0.272-0.272-0.714-0.272-0.986,0s-0.272,0.714,0,0.986l4.709,4.711l-4.71,4.711c-0.272,0.271-0.272,0.713,0,0.986c0.136,0.136,0.314,0.203,0.492,0.203c0.179,0,0.357-0.067,0.493-0.203l4.711-4.711l4.71,4.711c0.137,0.136,0.314,0.203,0.494,0.203c0.178,0,0.355-0.067,0.492-0.203c0.273-0.273,0.273-0.715,0-0.986l-4.711-4.711l4.711-4.711C16.172,4.759,16.172,4.317,15.898,4.045z"></path>
</svg>
</div>
</div>
<div class="w-full mt-12 p-3 dark:bg-gray-900 border dark:border-gray-800 rounded shadow">
<div class="border-b bg-white-200 dark:bg-gray-900 dark:border-gray-800 p-3">
<h5 class="font-bold uppercase dark:text-gray-600">Connected users</h5>
</div>
<br>
<div class="relative mt-1 ">
<div class="absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none">
<svg class="w-5 h-5 text-gray-500 dark:text-gray-400" fill="currentColor" viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd"
d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z"
clip-rule="evenodd"></path>
</svg>
</div>
<input
x-ref="searchField"
x-model="search"
x-on:click="viewPage(0)"
x-on:keydown.window.prevent.slash=" viewPage(0), $refs.searchField.focus()"
placeholder="Search..."
type="search"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full pl-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
/>
</div>
<div class="overflow-auto">
<table class="min-w-full bg-white-200 dark:bg-gray-900">
<thead class="bg-slate-200 dark:bg-gray-800 dark:text-white">
<tr>
<th class="w-1/3 text-left py-3 px-4 uppercase font-semibold text-sm">PeerID</th>
<th class="w-1/3 text-left py-3 px-4 uppercase font-semibold text-sm">Time</th>
</tr>
</thead>
<tbody class="text-black dark:text-white">
<template x-for="(d, index) in filtered" :key="index">
<tr
>
<td x-text="d.PeerID" class="text-left py-3 px-4"></td>
<td x-text="d.Timestamp" class="text-left py-3 px-4"></td>
</tr>
</template>
</tbody>
</table>
<!--Pagination Buttons-->
<div
class="w-full md:w-1/2 mx-auto py-6 flex justify-between items-center"
x-show="pageCount() > 1"
>
<!--First Button-->
<button
x-on:click="viewPage(0)"
:disabled="pageNumber==0"
:class="{ 'disabled cursor-not-allowed text-gray-600' : pageNumber==0 }"
>
<svg
class="h-8 w-8 text-indigo-600"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polygon points="19 20 9 12 19 4 19 20"></polygon>
<line x1="5" y1="19" x2="5" y2="5"></line>
</svg>
</button>
<!--Previous Button-->
<button
x-on:click="prevPage"
:disabled="pageNumber==0"
:class="{ 'disabled cursor-not-allowed text-gray-600' : pageNumber==0 }"
>
<svg
class="h-8 w-8 text-indigo-600"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="15 18 9 12 15 6"></polyline>
</svg>
</button>
<!-- Display page numbers -->
<template x-for="(page,index) in pages()" :key="page">
<button
class="px-3 py-2 rounded"
:class="{ 'bg-indigo-600 text-white font-bold' : page === pageNumber }"
type="button"
x-on:click="viewPage(page)"
>
<span x-text="page" class="dark:text-white"></span>
</button>
</template>
<!--Next Button-->
<button
x-on:click="nextPage"
:disabled="pageNumber >= pageCount() -1"
:class="{ 'disabled cursor-not-allowed text-gray-600' : pageNumber >= pageCount() -1 }"
>
<svg
class="h-8 w-8 text-indigo-600"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="9 18 15 12 9 6"></polyline>
</svg>
</button>
<!--Last Button-->
<button
x-on:click="viewPage(Math.ceil(total/size)-1)"
:disabled="pageNumber >= pageCount() -1"
:class="{ 'disabled cursor-not-allowed text-gray-600' : pageNumber >= pageCount() -1 }"
>
<svg
class="h-8 w-8 text-indigo-600"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polygon points="5 4 15 12 5 20 5 4"></polygon>
<line x1="19" y1="5" x2="19" y2="19"></line>
</svg>
</button>
</div>
<!-- /Pagination Buttons-->
</div>
</div>
</div>
<!--/ Summary Content-->
</div>
</div>
<!--END Index /container-->
<footer class="dark:bg-gray-900 border-t dark:border-gray-400 shadow">
<div class="container max-w-md mx-auto flex py-8">
<div class="w-full mx-auto flex flex-wrap">
<div class="flex w-full md:w-1/2 ">
<div class="px-8">
<h3 class="font-bold font-bold dark:text-gray-100">About</h3>
<p class="py-4 text-gray-600 text-sm">
<strong>EdgeVPN</strong> by <a href="https://github.com/mudler/edgevpn">Ettore Di Giacinto</a>.<br>
License <a href="https://github.com/mudler/edgevpn/blob/master/LICENSE">Apache v2</a>. <br>
Logo originally made by <a href="https://www.flaticon.com/authors/uniconlabs" title="Uniconlabs">Uniconlabs</a> from <a href="https://www.flaticon.com/" title="Flaticon">www.flaticon.com</a>
</p>
</div>
</div>
<div class="flex w-full md:w-1/2">
<div class="px-8">
<h3 class="font-bold font-bold dark:text-gray-100">Links</h3>
<ul class="list-reset items-center text-sm pt-3">
<li>
<a class="inline-block text-gray-600 no-underline hover:text-black dark:hover:text-gray-100 hover:text-underline py-1" href="https://github.com/mudler/edgevpn" target=_blank><i class="fa-brands fa-github-alt fa-fw mr-3"></i>Github</a>
</li>
<li>
<a class="inline-block text-gray-600 no-underline hover:text-black dark:hover:text-gray-100 hover:text-underline py-1" href="https://mudler.github.io/edgevpn/docs" target=_blank><i class="fas fa-book fa-fw mr-3"></i>Documentation</a>
</li>
<li>
<a class="inline-block text-gray-600 no-underline hover:text-black dark:hover:text-gray-100 hover:text-underline py-1" href="https://github.com/mudler/edgevpn/issues/new" target=_blank><i class="fas fa-bug fa-fw mr-3"></i>Report issue</a>
</li>
</ul>
</div>
</div>
</div>
</div>
</footer>
<script>
function bytesToSize(bytes, decimals = 2) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
var s = sizes[i]
if (!sizes[i]) {
s = "B"
}
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + s;
}
function scaleSize(bytes, scale = 1, decimals = 2) {
if (bytes === 0) return '0';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
//const i = Math.floor(Math.log(bytes) / Math.log(k));
//console.log(i)
return parseFloat((bytes / Math.pow(k, scale)).toFixed(dm));
}
const range = (start, stop, step) => Array.from({ length: (stop - start) / step + 1}, (_, i) => start + (i * step));
function calcPages(n, total, size) {
start = 1;
if (n > 5 ){
start = n - 5
}
end = Math.ceil(total / size);
if (end - n > 5 ){
end = n + 5
// Math.ceil(this.total / this.size) - 10
}
return range(start, end ,1)
}
function filter(obj, key) {
const start = obj.pageNumber * obj.size,
end = start + obj.size;
if (obj.search === "") {
obj.total = obj.data.length;
return obj.data.slice(start, end);
}
//Return the total results of the filters
obj.total = obj.data.filter((item) => {
return item[key]
.toLowerCase()
.includes(obj.search.toLowerCase());
}).length;
//Return the filtered data
return obj.data
.filter((item) => {
return item[key]
.toLowerCase()
.includes(obj.search.toLowerCase());
})
.slice(start, end);
}
function endRes(obj) {
let resultsOnPage = (obj.pageNumber + 1) * obj.size;
if (resultsOnPage <= obj.total) {
return resultsOnPage;
}
return obj.total;
}
function sortData(key, order = "asc") {
return function innerSort(a, b) {
if (!a.hasOwnProperty(key) || !b.hasOwnProperty(key)) {
return 0;
}
const varA = typeof a[key] === "string" ? a[key].toUpperCase() : a[key];
const varB = typeof b[key] === "string" ? b[key].toUpperCase() : b[key];
let comparison = 0;
if (varA > varB) {
comparison = 1;
} else if (varA < varB) {
comparison = -1;
}
return order === "desc" ? comparison * -1 : comparison;
}
}
function machines(){
return {
data: [
],
open: false,
title: "",
message: "",
success: false,
openToast(title, message, success) {
this.title = title
this.message = message
this.success = success
this.open = true
setTimeout(() => {
this.open = false
}, 5000)
},
deleteItem(item) {
fetch('/api/ledger/machines/'.concat("",item), {
method: 'DELETE',
});
this.openToast("Delete", "Announcing deletion to the blockchain, please wait", true);
},
updateItems() {
fetch('/api/machines')
.then(response => response.json())
.then(data => {
data.sort(sortData("Address","asc"));
this.data = data;
} )
},
search: "",
pageNumber: 0,
size: 10,
total: "",
get filtered() {return filter(this, "Address") },
//Create array of all pages (for loop to display page numbers)
pages() {
return calcPages(this.pageNumber, this.total, this.size)
},
//Next Page
nextPage() {
this.pageNumber++;
},
//Previous Page
prevPage() {
this.pageNumber--;
},
//Total number of pages
pageCount() {
return Math.ceil(this.total / this.size);
},
//Return the start range of the paginated results
startResults() {
return this.pageNumber * this.size + 1;
},
//Return the end range of the paginated results
endResults() {
return endRes(this)
},
//Link to navigate to page
viewPage(index) {
this.pageNumber = index;
},
};
}
function peerstore(){
return {
data: [
],
open: false,
title: "",
message: "",
success: false,
openToast(title, message, success) {
this.title = title
this.message = message
this.success = success
this.open = true
setTimeout(() => {
this.open = false
}, 5000)
},
updateItems() {
fetch('/api/peerstore')
.then(response => response.json())
.then(data => {
data.sort(sortData("ID","asc"));
this.data = data;
} )
},
search: "",
pageNumber: 0,
size: 10,
total: "",
get filtered() {return filter(this, "ID") },
//Create array of all pages (for loop to display page numbers)
pages() {
return calcPages(this.pageNumber, this.total, this.size)
},
//Next Page
nextPage() {
this.pageNumber++;
},
//Previous Page
prevPage() {
this.pageNumber--;
},
//Total number of pages
pageCount() {
return Math.ceil(this.total / this.size);
},
//Return the start range of the paginated results
startResults() {
return this.pageNumber * this.size + 1;
},
//Return the end range of the paginated results
endResults() {
return endRes(this)
},
//Link to navigate to page
viewPage(index) {
this.pageNumber = index;
},
};
}
function nodes(){
return {
data: [
],
open: false,
title: "",
message: "",
success: false,
openToast(title, message, success) {
this.title = title
this.message = message
this.success = success
this.open = true
setTimeout(() => {
this.open = false
}, 5000)
},
updateItems() {
fetch('/api/nodes')
.then(response => response.json())
.then(data => {
data.sort(sortData("ID","asc"));
this.data = data;
} )
},
search: "",
pageNumber: 0,
size: 10,
total: "",
get filtered() {return filter(this, "ID") },
//Create array of all pages (for loop to display page numbers)
pages() {
return calcPages(this.pageNumber, this.total, this.size)
},
//Next Page
nextPage() {
this.pageNumber++;
},
//Previous Page
prevPage() {
this.pageNumber--;
},
//Total number of pages
pageCount() {
return Math.ceil(this.total / this.size);
},
//Return the start range of the paginated results
startResults() {
return this.pageNumber * this.size + 1;
},
//Return the end range of the paginated results
endResults() {
return endRes(this)
},
//Link to navigate to page
viewPage(index) {
this.pageNumber = index;
},
};
}
function users(){
return {
data: [
],
open: false,
title: "",
message: "",
success: false,
openToast(title, message, success) {
this.title = title
this.message = message
this.success = success
this.open = true
setTimeout(() => {
this.open = false
}, 5000)
},
updateItems() {
fetch('/api/users')
.then(response => response.json())
.then(data => {
data.sort(sortData("ID","asc"));
this.data = data;
} )
},
search: "",
pageNumber: 0,
size: 10,
total: "",
get filtered() {return filter(this, "ID") },
//Create array of all pages (for loop to display page numbers)
pages() {
return calcPages(this.pageNumber, this.total, this.size)
},
//Next Page
nextPage() {
this.pageNumber++;
},
//Previous Page
prevPage() {
this.pageNumber--;
},
//Total number of pages
pageCount() {
return Math.ceil(this.total / this.size);
},
//Return the start range of the paginated results
startResults() {
return this.pageNumber * this.size + 1;
},
//Return the end range of the paginated results
endResults() {
return endRes(this)
},
//Link to navigate to page
viewPage(index) {
this.pageNumber = index;
},
};
}
function dns(){
return {
data: [
],
open: false,
title: "",
message: "",
success: false,
openToast(title, message, success) {
this.title = title
this.message = message
this.success = success
this.open = true
setTimeout(() => {
this.open = false
}, 5000)
},
deleteItem(item) {
fetch('/api/ledger/dns/'.concat("",item), {
method: 'DELETE',
});
this.openToast("Delete", "Announcing deletion to the blockchain, please wait", true);
},
updateItems() {
fetch('/api/dns')
.then(response => response.json())
.then(data => {
data.sort(sortData("Regex","asc"));
this.data = data;
} )
},
search: "",
pageNumber: 0,
size: 10,
total: "",
get filtered() {return filter(this, "Regex") },
//Create array of all pages (for loop to display page numbers)
pages() {
return calcPages(this.pageNumber, this.total, this.size)
},
//Next Page
nextPage() {
this.pageNumber++;
},
//Previous Page
prevPage() {
this.pageNumber--;
},
//Total number of pages
pageCount() {
return Math.ceil(this.total / this.size);
},
//Return the start range of the paginated results
startResults() {
return this.pageNumber * this.size + 1;
},
//Return the end range of the paginated results
endResults() {
return endRes(this)
},
//Link to navigate to page
viewPage(index) {
this.pageNumber = index;
},
};
}
function services(){
return {
data: [
],
open: false,
title: "",
message: "",
success: false,
openToast(title, message, success) {
this.title = title
this.message = message
this.success = success
this.open = true
setTimeout(() => {
this.open = false
}, 5000)
},
updateItems() {
fetch('/api/services')
.then(response => response.json())
.then(data => {
data.sort(sortData("Name","asc"));
this.data = data;
} )
},
search: "",
pageNumber: 0,
size: 10,
total: "",
get filtered() {return filter(this, "Name") },
//Create array of all pages (for loop to display page numbers)
pages() {
return calcPages(this.pageNumber, this.total, this.size)
},
//Next Page
nextPage() {
this.pageNumber++;
},
//Previous Page
prevPage() {
this.pageNumber--;
},
//Total number of pages
pageCount() {
return Math.ceil(this.total / this.size);
},
//Return the start range of the paginated results
startResults() {
return this.pageNumber * this.size + 1;
},
//Return the end range of the paginated results
endResults() {
return endRes(this)
},
//Link to navigate to page
viewPage(index) {
this.pageNumber = index;
},
};
}
function files(){
return {
data: [
],
open: false,
title: "",
message: "",
success: false,
openToast(title, message, success) {
this.title = title
this.message = message
this.success = success
this.open = true
setTimeout(() => {
this.open = false
}, 5000)
},
updateItems() {
fetch('/api/files')
.then(response => response.json())
.then(data => {
data.sort(sortData("Name","asc"));
this.data = data;
} )
},
search: "",
pageNumber: 0,
size: 10,
total: "",
get filtered() {return filter(this, "Name") },
//Create array of all pages (for loop to display page numbers)
pages() {
return calcPages(this.pageNumber, this.total, this.size)
},
//Next Page
nextPage() {
this.pageNumber++;
},
//Previous Page
prevPage() {
this.pageNumber--;
},
//Total number of pages
pageCount() {
return Math.ceil(this.total / this.size);
},
//Return the start range of the paginated results
startResults() {
return this.pageNumber * this.size + 1;
},
//Return the end range of the paginated results
endResults() {
return endRes(this)
},
//Link to navigate to page
viewPage(index) {
this.pageNumber = index;
},
};
}
function blockchain(){
return {
blockchain: {},
updateItems() {
fetch('/api/blockchain')
.then(response => response.json())
.then(data => this.blockchain = data )
}
};
}
function syntaxHighlight(json) {
json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
var cls = 'number';
if (/^"/.test(match)) {
if (/:$/.test(match)) {
cls = 'key';
} else {
cls = 'string';
}
} else if (/true|false/.test(match)) {
cls = 'boolean';
} else if (/null/.test(match)) {
cls = 'null';
}
return '<span class="' + cls + '">' + match + '</span>';
});
}
function summary(){
return {
summary: {},
chart: null,
metrics: {},
initChart() {
this.chart = new ApexCharts(this.$refs.chart, {
chart: {
type: 'area',
height: 80,
sparkline: {
enabled: true
},
dropShadow: {
enabled: true,
top: 1,
left: 1,
blur: 2,
opacity: 0.2,
}
},
dataLabels: {
enabled: false,
},
series: [
{
name: "Download",
data: [],
},
{
name: "Upload",
data: [],
},
],
stroke: {
curve: 'smooth'
},
markers: {
size: 0
},
grid: {
padding: {
top: 20,
bottom: 10,
}
},
colors: ['#247BA0', '#FF1654' ],
noData: {
text: "Loading...",
},
xaxis: {
labels: {
show: false,
},
},
tooltip: {
x: {
show: false
},
y: {
formatter: function(value, { series, seriesIndex, dataPointIndex, w }) {
return value + ' KB/s'
}
}
}
})
this.chart.render()
},
updateItems() {
fetch('/api/summary')
.then(response => response.json())
.then(data => this.summary = data )
fetch('/api/metrics')
.then(response => response.json())
.then(data => {
this.metrics = data;
this.chart.appendData([{ data: [ scaleSize(data.RateIn) ] }, { data: [ scaleSize(data.RateOut) ] } ]);
} )
}
};
}
/*Toggle dropdown list*/
/*https://gist.github.com/slavapas/593e8e50cf4cc16ac972afcbad4f70c8*/
var navMenuDiv = document.getElementById("nav-content");
var navMenu = document.getElementById("nav-toggle");
document.onclick = check;
function check(e){
var target = (e && e.target) || (event && event.srcElement);
//Nav Menu
if (!checkParent(target, navMenuDiv)) {
// click NOT on the menu
if (checkParent(target, navMenu)) {
// click on the link
if (navMenuDiv.classList.contains("hidden")) {
navMenuDiv.classList.remove("hidden");
} else {navMenuDiv.classList.add("hidden");}
} else {
// click both outside link and outside menu, hide menu
navMenuDiv.classList.add("hidden");
}
}
}
function checkParent(t, elm) {
while(t.parentNode) {
if( t == elm ) {return true;}
t = t.parentNode;
}
return false;
}
</script>
</body>
</html>