Browse Source

typescript, vendors

pull/1250/head
Bernd Storath 11 months ago
parent
commit
619d532315
  1. 207
      src/app.vue
  2. 6
      src/assets/css/app.css
  3. 6
      src/package.json
  4. 5
      src/plugins/apexcharts.client.ts
  5. 113
      src/pnpm-lock.yaml
  6. 11
      src/public/manifest.json
  7. 19
      src/utils/api.ts
  8. 25
      src/utils/localStorage.ts

207
src/app.vue

@ -26,10 +26,8 @@
<path <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" /> 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> </svg>
<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" /> 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> </button>
<!-- Show / hide charts --> <!-- Show / hide charts -->
<label v-if="uiChartType > 0" <label v-if="uiChartType > 0"
@ -216,7 +214,7 @@
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" 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" /> clip-rule="evenodd" />
</svg> </svg>
{{ client.transferTxCurrent | bytes }}/s {{ bytes(client.transferTxCurrent) }}/s
</span> </span>
<!-- Inline Transfer RX --> <!-- Inline Transfer RX -->
@ -229,13 +227,13 @@
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" 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" /> clip-rule="evenodd" />
</svg> </svg>
{{ client.transferRxCurrent | bytes }}/s {{ bytes(client.transferRxCurrent) }}/s
</span> </span>
<!-- Last seen --> <!-- Last seen -->
<span class="text-gray-400 dark:text-neutral-500 whitespace-nowrap" <span class="text-gray-400 dark:text-neutral-500 whitespace-nowrap"
v-if="client.latestHandshakeAt" v-if="client.latestHandshakeAt"
:title="$t('lastSeen') + dateTime(new Date(client.latestHandshakeAt))"> :title="$t('lastSeen') + dateTime(new Date(client.latestHandshakeAt))">
{{ !uiTrafficStats ? " · " : "" }}{{ new Date(client.latestHandshakeAt) | timeago }} {{ !uiTrafficStats ? " · " : "" }}{{ timeago(new Date(client.latestHandshakeAt)) }}
</span> </span>
</div> </div>
</div> </div>
@ -254,8 +252,7 @@
clip-rule="evenodd" /> clip-rule="evenodd" />
</svg> </svg>
<div> <div>
<span class="text-gray-700 dark:text-neutral-200">{{ client.transferTxCurrent | <span class="text-gray-700 dark:text-neutral-200">{{ bytes(client.transferTxCurrent) }}/s</span>
bytes }}/s</span>
<!-- Total TX --> <!-- Total TX -->
<br><span class="font-regular" style="font-size:0.85em">{{ bytes(client.transferTx) <br><span class="font-regular" style="font-size:0.85em">{{ bytes(client.transferTx)
}}</span> }}</span>
@ -275,8 +272,7 @@
clip-rule="evenodd" /> clip-rule="evenodd" />
</svg> </svg>
<div> <div>
<span class="text-gray-700 dark:text-neutral-200">{{ client.transferRxCurrent | <span class="text-gray-700 dark:text-neutral-200">{{ bytes(client.transferRxCurrent) }}/s</span>
bytes }}/s</span>
<!-- Total RX --> <!-- Total RX -->
<br><span class="font-regular" style="font-size:0.85em">{{ bytes(client.transferRx) <br><span class="font-regular" style="font-size:0.85em">{{ bytes(client.transferRx)
}}</span> }}</span>
@ -601,10 +597,36 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import "~/assets/css/app.css";
import {sha256} from "js-sha256";
import {format as timeago} from "timeago.js";
useHead({ useHead({
bodyAttrs: { bodyAttrs: {
class: 'bg-gray-50 dark:bg-neutral-800' class: 'bg-gray-50 dark:bg-neutral-800'
} },
link: [
{
rel: "manifest",
href: "/manifest.json"
},
{
rel: "icon",
type: "image/png",
href: "/favicon.png"
},
{
rel: "apple-touch-icon",
href: "/apple-touch-icon.png"
}
],
meta: [
{
name: "apple-mobile-web-app-capable",
content: "yes"
}
],
title: "WireGuard"
}) })
const UI_CHART_TYPES = [ const UI_CHART_TYPES = [
@ -625,25 +647,54 @@ const authenticating = ref(false);
const password = ref<null|string>(null); const password = ref<null|string>(null);
const requiresPassword = ref<null|boolean>(null); const requiresPassword = ref<null|boolean>(null);
const clients = ref<null|unknown[]>(null); type Client = {
const clientsPersist = ref({}); id: string,
const clientDelete = ref(null); name: string,
const clientCreate = ref(null); address: string
const clientCreateName = ref(''); enabled: boolean,
const clientEditName = ref(null); transferRx: number,
const clientEditNameId = ref(null); transferTx: number,
const clientEditAddress = ref(null); transferTxSeries: number,
const clientEditAddressId = ref(null); transferRxSeries: number,
const qrcode = ref(null); avatar?: string,
latestHandshakeAt: string|null,
createdAt: Date,
downloadableConfig: boolean,
updatedAt: Date
} & Omit<ClientPersist, 'transferRxSeries' | 'transferTxSeries'>
type ClientPersist = {
transferRxHistory: number[],
transferRxPrevious: number
transferRxCurrent: number,
transferRxSeries: {name: string, data: number[]}[],
hoverRx: unknown
transferTxHistory: number[],
transferTxPrevious: number,
transferTxCurrent: number,
transferTxSeries: {name: string, data: number[]}[],
hoverTx: unknown,
}
const clients = ref<null|Client[]>(null);
const clientsPersist = ref<Record<string, ClientPersist>>({});
const clientDelete = ref<null|Client>(null);
const clientCreate = ref<null|boolean>(null);
const clientCreateName = ref<string>('');
const clientEditName = ref<null|string>(null);
const clientEditNameId = ref<null|string>(null);
const clientEditAddress = ref<null|string>(null);
const clientEditAddressId = ref<null|string>(null);
const qrcode = ref<null|string>(null);
const currentRelease = ref(null); const currentRelease = ref(null);
const latestRelease = ref(null); const latestRelease = ref<null | {version: number, changelog: string}>(null);
const uiTrafficStats = ref(false); const uiTrafficStats = ref(false);
const uiChartType = ref(0); const uiChartType = ref(0);
const uiShowCharts = ref(getItem('uiShowCharts') === '1'); const uiShowCharts = ref(getItem('uiShowCharts') === '1');
const uiTheme = ref(getItem('theme') || 'auto'); const uiTheme = ref<Theme>(getItem('theme') || 'auto');
const prefersDarkScheme = import.meta.client ? window.matchMedia('(prefers-color-scheme: dark)') : null; const prefersDarkScheme = import.meta.client ? window.matchMedia('(prefers-color-scheme: dark)') : null;
const theme = computed(() => { const theme = computed(() => {
@ -754,12 +805,12 @@ async function refresh({
client.avatar = `https://gravatar.com/avatar/${sha256(client.name.toLowerCase().trim())}.jpg`; client.avatar = `https://gravatar.com/avatar/${sha256(client.name.toLowerCase().trim())}.jpg`;
} }
if (!clientsPersist[client.id]) { if (!clientsPersist.value[client.id]) {
clientsPersist[client.id] = {}; clientsPersist.value[client.id] = {};
clientsPersist[client.id].transferRxHistory = Array(50).fill(0); clientsPersist.value[client.id].transferRxHistory = Array(50).fill(0);
clientsPersist[client.id].transferRxPrevious = client.transferRx; clientsPersist.value[client.id].transferRxPrevious = client.transferRx;
clientsPersist[client.id].transferTxHistory = Array(50).fill(0); clientsPersist.value[client.id].transferTxHistory = Array(50).fill(0);
clientsPersist[client.id].transferTxPrevious = client.transferTx; clientsPersist.value[client.id].transferTxPrevious = client.transferTx;
} }
// Debug // Debug
@ -768,47 +819,47 @@ async function refresh({
// client.latestHandshakeAt = new Date(); // client.latestHandshakeAt = new Date();
// this.requiresPassword = true; // this.requiresPassword = true;
clientsPersist[client.id].transferRxCurrent = client.transferRx - clientsPersist[client.id].transferRxPrevious; clientsPersist.value[client.id].transferRxCurrent = client.transferRx - clientsPersist.value[client.id].transferRxPrevious;
clientsPersist[client.id].transferRxPrevious = client.transferRx; clientsPersist.value[client.id].transferRxPrevious = client.transferRx;
clientsPersist[client.id].transferTxCurrent = client.transferTx - clientsPersist[client.id].transferTxPrevious; clientsPersist.value[client.id].transferTxCurrent = client.transferTx - clientsPersist.value[client.id].transferTxPrevious;
clientsPersist[client.id].transferTxPrevious = client.transferTx; clientsPersist.value[client.id].transferTxPrevious = client.transferTx;
if (updateCharts) { if (updateCharts) {
clientsPersist[client.id].transferRxHistory.push(clientsPersist[client.id].transferRxCurrent); clientsPersist.value[client.id].transferRxHistory.push(clientsPersist.value[client.id].transferRxCurrent);
clientsPersist[client.id].transferRxHistory.shift(); clientsPersist.value[client.id].transferRxHistory.shift();
clientsPersist[client.id].transferTxHistory.push(clientsPersist[client.id].transferTxCurrent); clientsPersist.value[client.id].transferTxHistory.push(clientsPersist.value[client.id].transferTxCurrent);
clientsPersist[client.id].transferTxHistory.shift(); clientsPersist.value[client.id].transferTxHistory.shift();
clientsPersist[client.id].transferTxSeries = [{ clientsPersist.value[client.id].transferTxSeries = [{
name: 'Tx', name: 'Tx',
data: clientsPersist[client.id].transferTxHistory, data: clientsPersist.value[client.id].transferTxHistory,
}]; }];
clientsPersist[client.id].transferRxSeries = [{ clientsPersist.value[client.id].transferRxSeries = [{
name: 'Rx', name: 'Rx',
data: clientsPersist[client.id].transferRxHistory, data: clientsPersist.value[client.id].transferRxHistory,
}]; }];
client.transferTxHistory = clientsPersist[client.id].transferTxHistory; client.transferTxHistory = clientsPersist.value[client.id].transferTxHistory;
client.transferRxHistory = clientsPersist[client.id].transferRxHistory; client.transferRxHistory = clientsPersist.value[client.id].transferRxHistory;
client.transferMax = Math.max(...client.transferTxHistory, ...client.transferRxHistory); client.transferMax = Math.max(...client.transferTxHistory, ...client.transferRxHistory);
client.transferTxSeries = clientsPersist[client.id].transferTxSeries; client.transferTxSeries = clientsPersist.value[client.id].transferTxSeries;
client.transferRxSeries = clientsPersist[client.id].transferRxSeries; client.transferRxSeries = clientsPersist.value[client.id].transferRxSeries;
} }
client.transferTxCurrent = clientsPersist[client.id].transferTxCurrent; client.transferTxCurrent = clientsPersist.value[client.id].transferTxCurrent;
client.transferRxCurrent = clientsPersist[client.id].transferRxCurrent; client.transferRxCurrent = clientsPersist.value[client.id].transferRxCurrent;
client.hoverTx = clientsPersist[client.id].hoverTx; client.hoverTx = clientsPersist.value[client.id].hoverTx;
client.hoverRx = clientsPersist[client.id].hoverRx; client.hoverRx = clientsPersist.value[client.id].hoverRx;
return client; return client;
}); });
} }
function login(e) { function login(e: Event) {
e.preventDefault(); e.preventDefault();
if (!password) return; if (!password) return;
@ -816,7 +867,7 @@ function login(e) {
authenticating.value = true; authenticating.value = true;
api.createSession({ api.createSession({
password: password.value, password: password.value
}) })
.then(async () => { .then(async () => {
const session = await api.getSession(); const session = await api.getSession();
@ -834,7 +885,7 @@ function login(e) {
}); });
} }
function logout(e) { function logout(e: Event) {
e.preventDefault(); e.preventDefault();
api.deleteSession() api.deleteSession()
@ -854,27 +905,36 @@ function createClient() {
.catch((err) => alert(err.message || err.toString())) .catch((err) => alert(err.message || err.toString()))
.finally(() => refresh().catch(console.error)); .finally(() => refresh().catch(console.error));
} }
function deleteClient(client) { function deleteClient(client: Client|null) {
if (client === null) {
return;
}
api.deleteClient({ clientId: client.id }) api.deleteClient({ clientId: client.id })
.catch((err) => alert(err.message || err.toString())) .catch((err) => alert(err.message || err.toString()))
.finally(() => refresh().catch(console.error)); .finally(() => refresh().catch(console.error));
} }
function enableClient(client) { function enableClient(client: Client) {
api.enableClient({ clientId: client.id }) api.enableClient({ clientId: client.id })
.catch((err) => alert(err.message || err.toString())) .catch((err) => alert(err.message || err.toString()))
.finally(() => refresh().catch(console.error)); .finally(() => refresh().catch(console.error));
} }
function disableClient(client) { function disableClient(client: Client) {
api.disableClient({ clientId: client.id }) api.disableClient({ clientId: client.id })
.catch((err) => alert(err.message || err.toString())) .catch((err) => alert(err.message || err.toString()))
.finally(() => refresh().catch(console.error)); .finally(() => refresh().catch(console.error));
} }
function updateClientName(client, name: string) { function updateClientName(client: Client, name: string) {
if (name === null) {
return;
}
api.updateClientName({ clientId: client.id, name }) api.updateClientName({ clientId: client.id, name })
.catch((err) => alert(err.message || err.toString())) .catch((err) => alert(err.message || err.toString()))
.finally(() => refresh().catch(console.error)); .finally(() => refresh().catch(console.error));
} }
function updateClientAddress(client, address: string) { function updateClientAddress(client: Client, address: string|null) {
if (address === null) {
return;
}
api.updateClientAddress({ clientId: client.id, address }) api.updateClientAddress({ clientId: client.id, address })
.catch((err) => alert(err.message || err.toString())) .catch((err) => alert(err.message || err.toString()))
.finally(() => refresh().catch(console.error)); .finally(() => refresh().catch(console.error));
@ -895,15 +955,16 @@ function restoreConfig(e) {
alert('Failed to load your file!'); alert('Failed to load your file!');
} }
} }
function toggleTheme() { function toggleTheme() {
const themes = ['light', 'dark', 'auto']; const themes = ['light', 'dark', 'auto'] as Theme[];
const currentIndex = themes.indexOf(uiTheme.value); const currentIndex = themes.indexOf(uiTheme.value);
const newIndex = (currentIndex + 1) % themes.length; const newIndex = (currentIndex + 1) % themes.length;
uiTheme.value = themes[newIndex]; uiTheme.value = themes[newIndex];
setItem('theme', uiTheme.value); setItem('theme', uiTheme.value);
setTheme(uiTheme.value); setTheme(uiTheme.value);
} }
function setTheme(theme) { function setTheme(theme: Theme) {
const { classList } = document.documentElement; const { classList } = document.documentElement;
const shouldAddDarkClass = theme === 'dark' || (theme === 'auto' && prefersDarkScheme?.matches); const shouldAddDarkClass = theme === 'dark' || (theme === 'auto' && prefersDarkScheme?.matches);
classList.toggle('dark', shouldAddDarkClass); classList.toggle('dark', shouldAddDarkClass);
@ -914,7 +975,7 @@ function handlePrefersChange(e: MediaQueryListEventMap["change"]) {
} }
} }
function toggleCharts() { function toggleCharts() {
setItem('uiShowCharts', uiShowCharts.value ? 1 : 0); setItem('uiShowCharts', uiShowCharts.value ? '1' : '0');
} }
const {availableLocales, locale} = useI18n(); const {availableLocales, locale} = useI18n();
@ -972,7 +1033,7 @@ onMounted(() => {
.then((releases) => { .then((releases) => {
const releasesArray = Object.entries(releases).map(([version, changelog]) => ({ const releasesArray = Object.entries(releases).map(([version, changelog]) => ({
version: parseInt(version, 10), version: parseInt(version, 10),
changelog, changelog: changelog as string,
})); }));
releasesArray.sort((a, b) => { releasesArray.sort((a, b) => {
return b.version - a.version; return b.version - a.version;
@ -1009,13 +1070,12 @@ const chartOptionsRX = computed(() => {
}) })
const updateCharts = computed(() => { const updateCharts = computed(() => {
return uiChartType.value > 0 && uiShowCharts; return uiChartType.value > 0 && uiShowCharts.value;
}) })
function bytes(bytes, decimals, kib, maxunit) { function bytes(bytes: number, decimals? = 2, kib? = false, maxunit?: string) {
kib = kib || false;
if (bytes === 0) return '0 B'; if (bytes === 0) return '0 B';
if (Number.isNaN(parseFloat(bytes)) && !Number.isFinite(bytes)) return 'NaN'; if (Number.isNaN(bytes) && !Number.isFinite(bytes)) return 'NaN';
const k = kib ? 1024 : 1000; const k = kib ? 1024 : 1000;
const dm = decimals != null && !Number.isNaN(decimals) && decimals >= 0 ? decimals : 2; const dm = decimals != null && !Number.isNaN(decimals) && decimals >= 0 ? decimals : 2;
const sizes = kib const sizes = kib
@ -1026,26 +1086,7 @@ function bytes(bytes, decimals, kib, maxunit) {
const index = sizes.indexOf(maxunit); const index = sizes.indexOf(maxunit);
if (index !== -1) i = index; if (index !== -1) i = index;
} }
// eslint-disable-next-line no-restricted-properties
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`; return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
} }
function getItem(item: string) {
if (import.meta.client) {
return localStorage.getItem(item)
} else {
return undefined
}
}
function setItem(item: string, value: string) {
if (import.meta.client) {
localStorage.setItem(item, value)
return true
} else {
return false
}
}
</script> </script>

6
src/assets/css/app.css

@ -0,0 +1,6 @@
[v-cloak] {
display: none;
}
.line-chart .apexcharts-svg{
transform: translateY(3px);
}

6
src/package.json

@ -18,11 +18,15 @@
"dependencies": { "dependencies": {
"@nuxtjs/i18n": "^8.3.3", "@nuxtjs/i18n": "^8.3.3",
"@nuxtjs/tailwindcss": "^6.12.1", "@nuxtjs/tailwindcss": "^6.12.1",
"apexcharts": "^3.51.0",
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3",
"debug": "^4.3.6", "debug": "^4.3.6",
"js-sha256": "^0.11.0",
"nuxt": "^3.12.4", "nuxt": "^3.12.4",
"qrcode": "^1.5.3", "qrcode": "^1.5.3",
"vue": "latest" "timeago.js": "^4.0.2",
"vue": "latest",
"vue3-apexcharts": "^1.5.3"
}, },
"devDependencies": { "devDependencies": {
"@nuxt/eslint": "^0.5.0", "@nuxt/eslint": "^0.5.0",

5
src/plugins/apexcharts.client.ts

@ -0,0 +1,5 @@
import VueApexCharts from "vue3-apexcharts";
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.use(VueApexCharts);
});

113
src/pnpm-lock.yaml

@ -14,21 +14,33 @@ importers:
'@nuxtjs/tailwindcss': '@nuxtjs/tailwindcss':
specifier: ^6.12.1 specifier: ^6.12.1
version: 6.12.1([email protected])([email protected]) version: 6.12.1([email protected])([email protected])
apexcharts:
specifier: ^3.51.0
version: 3.51.0
bcryptjs: bcryptjs:
specifier: ^2.4.3 specifier: ^2.4.3
version: 2.4.3 version: 2.4.3
debug: debug:
specifier: ^4.3.6 specifier: ^4.3.6
version: 4.3.6 version: 4.3.6
js-sha256:
specifier: ^0.11.0
version: 0.11.0
nuxt: nuxt:
specifier: ^3.12.4 specifier: ^3.12.4
version: 3.12.4(@parcel/[email protected])(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected]))([email protected]([email protected])) version: 3.12.4(@parcel/[email protected])(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected]))([email protected]([email protected]))
qrcode: qrcode:
specifier: ^1.5.3 specifier: ^1.5.3
version: 1.5.3 version: 1.5.3
timeago.js:
specifier: ^4.0.2
version: 4.0.2
vue: vue:
specifier: latest specifier: latest
version: 3.4.35([email protected]) version: 3.4.35([email protected])
vue3-apexcharts:
specifier: ^1.5.3
version: 1.5.3([email protected])([email protected]([email protected]))
devDependencies: devDependencies:
'@nuxt/eslint': '@nuxt/eslint':
specifier: ^0.5.0 specifier: ^0.5.0
@ -1450,6 +1462,9 @@ packages:
'@vue/[email protected]': '@vue/[email protected]':
resolution: {integrity: sha512-hvuhBYYDe+b1G8KHxsQ0diDqDMA8D9laxWZhNAjE83VZb5UDaXl9Xnz7cGdDSyiHM90qqI/CyGMcpBpiDy6VVQ==} resolution: {integrity: sha512-hvuhBYYDe+b1G8KHxsQ0diDqDMA8D9laxWZhNAjE83VZb5UDaXl9Xnz7cGdDSyiHM90qqI/CyGMcpBpiDy6VVQ==}
'@yr/[email protected]':
resolution: {integrity: sha512-FQXkOta0XBSUPHndIKON2Y9JeQz5ZeMqLYZVVK93FliNBFm7LNMIZmY6FrMEB9XPcDbE2bekMbZD6kzDkxwYjA==}
[email protected]: [email protected]:
resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
@ -1518,6 +1533,9 @@ packages:
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
engines: {node: '>= 8'} engines: {node: '>= 8'}
[email protected]:
resolution: {integrity: sha512-WpCdVdGiJjf9SAyEeg2rl3q5OqCcNqiEmH0+filMraUiH6Vqyn5GFeMMyH0pon44xjNr1G0xzIRERKRmsGEuRA==}
[email protected]: [email protected]:
resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==}
@ -2701,6 +2719,9 @@ packages:
resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==} resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==}
hasBin: true hasBin: true
[email protected]:
resolution: {integrity: sha512-6xNlKayMZvds9h1Y1VWc0fQHQ82BxTXizWPEtEeGvmOUYpBRy4gbWroHLpzowe6xiQhHpelCQiE7HEdznyBL9Q==}
[email protected]: [email protected]:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
@ -3898,6 +3919,37 @@ packages:
[email protected]: [email protected]:
resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==} resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==}
[email protected]:
resolution: {integrity: sha512-JzNHBc2fLQMzYCZ90KZHN2ohXL0BQJGQimK1kGk6AvSeibuKcIdDX9Kr0dT9+UJ5O8nYA0RB839Lhvk4CY4MZw==}
engines: {node: '>= 0.8.0'}
[email protected]:
resolution: {integrity: sha512-//ctPdJMGy22YoYGV+3HEfHbm6/69LJUTAqI2/5qBvaNHZ9uUFVC82B0Pl299HzgH13rKrBgi4+XyXXyVWWthA==}
engines: {node: '>= 0.8.0'}
[email protected]:
resolution: {integrity: sha512-xkGBwU+dKBzqg5PtilaTb0EYPqPfJ9Q6saVldX+5vCRy31P6TlRCP3U9NxH3HEufkKkpNgdTLBJnmhDHeTqAkw==}
engines: {node: '>= 0.8.0'}
[email protected]:
resolution: {integrity: sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA==}
[email protected]:
resolution: {integrity: sha512-49HWI9X4XQR/JG1qXkSDV8xViuTLIWm/B/7YuQELV5KMOPtXjiwH4XPJvr/ghEDibmLQ9Oc22dpWpG0vUDDNww==}
engines: {node: '>= 0.8.0'}
[email protected]:
resolution: {integrity: sha512-9k5sXJuPKp+mVzXNvxz7U0uC9oVMQrrf7cFsETznzUDDm0x8+77dtZkWdMfRlmbkEEYvUn9btKuZ3n41oNA+uw==}
engines: {node: '>= 0.8.0'}
[email protected]:
resolution: {integrity: sha512-tH6ABEyJsAOVAhwcCjF8mw4crjXSI1aa7j2VQR8ZuJ37H2MBUbyeqYr5nEO7sSN3cy9AR9DUwNg0t/962HlDbQ==}
engines: {node: '>= 0.8.0'}
[email protected]:
resolution: {integrity: sha512-h5IS/hKkuVCbKSieR9uQCj9w+zLHoPh+ce19bBYyqF53g6mnPB8sAtIbe1s9dh2S2fCmYX2xel1Ln3PJBbK4kw==}
engines: {node: '>= 0.8.0'}
[email protected]: [email protected]:
resolution: {integrity: sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==} resolution: {integrity: sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==}
engines: {node: '>=14.0.0'} engines: {node: '>=14.0.0'}
@ -3952,6 +4004,9 @@ packages:
[email protected]: [email protected]:
resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
[email protected]:
resolution: {integrity: sha512-a7wPxPdVlQL7lqvitHGGRsofhdwtkoSXPGATFuSOA2i1ZNQEPLrGnj68vOp2sOJTCFAQVXPeNMX/GctBaO9L2w==}
[email protected]: [email protected]:
resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
@ -4302,6 +4357,12 @@ packages:
peerDependencies: peerDependencies:
typescript: '>=5.0.0' typescript: '>=5.0.0'
[email protected]:
resolution: {integrity: sha512-yaHTPoj0iVKAtEVg8wEwIwwvf0VG+lPYNufCf3txRzYQOqdKPoZaZ9P3Dj3X+2A1XY9O1kcTk9HVqvLo+rppvQ==}
peerDependencies:
apexcharts: '> 3.0.0'
vue: '> 3.0.0'
[email protected]: [email protected]:
resolution: {integrity: sha512-+fl/GLmI4GPileHftVlCdB7fUL4aziPcqTudpTGXCT8s+iZWuOCeNEB5haX6Uz2IpRrbEXOgIFbe+XciCuGbNQ==} resolution: {integrity: sha512-+fl/GLmI4GPileHftVlCdB7fUL4aziPcqTudpTGXCT8s+iZWuOCeNEB5haX6Uz2IpRrbEXOgIFbe+XciCuGbNQ==}
peerDependencies: peerDependencies:
@ -6046,6 +6107,8 @@ snapshots:
'@vue/[email protected]': {} '@vue/[email protected]': {}
'@yr/[email protected]': {}
[email protected]: {} [email protected]: {}
[email protected]: [email protected]:
@ -6107,6 +6170,16 @@ snapshots:
normalize-path: 3.0.0 normalize-path: 3.0.0
picomatch: 2.3.1 picomatch: 2.3.1
[email protected]:
dependencies:
'@yr/monotone-cubic-spline': 1.0.3
svg.draggable.js: 2.2.2
svg.easing.js: 2.0.0
svg.filter.js: 2.0.2
svg.pathmorphing.js: 0.1.3
svg.resize.js: 1.4.3
svg.select.js: 3.0.1
[email protected]: {} [email protected]: {}
[email protected]: [email protected]:
@ -7390,6 +7463,8 @@ snapshots:
[email protected]: {} [email protected]: {}
[email protected]: {}
[email protected]: {} [email protected]: {}
[email protected]: {} [email protected]: {}
@ -8732,6 +8807,37 @@ snapshots:
[email protected]: {} [email protected]: {}
[email protected]:
dependencies:
svg.js: 2.7.1
[email protected]:
dependencies:
svg.js: 2.7.1
[email protected]:
dependencies:
svg.js: 2.7.1
[email protected]: {}
[email protected]:
dependencies:
svg.js: 2.7.1
[email protected]:
dependencies:
svg.js: 2.7.1
svg.select.js: 2.1.2
[email protected]:
dependencies:
svg.js: 2.7.1
[email protected]:
dependencies:
svg.js: 2.7.1
[email protected]: [email protected]:
dependencies: dependencies:
'@trysound/sax': 0.2.0 '@trysound/sax': 0.2.0
@ -8828,6 +8934,8 @@ snapshots:
dependencies: dependencies:
any-promise: 1.3.0 any-promise: 1.3.0
[email protected]: {}
[email protected]: {} [email protected]: {}
[email protected]: {} [email protected]: {}
@ -9194,6 +9302,11 @@ snapshots:
semver: 7.6.3 semver: 7.6.3
typescript: 5.5.4 typescript: 5.5.4
[email protected]([email protected])([email protected]([email protected])):
dependencies:
apexcharts: 3.51.0
vue: 3.4.35([email protected])
[email protected]([email protected]): [email protected]([email protected]):
dependencies: dependencies:
'@vue/compiler-dom': 3.4.35 '@vue/compiler-dom': 3.4.35

11
src/public/manifest.json

@ -0,0 +1,11 @@
{
"name": "WireGuard",
"display": "standalone",
"background_color": "#fff",
"icons": [
{
"src": "/favicon.png",
"type": "image/png"
}
]
}

19
src/utils/api.ts

@ -1,6 +1,21 @@
/* eslint-disable no-unused-vars */ /* eslint-disable no-unused-vars */
/* eslint-disable no-undef */ /* eslint-disable no-undef */
export type APIClient = {
"id": string,
"name": string,
"enabled": boolean,
"address": string,
"publicKey": string,
"createdAt": string,
"updatedAt": string,
"downloadableConfig": boolean,
"persistentKeepalive": string,
"latestHandshakeAt": null,
"transferRx": number,
"transferTx": number
}
class API { class API {
async call({ method, path, body }: { async call({ method, path, body }: {
@ -66,7 +81,7 @@ class API {
}); });
} }
async createSession({ password }: {password: string}) { async createSession({ password }: {password: string|null}) {
return this.call({ return this.call({
method: 'post', method: 'post',
path: '/session', path: '/session',
@ -85,7 +100,7 @@ class API {
return this.call({ return this.call({
method: 'get', method: 'get',
path: '/wireguard/client', path: '/wireguard/client',
}).then((clients) => clients.map((client) => ({ }).then((clients: APIClient[]) => clients.map((client) => ({
...client, ...client,
createdAt: new Date(client.createdAt), createdAt: new Date(client.createdAt),
updatedAt: new Date(client.updatedAt), updatedAt: new Date(client.updatedAt),

25
src/utils/localStorage.ts

@ -0,0 +1,25 @@
export type Theme = 'light' | 'dark' | 'auto'
export type LocalStorage = {
theme: Theme,
uiShowCharts: '1' | '0',
lang: string
}
export function getItem<K extends keyof LocalStorage>(item: K): LocalStorage[K]|null {
if (import.meta.client) {
return localStorage.getItem(item) as LocalStorage[K]|null
} else {
return null
}
}
export function setItem<K extends keyof LocalStorage>(item: K, value: LocalStorage[K]) {
if (import.meta.client) {
localStorage.setItem(item, value)
return true
} else {
return false
}
}
Loading…
Cancel
Save