mirror of https://github.com/wg-easy/wg-easy
14 changed files with 274 additions and 220 deletions
@ -0,0 +1,44 @@ |
|||
<template> |
|||
<SelectRoot v-model="langProxy" :default-value="locale"> |
|||
<SelectTrigger |
|||
class="inline-flex h-8 items-center justify-around gap-2 rounded bg-gray-200 px-3 text-sm leading-none dark:bg-neutral-700 dark:text-neutral-400" |
|||
aria-label="Select language" |
|||
> |
|||
<IconsLanguage class="size-3" /> |
|||
<SelectValue /> |
|||
<IconsArrowDown class="size-3" /> |
|||
</SelectTrigger> |
|||
|
|||
<SelectPortal> |
|||
<SelectContent |
|||
class="min-w-28 rounded bg-gray-300 dark:bg-neutral-500" |
|||
position="popper" |
|||
> |
|||
<SelectViewport class="p-2"> |
|||
<SelectItem |
|||
v-for="(option, index) in langs" |
|||
:key="index" |
|||
:value="option.code" |
|||
class="relative flex h-6 items-center rounded px-3 text-sm leading-none outline-none hover:bg-red-800 hover:text-white data-[state=checked]:underline dark:text-white" |
|||
> |
|||
<SelectItemText> |
|||
{{ option.name }} |
|||
</SelectItemText> |
|||
</SelectItem> |
|||
</SelectViewport> |
|||
</SelectContent> |
|||
</SelectPortal> |
|||
</SelectRoot> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
const { locales, locale, setLocale } = useI18n(); |
|||
|
|||
const langProxy = ref(locale); |
|||
|
|||
watchEffect(() => { |
|||
setLocale(langProxy.value); |
|||
}); |
|||
|
|||
const langs = locales.value.sort((a, b) => a.code.localeCompare(b.code)); |
|||
</script> |
@ -0,0 +1,15 @@ |
|||
<template> |
|||
<svg |
|||
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="m10.5 21 5.25-11.25L21 21m-9-3h7.5M3 5.621a48.474 48.474 0 0 1 6-.371m0 0c1.12 0 2.233.038 3.334.114M9 5.25V3m3.334 2.364C11.176 10.658 7.69 15.08 3 17.502m9.334-12.138c.896.061 1.785.147 2.666.257m-4.589 8.495a18.023 18.023 0 0 1-3.827-5.802" |
|||
/> |
|||
</svg> |
|||
</template> |
@ -0,0 +1,38 @@ |
|||
<template> |
|||
<footer> |
|||
<p class="m-10 text-center text-xs text-gray-300 dark:text-neutral-600"> |
|||
<a |
|||
class="hover:underline" |
|||
target="_blank" |
|||
href="https://github.com/wg-easy/wg-easy" |
|||
>WireGuard Easy</a |
|||
> |
|||
({{ globalStore.currentRelease }}) © 2021-2025 by |
|||
<a |
|||
class="hover:underline" |
|||
target="_blank" |
|||
href="https://emile.nl/?ref=wg-easy" |
|||
>Emile Nijssen</a |
|||
> |
|||
is licensed under |
|||
<a |
|||
class="hover:underline" |
|||
target="_blank" |
|||
href="https://spdx.org/licenses/AGPL-3.0-only.html" |
|||
>AGPL-3.0-only</a |
|||
> |
|||
· |
|||
<a |
|||
class="hover:underline" |
|||
href="https://github.com/sponsors/WeeJeWel" |
|||
target="_blank" |
|||
>{{ $t('donate') }}</a |
|||
> |
|||
</p> |
|||
</footer> |
|||
</template> |
|||
|
|||
<script lang="ts" setup> |
|||
const globalStore = useGlobalStore(); |
|||
globalStore.fetchRelease(); |
|||
</script> |
@ -1,22 +1,16 @@ |
|||
<template> |
|||
<div> |
|||
<p class="p-8 text-center text-lg"> |
|||
{{ $t('setup.messageSetupLanguage') }} |
|||
<p class="px-8 pt-8 text-center text-2xl"> |
|||
{{ $t('setup.messageWelcome.whatIs') }} |
|||
</p> |
|||
<div class="mb-8 flex justify-center"> |
|||
<UiChooseLang /> |
|||
</div> |
|||
<div> |
|||
<NuxtLink to="/setup/2"><BaseButton>Continue</BaseButton></NuxtLink> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
definePageMeta({ |
|||
layout: 'setup', |
|||
}); |
|||
|
|||
const setupStore = useSetupStore(); |
|||
setupStore.setStep(1); |
|||
</script> |
|||
|
@ -1,16 +1,83 @@ |
|||
<template> |
|||
<div> |
|||
<p class="px-8 pt-8 text-center text-2xl"> |
|||
{{ $t('setup.messageWelcome.whatIs') }} |
|||
<p class="p-8 text-center text-lg"> |
|||
{{ $t('setup.messageSetupCreateAdminUser') }} |
|||
</p> |
|||
<NuxtLink to="/setup/3"><BaseButton>Continue</BaseButton></NuxtLink> |
|||
<form id="newAccount"></form> |
|||
<div> |
|||
<Label for="username">{{ $t('username') }}</Label> |
|||
<input |
|||
id="username" |
|||
v-model="username" |
|||
form="newAccount" |
|||
type="text" |
|||
autocomplete="username" |
|||
class="mb-5 w-full rounded-lg border-2 border-gray-100 px-3 py-2 text-sm text-gray-500 focus:border-red-800 focus:outline-0 focus:ring-0 dark:border-neutral-800 dark:bg-neutral-700 dark:text-gray-200 dark:placeholder:text-neutral-400 dark:focus:border-red-800" |
|||
/> |
|||
</div> |
|||
<div> |
|||
<Label for="password">{{ $t('setup.newPassword') }}</Label> |
|||
<input |
|||
id="password" |
|||
v-model="password" |
|||
form="newAccount" |
|||
type="password" |
|||
autocomplete="new-password" |
|||
class="mb-5 w-full rounded-lg border-2 border-gray-100 px-3 py-2 text-sm text-gray-500 focus:border-red-800 focus:outline-0 focus:ring-0 dark:border-neutral-800 dark:bg-neutral-700 dark:text-gray-200 dark:placeholder:text-neutral-400 dark:focus:border-red-800" |
|||
/> |
|||
</div> |
|||
<div> |
|||
<Label for="accept">{{ $t('setup.accept') }}</Label> |
|||
<input |
|||
id="accept" |
|||
v-model="accept" |
|||
form="newAccount" |
|||
type="checkbox" |
|||
class="ml-2" |
|||
/> |
|||
</div> |
|||
<BaseButton @click="newAccount">Create Account</BaseButton> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
<script lang="ts" setup> |
|||
import { FetchError } from 'ofetch'; |
|||
const { t } = useI18n(); |
|||
|
|||
definePageMeta({ |
|||
layout: 'setup', |
|||
}); |
|||
|
|||
const setupStore = useSetupStore(); |
|||
setupStore.setStep(2); |
|||
const router = useRouter(); |
|||
const username = ref<null | string>(null); |
|||
const password = ref<null | string>(null); |
|||
const accept = ref<boolean>(true); |
|||
|
|||
const toast = useToast(); |
|||
|
|||
async function newAccount() { |
|||
try { |
|||
if (!username.value || !password.value) { |
|||
toast.showToast({ |
|||
type: 'error', |
|||
title: t('setup.requirements'), |
|||
message: t('setup.emptyFields'), |
|||
}); |
|||
return; |
|||
} |
|||
|
|||
await setupStore.step2(username.value, password.value, accept.value); |
|||
await router.push('/setup/3'); |
|||
} catch (error) { |
|||
if (error instanceof FetchError) { |
|||
toast.showToast({ |
|||
type: 'error', |
|||
title: t('setup.requirements'), |
|||
message: error.data.message, |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
@ -1,83 +1,19 @@ |
|||
<template> |
|||
<div> |
|||
<p class="p-8 text-center text-lg"> |
|||
{{ $t('setup.messageSetupCreateAdminUser') }} |
|||
{{ 'Do you have a existing Setup?' }} |
|||
</p> |
|||
<form id="newAccount"></form> |
|||
<div> |
|||
<Label for="username">{{ $t('username') }}</Label> |
|||
<input |
|||
id="username" |
|||
v-model="username" |
|||
form="newAccount" |
|||
type="text" |
|||
autocomplete="username" |
|||
class="mb-5 w-full rounded-lg border-2 border-gray-100 px-3 py-2 text-sm text-gray-500 focus:border-red-800 focus:outline-0 focus:ring-0 dark:border-neutral-800 dark:bg-neutral-700 dark:text-gray-200 dark:placeholder:text-neutral-400 dark:focus:border-red-800" |
|||
/> |
|||
</div> |
|||
<div> |
|||
<Label for="password">{{ $t('setup.newPassword') }}</Label> |
|||
<input |
|||
id="password" |
|||
v-model="password" |
|||
form="newAccount" |
|||
type="password" |
|||
autocomplete="new-password" |
|||
class="mb-5 w-full rounded-lg border-2 border-gray-100 px-3 py-2 text-sm text-gray-500 focus:border-red-800 focus:outline-0 focus:ring-0 dark:border-neutral-800 dark:bg-neutral-700 dark:text-gray-200 dark:placeholder:text-neutral-400 dark:focus:border-red-800" |
|||
/> |
|||
<div class="mb-8 flex justify-center"> |
|||
<NuxtLink to="/setup/4"><BaseButton>No</BaseButton></NuxtLink> |
|||
<NuxtLink to="/setup/migrate"><BaseButton>Yes</BaseButton></NuxtLink> |
|||
</div> |
|||
<div> |
|||
<Label for="accept">{{ $t('setup.accept') }}</Label> |
|||
<input |
|||
id="accept" |
|||
v-model="accept" |
|||
form="newAccount" |
|||
type="checkbox" |
|||
class="ml-2" |
|||
/> |
|||
</div> |
|||
<BaseButton @click="newAccount">Create Account</BaseButton> |
|||
</div> |
|||
</template> |
|||
|
|||
<script lang="ts" setup> |
|||
import { FetchError } from 'ofetch'; |
|||
const { t } = useI18n(); |
|||
|
|||
definePageMeta({ |
|||
layout: 'setup', |
|||
}); |
|||
|
|||
const setupStore = useSetupStore(); |
|||
setupStore.setStep(3); |
|||
const router = useRouter(); |
|||
const username = ref<null | string>(null); |
|||
const password = ref<null | string>(null); |
|||
const accept = ref<boolean>(true); |
|||
|
|||
const toast = useToast(); |
|||
|
|||
async function newAccount() { |
|||
try { |
|||
if (!username.value || !password.value) { |
|||
toast.showToast({ |
|||
type: 'error', |
|||
title: t('setup.requirements'), |
|||
message: t('setup.emptyFields'), |
|||
}); |
|||
return; |
|||
} |
|||
|
|||
await setupStore.step3(username.value, password.value, accept.value); |
|||
await router.push('/setup/4'); |
|||
} catch (error) { |
|||
if (error instanceof FetchError) { |
|||
toast.showToast({ |
|||
type: 'error', |
|||
title: t('setup.requirements'), |
|||
message: error.data.message, |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
@ -1,19 +1,71 @@ |
|||
<template> |
|||
<div> |
|||
<p class="p-8 text-center text-lg"> |
|||
{{ 'Do you have a existing Setup?' }} |
|||
{{ $t('setup.messageSetupHostPort') }} |
|||
</p> |
|||
<div class="mb-8 flex justify-center"> |
|||
<NuxtLink to="/setup/5"><BaseButton>No</BaseButton></NuxtLink> |
|||
<NuxtLink to="/setup/migrate"><BaseButton>Yes</BaseButton></NuxtLink> |
|||
<div> |
|||
<Label for="host">{{ $t('setup.host') }}</Label> |
|||
<input |
|||
id="host" |
|||
v-model="host" |
|||
type="text" |
|||
class="mb-5 w-full rounded-lg border-2 border-gray-100 px-3 py-2 text-sm text-gray-500 focus:border-red-800 focus:outline-0 focus:ring-0 dark:border-neutral-800 dark:bg-neutral-700 dark:text-gray-200 dark:placeholder:text-neutral-400 dark:focus:border-red-800" |
|||
placeholder="vpn.example.com" |
|||
/> |
|||
</div> |
|||
<div> |
|||
<Label for="port">{{ $t('setup.port') }}</Label> |
|||
<input |
|||
id="port" |
|||
v-model="port" |
|||
type="number" |
|||
:min="1" |
|||
:max="65535" |
|||
class="mb-5 w-full rounded-lg border-2 border-gray-100 px-3 py-2 text-sm text-gray-500 focus:border-red-800 focus:outline-0 focus:ring-0 dark:border-neutral-800 dark:bg-neutral-700 dark:text-gray-200 dark:placeholder:text-neutral-400 dark:focus:border-red-800" |
|||
/> |
|||
</div> |
|||
<BaseButton @click="updateHostPort">Continue</BaseButton> |
|||
</div> |
|||
</template> |
|||
|
|||
<script lang="ts" setup> |
|||
<script setup lang="ts"> |
|||
import { FetchError } from 'ofetch'; |
|||
|
|||
definePageMeta({ |
|||
layout: 'setup', |
|||
}); |
|||
|
|||
const { t } = useI18n(); |
|||
|
|||
const setupStore = useSetupStore(); |
|||
setupStore.setStep(4); |
|||
const router = useRouter(); |
|||
const host = ref<null | string>(null); |
|||
const port = ref<number>(51820); |
|||
|
|||
const toast = useToast(); |
|||
|
|||
async function updateHostPort() { |
|||
if (!host.value || !port.value) { |
|||
toast.showToast({ |
|||
type: 'error', |
|||
title: t('setup.requirements'), |
|||
message: t('setup.emptyFields'), |
|||
}); |
|||
return; |
|||
} |
|||
|
|||
try { |
|||
await setupStore.step4(host.value, port.value); |
|||
await router.push('/setup/success'); |
|||
} catch (error) { |
|||
if (error instanceof FetchError) { |
|||
toast.showToast({ |
|||
type: 'error', |
|||
title: t('setup.requirements'), |
|||
message: error.data.message, |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
@ -1,71 +0,0 @@ |
|||
<template> |
|||
<div> |
|||
<p class="p-8 text-center text-lg"> |
|||
{{ $t('setup.messageSetupHostPort') }} |
|||
</p> |
|||
<div> |
|||
<Label for="host">{{ $t('setup.host') }}</Label> |
|||
<input |
|||
id="host" |
|||
v-model="host" |
|||
type="text" |
|||
class="mb-5 w-full rounded-lg border-2 border-gray-100 px-3 py-2 text-sm text-gray-500 focus:border-red-800 focus:outline-0 focus:ring-0 dark:border-neutral-800 dark:bg-neutral-700 dark:text-gray-200 dark:placeholder:text-neutral-400 dark:focus:border-red-800" |
|||
placeholder="vpn.example.com" |
|||
/> |
|||
</div> |
|||
<div> |
|||
<Label for="port">{{ $t('setup.port') }}</Label> |
|||
<input |
|||
id="port" |
|||
v-model="port" |
|||
type="number" |
|||
:min="1" |
|||
:max="65535" |
|||
class="mb-5 w-full rounded-lg border-2 border-gray-100 px-3 py-2 text-sm text-gray-500 focus:border-red-800 focus:outline-0 focus:ring-0 dark:border-neutral-800 dark:bg-neutral-700 dark:text-gray-200 dark:placeholder:text-neutral-400 dark:focus:border-red-800" |
|||
/> |
|||
</div> |
|||
<BaseButton @click="updateHostPort">Continue</BaseButton> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { FetchError } from 'ofetch'; |
|||
|
|||
definePageMeta({ |
|||
layout: 'setup', |
|||
}); |
|||
|
|||
const { t } = useI18n(); |
|||
|
|||
const setupStore = useSetupStore(); |
|||
setupStore.setStep(5); |
|||
const router = useRouter(); |
|||
const host = ref<null | string>(null); |
|||
const port = ref<number>(51820); |
|||
|
|||
const toast = useToast(); |
|||
|
|||
async function updateHostPort() { |
|||
if (!host.value || !port.value) { |
|||
toast.showToast({ |
|||
type: 'error', |
|||
title: t('setup.requirements'), |
|||
message: t('setup.emptyFields'), |
|||
}); |
|||
return; |
|||
} |
|||
|
|||
try { |
|||
await setupStore.step5(host.value, port.value); |
|||
await router.push('/setup/success'); |
|||
} catch (error) { |
|||
if (error instanceof FetchError) { |
|||
toast.showToast({ |
|||
type: 'error', |
|||
title: t('setup.requirements'), |
|||
message: error.data.message, |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
</script> |
Loading…
Reference in new issue