Browse Source

lint fixes

pull/1250/head
Bernd Storath 11 months ago
parent
commit
bfaaceb780
  1. 390
      src/app.vue
  2. 4
      src/eslint.config.mjs
  3. 2
      src/middleware/session.ts
  4. 5
      src/nuxt.config.ts
  5. 2
      src/package.json
  6. 235
      src/pnpm-lock.yaml
  7. 10
      src/utils/WireGuard.ts
  8. 4
      src/utils/api.ts
  9. 8
      src/utils/cmd.ts
  10. 10
      src/wgpw.js

390
src/app.vue

@ -3,57 +3,70 @@
<div v-if="authenticated === true">
<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="/logo.png" width="32" class="inline align-middle dark:bg mr-2" /><span
<img src="/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"
<button
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"
:title="$t(`theme.${uiTheme}`)"
@click="toggleTheme">
<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"
<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"
<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"
<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"
<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"
<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" />
</button>
<!-- Show / hide charts -->
<label v-if="uiChartType > 0"
<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" viewBox="0 0 24 24" stroke-width="1.5"
<input v-model="uiShowCharts" type="checkbox" value="" class="sr-only peer" @change="toggleCharts">
<svg
xmlns="http://www.w3.org/2000/svg" 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"
<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"
<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"
<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"
<div class="text-sm text-gray-400 dark:text-neutral-400 mb-5"/>
<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}`">
<div class="container mx-auto flex flex-row flex-auto items-center">
@ -62,7 +75,8 @@
<p>{{ latestRelease.changelog }}</p>
</div>
<a href="https://github.com/wg-easy/wg-easy#updating" target="_blank"
<a
href="https://github.com/wg-easy/wg-easy#updating" target="_blank"
class="p-3 rounded-md bg-white dark:bg-red-100 float-right font-sm font-semibold text-red-800 dark:text-red-600 flex-shrink-0 border-2 border-red-800 dark:border-red-600 hover:border-white dark:hover:border-red-600 hover:text-white dark:hover:text-red-100 hover:bg-red-800 dark:hover:bg-red-600 transition-all">
{{ $t("update") }}
</a>
@ -76,35 +90,43 @@
</div>
<div class="flex md:block md:flex-shrink-0">
<!-- Restore configuration -->
<label for="inputRC" :title="$t('titleRestoreConfig')"
<label
for="inputRC" :title="$t('titleRestoreConfig')"
class="hover:cursor-pointer hover:bg-red-800 hover:border-red-800 hover:text-white text-gray-700 dark:text-neutral-200 max-md:border-r-0 border-2 border-gray-100 dark:border-neutral-600 py-2 px-4 rounded-l-full md:rounded inline-flex items-center transition">
<svg inline class="w-4 md:mr-2" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
<svg
inline class="w-4 md:mr-2" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round"
d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0 3.181 3.183a8.25 8.25 0 0 0 13.803-3.7M4.031 9.865a8.25 8.25 0 0 1 13.803-3.7l3.181 3.182m0-4.991v4.99">
</path>
<path
stroke-linecap="round" stroke-linejoin="round"
d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0 3.181 3.183a8.25 8.25 0 0 0 13.803-3.7M4.031 9.865a8.25 8.25 0 0 1 13.803-3.7l3.181 3.182m0-4.991v4.99"/>
</svg>
<span class="max-md:hidden text-sm">{{ $t("restore") }}</span>
<input id="inputRC" type="file" name="configurationfile" accept="text/*,.json" @change="restoreConfig"
class="hidden" />
<input
id="inputRC" type="file" name="configurationfile" accept="text/*,.json" class="hidden"
@change="restoreConfig" >
</label>
<!-- Backup configuration -->
<a href="./api/wireguard/backup" :title="$t('titleBackupConfig')"
<a
href="./api/wireguard/backup" :title="$t('titleBackupConfig')"
class="hover:bg-red-800 hover:border-red-800 hover:text-white text-gray-700 dark:text-neutral-200 max-md:border-x-0 border-2 border-gray-100 dark:border-neutral-600 py-2 px-4 md:rounded inline-flex items-center transition">
<svg inline class="w-4 md:mr-2 size-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
<svg
inline class="w-4 md:mr-2 size-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round"
d="M5.25 14.25h13.5m-13.5 0a3 3 0 0 1-3-3m3 3a3 3 0 1 0 0 6h13.5a3 3 0 1 0 0-6m-16.5-3a3 3 0 0 1 3-3h13.5a3 3 0 0 1 3 3m-19.5 0a4.5 4.5 0 0 1 .9-2.7L5.737 5.1a3.375 3.375 0 0 1 2.7-1.35h7.126c1.062 0 2.062.5 2.7 1.35l2.587 3.45a4.5 4.5 0 0 1 .9 2.7m0 0a3 3 0 0 1-3 3m0 3h.008v.008h-.008v-.008Zm0-6h.008v.008h-.008v-.008Zm-3 6h.008v.008h-.008v-.008Zm0-6h.008v.008h-.008v-.008Z">
</path>
<path
stroke-linecap="round" stroke-linejoin="round"
d="M5.25 14.25h13.5m-13.5 0a3 3 0 0 1-3-3m3 3a3 3 0 1 0 0 6h13.5a3 3 0 1 0 0-6m-16.5-3a3 3 0 0 1 3-3h13.5a3 3 0 0 1 3 3m-19.5 0a4.5 4.5 0 0 1 .9-2.7L5.737 5.1a3.375 3.375 0 0 1 2.7-1.35h7.126c1.062 0 2.062.5 2.7 1.35l2.587 3.45a4.5 4.5 0 0 1 .9 2.7m0 0a3 3 0 0 1-3 3m0 3h.008v.008h-.008v-.008Zm0-6h.008v.008h-.008v-.008Zm-3 6h.008v.008h-.008v-.008Zm0-6h.008v.008h-.008v-.008Z"/>
</svg>
<span class="max-md:hidden text-sm">{{ $t("backup") }}</span>
</a>
<!-- New client -->
<button @click="clientCreate = true; clientCreateName = '';"
class="hover:bg-red-800 hover:border-red-800 hover:text-white text-gray-700 dark:text-neutral-200 max-md:border-l-0 border-2 border-gray-100 dark:border-neutral-600 py-2 px-4 rounded-r-full md:rounded inline-flex items-center transition">
<svg class="w-4 md:mr-2" inline xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
<button
class="hover:bg-red-800 hover:border-red-800 hover:text-white text-gray-700 dark:text-neutral-200 max-md:border-l-0 border-2 border-gray-100 dark:border-neutral-600 py-2 px-4 rounded-r-full md:rounded inline-flex items-center transition"
@click="clientCreate = true; clientCreateName = '';">
<svg
class="w-4 md:mr-2" 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"
<path
stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
</svg>
<span class="max-md:hidden text-sm">{{ $t("new") }}</span>
@ -114,20 +136,22 @@
<div>
<!-- Client -->
<div v-if="clients && clients.length > 0" v-for="client in clients" :key="client.id"
<div
v-for="client in clients" v-if="clients && clients.length > 0" :key="client.id"
class="relative overflow-hidden border-b last:border-b-0 border-gray-100 dark:border-neutral-600 border-solid">
<!-- Chart -->
<div v-if="uiChartType"
<div
v-if="uiChartType"
:class="`absolute z-0 bottom-0 left-0 right-0 h-6 ${uiChartType === 1 && 'line-chart'}`">
<apexchart width="100%" height="100%" :options="chartOptionsTX" :series="client.transferTxSeries">
</apexchart>
<apexchart width="100%" height="100%" :options="chartOptionsTX" :series="client.transferTxSeries"/>
</div>
<div v-if="uiChartType"
<div
v-if="uiChartType"
:class="`absolute z-0 top-0 left-0 right-0 h-6 ${uiChartType === 1 && 'line-chart'}`">
<apexchart width="100%" height="100%" :options="chartOptionsRX" :series="client.transferRxSeries"
style="transform: scaleY(-1);">
</apexchart>
<apexchart
width="100%" height="100%" :options="chartOptionsRX" :series="client.transferRxSeries"
style="transform: scaleY(-1);"/>
</div>
<div class="relative py-3 md:py-5 px-3 z-10 flex flex-col sm:flex-row justify-between gap-3">
@ -135,19 +159,20 @@
<!-- 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"
<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"
<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>
<img v-if="client.avatar" :src="client.avatar" class="w-10 rounded-full absolute top-0 left-0" />
<img v-if="client.avatar" :src="client.avatar" class="w-10 rounded-full absolute top-0 left-0" >
<div
v-if="client.latestHandshakeAt && ((new Date().getTime() - new Date(client.latestHandshakeAt).getTime() < 1000 * 60 * 10))">
<div
class="animate-ping w-4 h-4 p-1 bg-red-100 dark:bg-red-100 rounded-full absolute -bottom-1 -right-1">
</div>
<div class="w-2 h-2 bg-red-800 dark:bg-red-600 rounded-full absolute bottom-0 right-0"></div>
class="animate-ping w-4 h-4 p-1 bg-red-100 dark:bg-red-100 rounded-full absolute -bottom-1 -right-1"/>
<div class="w-2 h-2 bg-red-800 dark:bg-red-600 rounded-full absolute bottom-0 right-0"/>
</div>
</div>
@ -156,26 +181,32 @@
<!-- Name -->
<div class="flex flex-col flex-grow gap-1">
<div class="text-gray-700 dark:text-neutral-200 group text-sm md:text-base"
<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="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"
<input
v-show="clientEditNameId === client.id" :ref="'client-' + client.id + '-name'"
v-model="clientEditName"
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"
@keyup.enter="updateClientName(client, clientEditName); clientEditName = null; clientEditNameId = null;"
@keyup.escape="clientEditName = null; clientEditNameId = null;" >
<span
v-show="clientEditNameId !== client.id"
class="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"
<span
v-show="clientEditNameId !== client.id"
class="cursor-pointer opacity-0 group-hover:opacity-100 transition-opacity"
@click="clientEditName = client.name; clientEditNameId = client.id; setTimeout(() => $refs['client-' + client.id + '-name'][0].select(), 1);">
<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"
<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>
@ -184,33 +215,40 @@
<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" />
<input
v-show="clientEditAddressId === client.id" :ref="'client-' + client.id + '-address'"
v-model="clientEditAddress"
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"
@keyup.enter="updateClientAddress(client, clientEditAddress); clientEditAddress = null; clientEditAddressId = null;"
@keyup.escape="clientEditAddress = null; clientEditAddressId = null;" >
<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"
<span
v-show="clientEditAddressId !== client.id"
class="cursor-pointer opacity-0 group-hover:opacity-100 transition-opacity"
@click="clientEditAddress = client.address; clientEditAddressId = client.id; setTimeout(() => $refs['client-' + client.id + '-address'][0].select(), 1);">
<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"
<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"
<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"
<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"
<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>
@ -218,20 +256,24 @@
</span>
<!-- Inline Transfer RX -->
<span v-if="!uiTrafficStats && client.transferRx" class="whitespace-nowrap"
<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"
<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"
<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>
{{ bytes(client.transferRxCurrent) }}/s
</span>
<!-- Last seen -->
<span class="text-gray-400 dark:text-neutral-500 whitespace-nowrap"
<span
v-if="client.latestHandshakeAt"
class="text-gray-400 dark:text-neutral-500 whitespace-nowrap"
:title="$t('lastSeen') + dateTime(new Date(client.latestHandshakeAt))">
{{ !uiTrafficStats ? " · " : "" }}{{ timeago(new Date(client.latestHandshakeAt)) }}
</span>
@ -239,15 +281,18 @@
</div>
<!-- Info -->
<div v-if="uiTrafficStats"
<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 -->
<div class="min-w-20 md:min-w-24" v-if="client.transferTx">
<div v-if="client.transferTx" class="min-w-20 md:min-w-24">
<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"
<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"
<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>
@ -262,12 +307,14 @@
</div>
<!-- Transfer RX -->
<div class="min-w-20 md:min-w-24" v-if="client.transferRx">
<div v-if="client.transferRx" class="min-w-20 md:min-w-24">
<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"
<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"
<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>
@ -289,35 +336,41 @@
<div class="text-gray-400 dark:text-neutral-400 flex gap-1 items-center justify-between">
<!-- Enable/Disable -->
<div @click="disableClient(client)" v-if="client.enabled === true" :title="$t('disableClient')"
class="inline-block align-middle rounded-full w-10 h-6 mr-1 bg-red-800 cursor-pointer hover:bg-red-700 transition-all">
<div class="rounded-full w-4 h-4 m-1 ml-5 bg-white"></div>
<div
v-if="client.enabled === true" :title="$t('disableClient')" class="inline-block align-middle rounded-full w-10 h-6 mr-1 bg-red-800 cursor-pointer hover:bg-red-700 transition-all"
@click="disableClient(client)">
<div class="rounded-full w-4 h-4 m-1 ml-5 bg-white"/>
</div>
<div @click="enableClient(client)" v-if="client.enabled === false" :title="$t('enableClient')"
class="inline-block align-middle rounded-full w-10 h-6 mr-1 bg-gray-200 dark:bg-neutral-400 cursor-pointer hover:bg-gray-300 dark:hover:bg-neutral-500 transition-all">
<div
v-if="client.enabled === false" :title="$t('enableClient')" class="inline-block align-middle rounded-full w-10 h-6 mr-1 bg-gray-200 dark:bg-neutral-400 cursor-pointer hover:bg-gray-300 dark:hover:bg-neutral-500 transition-all"
@click="enableClient(client)">
<div class="rounded-full w-4 h-4 m-1 bg-white"></div>
<div class="rounded-full w-4 h-4 m-1 bg-white"/>
</div>
<!-- Show QR-->
<button :disabled="!client.downloadableConfig"
<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"
<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"
<path
stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 4v1m6 11h2m-6 0h-2v4m0-11v3m0 0h.01M12 12h4.01M16 20h4M4 12h4m12 0h.01M5 8h2a1 1 0 001-1V5a1 1 0 00-1-1H5a1 1 0 00-1 1v2a1 1 0 001 1zm12 0h2a1 1 0 001-1V5a1 1 0 00-1-1h-2a1 1 0 00-1 1v2a1 1 0 001 1zM5 20h2a1 1 0 001-1v-2a1 1 0 00-1-1H5a1 1 0 00-1 1v2a1 1 0 001 1z" />
</svg>
</button>
<!-- Download Config -->
<a :disabled="!client.downloadableConfig"
<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"
@ -326,9 +379,11 @@
'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"
<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"
<path
stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
</svg>
</a>
@ -339,7 +394,8 @@
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('deleteClient')" @click="clientDelete = client">
<svg class="w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd"
<path
fill-rule="evenodd"
d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z"
clip-rule="evenodd" />
</svg>
@ -352,12 +408,15 @@
</div>
<div v-if="clients && clients.length === 0">
<p class="text-center m-10 text-gray-400 dark:text-neutral-400 text-sm">
{{ $t("noClients") }}<br /><br />
<button @click="clientCreate = true; clientCreateName = '';"
class="bg-red-800 hover:bg-red-700 text-white border-2 border-none py-2 px-4 rounded inline-flex items-center transition">
<svg class="w-4 mr-2" inline xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
{{ $t("noClients") }}<br ><br >
<button
class="bg-red-800 hover:bg-red-700 text-white border-2 border-none py-2 px-4 rounded inline-flex items-center transition"
@click="clientCreate = true; clientCreateName = '';">
<svg
class="w-4 mr-2" 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"
<path
stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
</svg>
<span class="text-sm">{{ $t("newClient") }}</span>
@ -365,12 +424,13 @@
</p>
</div>
<div v-if="clients === null" class="text-gray-200 dark:text-red-300 p-5">
<svg class="w-5 animate-spin mx-auto" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
<svg
class="w-5 animate-spin mx-auto" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
fill="currentColor">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z">
</path>
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"/>
<path
class="opacity-75" fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"/>
</svg>
</div>
</div>
@ -380,14 +440,16 @@
<div v-if="qrcode">
<div class="bg-black bg-opacity-50 fixed top-0 right-0 left-0 bottom-0 flex items-center justify-center z-20">
<div class="bg-white rounded-md shadow-lg relative p-8">
<button @click="qrcode = null"
class="absolute right-4 top-4 text-gray-600 dark:text-neutral-500 hover:text-gray-800 dark:hover:text-neutral-700">
<svg class="w-8" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
<button
class="absolute right-4 top-4 text-gray-600 dark:text-neutral-500 hover:text-gray-800 dark:hover:text-neutral-700"
@click="qrcode = null">
<svg
class="w-8" 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="M6 18L18 6M6 6l12 12" />
</svg>
</button>
<img :src="qrcode" />
<img :src="qrcode" >
</div>
</div>
</div>
@ -406,7 +468,7 @@
To: "opacity-0"
-->
<div class="fixed inset-0 transition-opacity" aria-hidden="true">
<div class="absolute inset-0 bg-gray-500 dark:bg-black opacity-75 dark:opacity-50"></div>
<div class="absolute inset-0 bg-gray-500 dark:bg-black opacity-75 dark:opacity-50"/>
</div>
<!-- This element is to trick the browser into centering the modal contents. -->
@ -428,37 +490,42 @@
<div class="sm:flex sm:items-start">
<div
class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-red-800 sm:mx-0 sm:h-10 sm:w-10">
<svg class="h-6 w-6 text-white" inline xmlns="http://www.w3.org/2000/svg" fill="none"
<svg
class="h-6 w-6 text-white" 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"
<path
stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
</svg>
</div>
<div class="flex-grow mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<h3 class="text-lg leading-6 font-medium text-gray-900 dark:text-neutral-200" id="modal-headline">
<h3 id="modal-headline" class="text-lg leading-6 font-medium text-gray-900 dark:text-neutral-200">
{{ $t("newClient") }}
</h3>
<div class="mt-2">
<p class="text-sm text-gray-500">
<input
class="rounded p-2 border-2 dark:bg-neutral-700 dark:text-neutral-200 border-gray-100 dark:border-neutral-600 focus:border-gray-200 focus:dark:border-neutral-500 dark:placeholder:text-neutral-400 outline-none w-full"
type="text" v-model.trim="clientCreateName" :placeholder="$t('name')" />
v-model.trim="clientCreateName"
class="rounded p-2 border-2 dark:bg-neutral-700 dark:text-neutral-200 border-gray-100 dark:border-neutral-600 focus:border-gray-200 focus:dark:border-neutral-500 dark:placeholder:text-neutral-400 outline-none w-full" type="text" :placeholder="$t('name')" >
</p>
</div>
</div>
</div>
</div>
<div class="bg-gray-50 dark:bg-neutral-700 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
<button v-if="clientCreateName.length" type="button" @click="createClient(); clientCreate = null"
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-800 text-base font-medium text-white hover:bg-red-700 focus:outline-none sm:ml-3 sm:w-auto sm:text-sm">
<button
v-if="clientCreateName.length" type="button" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-800 text-base font-medium text-white hover:bg-red-700 focus:outline-none sm:ml-3 sm:w-auto sm:text-sm"
@click="createClient(); clientCreate = null">
{{ $t("create") }}
</button>
<button v-else type="button"
<button
v-else type="button"
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-gray-200 dark:bg-neutral-400 text-base font-medium text-white dark:text-neutral-300 sm:ml-3 sm:w-auto sm:text-sm cursor-not-allowed">
{{ $t("create") }}
</button>
<button type="button" @click="clientCreate = null"
class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 dark:border-neutral-500 shadow-sm px-4 py-2 bg-white dark:bg-neutral-500 text-base font-medium text-gray-700 dark:text-neutral-50 hover:bg-gray-50 dark:hover:bg-neutral-600 dark:hover:border-neutral-600 focus:outline-none sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm">
<button
type="button" class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 dark:border-neutral-500 shadow-sm px-4 py-2 bg-white dark:bg-neutral-500 text-base font-medium text-gray-700 dark:text-neutral-50 hover:bg-gray-50 dark:hover:bg-neutral-600 dark:hover:border-neutral-600 focus:outline-none sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"
@click="clientCreate = null">
{{ $t("cancel") }}
</button>
</div>
@ -480,7 +547,7 @@
To: "opacity-0"
-->
<div class="fixed inset-0 transition-opacity" aria-hidden="true">
<div class="absolute inset-0 bg-gray-500 dark:bg-black opacity-75 dark:opacity-50"></div>
<div class="absolute inset-0 bg-gray-500 dark:bg-black opacity-75 dark:opacity-50"/>
</div>
<!-- This element is to trick the browser into centering the modal contents. -->
@ -503,14 +570,16 @@
<div
class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10">
<!-- Heroicon name: outline/exclamation -->
<svg class="h-6 w-6 text-red-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
<svg
class="h-6 w-6 text-red-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
<path
stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
</div>
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<h3 class="text-lg leading-6 font-medium text-gray-900 dark:text-neutral-200" id="modal-headline">
<h3 id="modal-headline" class="text-lg leading-6 font-medium text-gray-900 dark:text-neutral-200">
{{ $t("deleteClient") }}
</h3>
<div class="mt-2">
@ -522,12 +591,14 @@
</div>
</div>
<div class="bg-gray-50 dark:bg-neutral-600 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
<button type="button" @click="deleteClient(clientDelete); clientDelete = null"
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 dark:bg-red-600 text-base font-medium text-white dark:text-white hover:bg-red-700 dark:hover:bg-red-700 focus:outline-none sm:ml-3 sm:w-auto sm:text-sm">
<button
type="button" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 dark:bg-red-600 text-base font-medium text-white dark:text-white hover:bg-red-700 dark:hover:bg-red-700 focus:outline-none sm:ml-3 sm:w-auto sm:text-sm"
@click="deleteClient(clientDelete); clientDelete = null">
{{ $t("deleteClient") }}
</button>
<button type="button" @click="clientDelete = null"
class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 dark:border-neutral-500 shadow-sm px-4 py-2 bg-white dark:bg-neutral-500 text-base font-medium text-gray-700 dark:text-neutral-50 hover:bg-gray-50 dark:hover:bg-neutral-600 dark:hover:border-neutral-600 focus:outline-none sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm">
<button
type="button" class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 dark:border-neutral-500 shadow-sm px-4 py-2 bg-white dark:bg-neutral-500 text-base font-medium text-gray-700 dark:text-neutral-50 hover:bg-gray-50 dark:hover:bg-neutral-600 dark:hover:border-neutral-600 focus:outline-none sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"
@click="clientDelete = null">
{{ $t("cancel") }}
</button>
</div>
@ -538,37 +609,44 @@
<div v-if="authenticated === false">
<h1 class="text-4xl font-medium my-16 text-gray-700 dark:text-neutral-200 text-center">
<img src="/logo.png" width="32" class="inline align-middle dark:bg" />
<img src="/logo.png" width="32" class="inline align-middle dark:bg" >
<span class="align-middle">WireGuard</span>
</h1>
<form @submit="login"
class="shadow rounded-md bg-white dark:bg-neutral-700 mx-auto w-64 p-5 overflow-hidden mt-10">
<form
class="shadow rounded-md bg-white dark:bg-neutral-700 mx-auto w-64 p-5 overflow-hidden mt-10"
@submit="login">
<!-- Avatar -->
<div class="h-20 w-20 mb-10 mt-5 mx-auto rounded-full bg-red-800 dark:bg-red-800 relative overflow-hidden">
<svg class="w-10 h-10 m-5 text-white dark:text-white" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
<svg
class="w-10 h-10 m-5 text-white dark:text-white" 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>
</div>
<input type="password" name="password" :placeholder="$t('password')" v-model="password"
class="px-3 py-2 text-sm dark:bg-neutral-700 text-gray-500 dark:text-gray-500 mb-5 border-2 border-gray-100 dark:border-neutral-800 rounded-lg w-full focus:border-red-800 dark:focus:border-red-800 dark:placeholder:text-neutral-400 outline-none" />
<input
v-model="password" type="password" name="password" :placeholder="$t('password')"
class="px-3 py-2 text-sm dark:bg-neutral-700 text-gray-500 dark:text-gray-500 mb-5 border-2 border-gray-100 dark:border-neutral-800 rounded-lg w-full focus:border-red-800 dark:focus:border-red-800 dark:placeholder:text-neutral-400 outline-none" >
<button v-if="authenticating"
<button
v-if="authenticating"
class="bg-red-800 dark:bg-red-800 w-full rounded shadow py-2 text-sm text-white dark:text-white cursor-not-allowed">
<svg class="w-5 animate-spin mx-auto" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
<svg
class="w-5 animate-spin mx-auto" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
fill="currentColor">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z">
</path>
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"/>
<path
class="opacity-75" fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"/>
</svg>
</button>
<input v-if="!authenticating && password" type="submit"
<input
v-if="!authenticating && password" type="submit"
class="bg-red-800 dark:bg-red-800 w-full rounded shadow py-2 text-sm text-white dark:text-white hover:bg-red-700 dark:hover:bg-red-700 transition cursor-pointer"
:value="$t('signIn')">
<input v-if="!authenticating && !password" type="submit"
<input
v-if="!authenticating && !password" type="submit"
class="bg-gray-200 dark:bg-neutral-800 w-full rounded shadow py-2 text-sm text-white dark:text-white cursor-not-allowed"
:value="$t('signIn')">
</form>
@ -576,23 +654,27 @@
<div v-if="authenticated === null" class="text-gray-300 dark:text-red-300 pt-24 pb-12">
<svg class="w-5 animate-spin mx-auto" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
<svg
class="w-5 animate-spin mx-auto" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
fill="currentColor">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z">
</path>
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"/>
<path
class="opacity-75" fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"/>
</svg>
</div>
</div>
<p v-cloak class="text-center m-10 text-gray-300 dark:text-neutral-600 text-xs"> <a class="hover:underline"
<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"
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"
href="https://github.com/sponsors/WeeJeWel" target="_blank">{{ $t("donate") }}</a></p>
</template>
@ -797,7 +879,7 @@ function dateTime(value: Date) {
async function refresh({
updateCharts = false,
} = {}) {
if (!authenticated) return;
if (!authenticated.value) return;
const _clients = await api.getClients();
clients.value = _clients.map((client) => {
@ -862,7 +944,7 @@ async function refresh({
function login(e: Event) {
e.preventDefault();
if (!password) return;
if (!password.value) return;
if (authenticating.value) return;
authenticating.value = true;

4
src/eslint.config.mjs

@ -1,3 +1,3 @@
import withNuxt from './.nuxt/eslint.config.mjs'
import { createConfigForNuxt } from '@nuxt/eslint-config/flat'
export default withNuxt()
export default createConfigForNuxt({})

2
src/middleware/session.ts

@ -1,4 +1,4 @@
export default defineNuxtRouteMiddleware(async (to, from) => {
export default defineNuxtRouteMiddleware(async (to) => {
if (REQUIRES_PASSWORD || !to.path.startsWith('/api/')) {
return abortNavigation();
}

5
src/nuxt.config.ts

@ -2,8 +2,5 @@
export default defineNuxtConfig({
compatibilityDate: '2024-04-03',
devtools: { enabled: true },
modules: ["@nuxtjs/i18n", "@nuxtjs/tailwindcss"],
$development: {
modules: ["@nuxt/eslint"]
}
modules: ["@nuxtjs/i18n", "@nuxtjs/tailwindcss"]
})

2
src/package.json

@ -29,7 +29,7 @@
"vue3-apexcharts": "^1.5.3"
},
"devDependencies": {
"@nuxt/eslint": "^0.5.0",
"@nuxt/eslint-config": "^0.5.0",
"@types/bcryptjs": "^2.4.6",
"@types/debug": "^4.1.12",
"@types/qrcode": "^1.5.5",

235
src/pnpm-lock.yaml

@ -42,9 +42,9 @@ importers:
specifier: ^1.5.3
version: 1.5.3([email protected])([email protected]([email protected]))
devDependencies:
'@nuxt/eslint':
'@nuxt/eslint-config':
specifier: ^0.5.0
version: 0.5.0([email protected])([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected]))
version: 0.5.0([email protected])([email protected])
'@types/bcryptjs':
specifier: ^2.4.6
version: 2.4.6
@ -77,10 +77,6 @@ packages:
'@antfu/[email protected]':
resolution: {integrity: sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==}
'@apidevtools/[email protected]':
resolution: {integrity: sha512-pRrmXMCwnmrkS3MLgAIW5dXRzeTv6GLjkjb4HmxNnvAKXN1Nfzp4KmGADBQvlVUcqi+a5D+hfGDLLnd5NnYxog==}
engines: {node: '>= 16'}
'@babel/[email protected]':
resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==}
engines: {node: '>=6.9.0'}
@ -687,12 +683,6 @@ packages:
resolution: {integrity: sha512-BlYOpej8AQ8Ev9xVqroV7a02JK3SkBAaN9GfMMH9W6Ch8FlQlkjGw4Ir7+FgYwfirivAf4t+GtzuAxqfukmISA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@eslint/[email protected]':
resolution: {integrity: sha512-m7uUwWhYU4DoHTakyLIRauqnYK05Bv8G+R47IN7a6KC4XXAjIw/OeztgBU5tjGNfzsSOM8nr9Rf779B7D3GPtw==}
hasBin: true
peerDependencies:
eslint: ^8.50.0 || ^9.0.0
'@eslint/[email protected]':
resolution: {integrity: sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@ -796,9 +786,6 @@ packages:
'@jridgewell/[email protected]':
resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
'@jsdevtools/[email protected]':
resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==}
'@koa/[email protected]':
resolution: {integrity: sha512-ribfPYfHb+Uw3b27Eiw6NPqjhIhTpVFzEWLwyc/1Xp+DCdwRRyIlAUODX+9bPARF6aQtUu1+/PHzdNvRzcs/+Q==}
engines: {node: '>= 12'}
@ -834,26 +821,14 @@ packages:
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'}
'@nodelib/[email protected]':
resolution: {integrity: sha512-ktI9+PxfHYtKjF3cLTUAh2N+b8MijCRPNwKJNqTVdL0gB0QxLU2rIRaZ1t71oEa3YBDE6bukH1sR0+CDnpp/Mg==}
engines: {node: '>=16.14.0'}
'@nodelib/[email protected]':
resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
engines: {node: '>= 8'}
'@nodelib/[email protected]':
resolution: {integrity: sha512-2tQOI38s19P9i7X/Drt0v8iMA+KMsgdhB/dyPER+e+2Y8L1Z7QvnuRdW/uLuf5YRFUYmnj4bMA6qCuZHFI1GDQ==}
engines: {node: '>=16.14.0'}
'@nodelib/[email protected]':
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
engines: {node: '>= 8'}
'@nodelib/[email protected]':
resolution: {integrity: sha512-54voNDBobGdMl3BUXSu7UaDh1P85PGHWlJ5e0XhPugo1JulOyCtp2I+5ri4wplGDJ8QGwPEQW7/x3yTLU7yF1A==}
engines: {node: '>=16.14.0'}
'@nuxt/[email protected]':
resolution: {integrity: sha512-GBzP8zOc7CGWyFQS6dv1lQz8VVpz5C2yRszbXufwG/9zhStTIH50EtD87NmWbTMwXDvZLNg8GIpb1UFdH93JCA==}
@ -882,18 +857,6 @@ packages:
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
'@nuxt/[email protected]':
resolution: {integrity: sha512-5VA79fN6Dm2N9Hs3921sGdFeWZlJtaf6aDHTGu/X67+hWQZND/KHYxBRx/ShxWlJV4P0JqKp1pHiJIOdopdd8g==}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
eslint-webpack-plugin: ^4.1.0
vite-plugin-eslint2: ^4.4.0
peerDependenciesMeta:
eslint-webpack-plugin:
optional: true
vite-plugin-eslint2:
optional: true
'@nuxt/[email protected]':
resolution: {integrity: sha512-aNRD1ylzijY0oYolldNcZJXVyxdGzNTl+Xd0UYyFQCu9f4wqUZqQ9l+b7arCEzchr96pMK0xdpvLcS3xo1wDcw==}
engines: {node: ^14.18.0 || >=16.10.0}
@ -1379,12 +1342,6 @@ packages:
'@volar/[email protected]':
resolution: {integrity: sha512-sXh5Y8sqGUkgxpMWUGvRXggxYHAVxg0Pa1C42lQZuPDrW6vHJPR0VCK8Sr7WJsAW530HuNQT/ZIskmXtxjybMQ==}
'@voxpelli/[email protected]':
resolution: {integrity: sha512-jOva73R+0Nc5/pyS/piBSjQzO4EehME7rPSkBpPC9PYSta+yj3OpF14v0m0HLLYLVNuyHbBjQh5QvGIZwTH2eA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
'@eslint/config-array': '>=0.16.0'
'@vue-macros/[email protected]':
resolution: {integrity: sha512-xOLXUfmQD9XAhy0wnGwmxDY/V9W7q2/Yll7QKcLWD4R4KfTJg4DeGbF4rQMuFDWmaCWeusWA1cxUEHa9BePP8Q==}
engines: {node: '>=16.14.0'}
@ -1659,12 +1616,6 @@ packages:
resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==}
engines: {node: '>=18'}
[email protected]:
resolution: {integrity: sha512-GuziW3fSSmopcx4KRymQEJVbZUfqlCqcq7dvs6TYwKRZiegK/2buMxQTPs6MGlNv50wms1699qYO54R8XfRX4w==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
peerDependencies:
esbuild: '>=0.18'
[email protected]:
resolution: {integrity: sha512-KDU0TvSvVdaYcQKQ6iPHATGz/7p/KiVjPg4vQrB6Jg/wX9R0yl5RZxWm9IoZqaIHD2+6PZd81+KMGwRr/lRIUg==}
peerDependencies:
@ -2218,11 +2169,6 @@ packages:
resolution: {integrity: sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
[email protected]:
resolution: {integrity: sha512-NXuFC16JBS8H11cD8DJcmSzpv2+MljyDvksSbx4ak5zXebk7SEFMIdk/idYGXgevs0Lz4BClYG7b4MtD0+tFVg==}
peerDependencies:
eslint: ^8.45.0 || ^9.0.0
[email protected]:
resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@ -2339,10 +2285,6 @@ packages:
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
engines: {node: '>=10'}
[email protected]:
resolution: {integrity: sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==}
engines: {node: '>=18'}
[email protected]:
resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==}
engines: {node: '>=16'}
@ -2736,6 +2678,10 @@ packages:
resolution: {integrity: sha512-YtOli5Cmzy3q4dP26GraSOeAhqecewG04hoO8DY56CH4KJ9Fvv5qKWUCCo3HZob7esJQHCv6/+bnTy72xZZaVQ==}
engines: {node: '>=12.0.0'}
[email protected]:
resolution: {integrity: sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==}
engines: {node: '>=12.0.0'}
[email protected]:
resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==}
hasBin: true
@ -2756,9 +2702,6 @@ packages:
[email protected]:
resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
[email protected]:
resolution: {integrity: sha512-MhjvNC3MfEyYmKiC1rEzwDTCc22+hWU/2HKVfnklar4tifbkT8oZvvamEG1n550JeCmJ0V+2ly+5fF5K+lIExg==}
[email protected]:
resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
@ -2843,10 +2786,6 @@ packages:
resolution: {integrity: sha512-7/HamOm5YD9Wb7CFgAZkKgVPA96WwhcTQoqtm2VTZGVbVVn3IWKRBTgrU7cchA3Q8k9iCsG8Osoi9GX4JsGM9g==}
hasBin: true
[email protected]:
resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
[email protected]:
resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==}
engines: {node: '>=14'}
@ -2859,10 +2798,6 @@ packages:
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
engines: {node: '>=10'}
[email protected]:
resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
[email protected]:
resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==}
@ -3188,10 +3123,6 @@ packages:
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
engines: {node: '>=10'}
[email protected]:
resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
[email protected]:
resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==}
engines: {node: '>=8'}
@ -3200,10 +3131,6 @@ packages:
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
engines: {node: '>=10'}
[email protected]:
resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
[email protected]:
resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
engines: {node: '>=6'}
@ -3248,10 +3175,6 @@ packages:
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
engines: {node: '>=8'}
[email protected]:
resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
[email protected]:
resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
engines: {node: '>=0.10.0'}
@ -4115,9 +4038,6 @@ packages:
resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==}
engines: {node: '>=18'}
[email protected]:
resolution: {integrity: sha512-/UvKRfWx3mNDWwWQhR62HsoM3wxHwYdTq8ellZzMOHnnw4Dp8tovgthyW7DjTrbjDL+i4idOp06voz2VKlvrLw==}
[email protected]:
resolution: {integrity: sha512-4gtacoNH6YPx2Aa5Xfyrf8pU2RdXjWUACb/eF7bH1AcZtqs+6ijbNB0M3BPENbtVjnCcg8tw9UJ1jQGbCzKA6g==}
@ -4481,10 +4401,6 @@ packages:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
engines: {node: '>=10'}
[email protected]:
resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==}
engines: {node: '>=12.20'}
[email protected]:
resolution: {integrity: sha512-8F0OI5dpWIA5IGG5NHUg9staDwz/ZPxZtvGVf01j7vHqSyZ0raHY+78atOVxRqb73AotX22uV1pXt3gYSstGag==}
@ -4503,12 +4419,6 @@ snapshots:
'@antfu/[email protected]': {}
'@apidevtools/[email protected]':
dependencies:
'@jsdevtools/ono': 7.1.3
'@types/json-schema': 7.0.15
js-yaml: 4.1.0
'@babel/[email protected]':
dependencies:
'@babel/highlight': 7.24.7
@ -4961,31 +4871,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@eslint/[email protected]([email protected])':
dependencies:
'@eslint/config-array': 0.17.1
'@voxpelli/config-array-find-files': 0.1.2(@eslint/[email protected])
bundle-require: 5.0.0([email protected])
cac: 6.7.14
chokidar: 3.6.0
esbuild: 0.21.5
eslint: 9.8.0
fast-glob: 3.3.2
find-up: 7.0.0
get-port-please: 3.1.2
h3: 1.12.0
minimatch: 9.0.5
mlly: 1.7.1
mrmime: 2.0.0
open: 10.1.0
picocolors: 1.0.1
ws: 8.18.0
transitivePeerDependencies:
- bufferutil
- supports-color
- uWebSockets.js
- utf-8-validate
'@eslint/[email protected]':
dependencies:
ajv: 6.12.6
@ -5102,8 +4987,6 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.0
'@jsdevtools/[email protected]': {}
'@koa/[email protected]':
dependencies:
debug: 4.3.6
@ -5159,25 +5042,13 @@ snapshots:
'@nodelib/fs.stat': 2.0.5
run-parallel: 1.2.0
'@nodelib/[email protected]':
dependencies:
'@nodelib/fs.stat': 3.0.0
run-parallel: 1.2.0
'@nodelib/[email protected]': {}
'@nodelib/[email protected]': {}
'@nodelib/[email protected]':
dependencies:
'@nodelib/fs.scandir': 2.1.5
fastq: 1.17.1
'@nodelib/[email protected]':
dependencies:
'@nodelib/fs.scandir': 3.0.0
fastq: 1.17.1
'@nuxt/[email protected]': {}
'@nuxt/[email protected]([email protected])([email protected])([email protected](@types/[email protected])([email protected]))':
@ -5284,32 +5155,6 @@ snapshots:
- supports-color
- typescript
'@nuxt/[email protected]([email protected])([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected]))':
dependencies:
'@eslint/config-inspector': 0.5.2([email protected])
'@nuxt/devtools-kit': 1.3.9([email protected])([email protected])([email protected](@types/[email protected])([email protected]))
'@nuxt/eslint-config': 0.5.0([email protected])([email protected])
'@nuxt/eslint-plugin': 0.5.0([email protected])([email protected])
'@nuxt/kit': 3.12.4([email protected])([email protected])
chokidar: 3.6.0
eslint: 9.8.0
eslint-flat-config-utils: 0.3.0
eslint-typegen: 0.3.0([email protected])
find-up: 7.0.0
get-port-please: 3.1.2
mlly: 1.7.1
pathe: 1.1.2
unimport: 3.10.0([email protected])
transitivePeerDependencies:
- bufferutil
- magicast
- rollup
- supports-color
- typescript
- uWebSockets.js
- utf-8-validate
- vite
'@nuxt/[email protected]([email protected])([email protected])':
dependencies:
'@nuxt/schema': 3.12.4([email protected])
@ -5961,11 +5806,6 @@ snapshots:
path-browserify: 1.0.1
vscode-uri: 3.0.8
'@voxpelli/[email protected](@eslint/[email protected])':
dependencies:
'@eslint/config-array': 0.17.1
'@nodelib/fs.walk': 2.0.0
'@vue-macros/[email protected]([email protected])([email protected]([email protected]))':
dependencies:
'@babel/types': 7.25.2
@ -6306,11 +6146,6 @@ snapshots:
dependencies:
run-applescript: 7.0.0
[email protected]([email protected]):
dependencies:
esbuild: 0.21.5
load-tsconfig: 0.2.5
[email protected]([email protected]):
dependencies:
chokidar: 3.6.0
@ -6868,7 +6703,7 @@ snapshots:
'@eslint-community/regexpp': 4.11.0
comment-parser: 1.4.1
eslint: 9.8.0
jsdoc-type-pratt-parser: 4.0.0
jsdoc-type-pratt-parser: 4.1.0
refa: 0.12.1
regexp-ast-analysis: 0.7.1
scslre: 0.3.0
@ -6917,13 +6752,6 @@ snapshots:
esrecurse: 4.3.0
estraverse: 5.3.0
[email protected]([email protected]):
dependencies:
'@types/eslint': 9.6.0
eslint: 9.8.0
json-schema-to-typescript-lite: 14.0.1
ohash: 1.1.3
[email protected]: {}
[email protected]: {}
@ -7080,12 +6908,6 @@ snapshots:
locate-path: 6.0.0
path-exists: 4.0.0
[email protected]:
dependencies:
locate-path: 7.2.0
path-exists: 5.0.0
unicorn-magic: 0.1.0
[email protected]:
dependencies:
flatted: 3.3.1
@ -7475,6 +7297,8 @@ snapshots:
[email protected]: {}
[email protected]: {}
[email protected]: {}
[email protected]: {}
@ -7485,11 +7309,6 @@ snapshots:
[email protected]: {}
[email protected]:
dependencies:
'@apidevtools/json-schema-ref-parser': 11.7.0
'@types/json-schema': 7.0.15
[email protected]: {}
[email protected]: {}
@ -7618,8 +7437,6 @@ snapshots:
transitivePeerDependencies:
- uWebSockets.js
[email protected]: {}
[email protected]:
dependencies:
mlly: 1.7.1
@ -7633,10 +7450,6 @@ snapshots:
dependencies:
p-locate: 5.0.0
[email protected]:
dependencies:
p-locate: 6.0.0
[email protected]: {}
[email protected]: {}
@ -8100,10 +7913,6 @@ snapshots:
dependencies:
yocto-queue: 0.1.0
[email protected]:
dependencies:
yocto-queue: 1.1.1
[email protected]:
dependencies:
p-limit: 2.3.0
@ -8112,10 +7921,6 @@ snapshots:
dependencies:
p-limit: 3.1.0
[email protected]:
dependencies:
p-limit: 4.0.0
[email protected]: {}
[email protected]: {}
@ -8157,8 +7962,6 @@ snapshots:
[email protected]: {}
[email protected]: {}
[email protected]: {}
[email protected]: {}
@ -9021,24 +8824,6 @@ snapshots:
[email protected]: {}
[email protected]([email protected]):
dependencies:
'@rollup/pluginutils': 5.1.0([email protected])
acorn: 8.12.1
escape-string-regexp: 5.0.0
estree-walker: 3.0.3
fast-glob: 3.3.2
local-pkg: 0.5.0
magic-string: 0.30.11
mlly: 1.7.1
pathe: 1.1.2
pkg-types: 1.1.3
scule: 1.3.0
strip-literal: 2.1.0
unplugin: 1.12.0
transitivePeerDependencies:
- rollup
[email protected]([email protected]):
dependencies:
'@rollup/pluginutils': 5.1.0([email protected])
@ -9419,8 +9204,6 @@ snapshots:
[email protected]: {}
[email protected]: {}
[email protected]: {}
[email protected]:

10
src/utils/WireGuard.ts

@ -1,13 +1,13 @@
import fs from 'node:fs/promises';
import path from 'path';
import debug_logger from 'debug'
const debug = debug_logger('WireGuard')
import crypto from 'node:crypto';
import QRCode from 'qrcode';
import { WG_PATH, WG_HOST, WG_PORT, WG_CONFIG_PORT, WG_MTU, WG_DEFAULT_DNS, WG_DEFAULT_ADDRESS, WG_PERSISTENT_KEEPALIVE, WG_ALLOWED_IPS, WG_PRE_UP, WG_POST_UP, WG_PRE_DOWN, WG_POST_DOWN } from '~/utils/config';
import { exec } from '~/utils/cmd';
import { isValidIPv4 } from '~/utils/ip';
const debug = debug_logger('WireGuard')
class ServerError extends Error {
statusCode: number;
@ -31,7 +31,7 @@ class WireGuard {
config = await fs.readFile(path.join(WG_PATH, 'wg0.json'), 'utf8');
config = JSON.parse(config);
debug('Configuration loaded.');
} catch (err) {
} catch {
const privateKey = await exec('wg genkey');
const publicKey = await exec(`echo ${privateKey} | wg pubkey`, {
log: 'echo ***hidden*** | wg pubkey',
@ -157,9 +157,9 @@ ${client.preSharedKey ? `PresharedKey = ${client.preSharedKey}\n` : ''
.forEach((line) => {
const [
publicKey,
preSharedKey, // eslint-disable-line no-unused-vars
endpoint, // eslint-disable-line no-unused-vars
allowedIps, // eslint-disable-line no-unused-vars
_preSharedKey,
_endpoint,
_allowedIps,
latestHandshakeAt,
transferRx,
transferTx,

4
src/utils/api.ts

@ -1,5 +1,5 @@
/* eslint-disable no-unused-vars */
/* eslint-disable no-undef */
export type APIClient = {
"id": string,

8
src/utils/cmd.ts

@ -1,13 +1,11 @@
import childProcess from 'child_process';
export function exec(cmd, {
log = true,
} = {}) {
export function exec(cmd: string, {log}: {log: boolean|string} = {log: true}) {
if (typeof log === 'string') {
// eslint-disable-next-line no-console
console.log(`$ ${log}`);
} else if (log === true) {
// eslint-disable-next-line no-console
console.log(`$ ${cmd}`);
}

10
src/wgpw.js

@ -6,7 +6,7 @@ const generateHash = async (password) => {
try {
const salt = await bcrypt.genSalt(12);
const hash = await bcrypt.hash(password, salt);
// eslint-disable-next-line no-console
console.log(`PASSWORD_HASH='${hash}'`);
} catch (error) {
throw new Error(`Failed to generate hash : ${error}`);
@ -18,10 +18,10 @@ const comparePassword = async (password, hash) => {
try {
const match = await bcrypt.compare(password, hash);
if (match) {
// eslint-disable-next-line no-console
console.log('Password matches the hash !');
} else {
// eslint-disable-next-line no-console
console.log('Password does not match the hash.');
}
} catch (error) {
@ -44,9 +44,9 @@ const comparePassword = async (password, hash) => {
await generateHash(password);
}
} catch (error) {
// eslint-disable-next-line no-console
console.error(error);
// eslint-disable-next-line no-process-exit
process.exit(1);
}
})();

Loading…
Cancel
Save