mirror of https://github.com/wg-easy/wg-easy
Browse Source
* deprecate other languages new ui has too many new strings * fix wrong license in readme * properly fetch release * order safe data structure for migrations * empty server allowed ips by default * show userconfig in admin panel * remove routes, fix config * add ability to update clients * handle form submit using js avoid weird behavior with FormData * global toast, be able to update client * update packages * fix date field * delete client using radix dialog * remove lang from backend, let users decide * be able to change interface and general * be able to update user config * consistent allowedips * fix array field * improve avatar, code cleanup * basic metrics support * remove dateTime helper * be able to change hooks * start cidr update * be able to update cidrpull/1619/head
committed by
GitHub
103 changed files with 2331 additions and 2546 deletions
@ -5,5 +5,5 @@ |
|||
"dev": "docker compose -f docker-compose.dev.yml up", |
|||
"build": "docker build -t wg-easy ." |
|||
}, |
|||
"packageManager": "[email protected].0" |
|||
"packageManager": "[email protected].3" |
|||
} |
|||
|
@ -1,99 +1,41 @@ |
|||
<template> |
|||
<div |
|||
v-if="modalStore.clientDelete" |
|||
class="fixed inset-0 z-10 overflow-y-auto" |
|||
> |
|||
<div |
|||
class="flex min-h-screen items-center justify-center px-4 pb-20 pt-4 text-center sm:block sm:p-0" |
|||
> |
|||
<!-- |
|||
Background overlay, show/hide based on modal state. |
|||
|
|||
Entering: "ease-out duration-300" |
|||
From: "opacity-0" |
|||
To: "opacity-100" |
|||
Leaving: "ease-in duration-200" |
|||
From: "opacity-100" |
|||
To: "opacity-0" |
|||
--> |
|||
<div class="fixed inset-0 transition-opacity" aria-hidden="true"> |
|||
<div |
|||
class="absolute inset-0 bg-gray-500 opacity-75 dark:bg-black dark:opacity-50" |
|||
/> |
|||
</div> |
|||
|
|||
<!-- This element is to trick the browser into centering the modal contents. --> |
|||
<span |
|||
class="hidden sm:inline-block sm:h-screen sm:align-middle" |
|||
aria-hidden="true" |
|||
>​</span |
|||
> |
|||
<!-- |
|||
Modal panel, show/hide based on modal state. |
|||
|
|||
Entering: "ease-out duration-300" |
|||
From: "opacity-0 tranneutral-y-4 sm:tranneutral-y-0 sm:scale-95" |
|||
To: "opacity-100 tranneutral-y-0 sm:scale-100" |
|||
Leaving: "ease-in duration-200" |
|||
From: "opacity-100 tranneutral-y-0 sm:scale-100" |
|||
To: "opacity-0 tranneutral-y-4 sm:tranneutral-y-0 sm:scale-95" |
|||
--> |
|||
<div |
|||
class="inline-block w-full transform overflow-hidden rounded-lg bg-white text-left align-bottom shadow-xl transition-all sm:my-8 sm:max-w-lg sm:align-middle dark:bg-neutral-700" |
|||
role="dialog" |
|||
aria-modal="true" |
|||
aria-labelledby="modal-headline" |
|||
<DialogRoot :modal="true"> |
|||
<DialogTrigger :class="triggerClass"><slot /></DialogTrigger> |
|||
<DialogPortal> |
|||
<DialogOverlay |
|||
class="data-[state=open]:animate-overlayShow fixed inset-0 z-30 bg-gray-500 opacity-75 dark:bg-black dark:opacity-50" |
|||
/> |
|||
<DialogContent |
|||
class="data-[state=open]:animate-contentShow fixed left-1/2 top-1/2 z-[100] max-h-[85vh] w-[90vw] max-w-md -translate-x-1/2 -translate-y-1/2 rounded-md p-6 shadow-2xl focus:outline-none dark:bg-neutral-700" |
|||
> |
|||
<div class="bg-white px-4 pb-4 pt-5 sm:p-6 sm:pb-4 dark:bg-neutral-700"> |
|||
<div class="sm:flex sm:items-start"> |
|||
<div |
|||
class="mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10" |
|||
> |
|||
<IconsWarning class="h-6 w-6 text-red-600" /> |
|||
</div> |
|||
<div class="mt-3 text-center sm:ml-4 sm:mt-0 sm:text-left"> |
|||
<h3 |
|||
id="modal-headline" |
|||
class="text-lg font-medium leading-6 text-gray-900 dark:text-neutral-200" |
|||
> |
|||
{{ $t('deleteClient') }} |
|||
</h3> |
|||
<div class="mt-2"> |
|||
<p class="text-sm text-gray-500 dark:text-neutral-300"> |
|||
{{ $t('deleteDialog1') }} |
|||
<strong>{{ modalStore.clientDelete.name }}</strong |
|||
>? {{ $t('deleteDialog2') }} |
|||
</p> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div |
|||
class="bg-gray-50 px-4 py-3 sm:flex sm:flex-row-reverse sm:px-6 dark:bg-neutral-600" |
|||
<DialogTitle |
|||
class="m-0 text-lg font-semibold text-gray-900 dark:text-neutral-200" |
|||
> |
|||
{{ $t('deleteClient') }} |
|||
</DialogTitle> |
|||
<DialogDescription |
|||
class="mb-5 mt-2 text-sm leading-normal text-gray-500 dark:text-neutral-300" |
|||
> |
|||
<button |
|||
type="button" |
|||
class="inline-flex w-full justify-center rounded-md border border-transparent bg-red-600 px-4 py-2 text-base font-medium text-white shadow-sm hover:bg-red-700 focus:outline-none sm:ml-3 sm:w-auto sm:text-sm dark:bg-red-600 dark:text-white dark:hover:bg-red-700" |
|||
@click=" |
|||
modalStore.deleteClient(modalStore.clientDelete); |
|||
modalStore.clientDelete = null; |
|||
" |
|||
> |
|||
{{ $t('deleteClient') }} |
|||
</button> |
|||
<button |
|||
type="button" |
|||
class="mt-3 inline-flex w-full justify-center rounded-md border border-gray-300 bg-white px-4 py-2 text-base font-medium text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none sm:ml-3 sm:mt-0 sm:w-auto sm:text-sm dark:border-neutral-500 dark:bg-neutral-500 dark:text-neutral-50 dark:hover:border-neutral-600 dark:hover:bg-neutral-600" |
|||
@click="modalStore.clientDelete = null" |
|||
> |
|||
{{ $t('cancel') }} |
|||
</button> |
|||
{{ $t('deleteDialog1') }} |
|||
<strong>{{ 'test' }}</strong |
|||
>? {{ $t('deleteDialog2') }} |
|||
</DialogDescription> |
|||
<div class="mt-6 flex justify-end gap-2"> |
|||
<DialogClose as-child> |
|||
<BaseButton>{{ $t('cancel') }}</BaseButton> |
|||
</DialogClose> |
|||
<DialogClose as-child> |
|||
<BaseButton @click="$emit('delete')">{{ |
|||
$t('deleteClient') |
|||
}}</BaseButton> |
|||
</DialogClose> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</DialogContent> |
|||
</DialogPortal> |
|||
</DialogRoot> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
const modalStore = useModalStore(); |
|||
<script lang="ts" setup> |
|||
defineEmits(['delete']); |
|||
defineProps<{ triggerClass?: string }>(); |
|||
</script> |
|||
|
@ -0,0 +1,49 @@ |
|||
<template> |
|||
<DialogRoot :modal="true"> |
|||
<DialogTrigger :class="triggerClass"><slot /></DialogTrigger> |
|||
<DialogPortal> |
|||
<DialogOverlay |
|||
class="data-[state=open]:animate-overlayShow fixed inset-0 z-30 bg-gray-500 opacity-75 dark:bg-black dark:opacity-50" |
|||
/> |
|||
<DialogContent |
|||
class="data-[state=open]:animate-contentShow fixed left-1/2 top-1/2 z-[100] max-h-[85vh] w-[90vw] max-w-md -translate-x-1/2 -translate-y-1/2 rounded-md p-6 shadow-2xl focus:outline-none dark:bg-neutral-700" |
|||
> |
|||
<DialogTitle |
|||
class="m-0 text-lg font-semibold text-gray-900 dark:text-neutral-200" |
|||
> |
|||
Change CIDR |
|||
</DialogTitle> |
|||
<DialogDescription |
|||
class="mb-5 mt-2 text-sm leading-normal text-gray-500 dark:text-neutral-300" |
|||
> |
|||
<FormGroup> |
|||
<FormTextField id="address4" v-model="address4" label="IPv4" /> |
|||
<FormTextField id="address6" v-model="address6" label="IPv6" /> |
|||
</FormGroup> |
|||
</DialogDescription> |
|||
<div class="mt-6 flex justify-end gap-2"> |
|||
<DialogClose as-child> |
|||
<BaseButton>{{ $t('cancel') }}</BaseButton> |
|||
</DialogClose> |
|||
<DialogClose as-child> |
|||
<BaseButton @click="$emit('change', address4, address6)" |
|||
>Change</BaseButton |
|||
> |
|||
</DialogClose> |
|||
</div> |
|||
</DialogContent> |
|||
</DialogPortal> |
|||
</DialogRoot> |
|||
</template> |
|||
|
|||
<script lang="ts" setup> |
|||
defineEmits(['change']); |
|||
const props = defineProps<{ |
|||
triggerClass?: string; |
|||
address4: string; |
|||
address6: string; |
|||
}>(); |
|||
|
|||
const address4 = ref(props.address4); |
|||
const address6 = ref(props.address6); |
|||
</script> |
@ -1,11 +1,16 @@ |
|||
<template> |
|||
<input |
|||
:value="label" |
|||
type="button" |
|||
:type="type ?? 'button'" |
|||
class="col-span-2 rounded-lg border-2 border-gray-100 py-2 text-gray-500 focus:border-red-800 focus:outline-0 focus:ring-0 dark:border-neutral-800 dark:bg-neutral-700 dark:text-neutral-200 dark:placeholder:text-neutral-400" |
|||
/> |
|||
</template> |
|||
|
|||
<script lang="ts" setup> |
|||
defineProps<{ label: string }>(); |
|||
import type { InputTypeHTMLAttribute } from 'vue'; |
|||
|
|||
defineProps<{ |
|||
label: string; |
|||
type?: InputTypeHTMLAttribute; |
|||
}>(); |
|||
</script> |
|||
|
@ -0,0 +1,26 @@ |
|||
<template> |
|||
<Label :for="id" class="font-semibold md:align-middle md:leading-10"> |
|||
{{ label }} |
|||
</Label> |
|||
<input |
|||
:id="id" |
|||
v-model="data" |
|||
:name="id" |
|||
type="date" |
|||
class="rounded-lg border-2 border-gray-100 text-gray-500 focus:border-red-800 focus:outline-0 focus:ring-0 dark:border-neutral-800 dark:bg-neutral-700 dark:text-neutral-200 dark:placeholder:text-neutral-400" |
|||
/> |
|||
</template> |
|||
|
|||
<script lang="ts" setup> |
|||
defineProps<{ id: string; label: string }>(); |
|||
|
|||
const [data] = defineModel<string | null>({ |
|||
set(value) { |
|||
const temp = value?.trim() ?? null; |
|||
if (temp === '') { |
|||
return null; |
|||
} |
|||
return temp; |
|||
}, |
|||
}); |
|||
</script> |
@ -0,0 +1,5 @@ |
|||
<template> |
|||
<form> |
|||
<slot /> |
|||
</form> |
|||
</template> |
@ -0,0 +1,108 @@ |
|||
<template> |
|||
<main v-if="data"> |
|||
<FormElement @submit.prevent="submit"> |
|||
<FormGroup> |
|||
<FormHeading>Connection</FormHeading> |
|||
<FormTextField id="host" v-model="data.host" label="Host" /> |
|||
<FormNumberField id="port" v-model="data.port" label="Port" /> |
|||
</FormGroup> |
|||
<FormGroup> |
|||
<FormHeading>Allowed IPs</FormHeading> |
|||
<FormArrayField v-model="data.allowedIps" name="allowedIps" /> |
|||
</FormGroup> |
|||
<FormGroup> |
|||
<FormHeading>DNS</FormHeading> |
|||
<FormArrayField v-model="data.defaultDns" name="defaultDns" /> |
|||
</FormGroup> |
|||
<FormGroup> |
|||
<FormHeading>Advanced</FormHeading> |
|||
<FormNumberField id="mtu" v-model="data.mtu" label="MTU" /> |
|||
<FormNumberField |
|||
id="keepalive" |
|||
v-model="data.persistentKeepalive" |
|||
label="Persistent Keepalive" |
|||
/> |
|||
</FormGroup> |
|||
<FormGroup> |
|||
<FormHeading>Actions</FormHeading> |
|||
<FormActionField type="submit" label="Save" /> |
|||
<FormActionField label="Revert" @click="revert" /> |
|||
<AdminCidrDialog |
|||
trigger-class="col-span-2" |
|||
:address6="data.address6Range" |
|||
:address4="data.address4Range" |
|||
@change="changeCidr" |
|||
> |
|||
<FormActionField label="Change CIDR" class="w-full" /> |
|||
</AdminCidrDialog> |
|||
</FormGroup> |
|||
</FormElement> |
|||
</main> |
|||
</template> |
|||
|
|||
<script lang="ts" setup> |
|||
const toast = useToast(); |
|||
|
|||
const { data: _data, refresh } = await useFetch(`/api/admin/userconfig`, { |
|||
method: 'get', |
|||
}); |
|||
|
|||
const data = toRef(_data.value); |
|||
|
|||
async function submit() { |
|||
try { |
|||
const res = await $fetch(`/api/admin/userconfig`, { |
|||
method: 'post', |
|||
body: data.value, |
|||
}); |
|||
toast.showToast({ |
|||
type: 'success', |
|||
title: 'Success', |
|||
message: 'Saved', |
|||
}); |
|||
if (!res.success) { |
|||
throw new Error('Failed to save'); |
|||
} |
|||
await refreshNuxtData(); |
|||
} catch (e) { |
|||
if (e instanceof Error) { |
|||
toast.showToast({ |
|||
type: 'error', |
|||
title: 'Error', |
|||
message: e.message, |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
|
|||
async function revert() { |
|||
await refresh(); |
|||
data.value = toRef(_data.value).value; |
|||
} |
|||
|
|||
async function changeCidr(address4: string, address6: string) { |
|||
try { |
|||
const res = await $fetch(`/api/admin/userconfig/cidr`, { |
|||
method: 'post', |
|||
body: { address4, address6 }, |
|||
}); |
|||
toast.showToast({ |
|||
type: 'success', |
|||
title: 'Success', |
|||
message: 'Changed CIDR', |
|||
}); |
|||
if (!res.success) { |
|||
throw new Error('Failed to change CIDR'); |
|||
} |
|||
await refreshNuxtData(); |
|||
} catch (e) { |
|||
if (e instanceof Error) { |
|||
toast.showToast({ |
|||
type: 'error', |
|||
title: 'Error', |
|||
message: e.message, |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
</script> |
@ -1,24 +0,0 @@ |
|||
<template> |
|||
<div> |
|||
<FormGroup> |
|||
<FormHeading>Connection</FormHeading> |
|||
<FormTextField id="host" label="Host" /> |
|||
<FormTextField id="port" label="Port" /> |
|||
</FormGroup> |
|||
<FormGroup> |
|||
<FormHeading>Allowed IPs</FormHeading> |
|||
<FormArrayField /> |
|||
</FormGroup> |
|||
<FormGroup> |
|||
<FormHeading>DNS</FormHeading> |
|||
<FormArrayField /> |
|||
</FormGroup> |
|||
<FormGroup> |
|||
<FormHeading>Advanced</FormHeading> |
|||
<FormNumberField id="mtu" label="MTU" /> |
|||
<FormNumberField id="keepalive" label="Persistent Keepalive" /> |
|||
</FormGroup> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang="ts"></script> |
@ -0,0 +1,58 @@ |
|||
<template> |
|||
<main v-if="data"> |
|||
<FormElement @submit.prevent="submit"> |
|||
<FormGroup> |
|||
<FormTextField id="PreUp" v-model="data.PreUp" label="PreUp" /> |
|||
<FormTextField id="PostUp" v-model="data.PostUp" label="PostUp" /> |
|||
<FormTextField id="PreDown" v-model="data.PreDown" label="PreDown" /> |
|||
<FormTextField id="PostDown" v-model="data.PostDown" label="PostDown" /> |
|||
</FormGroup> |
|||
<FormGroup> |
|||
<FormHeading>Actions</FormHeading> |
|||
<FormActionField type="submit" label="Save" /> |
|||
<FormActionField label="Revert" @click="revert" /> |
|||
</FormGroup> |
|||
</FormElement> |
|||
</main> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
const toast = useToast(); |
|||
|
|||
const { data: _data, refresh } = await useFetch(`/api/admin/hooks`, { |
|||
method: 'get', |
|||
}); |
|||
|
|||
const data = toRef(_data.value); |
|||
|
|||
async function submit() { |
|||
try { |
|||
const res = await $fetch(`/api/admin/hooks`, { |
|||
method: 'post', |
|||
body: data.value, |
|||
}); |
|||
toast.showToast({ |
|||
type: 'success', |
|||
title: 'Success', |
|||
message: 'Saved', |
|||
}); |
|||
if (!res.success) { |
|||
throw new Error('Failed to save'); |
|||
} |
|||
await refreshNuxtData(); |
|||
} catch (e) { |
|||
if (e instanceof Error) { |
|||
toast.showToast({ |
|||
type: 'error', |
|||
title: 'Error', |
|||
message: e.message, |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
|
|||
async function revert() { |
|||
await refresh(); |
|||
data.value = toRef(_data.value).value; |
|||
} |
|||
</script> |
@ -1,9 +1,59 @@ |
|||
<template> |
|||
<div> |
|||
<FormGroup> |
|||
<FormNumberField id="session" label="Session Timeout" /> |
|||
</FormGroup> |
|||
</div> |
|||
<main v-if="data"> |
|||
<FormElement @submit.prevent="submit"> |
|||
<FormGroup> |
|||
<FormNumberField |
|||
id="session" |
|||
v-model="data.sessionTimeout" |
|||
label="Session Timeout" |
|||
/> |
|||
</FormGroup> |
|||
<FormGroup> |
|||
<FormHeading>Actions</FormHeading> |
|||
<FormActionField type="submit" label="Save" /> |
|||
<FormActionField label="Revert" @click="revert" /> |
|||
</FormGroup> |
|||
</FormElement> |
|||
</main> |
|||
</template> |
|||
|
|||
<script setup lang="ts"></script> |
|||
<script setup lang="ts"> |
|||
const toast = useToast(); |
|||
|
|||
const { data: _data, refresh } = await useFetch(`/api/admin/general`, { |
|||
method: 'get', |
|||
}); |
|||
|
|||
const data = toRef(_data.value); |
|||
|
|||
async function submit() { |
|||
try { |
|||
const res = await $fetch(`/api/admin/general`, { |
|||
method: 'post', |
|||
body: data.value, |
|||
}); |
|||
toast.showToast({ |
|||
type: 'success', |
|||
title: 'Success', |
|||
message: 'Saved', |
|||
}); |
|||
if (!res.success) { |
|||
throw new Error('Failed to save'); |
|||
} |
|||
await refreshNuxtData(); |
|||
} catch (e) { |
|||
if (e instanceof Error) { |
|||
toast.showToast({ |
|||
type: 'error', |
|||
title: 'Error', |
|||
message: e.message, |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
|
|||
async function revert() { |
|||
await refresh(); |
|||
data.value = toRef(_data.value).value; |
|||
} |
|||
</script> |
|||
|
@ -1,19 +1,58 @@ |
|||
<template> |
|||
<div> |
|||
<FormGroup> |
|||
<FormHeading>Interface Settings</FormHeading> |
|||
<FormNumberField id="mtu" label="MTU" /> |
|||
<FormNumberField id="port" label="Port" /> |
|||
<FormTextField id="device" label="Device" /> |
|||
</FormGroup> |
|||
<FormGroup> |
|||
<FormHeading>Scripts</FormHeading> |
|||
<FormTextField id="mtu" label="PreUp" /> |
|||
<FormTextField id="port" label="PostUp" /> |
|||
<FormTextField id="device" label="PreDown" /> |
|||
<FormTextField id="device" label="PostDown" /> |
|||
</FormGroup> |
|||
</div> |
|||
<main v-if="data"> |
|||
<FormElement @submit.prevent="submit"> |
|||
<FormGroup> |
|||
<FormHeading>Interface Settings</FormHeading> |
|||
<FormNumberField id="mtu" v-model="data.mtu" label="MTU" /> |
|||
<FormNumberField id="port" v-model="data.port" label="Port" /> |
|||
<FormTextField id="device" v-model="data.device" label="Device" /> |
|||
</FormGroup> |
|||
<FormGroup> |
|||
<FormHeading>Actions</FormHeading> |
|||
<FormActionField type="submit" label="Save" /> |
|||
<FormActionField label="Revert" @click="revert" /> |
|||
</FormGroup> |
|||
</FormElement> |
|||
</main> |
|||
</template> |
|||
|
|||
<script setup lang="ts"></script> |
|||
<script setup lang="ts"> |
|||
const toast = useToast(); |
|||
|
|||
const { data: _data, refresh } = await useFetch(`/api/admin/interface`, { |
|||
method: 'get', |
|||
}); |
|||
|
|||
const data = toRef(_data.value); |
|||
|
|||
async function submit() { |
|||
try { |
|||
const res = await $fetch(`/api/admin/interface`, { |
|||
method: 'post', |
|||
body: data.value, |
|||
}); |
|||
toast.showToast({ |
|||
type: 'success', |
|||
title: 'Success', |
|||
message: 'Saved', |
|||
}); |
|||
if (!res.success) { |
|||
throw new Error('Failed to save'); |
|||
} |
|||
await refreshNuxtData(); |
|||
} catch (e) { |
|||
if (e instanceof Error) { |
|||
toast.showToast({ |
|||
type: 'error', |
|||
title: 'Error', |
|||
message: e.message, |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
|
|||
async function revert() { |
|||
await refresh(); |
|||
data.value = toRef(_data.value).value; |
|||
} |
|||
</script> |
|||
|
@ -0,0 +1,26 @@ |
|||
export const useToast = defineStore('Toast', () => { |
|||
type ToastInterface = { |
|||
publish: (e: { title: string; message: string }) => void; |
|||
}; |
|||
|
|||
type ToastRef = Ref<null | ToastInterface>; |
|||
|
|||
const toast = ref<Ref<ToastRef> | null>(null); |
|||
|
|||
function setToast(toastInstance: ToastRef) { |
|||
toast.value = toastInstance; |
|||
} |
|||
|
|||
function showToast({ |
|||
title, |
|||
message, |
|||
}: { |
|||
type: 'success' | 'error'; |
|||
title: string; |
|||
message: string; |
|||
}) { |
|||
toast.value?.value?.publish({ title, message }); |
|||
} |
|||
|
|||
return { setToast, showToast }; |
|||
}); |
@ -1,56 +1,9 @@ |
|||
import en from './locales/en.json'; |
|||
import ua from './locales/ua.json'; |
|||
import ru from './locales/ru.json'; |
|||
import tr from './locales/tr.json'; |
|||
import no from './locales/no.json'; |
|||
import pl from './locales/pl.json'; |
|||
import fr from './locales/fr.json'; |
|||
import de from './locales/de.json'; |
|||
import ca from './locales/ca.json'; |
|||
import es from './locales/es.json'; |
|||
import ko from './locales/ko.json'; |
|||
import vi from './locales/vi.json'; |
|||
import nl from './locales/nl.json'; |
|||
import is from './locales/is.json'; |
|||
import pt from './locales/pt.json'; |
|||
import zhChs from './locales/zh-chs.json'; |
|||
import zhCht from './locales/zh-cht.json'; |
|||
import it from './locales/it.json'; |
|||
import th from './locales/th.json'; |
|||
import hi from './locales/hi.json'; |
|||
|
|||
export default defineI18nConfig(() => ({ |
|||
fallbackLocale: 'en', |
|||
legacy: false, |
|||
locale: 'en', |
|||
fallbackLocale: 'en', |
|||
messages: { |
|||
en, |
|||
ua, |
|||
ru, |
|||
// Müslüm Barış Korkmazer @babico
|
|||
tr, |
|||
// github.com/digvalley
|
|||
no, |
|||
// github.com/archont94
|
|||
pl, |
|||
// github.com/clem3109
|
|||
fr, |
|||
de, |
|||
// github.com/guillembonet
|
|||
ca, |
|||
// github.com/amarqz
|
|||
es, |
|||
ko, |
|||
// https://github.com/hoangneeee
|
|||
vi, |
|||
nl, |
|||
is, |
|||
pt, |
|||
zhChs, |
|||
zhCht, |
|||
it, |
|||
th, |
|||
// github.com/rahilarious
|
|||
hi, |
|||
}, |
|||
})); |
|||
|
@ -1,56 +0,0 @@ |
|||
{ |
|||
"setup": { |
|||
"welcome": "Сардэчна запрашаем на старонку пачатковай налады wg-easy!", |
|||
"msg": "Калі ласка, увядзіце лагін для адміністратара і прыдумайце бяспечны пароль. Гэтыя дадзеныя будуць выкарыстоўвацца для ўваходу ў панэль адміністравання.", |
|||
"newPassword": "Новы пароль", |
|||
"accept": "Я згодны з умовай", |
|||
"submitBtn": "Стварыць уліковы запіс адміністратара", |
|||
"usernameCondition": "Імя карыстальніка павінна быць не менш за 8 сімвалаў.", |
|||
"passwordCondition": "Пароль павінен быць не менш за 12 сімвалаў, уключваючы 1 вялікую літару, 1 малую літару, 1 лічбу і 1 спецыяльны сімвал.", |
|||
"usernamePlaceholder": "Адміністратар", |
|||
"passwordPlaceholder": "Надзейны пароль" |
|||
}, |
|||
"name": "Імя", |
|||
"password": "Пароль", |
|||
"signIn": "Увайсці", |
|||
"logout": "Выйсці", |
|||
"updateAvailable": "Даступна абнаўленне!", |
|||
"update": "Абнавіць", |
|||
"clients": "Кліенты", |
|||
"new": "Стварыць", |
|||
"deleteClient": "Выдаліць кліента", |
|||
"deleteDialog1": "Вы ўпэўнены, што хочаце выдаліць", |
|||
"deleteDialog2": "Гэта дзеянне немагчыма адмяніць.", |
|||
"cancel": "Закрыць", |
|||
"create": "Стварыць", |
|||
"createdOn": "Створана ў ", |
|||
"lastSeen": "Апошняе падключэнне ў ", |
|||
"totalDownload": "Усяго спампавана: ", |
|||
"totalUpload": "Усяго загружана: ", |
|||
"newClient": "Стварыць кліента", |
|||
"disableClient": "Выключыць кліента", |
|||
"enableClient": "Уключыць кліента", |
|||
"noClients": "Пакуль няма кліентаў.", |
|||
"noPrivKey": "Немагчыма стварыць канфігурацыю: у кліента няма вядомага прыватнага ключа.", |
|||
"showQR": "Паказаць QR-код", |
|||
"downloadConfig": "Спампаваць канфігурацыю", |
|||
"madeBy": "Аўтар", |
|||
"donate": "Падзякаваць", |
|||
"toggleCharts": "Паказаць/схаваць графікі", |
|||
"theme": { |
|||
"dark": "Цёмная тэма", |
|||
"light": "Светлая тэма", |
|||
"system": "Як у сістэме" |
|||
}, |
|||
"restore": "Аднавіць", |
|||
"backup": "Рэзервовая копія", |
|||
"titleRestoreConfig": "Аднавіць канфігурацыю", |
|||
"titleBackupConfig": "Стварыць рэзервовую копію канфігурацыі", |
|||
"rememberMe": "Запомніць мяне", |
|||
"titleRememberMe": "Заставацца ў сістэме пасля закрыцця браўзера", |
|||
"sort": "Сартыроўка", |
|||
"ExpireDate": "Дата заканчэння тэрміну", |
|||
"Permanent": "Бестэрмінова", |
|||
"OneTimeLink": "Стварыць кароткую аднаразовую спасылку", |
|||
"errorInit": "Памылка ініцыялізацыі." |
|||
} |
@ -1,27 +0,0 @@ |
|||
{ |
|||
"name": "Nom", |
|||
"password": "Contrasenya", |
|||
"signIn": "Iniciar sessió", |
|||
"logout": "Tanca sessió", |
|||
"updateAvailable": "Hi ha una actualització disponible!", |
|||
"update": "Actualitza", |
|||
"clients": "Clients", |
|||
"new": "Nou", |
|||
"deleteClient": "Esborra client", |
|||
"deleteDialog1": "Estàs segur que vols esborrar aquest client?", |
|||
"deleteDialog2": "Aquesta acció no es pot desfer.", |
|||
"cancel": "Cancel·la", |
|||
"create": "Crea", |
|||
"createdOn": "Creat el ", |
|||
"lastSeen": "Última connexió el ", |
|||
"totalDownload": "Baixada total: ", |
|||
"totalUpload": "Pujada total: ", |
|||
"newClient": "Nou client", |
|||
"disableClient": "Desactiva client", |
|||
"enableClient": "Activa client", |
|||
"noClients": "Encara no hi ha cap client.", |
|||
"showQR": "Mostra codi QR", |
|||
"downloadConfig": "Descarrega configuració", |
|||
"madeBy": "Fet per", |
|||
"donate": "Donatiu" |
|||
} |
@ -1,32 +0,0 @@ |
|||
{ |
|||
"name": "Name", |
|||
"password": "Passwort", |
|||
"signIn": "Anmelden", |
|||
"logout": "Abmelden", |
|||
"updateAvailable": "Eine Aktualisierung steht zur Verfügung!", |
|||
"update": "Aktualisieren", |
|||
"clients": "Clients", |
|||
"new": "Neu", |
|||
"deleteClient": "Client löschen", |
|||
"deleteDialog1": "Möchtest du wirklich löschen?", |
|||
"deleteDialog2": "Diese Aktion kann nicht rückgängig gemacht werden.", |
|||
"cancel": "Abbrechen", |
|||
"create": "Erstellen", |
|||
"createdOn": "Erstellt am ", |
|||
"lastSeen": "Zuletzt Online ", |
|||
"totalDownload": "Gesamt Download: ", |
|||
"totalUpload": "Gesamt Upload: ", |
|||
"newClient": "Neuer Client", |
|||
"disableClient": "Client deaktivieren", |
|||
"enableClient": "Client aktivieren", |
|||
"noClients": "Es wurden noch keine Clients konfiguriert.", |
|||
"noPrivKey": "Es ist kein Private Key für diesen Client bekannt. Eine Konfiguration kann nicht erstellt werden.", |
|||
"showQR": "Zeige den QR Code", |
|||
"downloadConfig": "Konfiguration herunterladen", |
|||
"madeBy": "Erstellt von", |
|||
"donate": "Spenden", |
|||
"restore": "Wiederherstellen", |
|||
"backup": "Sichern", |
|||
"titleRestoreConfig": "Stelle deine Konfiguration wieder her", |
|||
"titleBackupConfig": "Sichere deine Konfiguration" |
|||
} |
@ -1,37 +0,0 @@ |
|||
{ |
|||
"name": "Nombre", |
|||
"password": "Contraseña", |
|||
"signIn": "Iniciar sesión", |
|||
"logout": "Cerrar sesión", |
|||
"updateAvailable": "¡Hay una actualización disponible!", |
|||
"update": "Actualizar", |
|||
"clients": "Clientes", |
|||
"new": "Nuevo", |
|||
"deleteClient": "Eliminar cliente", |
|||
"deleteDialog1": "¿Estás seguro de que quieres borrar este cliente?", |
|||
"deleteDialog2": "Esta acción no podrá ser revertida.", |
|||
"cancel": "Cancelar", |
|||
"create": "Crear", |
|||
"createdOn": "Creado el ", |
|||
"lastSeen": "Última conexión el ", |
|||
"totalDownload": "Total descargado: ", |
|||
"totalUpload": "Total subido: ", |
|||
"newClient": "Nuevo cliente", |
|||
"disableClient": "Desactivar cliente", |
|||
"enableClient": "Activar cliente", |
|||
"noClients": "Aún no hay ningún cliente.", |
|||
"showQR": "Mostrar código QR", |
|||
"downloadConfig": "Descargar configuración", |
|||
"madeBy": "Hecho por", |
|||
"donate": "Donar", |
|||
"toggleCharts": "Mostrar/Ocultar gráficos", |
|||
"theme": { |
|||
"dark": "Modo oscuro", |
|||
"light": "Modo claro", |
|||
"system": "Modo automático" |
|||
}, |
|||
"restore": "Restaurar", |
|||
"backup": "Realizar copia de seguridad", |
|||
"titleRestoreConfig": "Restaurar su configuración", |
|||
"titleBackupConfig": "Realizar copia de seguridad de su configuración" |
|||
} |
@ -1,122 +0,0 @@ |
|||
{ |
|||
"pages": { |
|||
"me": "Compte", |
|||
"clients": "Clients" |
|||
}, |
|||
"me": { |
|||
"sectionGeneral": "Général", |
|||
"sectionPassword": "Mot de passe" |
|||
}, |
|||
"email": "E-Mail", |
|||
"save": "Enregistrer", |
|||
"updatePassword": "Changer le mot de passe", |
|||
"currentPassword": "Mot de passe actuel", |
|||
"confirmPassword": "Confirmer le mot de passe", |
|||
"setup": { |
|||
"welcome": "Bienvenue à votre première installation de wg-easy !", |
|||
"messageWelcome": { |
|||
"whatIs": "Vous avez trouvé le moyen le plus simple d'installer et de gérer WireGuard sur n'importe quel hôte Linux !", |
|||
"warning": "Tout d'abord, assurez-vous d'avoir une sauvegarde de vos données si vous voulez migrer vos utilisateurs vers votre nouveau wg-easy.", |
|||
"next": "Cliquez sur la flèche pour passer à l'étape suivante." |
|||
}, |
|||
"messageSetupLanguage": "Sélectionner votre langue.", |
|||
"messageSetupCreateAdminUser": "Veuillez renseigner votre nom d'utilisateur et votre mot de passe. Ces informations seront utilisées pour vous connecter à votre page d'administration.", |
|||
"messageSetupHostPort": "Veuillez entrer les informations de l'hôte et du port. Cela sera utilisé pour la configuration du client lors de la configuration de WireGuard sur leurs appareils.", |
|||
"messageSetupMigration": " Veuillez fournir le fichier de sauvegarde si vous souhaitez migrer vos données de la version précédente de wg-easy vers votre nouvelle installation.", |
|||
"messageSetupValidation": "Bienvenue sur wg-easy ! La meilleur application pour administrer son serveur VPN WireGuard.", |
|||
"emptyFields": "Des champs sont requis", |
|||
"chooseLang": "Choisir une langue...", |
|||
"newPassword": "Nouveau mot de passe", |
|||
"accept": "J'accepte les conditions d'utilisation", |
|||
"submitBtn": "Créer un compte administrateur", |
|||
"usernamePlaceholder": "Administrateur", |
|||
"passwordPlaceholder": "Mot de passe sapau", |
|||
"requirements": "Conditions de configuration", |
|||
"host": "Hôte", |
|||
"hostPlaceholder": "wg-easy.example.com", |
|||
"port": "Port", |
|||
"portPlaceholder": "443", |
|||
"migration": "Restaurer la sauvegarde" |
|||
}, |
|||
"zod": { |
|||
"id": "L'ID du client doit être un UUID valide", |
|||
"address4": "L'adresse IPv4 doit être une chaîne valide", |
|||
"name": "Le nom doit être une chaîne valide", |
|||
"nameMin": "Le nom doit contenir au moins 1 caractère", |
|||
"file": "Le fichier doit être une chaîne valide", |
|||
"username": "Le nom d'utilisateur doit être une chaîne valide", |
|||
"usernameMin": "Le nom d'utilisateur doit contenir au moins 8 caractères", |
|||
"password": "Le mot de passe doit être une chaîne valide", |
|||
"passwordMin": "Le mot de passe doit contenir au moins 12 caractères", |
|||
"passwordUppercase": "Le mot de passe doit contenir au moins 1 lettre majuscule", |
|||
"passwordLowercase": "Le mot de passe doit contenir au moins 1 lettre minuscule", |
|||
"passwordNumber": "Le mot de passe doit contenir au moins 1 chiffre", |
|||
"passwordSpecial": "Le mot de passe doit contenir au moins 1 caractère spécial", |
|||
"accept": "Veuillez accpter les conditions d'utilisation", |
|||
"remember": "La case « se souvenir de moi » doit être un booléen valide", |
|||
"expireDate": "La date d'expiration doit être une chaîne valide", |
|||
"expireDateMin": "La date d'expiration doit contenir au moins 1 caractère", |
|||
"otl": "Le lien à usage unique doit être une chaîne valide", |
|||
"otlMin": "Le lien à usage unique doit contenir au moins 1 caractère", |
|||
"features": "La clé doit être une chaîne valide", |
|||
"ftBool": "Le paramètre « activé » doit être un booléen valide", |
|||
"ftObj": "La valeur doit être un objet valide", |
|||
"ftObj2": "Les fonctionnalités doivent être un enregistrement valide", |
|||
"stat": "Les statistiques doivent être un objet valide", |
|||
"statBool": "Le paramètre « activé » doit être un booléen valide", |
|||
"statNumber": "Le type de graphique doit être un nombre valide", |
|||
"body": "Le corps doit être un objet valide", |
|||
"host": "L'hôte doit être une chaîne valide", |
|||
"hostMin": "L'hôte doit contenir au moins 1 caractère", |
|||
"port": "Le port doit être un nombre valide", |
|||
"portMin": "Le port doit être au moins 1", |
|||
"portMax": "Le port doit être au maximum 65535" |
|||
}, |
|||
"name": "Nom", |
|||
"username": "Nom d'utilisateur", |
|||
"password": "Mot de passe", |
|||
"signIn": "Se Connecter", |
|||
"logout": "Se déconnecter", |
|||
"updateAvailable": "Une mise à jour est disponible !", |
|||
"update": "Mise à jour", |
|||
"new": "Nouveau", |
|||
"deleteClient": "Supprimer ce client", |
|||
"deleteDialog1": "Êtes-vous sûr de vouloir supprimer", |
|||
"deleteDialog2": "Cette action ne peut pas être annulée.", |
|||
"cancel": "Annuler", |
|||
"create": "Créer", |
|||
"createdOn": "Créé le ", |
|||
"lastSeen": "Dernière connexion le ", |
|||
"totalDownload": "Téléchargement total : ", |
|||
"totalUpload": "Téléversement total : ", |
|||
"newClient": "Nouveau client", |
|||
"disableClient": "Désactiver ce client", |
|||
"enableClient": "Activer ce client", |
|||
"noClients": "Aucun client pour le moment.", |
|||
"noPrivKey": "Ce client n'a pas de clé privée connue. Impossible de créer la configuration.", |
|||
"showQR": "Afficher le code QR", |
|||
"downloadConfig": "Télécharger la configuration", |
|||
"madeBy": "Développé par", |
|||
"donate": "Faire un don", |
|||
"toggleCharts": "Afficher/masquer les graphiques", |
|||
"theme": { |
|||
"dark": "Thème sombre", |
|||
"light": "Thème clair", |
|||
"system": "Thème du système" |
|||
}, |
|||
"restore": "Restaurer", |
|||
"backup": "Sauvegarder", |
|||
"titleRestoreConfig": "Restaurer votre configuration", |
|||
"titleBackupConfig": "Sauvegarder votre configuration", |
|||
"rememberMe": "Se souvenir de moi", |
|||
"titleRememberMe": "Restez connecté après la fermeture du navigateur", |
|||
"sort": "Trier", |
|||
"ExpireDate": "Date d'expiration", |
|||
"Permanent": "Permanent", |
|||
"OneTimeLink": "Générer un lien court à usage unique", |
|||
"errorInit": "Échec de l'initialisation.", |
|||
"error": { |
|||
"clear": "Effacer", |
|||
"login": "Erreur de connexion" |
|||
} |
|||
} |
@ -1,28 +0,0 @@ |
|||
{ |
|||
"name": "नाम", |
|||
"password": "पासवर्ड", |
|||
"signIn": "लॉगिन", |
|||
"logout": "लॉगआउट", |
|||
"updateAvailable": "अपडेट उपलब्ध है!", |
|||
"update": "अपडेट", |
|||
"clients": "उपयोगकर्ताये", |
|||
"new": "नया", |
|||
"deleteClient": "उपयोगकर्ता हटाएँ", |
|||
"deleteDialog1": "क्या आपको पक्का हटाना है", |
|||
"deleteDialog2": "यह निर्णय पलट नहीं सकता।", |
|||
"cancel": "कुछ ना करें", |
|||
"create": "बनाएं", |
|||
"createdOn": "सर्जन तारीख ", |
|||
"lastSeen": "पिछली बार देखे गए थे ", |
|||
"totalDownload": "कुल डाउनलोड: ", |
|||
"totalUpload": "कुल अपलोड: ", |
|||
"newClient": "नया उपयोगकर्ता", |
|||
"disableClient": "उपयोगकर्ता स्थगित कीजिये", |
|||
"enableClient": "उपयोगकर्ता शुरू कीजिये", |
|||
"noClients": "अभी तक कोई भी उपयोगकर्ता नहीं है।", |
|||
"noPrivKey": "ये उपयोगकर्ता की कोई भी गुप्त चाबी नहीं हे। बना नहीं सकते।", |
|||
"showQR": "क्यू आर कोड देखिये", |
|||
"downloadConfig": "डाउनलोड कॉन्फीग्यूरेशन", |
|||
"madeBy": "सर्जक", |
|||
"donate": "दान करें" |
|||
} |
@ -1,27 +0,0 @@ |
|||
{ |
|||
"name": "Nafn", |
|||
"password": "Lykilorð", |
|||
"signIn": "Skrá inn", |
|||
"logout": "Útskráning", |
|||
"updateAvailable": "Það er uppfærsla í boði!", |
|||
"update": "Uppfæra", |
|||
"clients": "Viðskiptavinir", |
|||
"new": "Nýtt", |
|||
"deleteClient": "Eyða viðskiptavin", |
|||
"deleteDialog1": "Ertu viss um að þú viljir eyða", |
|||
"deleteDialog2": "Þessi aðgerð getur ekki verið afturkallað.", |
|||
"cancel": "Hætta við", |
|||
"create": "Búa til", |
|||
"createdOn": "Búið til á ", |
|||
"lastSeen": "Síðast séð á ", |
|||
"totalDownload": "Samtals Niðurhlaða: ", |
|||
"totalUpload": "Samtals Upphlaða: ", |
|||
"newClient": "Nýr Viðskiptavinur", |
|||
"disableClient": "Gera viðskiptavin óvirkan", |
|||
"enableClient": "Gera viðskiptavin virkan", |
|||
"noClients": "Engir viðskiptavinir ennþá.", |
|||
"showQR": "Sýna QR-kóða", |
|||
"downloadConfig": "Niðurhal Stillingar", |
|||
"madeBy": "Gert af", |
|||
"donate": "Gefa" |
|||
} |
@ -1,31 +0,0 @@ |
|||
{ |
|||
"name": "Nome", |
|||
"password": "Password", |
|||
"signIn": "Accedi", |
|||
"logout": "Esci", |
|||
"updateAvailable": "È disponibile un aggiornamento!", |
|||
"update": "Aggiorna", |
|||
"clients": "Client", |
|||
"new": "Nuovo", |
|||
"deleteClient": "Elimina Client", |
|||
"deleteDialog1": "Sei sicuro di voler eliminare", |
|||
"deleteDialog2": "Questa azione non può essere annullata.", |
|||
"cancel": "Annulla", |
|||
"create": "Crea", |
|||
"createdOn": "Creato il ", |
|||
"lastSeen": "Visto l'ultima volta il ", |
|||
"totalDownload": "Totale Download: ", |
|||
"totalUpload": "Totale Upload: ", |
|||
"newClient": "Nuovo Client", |
|||
"disableClient": "Disabilita Client", |
|||
"enableClient": "Abilita Client", |
|||
"noClients": "Non ci sono ancora client.", |
|||
"showQR": "Mostra codice QR", |
|||
"downloadConfig": "Scarica configurazione", |
|||
"madeBy": "Realizzato da", |
|||
"donate": "Donazione", |
|||
"restore": "Ripristina", |
|||
"backup": "Backup", |
|||
"titleRestoreConfig": "Ripristina la tua configurazione", |
|||
"titleBackupConfig": "Esegui il backup della tua configurazione" |
|||
} |
@ -1,37 +0,0 @@ |
|||
{ |
|||
"name": "이름", |
|||
"password": "암호", |
|||
"signIn": "로그인", |
|||
"logout": "로그아웃", |
|||
"updateAvailable": "업데이트가 있습니다!", |
|||
"update": "업데이트", |
|||
"clients": "클라이언트", |
|||
"new": "추가", |
|||
"deleteClient": "클라이언트 삭제", |
|||
"deleteDialog1": "삭제 하시겠습니까?", |
|||
"deleteDialog2": "이 작업은 취소할 수 없습니다.", |
|||
"cancel": "취소", |
|||
"create": "생성", |
|||
"createdOn": "생성일: ", |
|||
"lastSeen": "마지막 사용 날짜: ", |
|||
"totalDownload": "총 다운로드: ", |
|||
"totalUpload": "총 업로드: ", |
|||
"newClient": "새로운 클라이언트", |
|||
"disableClient": "클라이언트 비활성화", |
|||
"enableClient": "클라이언트 활성화", |
|||
"noClients": "아직 클라이언트가 없습니다.", |
|||
"showQR": "QR 코드 표시", |
|||
"downloadConfig": "구성 다운로드", |
|||
"madeBy": "만든 사람", |
|||
"donate": "기부", |
|||
"toggleCharts": "차트 표시/숨기기", |
|||
"theme": { |
|||
"dark": "어두운 테마", |
|||
"light": "밝은 테마", |
|||
"system": "자동 테마" |
|||
}, |
|||
"restore": "복원", |
|||
"backup": "백업", |
|||
"titleRestoreConfig": "구성 파일 복원", |
|||
"titleBackupConfig": "구성 파일 백업" |
|||
} |
@ -1,27 +0,0 @@ |
|||
{ |
|||
"name": "Naam", |
|||
"password": "Wachtwoord", |
|||
"signIn": "Inloggen", |
|||
"logout": "Uitloggen", |
|||
"updateAvailable": "Nieuw update beschikbaar!", |
|||
"update": "update", |
|||
"clients": "clients", |
|||
"new": "Nieuw", |
|||
"deleteClient": "client verwijderen", |
|||
"deleteDialog1": "Weet je zeker dat je wilt verwijderen", |
|||
"deleteDialog2": "Deze actie kan niet ongedaan worden gemaakt.", |
|||
"cancel": "Annuleren", |
|||
"create": "Creëren", |
|||
"createdOn": "Gemaakt op ", |
|||
"lastSeen": "Laatst gezien op ", |
|||
"totalDownload": "Totaal Gedownload: ", |
|||
"totalUpload": "Totaal Geupload: ", |
|||
"newClient": "Nieuwe client", |
|||
"disableClient": "client uitschakelen", |
|||
"enableClient": "client inschakelen", |
|||
"noClients": "Er zijn nog geen clients.", |
|||
"showQR": "QR-code weergeven", |
|||
"downloadConfig": "Configuratie downloaden", |
|||
"madeBy": "Gemaakt door", |
|||
"donate": "Doneren" |
|||
} |
@ -1,27 +0,0 @@ |
|||
{ |
|||
"name": "Navn", |
|||
"password": "Passord", |
|||
"signIn": "Logg Inn", |
|||
"logout": "Logg Ut", |
|||
"updateAvailable": "En ny oppdatering er tilgjengelig!", |
|||
"update": "Oppdater", |
|||
"clients": "Klienter", |
|||
"new": "Ny", |
|||
"deleteClient": "Slett Klient", |
|||
"deleteDialog1": "Er du sikker på at du vil slette?", |
|||
"deleteDialog2": "Denne handlingen kan ikke angres", |
|||
"cancel": "Avbryt", |
|||
"create": "Opprett", |
|||
"createdOn": "Opprettet ", |
|||
"lastSeen": "Sist sett ", |
|||
"totalDownload": "Total Nedlasting: ", |
|||
"totalUpload": "Total Opplasting: ", |
|||
"newClient": "Ny Klient", |
|||
"disableClient": "Deaktiver Klient", |
|||
"enableClient": "Aktiver Klient", |
|||
"noClients": "Ingen klienter opprettet enda.", |
|||
"showQR": "Vis QR Kode", |
|||
"downloadConfig": "Last Ned Konfigurasjon", |
|||
"madeBy": "Laget av", |
|||
"donate": "Doner" |
|||
} |
@ -1,27 +0,0 @@ |
|||
{ |
|||
"name": "Nazwa", |
|||
"password": "Hasło", |
|||
"signIn": "Zaloguj się", |
|||
"logout": "Wyloguj się", |
|||
"updateAvailable": "Dostępna aktualizacja!", |
|||
"update": "Aktualizuj", |
|||
"clients": "Klienci", |
|||
"new": "Stwórz klienta", |
|||
"deleteClient": "Usuń klienta", |
|||
"deleteDialog1": "Jesteś pewny że chcesz usunąć", |
|||
"deleteDialog2": "Tej akcji nie da się cofnąć.", |
|||
"cancel": "Anuluj", |
|||
"create": "Stwórz", |
|||
"createdOn": "Utworzono ", |
|||
"lastSeen": "Ostatnio widziany ", |
|||
"totalDownload": "Całkowite pobieranie: ", |
|||
"totalUpload": "Całkowite wysyłanie: ", |
|||
"newClient": "Nowy klient", |
|||
"disableClient": "Wyłączenie klienta", |
|||
"enableClient": "Włączenie klienta", |
|||
"noClients": "Nie ma jeszcze klientów.", |
|||
"showQR": "Pokaż kod QR", |
|||
"downloadConfig": "Pobierz konfigurację", |
|||
"madeBy": "Stworzone przez", |
|||
"donate": "Wsparcie autora" |
|||
} |
@ -1,27 +0,0 @@ |
|||
{ |
|||
"name": "Nome", |
|||
"password": "Palavra Chave", |
|||
"signIn": "Entrar", |
|||
"logout": "Sair", |
|||
"updateAvailable": "Existe uma atualização disponível!", |
|||
"update": "Atualizar", |
|||
"clients": "Clientes", |
|||
"new": "Novo", |
|||
"deleteClient": "Apagar Clientes", |
|||
"deleteDialog1": "Tem certeza que pretende apagar", |
|||
"deleteDialog2": "Esta ação não pode ser revertida.", |
|||
"cancel": "Cancelar", |
|||
"create": "Criar", |
|||
"createdOn": "Criado em ", |
|||
"lastSeen": "Último acesso em ", |
|||
"totalDownload": "Total Download: ", |
|||
"totalUpload": "Total Upload: ", |
|||
"newClient": "Novo Cliente", |
|||
"disableClient": "Desativar Cliente", |
|||
"enableClient": "Ativar Cliente", |
|||
"noClients": "Não existem ainda clientes.", |
|||
"showQR": "Apresentar o código QR", |
|||
"downloadConfig": "Descarregar Configuração", |
|||
"madeBy": "Feito por", |
|||
"donate": "Doar" |
|||
} |
@ -1,56 +0,0 @@ |
|||
{ |
|||
"setup": { |
|||
"welcome": "Добро пожаловать на страницу начальной настройки wg-easy !", |
|||
"msg": "Пожалуйста, введите логин для администратора и придумайте безопасный пароль. Эти данные будут использоваться для входа в панель администрирования.", |
|||
"newPassword": "Новый пароль", |
|||
"accept": "Я согласен с условием", |
|||
"submitBtn": "Создать учетную запись администратора", |
|||
"usernameCondition": "Имя пользователя должно быть не менее 8 символов.", |
|||
"passwordCondition": "Пароль должен быть не менее 12 символов, включая 1 заглавную букву, 1 прописную букву, 1 цифру и 1 специальный символ.", |
|||
"usernamePlaceholder": "Администратор", |
|||
"passwordPlaceholder": "Надежный пароль" |
|||
}, |
|||
"name": "Имя", |
|||
"password": "Пароль", |
|||
"signIn": "Войти", |
|||
"logout": "Выйти", |
|||
"updateAvailable": "Доступно обновление!", |
|||
"update": "Обновить", |
|||
"clients": "Клиенты", |
|||
"new": "Создать", |
|||
"deleteClient": "Удалить клиента", |
|||
"deleteDialog1": "Вы уверены, что хотите удалить", |
|||
"deleteDialog2": "Это действие невозможно отменить.", |
|||
"cancel": "Закрыть", |
|||
"create": "Создать", |
|||
"createdOn": "Создано в ", |
|||
"lastSeen": "Последнее подключение в ", |
|||
"totalDownload": "Всего скачано: ", |
|||
"totalUpload": "Всего загружено: ", |
|||
"newClient": "Создать клиента", |
|||
"disableClient": "Выключить клиента", |
|||
"enableClient": "Включить клиента", |
|||
"noClients": "Пока нет клиентов.", |
|||
"noPrivKey": "Невозможно создать конфигурацию: у клиента нет известного приватного ключа.", |
|||
"showQR": "Показать QR-код", |
|||
"downloadConfig": "Скачать конфигурацию", |
|||
"madeBy": "Автор", |
|||
"donate": "Поблагодарить", |
|||
"toggleCharts": "Показать/скрыть графики", |
|||
"theme": { |
|||
"dark": "Тёмная тема", |
|||
"light": "Светлая тема", |
|||
"system": "Как в системе" |
|||
}, |
|||
"restore": "Восстановить", |
|||
"backup": "Резервная копия", |
|||
"titleRestoreConfig": "Восстановить конфигурацию", |
|||
"titleBackupConfig": "Создать резервную копию конфигурации", |
|||
"rememberMe": "Запомнить меня", |
|||
"titleRememberMe": "Оставаться в системе после закрытия браузера", |
|||
"sort": "Сортировка", |
|||
"ExpireDate": "Дата истечения срока", |
|||
"Permanent": "Бессрочно", |
|||
"OneTimeLink": "Создать короткую одноразовую ссылку", |
|||
"errorInit": "Ошибка инициализации." |
|||
} |
@ -1,27 +0,0 @@ |
|||
{ |
|||
"name": "ชื่อ", |
|||
"password": "รหัสผ่าน", |
|||
"signIn": "ลงชื่อเข้าใช้", |
|||
"logout": "ออกจากระบบ", |
|||
"updateAvailable": "มีอัปเดตพร้อมใช้งาน!", |
|||
"update": "อัปเดต", |
|||
"clients": "Clients", |
|||
"new": "ใหม่", |
|||
"deleteClient": "ลบ Client", |
|||
"deleteDialog1": "คุณแน่ใจหรือไม่ว่าต้องการลบ", |
|||
"deleteDialog2": "การกระทำนี้;ไม่สามารถยกเลิกได้", |
|||
"cancel": "ยกเลิก", |
|||
"create": "สร้าง", |
|||
"createdOn": "สร้างเมื่อ ", |
|||
"lastSeen": "เห็นครั้งสุดท้ายเมื่อ ", |
|||
"totalDownload": "ดาวน์โหลดทั้งหมด: ", |
|||
"totalUpload": "อัพโหลดทั้งหมด: ", |
|||
"newClient": "Client ใหม่", |
|||
"disableClient": "ปิดการใช้งาน Client", |
|||
"enableClient": "เปิดการใช้งาน Client", |
|||
"noClients": "ยังไม่มี Clients เลย", |
|||
"showQR": "แสดงรหัส QR", |
|||
"downloadConfig": "ดาวน์โหลดการตั้งค่า", |
|||
"madeBy": "สร้างโดย", |
|||
"donate": "บริจาค" |
|||
} |
@ -1,38 +0,0 @@ |
|||
{ |
|||
"name": "İsim", |
|||
"password": "Şifre", |
|||
"signIn": "Giriş Yap", |
|||
"logout": "Çıkış Yap", |
|||
"updateAvailable": "Mevcut bir güncelleme var!", |
|||
"update": "Güncelle", |
|||
"clients": "Kullanıcılar", |
|||
"new": "Yeni", |
|||
"deleteClient": "Kullanıcı Sil", |
|||
"deleteDialog1": "Silmek istediğine emin misin", |
|||
"deleteDialog2": "Bu işlem geri alınamaz.", |
|||
"cancel": "İptal", |
|||
"create": "Oluştur", |
|||
"createdOn": "Şu saatte oluşturuldu: ", |
|||
"lastSeen": "Son görülme tarihi: ", |
|||
"totalDownload": "Toplam İndirme: ", |
|||
"totalUpload": "Toplam Yükleme: ", |
|||
"newClient": "Yeni Kullanıcı", |
|||
"disableClient": "Kullanıcıyı Devre Dışı Bırak", |
|||
"enableClient": "Kullanıcıyı Etkinleştir", |
|||
"noClients": "Henüz kullanıcı yok.", |
|||
"noPrivKey": "Bu istemcinin bilinen bir özel anahtarı yok. Yapılandırma oluşturulamıyor.", |
|||
"showQR": "QR Kodunu Göster", |
|||
"downloadConfig": "Yapılandırmayı İndir", |
|||
"madeBy": "Yapan Kişi: ", |
|||
"donate": "Bağış Yap", |
|||
"toggleCharts": "Grafiği göster/gizle", |
|||
"theme": { |
|||
"dark": "Karanlık tema", |
|||
"light": "Açık tema", |
|||
"system": "Otomatik tema" |
|||
}, |
|||
"restore": "Geri yükle", |
|||
"backup": "Yedekle", |
|||
"titleRestoreConfig": "Yapılandırmanızı geri yükleyin", |
|||
"titleBackupConfig": "Yapılandırmanızı yedekleyin" |
|||
} |
@ -1,38 +0,0 @@ |
|||
{ |
|||
"name": "Ім`я", |
|||
"password": "Пароль", |
|||
"signIn": "Увійти", |
|||
"logout": "Вихід", |
|||
"updateAvailable": "Доступне оновлення!", |
|||
"update": "Оновити", |
|||
"clients": "Клієнти", |
|||
"new": "Новий", |
|||
"deleteClient": "Видалити клієнта", |
|||
"deleteDialog1": "Ви впевнені, що бажаєте видалити", |
|||
"deleteDialog2": "Цю дію неможливо скасувати.", |
|||
"cancel": "Скасувати", |
|||
"create": "Створити", |
|||
"createdOn": "Створено ", |
|||
"lastSeen": "Останнє підключення в ", |
|||
"totalDownload": "Всього завантажено: ", |
|||
"totalUpload": "Всього відправлено: ", |
|||
"newClient": "Новий клієнт", |
|||
"disableClient": "Вимкнути клієнта", |
|||
"enableClient": "Увімкнути клієнта", |
|||
"noClients": "Ще немає клієнтів.", |
|||
"noPrivKey": "У цього клієнта немає відомого приватного ключа. Неможливо створити конфігурацію.", |
|||
"showQR": "Показати QR-код", |
|||
"downloadConfig": "Завантажити конфігурацію", |
|||
"madeBy": "Зроблено", |
|||
"donate": "Пожертвувати", |
|||
"toggleCharts": "Показати/сховати діаграми", |
|||
"theme": { |
|||
"dark": "Темна тема", |
|||
"light": "Світла тема", |
|||
"system": "Автоматична тема" |
|||
}, |
|||
"restore": "Відновити", |
|||
"backup": "Резервна копія", |
|||
"titleRestoreConfig": "Відновити конфігурацію", |
|||
"titleBackupConfig": "Створити резервну копію конфігурації" |
|||
} |
@ -1,38 +0,0 @@ |
|||
{ |
|||
"name": "Tên", |
|||
"password": "Mật khẩu", |
|||
"signIn": "Đăng nhập", |
|||
"logout": "Đăng xuất", |
|||
"updateAvailable": "Có bản cập nhật mới!", |
|||
"update": "Cập nhật", |
|||
"clients": "Danh sách người dùng", |
|||
"new": "Mới", |
|||
"deleteClient": "Xóa người dùng", |
|||
"deleteDialog1": "Bạn có chắc chắn muốn xóa", |
|||
"deleteDialog2": "Thao tác này không thể hoàn tác.", |
|||
"cancel": "Huỷ", |
|||
"create": "Tạo", |
|||
"createdOn": "Được tạo lúc ", |
|||
"lastSeen": "Lần xem cuối vào ", |
|||
"totalDownload": "Tổng dung lượng tải xuống: ", |
|||
"totalUpload": "Tổng dung lượng tải lên: ", |
|||
"newClient": "Người dùng mới", |
|||
"disableClient": "Vô hiệu hóa người dùng", |
|||
"enableClient": "Kích hoạt người dùng", |
|||
"noClients": "Hiện chưa có người dùng nào.", |
|||
"showQR": "Hiển thị mã QR", |
|||
"downloadConfig": "Tải xuống cấu hình", |
|||
"madeBy": "Được tạo bởi", |
|||
"donate": "Ủng hộ", |
|||
"toggleCharts": "Mở/Ẩn Biểu đồ", |
|||
"theme": { |
|||
"dark": "Dark theme", |
|||
"light": "Light theme", |
|||
"system": "System theme" |
|||
}, |
|||
"restore": "Khôi phục", |
|||
"backup": "Sao lưu", |
|||
"titleRestoreConfig": "Khôi phục cấu hình của bạn", |
|||
"titleBackupConfig": "Sao lưu cấu hình của bạn", |
|||
"sort": "Sắp xếp" |
|||
} |
@ -1,40 +0,0 @@ |
|||
{ |
|||
"name": "名称", |
|||
"password": "密码", |
|||
"signIn": "登录", |
|||
"logout": "退出", |
|||
"updateAvailable": "有新版本可用!", |
|||
"update": "更新", |
|||
"clients": "客户端", |
|||
"new": "新建", |
|||
"deleteClient": "删除客户端", |
|||
"deleteDialog1": "您确定要删除", |
|||
"deleteDialog2": "此操作无法撤销。", |
|||
"cancel": "取消", |
|||
"create": "创建", |
|||
"createdOn": "创建于 ", |
|||
"lastSeen": "最后访问于 ", |
|||
"totalDownload": "总下载: ", |
|||
"totalUpload": "总上传: ", |
|||
"newClient": "新建客户端", |
|||
"disableClient": "禁用客户端", |
|||
"enableClient": "启用客户端", |
|||
"noClients": "目前没有客户端。", |
|||
"noPrivKey": "此客户端没有已知的私钥。无法创建配置。", |
|||
"showQR": "显示二维码", |
|||
"downloadConfig": "下载配置", |
|||
"madeBy": "由", |
|||
"donate": "捐赠", |
|||
"toggleCharts": "显示/隐藏图表", |
|||
"theme": { "dark": "暗黑主题", "light": "明亮主题", "system": "自动主题" }, |
|||
"restore": "恢复", |
|||
"backup": "备份", |
|||
"titleRestoreConfig": "恢复您的配置", |
|||
"titleBackupConfig": "备份您的配置", |
|||
"rememberMe": "记住我", |
|||
"titleRememberMe": "关闭浏览器后保持登录", |
|||
"sort": "排序", |
|||
"ExpireDate": "到期日期", |
|||
"Permanent": "永久", |
|||
"OneTimeLink": "生成一次性短链接" |
|||
} |
@ -1,40 +0,0 @@ |
|||
{ |
|||
"name": "名字", |
|||
"password": "密碼", |
|||
"signIn": "登入", |
|||
"logout": "登出", |
|||
"updateAvailable": "有新版本可以使用!", |
|||
"update": "更新", |
|||
"clients": "使用者", |
|||
"new": "建立", |
|||
"deleteClient": "刪除使用者", |
|||
"deleteDialog1": "您確定要刪除", |
|||
"deleteDialog2": "此作業無法復原。", |
|||
"cancel": "取消", |
|||
"create": "建立", |
|||
"createdOn": "建立於 ", |
|||
"lastSeen": "最後存取於 ", |
|||
"totalDownload": "總下載: ", |
|||
"totalUpload": "總上傳: ", |
|||
"newClient": "新用戶", |
|||
"disableClient": "停用使用者", |
|||
"enableClient": "啟用使用者", |
|||
"noClients": "目前沒有使用者。", |
|||
"noPrivKey": "此使用者沒有已知的私鑰。無法創建配置。", |
|||
"showQR": "顯示 QR Code", |
|||
"downloadConfig": "下載 Config 檔", |
|||
"madeBy": "由", |
|||
"donate": "抖內", |
|||
"toggleCharts": "顯示/隱藏圖表", |
|||
"theme": { "dark": "暗黑主題", "light": "明亮主題", "system": "自動主題" }, |
|||
"restore": "恢復", |
|||
"backup": "備份", |
|||
"titleRestoreConfig": "恢復您的配置", |
|||
"titleBackupConfig": "備份您的配置", |
|||
"rememberMe": "記住我", |
|||
"titleRememberMe": "關閉瀏覽器後保持登錄", |
|||
"sort": "排序", |
|||
"ExpireDate": "到期日期", |
|||
"Permanent": "永久", |
|||
"OneTimeLink": "生成一次性短鏈接" |
|||
} |
@ -14,37 +14,38 @@ |
|||
"format": "prettier . --write", |
|||
"format:check": "prettier . --check", |
|||
"typecheck": "nuxt typecheck", |
|||
"check:runall": "nuxt build && nuxt typecheck && eslint . && prettier . --check" |
|||
"check:all": "pnpm typecheck && pnpm lint && pnpm format:check && pnpm build" |
|||
}, |
|||
"dependencies": { |
|||
"@eschricht/nuxt-color-mode": "^1.1.5", |
|||
"@nuxtjs/i18n": "^9.1.1", |
|||
"@nuxtjs/tailwindcss": "^6.12.2", |
|||
"@pinia/nuxt": "^0.9.0", |
|||
"@tailwindcss/forms": "^0.5.9", |
|||
"apexcharts": "^4.2.0", |
|||
"@tailwindcss/forms": "^0.5.10", |
|||
"apexcharts": "^4.3.0", |
|||
"argon2": "^0.41.1", |
|||
"basic-auth": "^2.0.1", |
|||
"cidr-tools": "^11.0.2", |
|||
"crc-32": "^1.2.2", |
|||
"debug": "^4.4.0", |
|||
"ip-bigint": "^8.2.0", |
|||
"is-cidr": "^5.1.0", |
|||
"is-ip": "^5.0.1", |
|||
"js-sha256": "^0.11.0", |
|||
"lowdb": "^7.0.1", |
|||
"nuxt": "^3.14.1592", |
|||
"nuxt": "^3.15.1", |
|||
"pinia": "^2.3.0", |
|||
"qrcode": "^1.5.4", |
|||
"radix-vue": "^1.9.11", |
|||
"radix-vue": "^1.9.12", |
|||
"semver": "^7.6.3", |
|||
"tailwindcss": "^3.4.16", |
|||
"tailwindcss": "^3.4.17", |
|||
"timeago.js": "^4.0.2", |
|||
"vue": "latest", |
|||
"vue3-apexcharts": "^1.8.0", |
|||
"zod": "^3.24.1" |
|||
}, |
|||
"devDependencies": { |
|||
"@nuxt/eslint-config": "^0.7.3", |
|||
"@nuxt/eslint-config": "^0.7.5", |
|||
"@types/debug": "^4.1.12", |
|||
"@types/qrcode": "^1.5.5", |
|||
"@types/semver": "^7.5.8", |
|||
@ -52,8 +53,8 @@ |
|||
"eslint-config-prettier": "^9.1.0", |
|||
"prettier": "^3.4.2", |
|||
"prettier-plugin-tailwindcss": "^0.6.9", |
|||
"typescript": "^5.7.2", |
|||
"vue-tsc": "^2.1.10" |
|||
"typescript": "^5.7.3", |
|||
"vue-tsc": "^2.2.0" |
|||
}, |
|||
"packageManager": "[email protected].0" |
|||
"packageManager": "[email protected].3" |
|||
} |
|||
|
File diff suppressed because it is too large
@ -0,0 +1,4 @@ |
|||
export default defineEventHandler(async () => { |
|||
const system = await Database.system.get(); |
|||
return system.general; |
|||
}); |
@ -0,0 +1,8 @@ |
|||
export default defineEventHandler(async (event) => { |
|||
const data = await readValidatedBody( |
|||
event, |
|||
validateZod(generalUpdateType, event) |
|||
); |
|||
await Database.system.updateGeneral(data); |
|||
return { success: true }; |
|||
}); |
@ -0,0 +1,4 @@ |
|||
export default defineEventHandler(async () => { |
|||
const system = await Database.system.get(); |
|||
return system.hooks; |
|||
}); |
@ -0,0 +1,9 @@ |
|||
export default defineEventHandler(async (event) => { |
|||
const data = await readValidatedBody( |
|||
event, |
|||
validateZod(hooksUpdateType, event) |
|||
); |
|||
await Database.system.updateHooks(data); |
|||
await WireGuard.saveConfig(); |
|||
return { success: true }; |
|||
}); |
@ -0,0 +1,4 @@ |
|||
export default defineEventHandler(async () => { |
|||
const system = await Database.system.get(); |
|||
return system.interface; |
|||
}); |
@ -0,0 +1,9 @@ |
|||
export default defineEventHandler(async (event) => { |
|||
const data = await readValidatedBody( |
|||
event, |
|||
validateZod(interfaceUpdateType, event) |
|||
); |
|||
await Database.system.updateInterface(data); |
|||
await WireGuard.saveConfig(); |
|||
return { success: true }; |
|||
}); |
@ -1,5 +0,0 @@ |
|||
export default defineEventHandler(async (event) => { |
|||
const { lang } = await readValidatedBody(event, validateZod(langType)); |
|||
await Database.system.updateLang(lang); |
|||
return { success: true }; |
|||
}); |
@ -0,0 +1,9 @@ |
|||
export default defineEventHandler(async (event) => { |
|||
const data = await readValidatedBody( |
|||
event, |
|||
validateZod(cidrUpdateType, event) |
|||
); |
|||
|
|||
await WireGuard.updateAddressRange(data); |
|||
return { success: true }; |
|||
}); |
@ -0,0 +1,4 @@ |
|||
export default defineEventHandler(async () => { |
|||
const system = await Database.system.get(); |
|||
return system.userConfig; |
|||
}); |
@ -0,0 +1,9 @@ |
|||
export default defineEventHandler(async (event) => { |
|||
const data = await readValidatedBody( |
|||
event, |
|||
validateZod(userConfigUpdateType, event) |
|||
); |
|||
await Database.system.updateUserConfig(data); |
|||
await WireGuard.saveConfig(); |
|||
return { success: true }; |
|||
}); |
@ -1,12 +0,0 @@ |
|||
export default defineEventHandler(async (event) => { |
|||
const { clientId } = await getValidatedRouterParams( |
|||
event, |
|||
validateZod(clientIdType) |
|||
); |
|||
const { address4 } = await readValidatedBody( |
|||
event, |
|||
validateZod(address4Type) |
|||
); |
|||
await WireGuard.updateClientAddress({ clientId, address4 }); |
|||
return { success: true }; |
|||
}); |
@ -1,9 +0,0 @@ |
|||
export default defineEventHandler(async (event) => { |
|||
const { clientId } = await getValidatedRouterParams( |
|||
event, |
|||
validateZod(clientIdType) |
|||
); |
|||
const { name } = await readValidatedBody(event, validateZod(nameType)); |
|||
await WireGuard.updateClientName({ clientId, name }); |
|||
return { success: true }; |
|||
}); |
@ -1,5 +0,0 @@ |
|||
export default defineEventHandler(async (event) => { |
|||
setHeader(event, 'Content-Type', 'application/json'); |
|||
const system = await Database.system.get(); |
|||
return system.general.lang; |
|||
}); |
@ -1,14 +0,0 @@ |
|||
export default defineEventHandler(async (event) => { |
|||
const setupDone = await Database.setup.done(); |
|||
if (setupDone) { |
|||
throw createError({ |
|||
statusCode: 400, |
|||
statusMessage: 'Invalid state', |
|||
}); |
|||
} |
|||
|
|||
const { lang } = await readValidatedBody(event, validateZod(langType)); |
|||
await Database.system.updateLang(lang); |
|||
await Database.setup.set(2); |
|||
return { success: true }; |
|||
}); |
@ -0,0 +1,14 @@ |
|||
export default defineEventHandler(async (event) => { |
|||
// TODO: check password
|
|||
|
|||
const system = await Database.system.get(); |
|||
if (!system.metrics.prometheus.enabled) { |
|||
throw createError({ |
|||
statusCode: 400, |
|||
message: 'Prometheus metrics are not enabled', |
|||
}); |
|||
} |
|||
|
|||
setHeader(event, 'Content-Type', 'text/plain'); |
|||
return WireGuard.getMetrics(); |
|||
}); |
@ -0,0 +1,13 @@ |
|||
export default defineEventHandler(async () => { |
|||
// TODO: check password
|
|||
|
|||
const system = await Database.system.get(); |
|||
if (!system.metrics.prometheus.enabled) { |
|||
throw createError({ |
|||
statusCode: 400, |
|||
message: 'Prometheus metrics are not enabled', |
|||
}); |
|||
} |
|||
|
|||
return WireGuard.getMetricsJSON(); |
|||
}); |
@ -0,0 +1,27 @@ |
|||
import type { DeepReadonly } from 'vue'; |
|||
import type { System } from '~~/services/database/repositories/system'; |
|||
|
|||
/** |
|||
* Replace all {{key}} in the template with the values[key] |
|||
*/ |
|||
export function template(templ: string, values: Record<string, string>) { |
|||
return templ.replace(/\{\{(\w+)\}\}/g, (match, key) => { |
|||
return values[key] !== undefined ? values[key] : match; |
|||
}); |
|||
} |
|||
|
|||
/** |
|||
* Available keys: |
|||
* - address4: IPv4 address range |
|||
* - address6: IPv6 address range |
|||
* - device: Network device |
|||
* - port: Port number |
|||
*/ |
|||
export function iptablesTemplate(templ: string, system: DeepReadonly<System>) { |
|||
return template(templ, { |
|||
address4: system.userConfig.address4Range, |
|||
address6: system.userConfig.address6Range, |
|||
device: system.interface.device, |
|||
port: system.interface.port.toString(), |
|||
}); |
|||
} |
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue