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> |
<template> |
||||
<div> |
<div> |
||||
<p class="p-8 text-center text-lg"> |
<p class="px-8 pt-8 text-center text-2xl"> |
||||
{{ $t('setup.messageSetupLanguage') }} |
{{ $t('setup.messageWelcome.whatIs') }} |
||||
</p> |
</p> |
||||
<div class="mb-8 flex justify-center"> |
|
||||
<UiChooseLang /> |
|
||||
</div> |
|
||||
<div> |
|
||||
<NuxtLink to="/setup/2"><BaseButton>Continue</BaseButton></NuxtLink> |
<NuxtLink to="/setup/2"><BaseButton>Continue</BaseButton></NuxtLink> |
||||
</div> |
</div> |
||||
</div> |
|
||||
</template> |
</template> |
||||
|
|
||||
<script setup lang="ts"> |
<script setup lang="ts"> |
||||
definePageMeta({ |
definePageMeta({ |
||||
layout: 'setup', |
layout: 'setup', |
||||
}); |
}); |
||||
|
|
||||
const setupStore = useSetupStore(); |
const setupStore = useSetupStore(); |
||||
setupStore.setStep(1); |
setupStore.setStep(1); |
||||
</script> |
</script> |
||||
|
@ -1,16 +1,83 @@ |
|||||
<template> |
<template> |
||||
<div> |
<div> |
||||
<p class="px-8 pt-8 text-center text-2xl"> |
<p class="p-8 text-center text-lg"> |
||||
{{ $t('setup.messageWelcome.whatIs') }} |
{{ $t('setup.messageSetupCreateAdminUser') }} |
||||
</p> |
</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> |
</div> |
||||
</template> |
</template> |
||||
|
|
||||
<script setup lang="ts"> |
<script lang="ts" setup> |
||||
|
import { FetchError } from 'ofetch'; |
||||
|
const { t } = useI18n(); |
||||
|
|
||||
definePageMeta({ |
definePageMeta({ |
||||
layout: 'setup', |
layout: 'setup', |
||||
}); |
}); |
||||
|
|
||||
const setupStore = useSetupStore(); |
const setupStore = useSetupStore(); |
||||
setupStore.setStep(2); |
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> |
</script> |
||||
|
@ -1,83 +1,19 @@ |
|||||
<template> |
<template> |
||||
<div> |
<div> |
||||
<p class="p-8 text-center text-lg"> |
<p class="p-8 text-center text-lg"> |
||||
{{ $t('setup.messageSetupCreateAdminUser') }} |
{{ 'Do you have a existing Setup?' }} |
||||
</p> |
</p> |
||||
<form id="newAccount"></form> |
<div class="mb-8 flex justify-center"> |
||||
<div> |
<NuxtLink to="/setup/4"><BaseButton>No</BaseButton></NuxtLink> |
||||
<Label for="username">{{ $t('username') }}</Label> |
<NuxtLink to="/setup/migrate"><BaseButton>Yes</BaseButton></NuxtLink> |
||||
<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> |
||||
<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> |
</div> |
||||
</template> |
</template> |
||||
|
|
||||
<script lang="ts" setup> |
<script lang="ts" setup> |
||||
import { FetchError } from 'ofetch'; |
|
||||
const { t } = useI18n(); |
|
||||
|
|
||||
definePageMeta({ |
definePageMeta({ |
||||
layout: 'setup', |
layout: 'setup', |
||||
}); |
}); |
||||
|
|
||||
const setupStore = useSetupStore(); |
const setupStore = useSetupStore(); |
||||
setupStore.setStep(3); |
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> |
</script> |
||||
|
@ -1,19 +1,71 @@ |
|||||
<template> |
<template> |
||||
<div> |
<div> |
||||
<p class="p-8 text-center text-lg"> |
<p class="p-8 text-center text-lg"> |
||||
{{ 'Do you have a existing Setup?' }} |
{{ $t('setup.messageSetupHostPort') }} |
||||
</p> |
</p> |
||||
<div class="mb-8 flex justify-center"> |
<div> |
||||
<NuxtLink to="/setup/5"><BaseButton>No</BaseButton></NuxtLink> |
<Label for="host">{{ $t('setup.host') }}</Label> |
||||
<NuxtLink to="/setup/migrate"><BaseButton>Yes</BaseButton></NuxtLink> |
<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> |
</div> |
||||
|
<BaseButton @click="updateHostPort">Continue</BaseButton> |
||||
</div> |
</div> |
||||
</template> |
</template> |
||||
|
|
||||
<script lang="ts" setup> |
<script setup lang="ts"> |
||||
|
import { FetchError } from 'ofetch'; |
||||
|
|
||||
definePageMeta({ |
definePageMeta({ |
||||
layout: 'setup', |
layout: 'setup', |
||||
}); |
}); |
||||
|
|
||||
|
const { t } = useI18n(); |
||||
|
|
||||
const setupStore = useSetupStore(); |
const setupStore = useSetupStore(); |
||||
setupStore.setStep(4); |
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> |
</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