initial commit. Cloned timetracker repository
This commit is contained in:
86
bo/src/router/index.ts
Normal file
86
bo/src/router/index.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
|
||||
import Default from '@/layouts/default.vue'
|
||||
import Empty from '@/layouts/empty.vue'
|
||||
import { currentLang, initLangs, langs } from './langs'
|
||||
import { getSettings } from './settings'
|
||||
|
||||
// Helper: read the non-HTTPOnly is_authenticated cookie set by the backend.
|
||||
// The backend sets it to "1" on login and removes it on logout.
|
||||
function isAuthenticated(): boolean {
|
||||
if (typeof document === 'undefined') return false
|
||||
return document.cookie.split('; ').some((c) => c === 'is_authenticated=1')
|
||||
}
|
||||
|
||||
|
||||
await initLangs()
|
||||
await getSettings()
|
||||
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.VITE_BASE_URL),
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
redirect: () => `/${currentLang.value?.iso_code}`,
|
||||
},
|
||||
{
|
||||
path: '/:locale',
|
||||
children: [
|
||||
// {
|
||||
// path: '',
|
||||
// component: Default,
|
||||
// children: [
|
||||
// ],
|
||||
// },
|
||||
{
|
||||
path: '',
|
||||
component: Empty,
|
||||
children: [
|
||||
{ path: '', component: () => import('../views/HomeView.vue'), name: 'home' },
|
||||
{ path: 'chart', component: () => import('../views/RepoChartView.vue'), name: 'chart' },
|
||||
{ path: 'login', component: () => import('@/views/LoginView.vue'), name: 'login', meta: { guest: true } },
|
||||
{ path: 'register', component: () => import('@/views/RegisterView.vue'), name: 'register', meta: { guest: true } },
|
||||
{ path: 'password-recovery', component: () => import('@/views/PasswordRecoveryView.vue'), name: 'password-recovery', meta: { guest: true } },
|
||||
{ path: 'reset-password', component: () => import('@/views/ResetPasswordForm.vue'), name: 'reset-password', meta: { guest: true } },
|
||||
{ path: 'verify-email', component: () => import('@/views/VerifyEmailView.vue'), name: 'verify-email', meta: { guest: true } },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
// Navigation guard: language handling + auth protection
|
||||
router.beforeEach((to, from, next) => {
|
||||
const locale = to.params.locale as string
|
||||
const localeLang = langs.find((x) => x.iso_code == locale)
|
||||
|
||||
// Check if the locale is valid
|
||||
if (locale && langs.length > 0) {
|
||||
const validLocale = langs.find((l) => l.lang_code === locale)
|
||||
|
||||
if (validLocale) {
|
||||
currentLang.value = localeLang
|
||||
|
||||
// Auth guard: if the route does NOT have meta.guest = true, require authentication
|
||||
if (!to.meta?.guest && !isAuthenticated()) {
|
||||
return next({ name: 'login', params: { locale } })
|
||||
}
|
||||
|
||||
return next()
|
||||
} else if (locale) {
|
||||
// Invalid locale - redirect to default language
|
||||
return next(`/${currentLang.value?.iso_code}${to.path.replace(`/${locale}`, '') || '/'}`)
|
||||
}
|
||||
}
|
||||
|
||||
// No locale in URL - redirect to default language
|
||||
if (!locale && to.path !== '/') {
|
||||
return next(`/${currentLang.value?.iso_code}${to.path}`)
|
||||
}
|
||||
|
||||
next()
|
||||
})
|
||||
|
||||
export default router
|
||||
30
bo/src/router/langs.ts
Normal file
30
bo/src/router/langs.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { useCookie } from "@/composable/useCookie"
|
||||
import { useFetchJson } from "@/composable/useFetchJson"
|
||||
import type { Language } from "@/types"
|
||||
import { reactive, ref } from "vue"
|
||||
|
||||
export const langs = reactive([] as Language[])
|
||||
export const currentLang = ref<Language>()
|
||||
|
||||
const deflang = ref<Language>()
|
||||
const cookie = useCookie()
|
||||
// Get available language codes for route matching
|
||||
// export const availableLocales = computed(() => langs.map((l) => l.lang_code))
|
||||
|
||||
// Initialize languages from API
|
||||
export async function initLangs() {
|
||||
try {
|
||||
const { items } = await useFetchJson<Language[]>('/api/v1/langs')
|
||||
langs.push(...items)
|
||||
|
||||
let idfromcookie = null
|
||||
const cc = cookie.getCookie('lang_id')
|
||||
if (cc) {
|
||||
idfromcookie = langs.find((x) => x.id == parseInt(cc))
|
||||
}
|
||||
deflang.value = items.find((x) => x.is_default == true)
|
||||
currentLang.value = idfromcookie ?? deflang.value
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch languages:', error)
|
||||
}
|
||||
}
|
||||
11
bo/src/router/settings.ts
Normal file
11
bo/src/router/settings.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { useFetchJson } from "@/composable/useFetchJson";
|
||||
import type { Resp } from "@/types";
|
||||
import type { Settings } from "@/types/settings";
|
||||
import { reactive } from "vue";
|
||||
|
||||
export const settings = reactive({} as Settings)
|
||||
|
||||
export async function getSettings() {
|
||||
const { items } = await useFetchJson<Resp<Settings>>('/api/v1/settings',)
|
||||
Object.assign(settings, items)
|
||||
}
|
||||
Reference in New Issue
Block a user