Browse Source

remove lang from backend, let users decide

pull/1572/head
Bernd Storath 3 months ago
parent
commit
851062aa1f
  1. 3
      src/app/app.vue
  2. 8
      src/app/components/ui/ChooseLang.vue
  3. 22
      src/app/pages/setup/1.vue
  4. 28
      src/app/stores/global.ts
  5. 12
      src/app/stores/setup.ts
  6. 1
      src/app/utils/localStorage.ts
  7. 3
      src/i18n/i18n.config.ts
  8. 5
      src/i18n/localeDetector.ts
  9. 2
      src/i18n/locales/en.json
  10. 18
      src/nuxt.config.ts
  11. 2
      src/package.json
  12. 4
      src/server/api/admin/general.get.ts
  13. 5
      src/server/api/admin/lang.post.ts
  14. 5
      src/server/api/lang.get.ts
  15. 14
      src/server/api/setup/1.post.ts
  16. 1
      src/server/middleware/session.ts
  17. 8
      src/server/utils/types.ts
  18. 9
      src/services/database/lowdb.ts
  19. 1
      src/services/database/migrations/1.ts
  20. 5
      src/services/database/repositories/system.ts
  21. 1
      src/shared/locales.ts

3
src/app/app.vue

@ -12,9 +12,6 @@
</template>
<script setup lang="ts">
const globalStore = useGlobalStore();
globalStore.setLanguage();
const toast = useToast();
const toastRef = useTemplateRef('toast');
toast.setToast(toastRef);

8
src/app/components/ui/ChooseLang.vue

@ -2,7 +2,7 @@
<SelectRoot v-model="langProxy" :default-value="locale">
<SelectTrigger
class="inline-flex h-[35px] min-w-[160px] items-center justify-between gap-[5px] rounded px-[15px] text-[13px] leading-none dark:bg-neutral-500 dark:text-white"
aria-label="Customise language"
aria-label="Customize language"
>
<SelectValue :placeholder="$t('setup.chooseLang')" />
<IconsArrowDown class="size-4" />
@ -31,9 +31,9 @@
</template>
<script setup lang="ts">
import { LOCALES } from '#shared/locales';
// TODO: improve
const { locale } = useI18n();
const { locale, locales } = useI18n();
const emit = defineEmits(['update:lang']);
const langProxy = ref(locale);
@ -42,5 +42,5 @@ watch(langProxy, (newVal) => {
emit('update:lang', newVal);
});
const langs = LOCALES.sort((a, b) => a.code.localeCompare(b.code));
const langs = locales.value.sort((a, b) => a.code.localeCompare(b.code));
</script>

22
src/app/pages/setup/1.vue

@ -6,18 +6,16 @@
<div class="mb-8 flex justify-center">
<UiChooseLang @update:lang="handleEventUpdateLang" />
</div>
<div><BaseButton @click="updateLang">Continue</BaseButton></div>
<div><BaseButton @click="nextStep">Continue</BaseButton></div>
</div>
</template>
<script setup lang="ts">
import { FetchError } from 'ofetch';
definePageMeta({
layout: 'setup',
});
const { t, locale, setLocale } = useI18n();
const { setLocale } = useI18n();
function handleEventUpdateLang(value: string) {
setLocale(value);
@ -26,20 +24,8 @@ const setupStore = useSetupStore();
setupStore.setStep(1);
const router = useRouter();
const toast = useToast();
async function updateLang() {
try {
await setupStore.step1(locale.value);
router.push('/setup/2');
} catch (error) {
if (error instanceof FetchError) {
toast.showToast({
type: 'error',
title: t('setup.requirements'),
message: error.data.message,
});
}
}
async function nextStep() {
router.push('/setup/2');
}
</script>

28
src/app/stores/global.ts

@ -3,21 +3,6 @@ import { defineStore } from 'pinia';
export const useGlobalStore = defineStore('Global', () => {
const sortClient = ref(true); // Sort clients by name, true = asc, false = desc
const { availableLocales, locale } = useI18n();
async function setLanguage() {
const { data: lang } = await useFetch('/api/lang', {
method: 'get',
});
if (
lang.value !== getItem('lang') &&
availableLocales.includes(lang.value!)
) {
setItem('lang', lang.value!);
locale.value = lang.value!;
}
}
const currentRelease = ref<null | string>(null);
const latestRelease = ref<null | { version: string; changelog: string }>(
null
@ -46,20 +31,8 @@ export const useGlobalStore = defineStore('Global', () => {
const uiChartType = ref(getItem('uiChartType') ?? 'area');
/**
* @throws if unsuccessful
*/
async function updateLang(lang: string) {
const response = await $fetch('/api/admin/lang', {
method: 'post',
body: { lang },
});
return response.success;
}
return {
sortClient,
setLanguage,
currentRelease,
latestRelease,
updateAvailable,
@ -67,6 +40,5 @@ export const useGlobalStore = defineStore('Global', () => {
uiShowCharts,
toggleCharts,
uiChartType,
updateLang,
};
});

12
src/app/stores/setup.ts

@ -1,17 +1,6 @@
import { defineStore } from 'pinia';
export const useSetupStore = defineStore('Setup', () => {
/**
* @throws if unsuccessful
*/
async function step1(lang: string) {
const response = await $fetch('/api/setup/1', {
method: 'post',
body: { lang },
});
return response.success;
}
/**
* @throws if unsuccessful
*/
@ -52,7 +41,6 @@ export const useSetupStore = defineStore('Setup', () => {
}
return {
step1,
step4,
step5,
runMigration,

1
src/app/utils/localStorage.ts

@ -1,6 +1,5 @@
export type LocalStorage = {
uiShowCharts: '1' | '0';
lang: string;
uiChartType: 'area' | 'bar' | 'line';
};

3
src/i18n/i18n.config.ts

@ -1,9 +1,8 @@
import en from './locales/en.json';
export default defineI18nConfig(() => ({
fallbackLocale: 'en',
legacy: false,
locale: 'en',
fallbackLocale: 'en',
messages: {
en,
},

5
src/i18n/localeDetector.ts

@ -4,7 +4,10 @@ export default defineI18nLocaleDetector((event, config) => {
return query.toString();
}
const cookie = tryCookieLocale(event, { lang: '', name: 'i18n_locale' });
const cookie = tryCookieLocale(event, {
lang: '',
name: 'i18n_redirected',
});
if (cookie) {
return cookie.toString();
}

2
src/i18n/locales/en.json

@ -19,7 +19,7 @@
"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 default language.",
"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.",

18
src/nuxt.config.ts

@ -23,6 +23,24 @@ export default defineNuxtConfig({
experimental: {
localeDetector: './localeDetector.ts',
},
locales: [
{
code: 'en',
language: 'en-US',
name: 'English',
},
{
code: 'de',
language: 'de-DE',
name: 'Deutsch',
},
],
defaultLocale: 'en',
vueI18n: './i18n.config.ts',
strategy: 'no_prefix',
detectBrowserLanguage: {
useCookie: true,
},
},
nitro: {
esbuild: {

2
src/package.json

@ -14,7 +14,7 @@
"format": "prettier . --write",
"format:check": "prettier . --check",
"typecheck": "nuxt typecheck",
"check:runall": "nuxt build && nuxt typecheck && eslint . && prettier . --check"
"check:all": "pnpm typecheck && pnpm lint && pnpm format:check && pnpm build"
},
"dependencies": {
"@eschricht/nuxt-color-mode": "^1.1.5",

4
src/server/api/admin/general.get.ts

@ -0,0 +1,4 @@
export default defineEventHandler(async () => {
const system = await Database.system.get();
return system.general;
});

5
src/server/api/admin/lang.post.ts

@ -1,5 +0,0 @@
export default defineEventHandler(async (event) => {
const { lang } = await readValidatedBody(event, validateZod(langType));
await Database.system.updateLang(lang);
return { success: true };
});

5
src/server/api/lang.get.ts

@ -1,5 +0,0 @@
export default defineEventHandler(async (event) => {
setHeader(event, 'Content-Type', 'application/json');
const system = await Database.system.get();
return system.general.lang;
});

14
src/server/api/setup/1.post.ts

@ -1,14 +0,0 @@
export default defineEventHandler(async (event) => {
const setupDone = await Database.setup.done();
if (setupDone) {
throw createError({
statusCode: 400,
statusMessage: 'Invalid state',
});
}
const { lang } = await readValidatedBody(event, validateZod(langType));
await Database.system.updateLang(lang);
await Database.setup.set(2);
return { success: true };
});

1
src/server/middleware/session.ts

@ -8,7 +8,6 @@ export default defineEventHandler(async (event) => {
!url.pathname.startsWith('/api/') ||
url.pathname.startsWith('/api/setup/') ||
url.pathname === '/api/session' ||
url.pathname === '/api/lang' ||
url.pathname === '/api/release'
) {
return;

8
src/server/utils/types.ts

@ -1,7 +1,6 @@
import type { ZodSchema, ZodTypeDef } from 'zod';
import { z, ZodError } from 'zod';
import type { H3Event, EventHandlerRequest } from 'h3';
import { LOCALES } from '#shared/locales';
const objectMessage = 'zod.body';
@ -12,13 +11,6 @@ const safeStringRefine = z
{ message: 'zod.stringMalformed' }
);
const langs = LOCALES.map((lang) => lang.code);
const lang = z.enum(['', ...langs]);
export const langType = z.object({
lang: lang,
});
const host = z
.string({ message: 'zod.host' })
.min(1, 'zod.hostMin')

9
src/services/database/lowdb.ts

@ -19,7 +19,7 @@ import {
type CreateClient,
type OneTimeLink,
} from './repositories/client';
import { SystemRepository, type Lang } from './repositories/system';
import { SystemRepository } from './repositories/system';
import { SetupRepository, type Steps } from './repositories/setup';
import type { DeepReadonly } from 'vue';
@ -74,13 +74,6 @@ class LowDBSystem extends SystemRepository {
return makeReadonly(system);
}
async updateLang(lang: Lang): Promise<void> {
DEBUG('Update Language');
this.#db.update((v) => {
v.system.general.lang = lang;
});
}
async updateClientsHostPort(host: string, port: number): Promise<void> {
DEBUG('Update Clients Host and Port endpoint');
this.#db.update((v) => {

1
src/services/database/migrations/1.ts

@ -18,7 +18,6 @@ export async function run1(db: Low<Database>) {
system: {
general: {
sessionTimeout: 3600, // 1 hour
lang: 'en',
},
// Config to configure Server
interface: {

5
src/services/database/repositories/system.ts

@ -1,8 +1,5 @@
import type { SessionConfig } from 'h3';
import type { DeepReadonly } from 'vue';
import type { LOCALES } from '#shared/locales';
export type Lang = (typeof LOCALES)[number]['code'];
export type IpTables = {
PreUp: string;
@ -50,7 +47,6 @@ export type Metrics = {
export type General = {
sessionTimeout: number;
lang: Lang;
};
export type System = {
@ -75,6 +71,5 @@ export type System = {
export abstract class SystemRepository {
abstract get(): Promise<DeepReadonly<System>>;
abstract updateLang(lang: Lang): Promise<void>;
abstract updateClientsHostPort(host: string, port: number): Promise<void>;
}

1
src/shared/locales.ts

@ -1 +0,0 @@
export const LOCALES = [{ code: 'en', name: 'English' }];
Loading…
Cancel
Save