From 0f38697b6b3e62bcdb161c2237d1020c38d1cea7 Mon Sep 17 00:00:00 2001
From: tetuaoro <65575727+tetuaoro@users.noreply.github.com>
Date: Sat, 28 Sep 2024 16:57:59 +0200
Subject: [PATCH] update: setup page
- add: host/port section
- i18n: french
- fix: fallback translation
---
src/app/pages/setup.vue | 95 +++++++++++++++----
src/app/stores/auth.ts | 10 +-
src/app/stores/setup.ts | 21 ++++
src/app/utils/api.ts | 15 ++-
src/i18n.config.ts | 1 +
src/locales/en.json | 18 +++-
src/locales/fr.json | 18 +++-
.../api/wireguard/clients/hostport.post.ts | 8 ++
src/server/utils/types.ts | 16 ++++
src/services/database/lowdb.ts | 8 ++
src/services/database/repositories/system.ts | 1 +
11 files changed, 171 insertions(+), 40 deletions(-)
create mode 100644 src/app/stores/setup.ts
create mode 100644 src/server/api/wireguard/clients/hostport.post.ts
diff --git a/src/app/pages/setup.vue b/src/app/pages/setup.vue
index 22ed603c..782bc0a4 100644
--- a/src/app/pages/setup.vue
+++ b/src/app/pages/setup.vue
@@ -8,28 +8,32 @@
-
{{ $t('setup.msgStepOne') }}
+
+ {{ $t('setup.messageSetupLanguage') }}
+
-
{{ $t('setup.msgStepTwo') }}
+
+ {{ $t('setup.messageSetupCreateAdminUser') }}
+
-
@@ -39,12 +43,12 @@
@@ -53,11 +57,41 @@
}}
-
+
-
Host/Port section
+
+ {{ $t('setup.messageSetupHostPort') }}
+
+
+ {{ $t('setup.host') }}
+
+
+
+ {{ $t('setup.port') }}
+
+
+
@@ -104,8 +138,12 @@ import { FetchError } from 'ofetch';
const { t, locale, setLocale } = useI18n();
const authStore = useAuthStore();
+const setupStore = useSetupStore();
const globalStore = useGlobalStore();
+const inputClass =
+ 'px-3 py-2 text-sm dark:bg-neutral-700 text-gray-500 dark:text-gray-500 mb-5 border-2 border-gray-100 dark:border-neutral-800 rounded-lg w-full focus:border-red-800 dark:focus:border-red-800 dark:placeholder:text-neutral-400 focus:outline-0 focus:ring-0';
+
type SetupError = {
title: string;
message: string;
@@ -113,10 +151,15 @@ type SetupError = {
const lang = ref(locale.value);
+/* STEP CREATE ADMIN USER MODELS */
const username = ref(null);
const password = ref(null);
const accept = ref(true);
+/* STEP SET HOST & PORT MODELS */
+const host = ref(null);
+const port = ref(null);
+
const step = ref(1);
const stepInvalide = ref([]);
const setupError = ref(null);
@@ -132,7 +175,6 @@ watch(setupError, (value) => {
function handleEventUpdateLang(value: string) {
lang.value = value;
- // TODO: if the translation does not exist, it shows the key instead of default (en)
setLocale(lang.value);
}
@@ -149,15 +191,19 @@ async function increaseStep() {
}
if (step.value === 3) {
- /* host/port */
+ await updateHostPort();
+ stepInvalide.value.push(3);
}
if (step.value === 4) {
/* migration */
+ stepInvalide.value.push(4);
}
if (step.value === 5) {
/* validation/welcome */
+ navigateTo('/login');
+ stepInvalide.value.push(5);
}
if (step.value < 5) step.value += 1;
@@ -198,14 +244,25 @@ async function newAccount() {
if (!username.value || !password.value) return;
try {
- const res = await authStore.signup(
- username.value,
- password.value,
- accept.value
- );
- if (res) {
- navigateTo('/login');
+ await setupStore.signup(username.value, password.value, accept.value);
+ // the next step need authentication
+ await authStore.login(username.value, password.value, false);
+ } catch (error) {
+ if (error instanceof FetchError) {
+ setupError.value = {
+ title: t('setup.requirements'),
+ message: error.data.message,
+ };
}
+ // increaseStep fn
+ throw error;
+ }
+}
+
+async function updateHostPort() {
+ if (!host.value || !port.value) return;
+ try {
+ await setupStore.updateHostPort(host.value, port.value);
} catch (error) {
if (error instanceof FetchError) {
setupError.value = {
diff --git a/src/app/stores/auth.ts b/src/app/stores/auth.ts
index fbb2e9e4..fd5fbd9d 100644
--- a/src/app/stores/auth.ts
+++ b/src/app/stores/auth.ts
@@ -6,14 +6,6 @@ export const useAuthStore = defineStore('Auth', () => {
email: string | null;
}>();
- /**
- * @throws if unsuccessful
- */
- async function signup(username: string, password: string, accept: boolean) {
- const response = await api.setupAccount({ username, password, accept });
- return response.success;
- }
-
/**
* @throws if unsuccessful
*/
@@ -36,5 +28,5 @@ export const useAuthStore = defineStore('Auth', () => {
userData.value = response.value;
}
- return { userData, login, logout, update, signup };
+ return { userData, login, logout, update };
});
diff --git a/src/app/stores/setup.ts b/src/app/stores/setup.ts
new file mode 100644
index 00000000..e5c70770
--- /dev/null
+++ b/src/app/stores/setup.ts
@@ -0,0 +1,21 @@
+import { defineStore } from 'pinia';
+
+export const useSetupStore = defineStore('Setup', () => {
+ /**
+ * @throws if unsuccessful
+ */
+ async function signup(username: string, password: string, accept: boolean) {
+ const response = await api.setupAdminUser({ username, password, accept });
+ return response.success;
+ }
+
+ /**
+ * @throws if unsuccessful
+ */
+ async function updateHostPort(host: string, port: number) {
+ const response = await api.setupHostPort({ host, port });
+ return response.success;
+ }
+
+ return { signup, updateHostPort };
+});
diff --git a/src/app/utils/api.ts b/src/app/utils/api.ts
index 304d3542..5f67d14b 100644
--- a/src/app/utils/api.ts
+++ b/src/app/utils/api.ts
@@ -115,7 +115,14 @@ class API {
});
}
- async setupAccount({
+ async updateLang({ lang }: { lang: string }) {
+ return $fetch('/api/lang', {
+ method: 'post',
+ body: { lang },
+ });
+ }
+
+ async setupAdminUser({
username,
password,
accept,
@@ -130,10 +137,10 @@ class API {
});
}
- async updateLang({ lang }: { lang: string }) {
- return $fetch('/api/lang', {
+ async setupHostPort({ host, port }: { host: string; port: number }) {
+ return $fetch('/api/wireguard/clients/hostport', {
method: 'post',
- body: { lang },
+ body: { host, port },
});
}
}
diff --git a/src/i18n.config.ts b/src/i18n.config.ts
index e3b02f43..cb9a8c80 100644
--- a/src/i18n.config.ts
+++ b/src/i18n.config.ts
@@ -45,6 +45,7 @@ const LOCALES = [
export { LOCALES };
export default defineI18nConfig(() => ({
+ fallbackLocale: 'en',
legacy: false,
locale: 'en',
messages: {
diff --git a/src/locales/en.json b/src/locales/en.json
index 9c291ed3..45e5e316 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -14,15 +14,20 @@
"confirmPassword": "Confirm Password",
"setup": {
"welcome": "Welcome to your first setup of wg-easy !",
- "msgStepOne": "Please choose a default language.",
- "msgStepTwo": "Please first enter an admin username and a strong secure password. This information will be used to log in to your administration panel.",
+ "messageSetupLanguage": "Please choose a default language.",
+ "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.",
"chooseLang": "Select a language...",
"newPassword": "New Password",
"accept": "I accept the condition",
"submitBtn": "Create admin account",
"usernamePlaceholder": "Administrator",
"passwordPlaceholder": "Strong password",
- "requirements": "Setup requirements"
+ "requirements": "Setup requirements",
+ "host": "Host",
+ "hostPlaceholder": "wg-easy.example.com",
+ "port": "Port",
+ "portPlaceholder": "443"
},
"zod": {
"id": "Client ID must be a valid UUID",
@@ -51,7 +56,12 @@
"stat": "statistics must be a valid object",
"statBool": "enabled must be a valid boolean",
"statNumber": "chartType must be a valid number",
- "body": "Body must be a valid object"
+ "body": "Body must be a valid object",
+ "host": "Host must be a valid string",
+ "hostMin": "Host must contain at least 1 character",
+ "port": "Port must be a valid number",
+ "portMin": "Port must be at least 1",
+ "portMax": "Port must be at most 65535"
},
"name": "Name",
"username": "Username",
diff --git a/src/locales/fr.json b/src/locales/fr.json
index 82aaf2d6..6f8a724b 100644
--- a/src/locales/fr.json
+++ b/src/locales/fr.json
@@ -14,15 +14,20 @@
"confirmPassword": "Confirmer le mot de passe",
"setup": {
"welcome": "Bienvenue dans votre première configuration de wg-easy !",
- "msgStepOne": "Sélectionner votre langue.",
- "msgStepTwo": "Veuillez renseigner votre nom d'utilisateur et votre mot de passe. Ces informations seront utilisées pour vous connecter à votre page d'administration.",
+ "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.",
"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"
+ "requirements": "Conditions de configuration",
+ "host": "Hôte",
+ "hostPlaceholder": "wg-easy.example.com",
+ "port": "Port",
+ "portPlaceholder": "443"
},
"zod": {
"id": "L'ID du client doit être un UUID valide",
@@ -51,7 +56,12 @@
"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"
+ "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",
diff --git a/src/server/api/wireguard/clients/hostport.post.ts b/src/server/api/wireguard/clients/hostport.post.ts
new file mode 100644
index 00000000..ab347ad6
--- /dev/null
+++ b/src/server/api/wireguard/clients/hostport.post.ts
@@ -0,0 +1,8 @@
+export default defineEventHandler(async (event) => {
+ const { host, port } = await readValidatedBody(
+ event,
+ validateZod(hostPortType, event)
+ );
+ await Database.system.updateClientsHostPort(host, port);
+ return { success: true };
+});
diff --git a/src/server/utils/types.ts b/src/server/utils/types.ts
index 3b13d37f..c07dbc61 100644
--- a/src/server/utils/types.ts
+++ b/src/server/utils/types.ts
@@ -73,14 +73,30 @@ const statistics = z.object(
{ message: 'zod.stat' } // i18n key
);
+const host = z
+ .string({ message: 'zod.host' })
+ .min(1, 'zod.hostMin')
+ .pipe(safeStringRefine);
+
+const port = z
+ .number({ message: 'zod.port' })
+ .min(1, 'zod.portMin')
+ .max(65535, 'zod.portMax');
+
const objectMessage = 'zod.body'; // i18n key
const langs = LOCALES.map((lang) => lang.value);
const lang = z.enum(['', ...langs]);
+
export const langType = z.object({
lang: lang,
});
+export const hostPortType = z.object({
+ host: host,
+ port: port,
+});
+
export const clientIdType = z.object(
{
clientId: id,
diff --git a/src/services/database/lowdb.ts b/src/services/database/lowdb.ts
index 1c906b82..5acc55fd 100644
--- a/src/services/database/lowdb.ts
+++ b/src/services/database/lowdb.ts
@@ -77,6 +77,14 @@ export class LowDBSystem extends SystemRepository {
v.system.general.lang = lang;
});
}
+
+ async updateClientsHostPort(host: string, port: number): Promise {
+ DEBUG('Update Clients Host and Port endpoint');
+ this.#db.update((v) => {
+ v.system.userConfig.host = host;
+ v.system.userConfig.port = port;
+ });
+ }
}
export class LowDBUser extends UserRepository {
diff --git a/src/services/database/repositories/system.ts b/src/services/database/repositories/system.ts
index c008be0c..38623b85 100644
--- a/src/services/database/repositories/system.ts
+++ b/src/services/database/repositories/system.ts
@@ -108,4 +108,5 @@ export abstract class SystemRepository {
abstract updateFeatures(features: Record): Promise;
abstract updateStatistics(statistics: Statistics): Promise;
abstract updateLang(lang: Lang): Promise;
+ abstract updateClientsHostPort(host: string, port: number): Promise;
}