fix(auth): secure sessions and app routing

Validate sessions against the API, honor configurable base paths, and route unknown pages to 404. Keep development ports stable so user and admin apps cannot be confused.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-09-24 20:14:11 +03:30
co-authored by Copilot
parent c0c2221d91
commit adb6ca47cd
56 changed files with 160 additions and 406 deletions
+10 -5
View File
@@ -1,5 +1,6 @@
import { createRouter, createWebHistory } from 'vue-router'
import { getCachedUser, isAuthenticated } from '@/services/authService'
import { useAuthStore } from '@/stores/auth'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
@@ -250,20 +251,24 @@ const router = createRouter({
component: () => import('@/views/profile/ProfileChecksView.vue'),
meta: { requiresAuth: true },
},
{
path: '/:pathMatch(.*)*',
name: 'not-found',
component: () => import('@/views/NotFoundView.vue'),
},
],
scrollBehavior() {
return { top: 0 }
},
})
router.beforeEach((to) => {
router.beforeEach(async (to) => {
const auth = useAuthStore()
await auth.hydrate()
const authed = isAuthenticated()
if (to.meta.requiresAuth && !authed) {
if (to.name === 'home' || to.path === '/') {
return { name: 'welcome' }
}
return { name: 'welcome', query: { redirect: to.fullPath } }
return { name: 'login', query: { redirect: to.fullPath } }
}
if (to.meta.guest && authed) {
+2 -1
View File
@@ -131,7 +131,8 @@ export async function fetchMe(): Promise<User | null> {
cacheUser(me)
return me
} catch {
return getCachedUser()
cacheUser(null)
return null
}
}
+3 -2
View File
@@ -123,11 +123,12 @@ export async function apiRequest<T>(path: string, options: RequestOptions = {}):
} catch {
// ignore
}
if (typeof window !== 'undefined' && !window.location.pathname.startsWith('/login')) {
const loginPath = `${import.meta.env.BASE_URL}login`
if (typeof window !== 'undefined' && !window.location.pathname.startsWith(loginPath)) {
const redirect = encodeURIComponent(
`${window.location.pathname}${window.location.search}`,
)
window.location.assign(`/login?redirect=${redirect}`)
window.location.assign(`${loginPath}?redirect=${redirect}`)
}
}
throw new ApiError(res.status, errorMessageFromBody(data, res.status))
+35
View File
@@ -0,0 +1,35 @@
<template>
<main class="not-found">
<p class="not-found__code">404</p>
<h1>صفحه پیدا نشد</h1>
<p>آدرسی که وارد کرده‌اید وجود ندارد.</p>
<RouterLink to="/">بازگشت به صفحه اصلی</RouterLink>
</main>
</template>
<style scoped>
.not-found {
display: grid;
min-height: 60vh;
place-content: center;
gap: 12px;
text-align: center;
}
.not-found__code {
margin: 0;
color: var(--color-primary, #c89b3c);
font-size: 72px;
font-weight: 800;
}
h1,
p {
margin: 0;
}
a {
color: var(--color-primary, #c89b3c);
font-weight: 700;
}
</style>