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') }} +

+
+ + +
+
+ + +
+
@@ -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; }