@ -3,6 +3,7 @@
< head >
< title > WireGuard< / title >
< meta charset = "utf-8" / >
< link href = "./css/app.css" rel = "stylesheet" >
< link rel = "manifest" href = "./manifest.json" >
< link rel = "icon" type = "image/png" href = "./img/favicon.png" >
@ -17,29 +18,57 @@
< / style >
< body class = "bg-gray-50 dark:bg-neutral-800" >
< div id = "app" >
< div v-cloak class = "container mx-auto max-w-3xl px-5 md:px-0" >
< div v-cloak class = "container mx-auto max-w-3xl px-3 md:px-0 mt-4 xs:mt-6" >
< div v-if = "authenticated === true" >
< span v-if = "requiresPassword"
class="text-sm text-gray-400 dark:text-neutral-400 mb-10 mr-2 mt-3 cursor-pointer hover:underline float-right"
@click="logout">
{{$t("logout")}}
< svg class = "h-3 inline" xmlns = "http://www.w3.org/2000/svg" fill = "none" viewBox = "0 0 24 24"
stroke="currentColor">
< path stroke-linecap = "round" stroke-linejoin = "round" stroke-width = "2"
d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1" />
< / svg >
< / span >
< h1 class = "text-4xl dark:text-neutral-200 font-medium mt-2 mb-2" >
< img src = "./img/logo.png" width = "32" class = "inline align-middle dark:bg" / >
< span class = "align-middle" > WireGuard< / span >
< / h1 >
< h2 class = "text-sm text-gray-400 dark:text-neutral-400 mb-10" > < / h2 >
< div class = "flex flex-col-reverse xxs:flex-row flex-auto items-center items-end gap-3" >
< h1 class = "text-4xl dark:text-neutral-200 font-medium flex-grow self-start mb-4" >
< img src = "./img/logo.png" width = "32" class = "inline align-middle dark:bg mr-2" / > < span class = "align-middle" > WireGuard< / span >
< / h1 >
< div class = "flex items-center grow-0 gap-3 items-end self-end xxs:self-center" >
<!-- Dark / light theme -->
< button @ click = "toggleTheme"
class="flex items-center justify-center w-8 h-8 rounded-full bg-gray-200 hover:bg-gray-300 dark:bg-neutral-700 dark:hover:bg-neutral-600 transition" :title="$t(`theme.${uiTheme}`)">
< svg v-if = "uiTheme === 'light'" xmlns = "http://www.w3.org/2000/svg" fill = "none" viewBox = "0 0 24 24" stroke-width = "1.5" stroke = "currentColor"
class="w-5 h-5">
< path stroke-linecap = "round" stroke-linejoin = "round"
d="M12 3v2.25m6.364.386-1.591 1.591M21 12h-2.25m-.386 6.364-1.591-1.591M12 18.75V21m-4.773-4.227-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0Z" />
< / svg >
< svg v-else-if = "uiTheme === 'dark'" xmlns = "http://www.w3.org/2000/svg" fill = "none" viewBox = "0 0 24 24" stroke-width = "1.5" stroke = "currentColor"
class="w-5 h-5 text-neutral-400">
< path stroke-linecap = "round" stroke-linejoin = "round"
d="M21.752 15.002A9.72 9.72 0 0 1 18 15.75c-5.385 0-9.75-4.365-9.75-9.75 0-1.33.266-2.597.748-3.752A9.753 9.753 0 0 0 3 11.25C3 16.635 7.365 21 12.75 21a9.753 9.753 0 0 0 9.002-5.998Z" />
< / svg >
< svg v-else xmlns = "http://www.w3.org/2000/svg" fill = "currentColor" viewBox = "0 0 24 24"
class="w-5 h-5 fill-gray-600 dark:fill-neutral-400">
< path
d="M12,2.2c-5.4,0-9.8,4.4-9.8,9.8s4.4,9.8,9.8,9.8s9.8-4.4,9.8-9.8S17.4,2.2,12,2.2z M3.8,12c0-4.5,3.7-8.2,8.2-8.2v16.5C7.5,20.2,3.8,16.5,3.8,12z" />
< / svg >
< path stroke-linecap = "round" stroke-linejoin = "round"
d="M9 17.25v1.007a3 3 0 0 1-.879 2.122L7.5 21h9l-.621-.621A3 3 0 0 1 15 18.257V17.25m6-12V15a2.25 2.25 0 0 1-2.25 2.25H5.25A2.25 2.25 0 0 1 3 15V5.25m18 0A2.25 2.25 0 0 0 18.75 3H5.25A2.25 2.25 0 0 0 3 5.25m18 0V12a2.25 2.25 0 0 1-2.25 2.25H5.25A2.25 2.25 0 0 1 3 12V5.25" />
< / svg >
< / button >
<!-- Show / hide charts -->
< label v-if = "uiChartType > 0" class = "inline-flex items-center justify-center cursor-pointer w-8 h-8 rounded-full bg-gray-200 hover:bg-gray-300 dark:bg-neutral-700 dark:hover:bg-neutral-600 whitespace-nowrap transition group" :title = "$t('toggleCharts')" >
< input type = "checkbox" value = "" class = "sr-only peer" v-model = "uiShowCharts" @ change = "toggleCharts" >
< svg xmlns = "http://www.w3.org/2000/svg" fill = "none" viewBox = "0 0 24 24" stroke-width = "1.5" fill = "currentColor"
class="w-5 h-5 peer fill-gray-400 peer-checked:fill-gray-600 dark:fill-neutral-600 peer-checked:dark:fill-neutral-400 group-hover:dark:fill-neutral-500 transition">
< path
d="M18.375 2.25c-1.035 0-1.875.84-1.875 1.875v15.75c0 1.035.84 1.875 1.875 1.875h.75c1.035 0 1.875-.84 1.875-1.875V4.125c0-1.036-.84-1.875-1.875-1.875h-.75ZM9.75 8.625c0-1.036.84-1.875 1.875-1.875h.75c1.036 0 1.875.84 1.875 1.875v11.25c0 1.035-.84 1.875-1.875 1.875h-.75a1.875 1.875 0 0 1-1.875-1.875V8.625ZM3 13.125c0-1.036.84-1.875 1.875-1.875h.75c1.036 0 1.875.84 1.875 1.875v6.75c0 1.035-.84 1.875-1.875 1.875h-.75A1.875 1.875 0 0 1 3 19.875v-6.75Z" />
< / svg >
< / label >
< span v-if = "requiresPassword"
class="text-sm text-gray-400 dark:text-neutral-400 cursor-pointer hover:underline"
@click="logout">
{{$t("logout")}}
< svg class = "h-3 inline" xmlns = "http://www.w3.org/2000/svg" fill = "none" viewBox = "0 0 24 24" stroke = "currentColor" >
< path stroke-linecap = "round" stroke-linejoin = "round" stroke-width = "2"
d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1" />
< / svg >
< / span >
< / div >
< / div >
< div class = "text-sm text-gray-400 dark:text-neutral-400 mb-5" > < / div >
< div v-if = "latestRelease"
class="bg-red-800 dark:bg-red-100 p-4 text-white dark:text-red-600 text-sm font-small mb-10 rounded-md shadow-lg"
:title="`v${currentRelease} → v${latestRelease.version}`">
@ -80,20 +109,23 @@
class="relative overflow-hidden border-b last:border-b-0 border-gray-100 dark:border-neutral-600 border-solid">
<!-- Chart -->
< div class = "absolute z-0 bottom-0 left-0 right-0" style = "top: 60%;" >
< apexchart width = "100%" height = "100%" :options = "client.c hartOptions" :series = "client.transferTxSeries" >
< div v-if = "uiChartType" class = "absolute z-0 bottom-0 left-0 right-0 h-6" >
< apexchart width = "100%" height = "100%" :options = "chartOptionsTX " :series = "client.transferTxSeries" >
< / apexchart >
< / div >
< div class = "absolute z-0 top-0 left-0 right-0" style = "bottom: 60%;" >
< apexchart width = "100%" height = "100%" :options = "client.c hartOptions" :series = "client.transferRxSeries"
< div v-if = "uiChartType" class = "absolute z-0 top-0 left-0 right-0 h-6" >
< apexchart width = "100%" height = "100%" :options = "chartOptionsRX " :series = "client.transferRxSeries"
style="transform: scaleY(-1);">
< / apexchart >
< / div >
< div class = "relative p-5 z-10 flex flex-col md:flex-row justify-between" >
< div class = "flex items-center pb-2 md:pb-0" >
< div class = "h-10 w-10 mr-5 rounded-full bg-gray-50 relative" >
< svg class = "w-6 m-2 text-gray-300" xmlns = "http://www.w3.org/2000/svg"
viewBox="0 0 20 20" fill="currentColor">
< div class = "relative py-3 md:py-5 px-3 z-10 flex flex-col sm:flex-row justify-between gap-3" >
< div class = "flex gap-3 md:gap-4 w-full items-center " >
<!-- Avatar -->
< div class = "h-10 w-10 mt-2 self-start rounded-full bg-gray-50 relative" >
< svg class = "w-6 m-2 text-gray-300" xmlns = "http://www.w3.org/2000/svg" viewBox = "0 0 20 20"
fill="currentColor">
< path fill-rule = "evenodd" d = "M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z"
clip-rule="evenodd" />
< / svg >
@ -108,52 +140,26 @@
< / div >
< / div >
< div class = "flex-grow" >
<!-- Name & Info -->
< div class = "flex flex-col xxs:flex-row w-full gap-2" >
<!-- Name -->
< div class = "text-gray-700 dark:text-neutral-200 group"
:title="$t('createdOn') + dateTime(new Date(client.createdAt))">
<!-- Show -->
< input v-show = "clientEditNameId === client.id" v-model = "clientEditName"
v-on:keyup.enter="updateClientName(client, clientEditName); clientEditName = null; clientEditNameId = null;"
v-on:keyup.escape="clientEditName = null; clientEditNameId = null;"
:ref="'client-' + client.id + '-name'"
class="rounded px-1 border-2 dark:bg-neutral-700 border-gray-100 dark:border-neutral-600 focus:border-gray-200 dark:focus:border-neutral-500 dark:placeholder:text-neutral-500 outline-none w-30" />
< span v-show = "clientEditNameId !== client.id"
class="inline-block border-t-2 border-b-2 border-transparent">{{client.name}}< / span >
<!-- Edit -->
< span v-show = "clientEditNameId !== client.id"
@click="clientEditName = client.name; clientEditNameId = client.id; setTimeout(() => $refs['client-' + client.id + '-name'][0].select(), 1);"
class="cursor-pointer opacity-0 group-hover:opacity-100 transition-opacity">
< svg xmlns = "http://www.w3.org/2000/svg"
class="h-4 w-4 inline align-middle opacity-25 hover:opacity-100" fill="none"
viewBox="0 0 24 24" stroke="currentColor">
< path stroke-linecap = "round" stroke-linejoin = "round" stroke-width = "2"
d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
< / svg >
< / span >
< / div >
<!-- Info -->
< div class = "text-gray-400 dark:text-neutral-400 text-xs" >
<!-- Address -->
< span class = "group block md:inline-block pb-1 md:pb-0" >
< div class = "flex flex-col flex-grow gap-1" >
< div class = "text-gray-700 dark:text-neutral-200 group text-sm md:text-base"
:title="$t('createdOn') + dateTime(new Date(client.createdAt))">
<!-- Show -->
< input v-show = "clientEditAddress Id === client.id" v-model = "clientEditAddress "
v-on:keyup.enter="updateClientAddress(client, clientEditAddress); clientEditAddress = null; clientEditAddress Id = null;"
v-on:keyup.escape="clientEditAddress = null; clientEditAddress Id = null;"
:ref="'client-' + client.id + '-address '"
class="rounded border-2 dark:bg-neutral-700 border-gray-100 dark:border-neutral-600 focus:border-gray-200 dark:focus:border-neutral-500 outline-none w-20 text-black dark:text-neutral-300 dark:placeholder:text-neutral-500" />
< span v-show = "clientEditAddress Id !== client.id"
class="inline-block border-t-2 border-b-2 border-transparent">{{client.address }}< / span >
< input v-show = "clientEditNameId === client.id" v-model = "clientEditName"
v-on:keyup.enter="updateClientName(client, clientEditName); clientEditName = null; clientEditNameId = null;"
v-on:keyup.escape="clientEditName = null; clientEditNameId = null;"
:ref="'client-' + client.id + '-name'"
class="rounded px-1 border-2 dark:bg-neutral-700 border-gray-100 dark:border-neutral-600 focus:border-gray-200 dark:focus:border-neutral-500 dark:placeholder:text-neutral-500 outline-none w-30" />
< span v-show = "clientEditNameId !== client.id"
class="border-t-2 border-b-2 border-transparent">{{client.name}}< / span >
<!-- Edit -->
< span v-show = "clientEditAddress Id !== client.id"
@click="clientEditAddress = client.address; clientEditAddressId = client.id; setTimeout(() => $refs['client-' + client.id + '-address '][0].select(), 1);"
< span v-show = "clientEditNameId !== client.id"
@click="clientEditName = client.name; clientEditNameId = client.id; setTimeout(() => $refs['client-' + client.id + '-name'][0].select(), 1);"
class="cursor-pointer opacity-0 group-hover:opacity-100 transition-opacity">
< svg xmlns = "http://www.w3.org/2000/svg"
class="h-4 w-4 inline align-middle opacity-25 hover:opacity-100" fill="none"
@ -162,39 +168,105 @@
d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
< / svg >
< / span >
< / span >
< / div >
<!-- Address -->
< div class = " block md:inline-block pb-1 md:pb-0 text-gray-500 dark:text-neutral-400 text-xs" >
< span class = "group" >
<!-- Show -->
< input v-show = "clientEditAddressId === client.id" v-model = "clientEditAddress"
v-on:keyup.enter="updateClientAddress(client, clientEditAddress); clientEditAddress = null; clientEditAddressId = null;"
v-on:keyup.escape="clientEditAddress = null; clientEditAddressId = null;"
:ref="'client-' + client.id + '-address'"
class="rounded border-2 dark:bg-neutral-700 border-gray-100 dark:border-neutral-600 focus:border-gray-200 dark:focus:border-neutral-500 outline-none w-20 text-black dark:text-neutral-300 dark:placeholder:text-neutral-500" />
< span v-show = "clientEditAddressId !== client.id"
class="inline-block ">{{client.address}}< / span >
<!-- Edit -->
< span v-show = "clientEditAddressId !== client.id"
@click="clientEditAddress = client.address; clientEditAddressId = client.id; setTimeout(() => $refs['client-' + client.id + '-address'][0].select(), 1);"
class="cursor-pointer opacity-0 group-hover:opacity-100 transition-opacity">
< svg xmlns = "http://www.w3.org/2000/svg"
class="h-4 w-4 inline align-middle opacity-25 hover:opacity-100" fill="none"
viewBox="0 0 24 24" stroke="currentColor">
< path stroke-linecap = "round" stroke-linejoin = "round" stroke-width = "2"
d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
< / svg >
< / span >
< / span >
<!-- Inline Transfer TX -->
< span v-if = "!uiTrafficStats && client.transferTx" class = "whitespace-nowrap" :title = "$t('totalDownload') + bytes(client.transferTx)" >
·
< svg class = "align-middle h-3 inline" xmlns = "http://www.w3.org/2000/svg" viewBox = "0 0 20 20" fill = "currentColor" >
< path fill-rule = "evenodd"
d="M16.707 10.293a1 1 0 010 1.414l-6 6a1 1 0 01-1.414 0l-6-6a1 1 0 111.414-1.414L9 14.586V3a1 1 0 012 0v11.586l4.293-4.293a1 1 0 011.414 0z"
clip-rule="evenodd" />
< / svg >
{{client.transferTxCurrent | bytes}}/s
< / span >
<!-- Inline Transfer RX -->
< span v-if = "!uiTrafficStats && client.transferRx" class = "whitespace-nowrap" :title = "$t('totalUpload') + bytes(client.transferRx)" >
·
< svg class = "align-middle h-3 inline" xmlns = "http://www.w3.org/2000/svg" viewBox = "0 0 20 20" fill = "currentColor" >
< path fill-rule = "evenodd"
d="M3.293 9.707a1 1 0 010-1.414l6-6a1 1 0 011.414 0l6 6a1 1 0 01-1.414 1.414L11 5.414V17a1 1 0 11-2 0V5.414L4.707 9.707a1 1 0 01-1.414 0z"
clip-rule="evenodd" />
< / svg >
{{client.transferRxCurrent | bytes}}/s
< / span >
<!-- Last seen -->
< span class = "text-gray-400 dark:text-neutral-500 whitespace-nowrap" v-if = "client.latestHandshakeAt"
:title="$t('lastSeen') + dateTime(new Date(client.latestHandshakeAt))">
{{!uiTrafficStats ? " · " : ""}}{{new Date(client.latestHandshakeAt) | timeago}}
< / span >
< / div >
< / div >
<!-- Info -->
< div v-if = "uiTrafficStats"
class="flex gap-2 items-center shrink-0 text-gray-400 dark:text-neutral-400 text-xs mt-px justify-end">
<!-- Transfer TX -->
< span v-if = "client.transferTx" :title = "$t('totalDownload') + bytes(client.transferTx)" >
·
< svg class = "align-middle h-3 inline" xmlns = "http://www.w3.org/2000/svg" viewBox = "0 0 20 20"
fill="currentColor">
< path fill-rule = "evenodd"
d="M16.707 10.293a1 1 0 010 1.414l-6 6a1 1 0 01-1.414 0l-6-6a1 1 0 111.414-1.414L9 14.586V3a1 1 0 012 0v11.586l4.293-4.293a1 1 0 011.414 0z"
clip-rule="evenodd" />
< / svg >
{{client.transferTxCurrent | bytes}}/s
< / span >
< div class = "min-w-20 md:min-w-24" v-if = "client.transferTx" >
< span class = "flex gap-1" :title = "$t('totalDownload') + bytes(client.transferTx)" >
< svg class = "align-middle h-3 inline mt-0.5" xmlns = "http://www.w3.org/2000/svg"
viewBox="0 0 20 20" fill="currentColor">
< path fill-rule = "evenodd"
d="M16.707 10.293a1 1 0 010 1.414l-6 6a1 1 0 01-1.414 0l-6-6a1 1 0 111.414-1.414L9 14.586V3a1 1 0 012 0v11.586l4.293-4.293a1 1 0 011.414 0z"
clip-rule="evenodd" />
< / svg >
< div >
< span class = "text-gray-700 dark:text-neutral-200" > {{client.transferTxCurrent |
bytes}}/s< / span >
<!-- Total TX -->
< br > < span class = "font-regular" style = "font-size:0.85em" > {{bytes(client.transferTx)}}< / span >
< / div >
< / span >
< / div >
<!-- Transfer RX -->
< span v-if = "client.transferRx" :title = "$t('totalUpload') + bytes(client.transferRx)" >
·
< svg class = "align-middle h-3 inline" xmlns = "http://www.w3.org/2000/svg" viewBox = "0 0 20 20"
fill="currentColor">
< path fill-rule = "evenodd"
d="M3.293 9.707a1 1 0 010-1.414l6-6a1 1 0 011.414 0l6 6a1 1 0 01-1.414 1.414L11 5.414V17a1 1 0 11-2 0V5.414L4.707 9.707a1 1 0 01-1.414 0z"
clip-rule="evenodd" />
< / svg >
{{client.transferRxCurrent | bytes}}/s
< / span >
<!-- Last seen -->
< span v-if = "client.latestHandshakeAt"
:title="$t('lastSeen') + dateTime(new Date(client.latestHandshakeAt))">
· {{new Date(client.latestHandshakeAt) | timeago}}
< / span >
< div class = "min-w-20 md:min-w-24" v-if = "client.transferRx" >
< span class = "flex gap-1" :title = "$t('totalUpload') + bytes(client.transferRx)" >
< svg class = "align-middle h-3 inline mt-0.5" xmlns = "http://www.w3.org/2000/svg"
viewBox="0 0 20 20" fill="currentColor">
< path fill-rule = "evenodd"
d="M3.293 9.707a1 1 0 010-1.414l6-6a1 1 0 011.414 0l6 6a1 1 0 01-1.414 1.414L11 5.414V17a1 1 0 11-2 0V5.414L4.707 9.707a1 1 0 01-1.414 0z"
clip-rule="evenodd" />
< / svg >
< div >
< span class = "text-gray-700 dark:text-neutral-200" > {{client.transferRxCurrent |
bytes}}/s< / span >
<!-- Total RX -->
< br > < span class = "font-regular" style = "font-size:0.85em" > {{bytes(client.transferRx)}}< / span >
< / div >
< / span >
< / div >
< / div >
< / div >
<!-- </div> --> <!-- <div class="flex flex - grow items - center"> -->
< / div >
< div class = "flex items-center justify-end" >
@ -214,9 +286,14 @@
<!-- Show QR -->
< button
class="align-middle bg-gray-100 dark:bg-neutral-600 dark:text-neutral-300 hover:bg-red-800 dark:hover:bg-red-800 hover:text-white dark:hover:text-white p-2 rounded transition"
:title="$t('showQR')" @click="qrcode = `./api/wireguard/client/${client.id}/qrcode.svg`">
< button :disabled = "!client.downloadableConfig"
class="align-middle bg-gray-100 dark:bg-neutral-600 dark:text-neutral-300 p-2 rounded transition"
:class="{
'hover:bg-red-800 dark:hover:bg-red-800 hover:text-white dark:hover:text-white': client.downloadableConfig,
'is-disabled': !client.downloadableConfig
}"
:title="!client.downloadableConfig ? $t('noPrivKey') : $t('showQR')"
@click="qrcode = `./api/wireguard/client/${client.id}/qrcode.svg`">
< svg class = "w-5" xmlns = "http://www.w3.org/2000/svg" fill = "none" viewBox = "0 0 24 24"
stroke="currentColor">
< path stroke-linecap = "round" stroke-linejoin = "round" stroke-width = "2"
@ -225,9 +302,16 @@
< / button >
<!-- Download Config -->
< a :href = "'./api/wireguard/client/' + client.id + '/configuration'" download
class="align-middle inline-block bg-gray-100 dark:bg-neutral-600 dark:text-neutral-300 hover:bg-red-800 dark:hover:bg-red-800 hover:text-white dark:hover:text-white p-2 rounded transition"
:title="$t('downloadConfig')">
< a :disabled = "!client.downloadableConfig"
:href="'./api/wireguard/client/' + client.id + '/configuration'"
:download="client.downloadableConfig ? 'configuration' : null"
class="align-middle inline-block bg-gray-100 dark:bg-neutral-600 dark:text-neutral-300 p-2 rounded transition"
:class="{
'hover:bg-red-800 dark:hover:bg-red-800 hover:text-white dark:hover:text-white': client.downloadableConfig,
'is-disabled': !client.downloadableConfig
}"
:title="!client.downloadableConfig ? $t('noPrivKey') : $t('downloadConfig')"
@click="if(!client.downloadableConfig) { $event.preventDefault(); }">
< svg class = "w-5" xmlns = "http://www.w3.org/2000/svg" fill = "none" viewBox = "0 0 24 24"
stroke="currentColor">
< path stroke-linecap = "round" stroke-linejoin = "round" stroke-width = "2"
@ -490,7 +574,7 @@
< / div >
< p v-cloak class = "text-center m-10 text-gray-300 dark:text-neutral-600 text-xs" > < a class = "hover:underline" target = "_blank"
< p v-cloak class = "text-center m-10 text-gray-300 dark:text-neutral-600 text-xs" > < a class = "hover:underline" target = "_blank"
href="https://github.com/wg-easy/wg-easy">WireGuard Easy< / a > © 2021-2024 by < a class = "hover:underline" target = "_blank"
href="https://emilenijssen.nl/?ref=wg-easy">Emile Nijssen< / a > is licensed under < a class = "hover:underline" target = "_blank"
href="http://creativecommons.org/licenses/by-nc-sa/4.0/">CC BY-NC-SA 4.0< / a > · < a class = "hover:underline"
@ -509,4 +593,4 @@
< script src = "./js/app.js" > < / script >
< / body >
< / html >
< / html >