From dd0cb370d7987daff7064893b9f1fd0ddda71b65 Mon Sep 17 00:00:00 2001
From: Bernd Storath <999999bst@gmail.com>
Date: Fri, 14 Feb 2025 12:37:42 +0100
Subject: [PATCH] improve
---
src/app/components/ClientCard/Config.vue | 2 +-
src/app/components/ClientCard/ExpireDate.vue | 2 +-
src/app/components/ClientCard/LastSeen.vue | 2 +-
src/app/components/ClientCard/Name.vue | 2 +-
.../components/ClientCard/OneTimeLinkBtn.vue | 2 +-
src/app/components/ClientCard/QRCode.vue | 2 +-
src/app/components/ClientCard/Switch.vue | 5 +-
src/app/components/Clients/CreateDialog.vue | 40 +++----
src/app/components/Clients/Empty.vue | 6 +-
src/app/components/Clients/New.vue | 2 +-
src/app/components/Clients/Sort.vue | 49 ++-------
src/app/components/base/Container.vue | 0
src/app/components/base/Toast.vue | 40 +++----
src/app/components/form/NullTextField.vue | 17 ++-
src/app/components/form/TextField.vue | 15 ++-
src/app/components/header/ChartToggle.vue | 2 +-
src/app/components/header/Update.vue | 4 +-
src/app/components/panel/head/Title.vue | 12 +--
src/app/components/ui/ChooseLang.vue | 45 --------
src/app/components/ui/Footer.vue | 2 +-
src/app/components/ui/UserMenu.vue | 27 +++--
src/app/composables/useSubmit.ts | 30 ++++--
src/app/pages/admin.vue | 1 +
src/app/pages/admin/config.vue | 6 +-
src/app/pages/admin/hooks.vue | 2 +-
src/app/pages/admin/index.vue | 2 +-
src/app/pages/admin/interface.vue | 12 ++-
src/app/pages/clients/[id].vue | 14 +--
src/app/pages/index.vue | 1 +
src/app/pages/login.vue | 54 +++++-----
src/app/pages/me.vue | 16 +--
src/app/pages/setup/1.vue | 7 +-
src/app/pages/setup/2.vue | 102 +++++++-----------
src/app/pages/setup/3.vue | 11 +-
src/app/pages/setup/4.vue | 86 +++++++--------
src/app/pages/setup/migrate.vue | 52 ++++-----
src/app/pages/setup/success.vue | 7 +-
src/app/stores/auth.ts | 23 +---
src/app/stores/global.ts | 10 +-
src/app/stores/setup.ts | 36 -------
src/i18n/locales/en.json | 93 +++++++---------
.../database/repositories/user/types.ts | 7 --
42 files changed, 348 insertions(+), 502 deletions(-)
delete mode 100644 src/app/components/base/Container.vue
delete mode 100644 src/app/components/ui/ChooseLang.vue
diff --git a/src/app/components/ClientCard/Config.vue b/src/app/components/ClientCard/Config.vue
index 2ce4ec61..eddbe1d9 100644
--- a/src/app/components/ClientCard/Config.vue
+++ b/src/app/components/ClientCard/Config.vue
@@ -3,7 +3,7 @@
:href="'/api/client/' + client.id + '/configuration'"
download
class="inline-block rounded bg-gray-100 p-2 align-middle transition hover:bg-red-800 hover:text-white dark:bg-neutral-600 dark:text-neutral-300 dark:hover:bg-red-800 dark:hover:text-white"
- :title="$t('downloadConfig')"
+ :title="$t('client.downloadConfig')"
>
diff --git a/src/app/components/ClientCard/ExpireDate.vue b/src/app/components/ClientCard/ExpireDate.vue
index 98ed042d..3d8e1e04 100644
--- a/src/app/components/ClientCard/ExpireDate.vue
+++ b/src/app/components/ClientCard/ExpireDate.vue
@@ -12,7 +12,7 @@ defineProps<{ client: LocalClient }>();
const { t, locale } = useI18n();
function expiredDateFormat(value: string | null) {
- if (value === null) return t('Permanent');
+ if (value === null) return t('client.permanent');
const dateTime = new Date(value);
return dateTime.toLocaleDateString(locale.value, {
year: 'numeric',
diff --git a/src/app/components/ClientCard/LastSeen.vue b/src/app/components/ClientCard/LastSeen.vue
index 7836047b..fda45fb3 100644
--- a/src/app/components/ClientCard/LastSeen.vue
+++ b/src/app/components/ClientCard/LastSeen.vue
@@ -2,7 +2,7 @@
· {{ timeago(new Date(client.latestHandshakeAt)) }}
diff --git a/src/app/components/ClientCard/Name.vue b/src/app/components/ClientCard/Name.vue
index 7e7057a6..435db786 100644
--- a/src/app/components/ClientCard/Name.vue
+++ b/src/app/components/ClientCard/Name.vue
@@ -1,7 +1,7 @@
{{ client.name }}
diff --git a/src/app/components/ClientCard/OneTimeLinkBtn.vue b/src/app/components/ClientCard/OneTimeLinkBtn.vue
index 314a48a6..31e77b8d 100644
--- a/src/app/components/ClientCard/OneTimeLinkBtn.vue
+++ b/src/app/components/ClientCard/OneTimeLinkBtn.vue
@@ -1,7 +1,7 @@
diff --git a/src/app/components/Clients/Empty.vue b/src/app/components/Clients/Empty.vue
index 97a102dd..004fa6be 100644
--- a/src/app/components/Clients/Empty.vue
+++ b/src/app/components/Clients/Empty.vue
@@ -1,10 +1,10 @@
- {{ $t('noClients') }}
+ {{ $t('client.empty') }}
-
+
- {{ $t('new') }}
+ {{ $t('client.new') }}
diff --git a/src/app/components/Clients/New.vue b/src/app/components/Clients/New.vue
index 54a92fe4..87b6a071 100644
--- a/src/app/components/Clients/New.vue
+++ b/src/app/components/Clients/New.vue
@@ -2,7 +2,7 @@
- {{ $t('new') }}
+ {{ $t('client.newShort') }}
diff --git a/src/app/components/Clients/Sort.vue b/src/app/components/Clients/Sort.vue
index 1b73546d..a3aaaf79 100644
--- a/src/app/components/Clients/Sort.vue
+++ b/src/app/components/Clients/Sort.vue
@@ -1,52 +1,15 @@
-
+ />
+
+ {{ $t('client.sort') }}
+
-
+
+
diff --git a/src/app/components/form/NullTextField.vue b/src/app/components/form/NullTextField.vue
index 74d2c6ec..6493032a 100644
--- a/src/app/components/form/NullTextField.vue
+++ b/src/app/components/form/NullTextField.vue
@@ -7,11 +7,24 @@
-
+
diff --git a/src/app/components/header/ChartToggle.vue b/src/app/components/header/ChartToggle.vue
index 24c5378b..ad69d8d0 100644
--- a/src/app/components/header/ChartToggle.vue
+++ b/src/app/components/header/ChartToggle.vue
@@ -2,7 +2,7 @@
-
{{ $t('updateAvailable') }}
+
{{ $t('update.updateAvailable') }}
{{ globalStore.latestRelease.changelog }}
@@ -15,7 +15,7 @@
target="_blank"
class="font-sm float-right flex-shrink-0 rounded-md border-2 border-red-800 bg-white p-3 font-semibold text-red-800 transition-all hover:border-white hover:bg-red-800 hover:text-white dark:border-red-600 dark:bg-red-100 dark:text-red-600 dark:hover:border-red-600 dark:hover:bg-red-600 dark:hover:text-red-100"
>
- {{ $t('update') }} →
+ {{ $t('update.update') }} →
diff --git a/src/app/components/panel/head/Title.vue b/src/app/components/panel/head/Title.vue
index d9bdf504..93a2db31 100644
--- a/src/app/components/panel/head/Title.vue
+++ b/src/app/components/panel/head/Title.vue
@@ -1,11 +1,11 @@
-
-
{{ text }}
+
+
diff --git a/src/app/components/ui/ChooseLang.vue b/src/app/components/ui/ChooseLang.vue
deleted file mode 100644
index a5df8e67..00000000
--- a/src/app/components/ui/ChooseLang.vue
+++ /dev/null
@@ -1,45 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
- {{ option.name }}
-
-
-
-
-
-
-
-
-
diff --git a/src/app/components/ui/Footer.vue b/src/app/components/ui/Footer.vue
index cfc5c356..3395848b 100644
--- a/src/app/components/ui/Footer.vue
+++ b/src/app/components/ui/Footer.vue
@@ -26,7 +26,7 @@
class="hover:underline"
href="https://github.com/sponsors/WeeJeWel"
target="_blank"
- >{{ $t('donate') }}{{ $t('layout.donate') }}
diff --git a/src/app/components/ui/UserMenu.vue b/src/app/components/ui/UserMenu.vue
index 8d48b3fe..ca4d19ec 100644
--- a/src/app/components/ui/UserMenu.vue
+++ b/src/app/components/ui/UserMenu.vue
@@ -52,10 +52,10 @@
@@ -67,16 +67,21 @@
const authStore = useAuthStore();
const toggleState = ref(false);
-async function logout() {
- try {
- await authStore.logout();
- navigateTo('/login');
- } catch (err) {
- if (err instanceof Error) {
- // TODO: better ui
- alert(err.message || err.toString());
- }
+const _submit = useSubmit(
+ '/api/session',
+ {
+ method: 'delete',
+ },
+ {
+ revert: async () => {
+ await navigateTo('/login');
+ },
+ noSuccessToast: true,
}
+);
+
+function submit() {
+ return _submit(undefined);
}
const fallbackName = computed(() => {
diff --git a/src/app/composables/useSubmit.ts b/src/app/composables/useSubmit.ts
index a5fd76c9..27d734c1 100644
--- a/src/app/composables/useSubmit.ts
+++ b/src/app/composables/useSubmit.ts
@@ -1,12 +1,19 @@
import type { NitroFetchRequest, NitroFetchOptions } from 'nitropack/types';
import { FetchError } from 'ofetch';
-type RevertFn = () => Promise;
+type RevertFn = (success: boolean) => Promise;
+
+type SubmitOpts = {
+ revert: RevertFn;
+ successMsg?: string;
+ errorMsg?: string;
+ noSuccessToast?: boolean;
+};
export function useSubmit<
R extends NitroFetchRequest,
O extends NitroFetchOptions & { body?: never },
->(url: R, options: O, revert: RevertFn, success?: string, error?: string) {
+>(url: R, options: O, opts: SubmitOpts) {
const toast = useToast();
const { t: $t } = useI18n();
@@ -16,15 +23,20 @@ export function useSubmit<
...options,
body: data,
});
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
if (!(res as any).success) {
- throw new Error(error || $t('toast.errored'));
+ throw new Error(opts.errorMsg || $t('toast.errored'));
}
- toast.showToast({
- type: 'success',
- message: success,
- });
- await revert();
+
+ if (!opts.noSuccessToast) {
+ toast.showToast({
+ type: 'success',
+ message: opts.successMsg,
+ });
+ }
+
+ await opts.revert(true);
} catch (e) {
if (e instanceof FetchError) {
toast.showToast({
@@ -39,7 +51,7 @@ export function useSubmit<
} else {
console.error(e);
}
- await revert();
+ await opts.revert(false);
}
};
}
diff --git a/src/app/pages/admin.vue b/src/app/pages/admin.vue
index 3e937134..71c0f6d6 100644
--- a/src/app/pages/admin.vue
+++ b/src/app/pages/admin.vue
@@ -37,6 +37,7 @@
diff --git a/src/app/pages/me.vue b/src/app/pages/me.vue
index e7009917..333422c3 100644
--- a/src/app/pages/me.vue
+++ b/src/app/pages/me.vue
@@ -65,8 +65,10 @@ const _submit = useSubmit(
{
method: 'post',
},
- async () => {
- authStore.update();
+ {
+ revert: () => {
+ return authStore.update();
+ },
}
);
@@ -83,10 +85,12 @@ const _updatePassword = useSubmit(
{
method: 'post',
},
- async () => {
- currentPassword.value = '';
- newPassword.value = '';
- confirmPassword.value = '';
+ {
+ revert: async () => {
+ currentPassword.value = '';
+ newPassword.value = '';
+ confirmPassword.value = '';
+ },
}
);
diff --git a/src/app/pages/setup/1.vue b/src/app/pages/setup/1.vue
index ac67462e..a059e0a5 100644
--- a/src/app/pages/setup/1.vue
+++ b/src/app/pages/setup/1.vue
@@ -1,9 +1,11 @@
- {{ $t('setup.messageWelcome.whatIs') }}
+ {{ $t('setup.welcomeDesc') }}
-
Continue
+
+ {{ $t('general.continue') }}
+
@@ -11,6 +13,7 @@
definePageMeta({
layout: 'setup',
});
+
const setupStore = useSetupStore();
setupStore.setStep(1);
diff --git a/src/app/pages/setup/2.vue b/src/app/pages/setup/2.vue
index 71e36e14..72cbffc1 100644
--- a/src/app/pages/setup/2.vue
+++ b/src/app/pages/setup/2.vue
@@ -1,83 +1,59 @@
- {{ $t('setup.messageSetupCreateAdminUser') }}
+ {{ $t('setup.createAdminDesc') }}
-
-
-
-
+
+
+
+
+
+
+
+
+ {{ $t('setup.createAccount') }}
+
-
-
-
-
-
-
-
-
-
Create Account
diff --git a/src/app/pages/setup/3.vue b/src/app/pages/setup/3.vue
index 620844be..54e3cae0 100644
--- a/src/app/pages/setup/3.vue
+++ b/src/app/pages/setup/3.vue
@@ -1,11 +1,15 @@
- {{ 'Do you have a existing Setup?' }}
+ {{ $t('setup.existingSetup') }}
- No
- Yes
+
+ {{ $t('general.no') }}
+
+
+ {{ $t('general.yes') }}
+
@@ -14,6 +18,7 @@
definePageMeta({
layout: 'setup',
});
+
const setupStore = useSetupStore();
setupStore.setStep(3);
diff --git a/src/app/pages/setup/4.vue b/src/app/pages/setup/4.vue
index 9cd88bd2..5c1b95a5 100644
--- a/src/app/pages/setup/4.vue
+++ b/src/app/pages/setup/4.vue
@@ -1,71 +1,59 @@
- {{ $t('setup.messageSetupHostPort') }}
+ {{ $t('setup.setupConfigDesc') }}
-
-
-
+
+
+
+
+
+
+
+
+ {{ $t('general.continue') }}
+
-
-
-
-
-
Continue
diff --git a/src/app/pages/setup/migrate.vue b/src/app/pages/setup/migrate.vue
index 77622c81..7eb5ddda 100644
--- a/src/app/pages/setup/migrate.vue
+++ b/src/app/pages/setup/migrate.vue
@@ -1,68 +1,56 @@
- {{ $t('setup.messageSetupMigration') }}
+ {{ $t('setup.setupMigrationDesc') }}
-
Upload
+
{{ $t('setup.upload') }}
diff --git a/src/app/stores/auth.ts b/src/app/stores/auth.ts
index b574945d..fab80ea0 100644
--- a/src/app/stores/auth.ts
+++ b/src/app/stores/auth.ts
@@ -14,26 +14,5 @@ export const useAuthStore = defineStore('Auth', () => {
}
}
- /**
- * @throws if unsuccessful
- */
- async function login(username: string, password: string, remember: boolean) {
- await $fetch('/api/session', {
- method: 'post',
- body: { username, password, remember },
- });
- return true as const;
- }
-
- /**
- * @throws if unsuccessful
- */
- async function logout() {
- const response = await $fetch('/api/session', {
- method: 'delete',
- });
- return response.success;
- }
-
- return { userData, login, logout, update, getSession };
+ return { userData, update, getSession };
});
diff --git a/src/app/stores/global.ts b/src/app/stores/global.ts
index 7f515872..923202a7 100644
--- a/src/app/stores/global.ts
+++ b/src/app/stores/global.ts
@@ -1,6 +1,8 @@
-import { defineStore } from 'pinia';
-
export const useGlobalStore = defineStore('Global', () => {
+ const { data: release } = useFetch('/api/release', {
+ method: 'get',
+ });
+
const sortClient = ref(true); // Sort clients by name, true = asc, false = desc
const currentRelease = ref
(null);
@@ -10,10 +12,6 @@ export const useGlobalStore = defineStore('Global', () => {
const updateAvailable = ref(false);
async function fetchRelease() {
- const { data: release } = await useFetch('/api/release', {
- method: 'get',
- });
-
if (!release.value) {
return;
}
diff --git a/src/app/stores/setup.ts b/src/app/stores/setup.ts
index 478c9e0a..e7bcc64e 100644
--- a/src/app/stores/setup.ts
+++ b/src/app/stores/setup.ts
@@ -1,39 +1,6 @@
import { defineStore } from 'pinia';
export const useSetupStore = defineStore('Setup', () => {
- /**
- * @throws if unsuccessful
- */
- async function step2(username: string, password: string, accept: boolean) {
- const response = await $fetch('/api/setup/2', {
- method: 'post',
- body: { username, password, accept },
- });
- return response.success;
- }
-
- /**
- * @throws if unsuccessful
- */
- async function step4(host: string, port: number) {
- const response = await $fetch('/api/setup/4', {
- method: 'post',
- body: { host, port },
- });
- return response.success;
- }
-
- /**
- * @throws if unsuccessful
- */
- async function runMigration(file: string) {
- const response = await $fetch('/api/setup/migrate', {
- method: 'post',
- body: { file },
- });
- return response.success;
- }
-
const step = ref(1);
const totalSteps = ref(5);
function setStep(i: number) {
@@ -41,9 +8,6 @@ export const useSetupStore = defineStore('Setup', () => {
}
return {
- step2,
- step4,
- runMigration,
step,
totalSteps,
setStep,
diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json
index 07e72bb7..1743c153 100644
--- a/src/i18n/locales/en.json
+++ b/src/i18n/locales/en.json
@@ -25,60 +25,39 @@
"updatePassword": "Update Password",
"mtu": "MTU",
"allowedIps": "Allowed IPs",
- "persistentKeepalive": "Persistent Keepalive"
+ "persistentKeepalive": "Persistent Keepalive",
+ "logout": "Logout",
+ "continue": "Continue",
+ "host": "Host",
+ "port": "Port",
+ "yes": "Yes",
+ "no": "No"
},
"setup": {
"welcome": "Welcome to your first setup of wg-easy !",
- "messageWelcome": {
- "whatIs": "You have found the easiest way to install and manage WireGuard on any Linux host!",
- "warning": "First of all, make sure you have a backup of your data if you want to migrate your users to your new wg-easy.",
- "next": "Click on the arrow button to proceed to the next step."
- },
- "messageSetupLanguage": "Please choose a language for the setup.",
- "messageSetupCreateAdminUser": "Please first enter an admin username and a strong secure password. This information will be used to log in to your administration panel.",
- "messageSetupHostPort": "Please enter the host and port information. This will be used for the client configuration when setting up WireGuard on their devices.",
- "messageSetupMigration": "Please provide the backup file if you want to migrate your data from your previous wg-easy version to your new setup.",
- "messageSetupValidation": "Welcome to wg-easy ! The easiest way to run WireGuard VPN and Web-based Admin UI.",
- "emptyFields": "The fields are required",
- "chooseLang": "Select a language...",
- "accept": "I accept the condition",
- "submitBtn": "Create admin account",
- "usernamePlaceholder": "Administrator",
- "passwordPlaceholder": "Strong password",
- "requirements": "Setup requirements",
- "host": "Host",
- "hostPlaceholder": "wg-easy.example.com",
- "port": "Port",
- "portPlaceholder": "443",
- "migration": "Restore the backup"
+ "welcomeDesc": "You have found the easiest way to install and manage WireGuard on any Linux host!",
+ "existingSetup": "Do you have an existing setup?",
+ "createAdminDesc": "Please first enter an admin username and a strong secure password. This information will be used to log in to your administration panel.",
+ "setupConfigDesc": "Please enter the host and port information. This will be used for the client configuration when setting up WireGuard on their devices.",
+ "setupMigrationDesc": "Please provide the backup file if you want to migrate your data from your previous wg-easy version to your new setup.",
+ "upload": "Upload",
+ "migration": "Restore the backup",
+ "createAccount": "Create Account",
+ "successful": "Setup successful"
+ },
+ "update": {
+ "updateAvailable": "There is an update available!",
+ "update": "Update"
},
- "logout": "Logout",
- "updateAvailable": "There is an update available!",
- "update": "Update",
- "new": "New",
- "createdOn": "Created on ",
- "lastSeen": "Last seen on ",
- "totalDownload": "Total Download: ",
- "totalUpload": "Total Upload: ",
- "newClient": "New Client",
- "disableClient": "Disable Client",
- "enableClient": "Enable Client",
- "noClients": "There are no clients yet.",
- "noPrivKey": "This client has no known private key. Cannot create Configuration.",
- "showQR": "Show QR Code",
- "downloadConfig": "Download Configuration",
- "madeBy": "Made by",
- "donate": "Donate",
- "toggleCharts": "Show/hide Charts",
"theme": {
"dark": "Dark theme",
"light": "Light theme",
"system": "System theme"
},
- "sort": "Sort",
- "Permanent": "Permanent",
- "OneTimeLink": "Generate short one time link",
- "errorInit": "Initialization failed.",
+ "layout": {
+ "toggleCharts": "Show/hide Charts",
+ "donate": "Donate"
+ },
"login": {
"signIn": "Sign In",
"rememberMe": "Remember me",
@@ -89,7 +68,11 @@
"login": "Log in error"
},
"client": {
+ "empty": "There are no clients yet.",
+ "newShort": "New",
+ "sort": "Sort",
"create": "Create Client",
+ "created": "Client created",
"new": "New Client",
"name": "Name",
"expireDate": "Expire Date",
@@ -98,7 +81,19 @@
"deleteDialog2": "This action cannot be undone.",
"enabled": "Enabled",
"address": "Address",
- "serverAllowedIps": "Server Allowed IPs"
+ "serverAllowedIps": "Server Allowed IPs",
+ "otlDesc": "Generate short one time link",
+ "permanent": "Permanent",
+ "createdOn": "Created on ",
+ "lastSeen": "Last seen on ",
+ "totalDownload": "Total Download: ",
+ "totalUpload": "Total Upload: ",
+ "newClient": "New Client",
+ "disableClient": "Disable Client",
+ "enableClient": "Enable Client",
+ "noPrivKey": "This client has no known private key. Cannot create Configuration.",
+ "showQR": "Show QR Code",
+ "downloadConfig": "Download Configuration"
},
"dialog": {
"change": "Change",
@@ -132,7 +127,6 @@
},
"config": {
"connection": "Connection",
- "host": "Host",
"hostDesc": "Public hostname clients will connect to (invalidates config)",
"portDesc": "Public UDP port clients will connect to (invalidates config)",
"allowedIpsDesc": "Allowed IPs clients will use (invalidates config)",
@@ -149,9 +143,6 @@
"mtuDesc": "MTU WireGuard will use",
"portDesc": "UDP Port WireGuard will listen on (could invalidate config)",
"changeCidr": "Change CIDR"
- },
- "generic": {
- "port": "Port"
}
},
"zod": {
@@ -180,8 +171,6 @@
"passwordNumber": "Password must have at least 1 number",
"passwordSpecial": "Password must have at least 1 special character",
"remember": "Remember",
- "accept": "Accept",
- "acceptTrue": "Accept Conditions to continue",
"name": "Name",
"email": "Email",
"emailInvalid": "Email must be a valid email",
diff --git a/src/server/database/repositories/user/types.ts b/src/server/database/repositories/user/types.ts
index 1c071926..51eac4e5 100644
--- a/src/server/database/repositories/user/types.ts
+++ b/src/server/database/repositories/user/types.ts
@@ -29,17 +29,10 @@ export const UserLoginSchema = z.object(
{ message: objectMessage }
);
-const accept = z
- .boolean({ message: t('zod.user.accept') })
- .refine((val) => val === true, {
- message: t('zod.user.acceptTrue'),
- });
-
export const UserSetupSchema = z.object(
{
username: username,
password: password,
- accept: accept,
},
{ message: objectMessage }
);