ilk
This commit is contained in:
96
ui/src/router/index.js
Normal file
96
ui/src/router/index.js
Normal file
@@ -0,0 +1,96 @@
|
||||
import { route } from 'quasar/wrappers'
|
||||
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||
|
||||
import routes from 'src/router/routes.js'
|
||||
|
||||
import { useAuthStore } from 'stores/authStore'
|
||||
import { usePermissionStore } from 'stores/permissionStore'
|
||||
|
||||
|
||||
export default route(function () {
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes
|
||||
})
|
||||
|
||||
|
||||
/* ============================================================
|
||||
🔐 GLOBAL GUARD
|
||||
============================================================ */
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
|
||||
const auth = useAuthStore()
|
||||
const perm = usePermissionStore()
|
||||
|
||||
|
||||
/* ================= PUBLIC ================= */
|
||||
|
||||
if (to.meta?.public === true) {
|
||||
return next()
|
||||
}
|
||||
|
||||
|
||||
/* ================= LOGIN ================= */
|
||||
|
||||
if (!auth.isAuthenticated) {
|
||||
return next('/login')
|
||||
}
|
||||
|
||||
|
||||
/* ================= PASSWORD ================= */
|
||||
|
||||
if (
|
||||
auth.mustChangePassword &&
|
||||
to.path !== '/first-password-change'
|
||||
) {
|
||||
return next('/first-password-change')
|
||||
}
|
||||
|
||||
|
||||
/* ================= ADMIN ================= */
|
||||
|
||||
if (auth.isAdmin) {
|
||||
return next()
|
||||
}
|
||||
|
||||
|
||||
/* ================= LOAD PERMS ================= */
|
||||
|
||||
if (!perm.loaded) {
|
||||
try {
|
||||
await perm.fetchPermissions()
|
||||
} catch (e) {
|
||||
console.error('Permission load failed', e)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ================= CHECK ================= */
|
||||
|
||||
const required = to.meta?.permission
|
||||
|
||||
if (!required) {
|
||||
return next()
|
||||
}
|
||||
|
||||
|
||||
const allowed = perm.hasApiPermission(required)
|
||||
|
||||
if (!allowed) {
|
||||
|
||||
console.warn('⛔ ACCESS DENIED:', {
|
||||
path: to.fullPath,
|
||||
permission: required
|
||||
})
|
||||
|
||||
return next('/unauthorized')
|
||||
}
|
||||
|
||||
|
||||
next()
|
||||
})
|
||||
|
||||
|
||||
return router
|
||||
})
|
||||
10
ui/src/router/meta.d.js
Normal file
10
ui/src/router/meta.d.js
Normal file
@@ -0,0 +1,10 @@
|
||||
// src/router/meta.js
|
||||
|
||||
/**
|
||||
* Route meta fields reference
|
||||
*
|
||||
* @typedef {Object} RouteMeta
|
||||
* @property {boolean} [public] - Auth gerekmez
|
||||
* @property {string} [permission] - Backend route permission (/api/...)
|
||||
*/
|
||||
export {}
|
||||
260
ui/src/router/routes.js
Normal file
260
ui/src/router/routes.js
Normal file
@@ -0,0 +1,260 @@
|
||||
// src/router/routes.js
|
||||
|
||||
const routes = [
|
||||
|
||||
/* ==========================================================
|
||||
🌍 ROOT
|
||||
========================================================== */
|
||||
{
|
||||
path: '/',
|
||||
redirect: '/login'
|
||||
},
|
||||
|
||||
|
||||
/* ==========================================================
|
||||
🔐 PUBLIC
|
||||
========================================================== */
|
||||
{
|
||||
path: '/login',
|
||||
component: () => import('layouts/EmptyLayout.vue'),
|
||||
meta: { public: true },
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'login',
|
||||
component: () => import('pages/MainPage.vue')
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
path: '/first-password-change',
|
||||
component: () => import('layouts/EmptyLayout.vue'),
|
||||
meta: { public: true },
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'first-password-change',
|
||||
component: () => import('pages/FirstPasswordChange.vue')
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
path: '/password-reset/:token',
|
||||
component: () => import('layouts/EmptyLayout.vue'),
|
||||
meta: { public: true },
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'password-reset',
|
||||
component: () => import('pages/ResetPassword.vue')
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
|
||||
/* ==========================================================
|
||||
🏠 MAIN APP
|
||||
========================================================== */
|
||||
{
|
||||
path: '/app',
|
||||
component: () => import('layouts/MainLayout.vue'),
|
||||
|
||||
children: [
|
||||
|
||||
/* ================= DASHBOARD ================= */
|
||||
|
||||
{
|
||||
path: '',
|
||||
name: 'dashboard',
|
||||
component: () => import('pages/Dashboard.vue'),
|
||||
meta: { permission: 'system:read' }
|
||||
},
|
||||
|
||||
|
||||
/* ================= PERMISSIONS ================= */
|
||||
|
||||
{
|
||||
path: 'permissions',
|
||||
name: 'permissions',
|
||||
component: () => import('pages/PermissionMatrix.vue'),
|
||||
meta: { permission: 'system:read' }
|
||||
},
|
||||
|
||||
{
|
||||
path: 'role-dept-permissions',
|
||||
name: 'role-dept-permissions',
|
||||
component: () => import('pages/RoleDepartmentPermissionPage.vue'),
|
||||
meta: { permission: 'user:update' }
|
||||
},
|
||||
|
||||
{
|
||||
path: 'user-permissions',
|
||||
name: 'user-permissions',
|
||||
component: () => import('pages/UserPermissionPage.vue'),
|
||||
meta: { permission: 'user:update' }
|
||||
},
|
||||
|
||||
|
||||
/* ================= FINANCE ================= */
|
||||
|
||||
{
|
||||
path: 'statementofaccount',
|
||||
name: 'statementofaccount',
|
||||
component: () => import('pages/statementofaccount.vue'),
|
||||
meta: { permission: 'finance:view' }
|
||||
},
|
||||
|
||||
{
|
||||
path: 'statementreport',
|
||||
name: 'statementreport',
|
||||
component: () => import('pages/StatementReport.vue'),
|
||||
meta: { permission: 'finance:view' }
|
||||
},
|
||||
|
||||
{
|
||||
path: 'statementheaderreport',
|
||||
name: 'statementheaderreport',
|
||||
component: () => import('pages/StatementHeaderReport.vue'),
|
||||
meta: { permission: 'finance:view' }
|
||||
},
|
||||
|
||||
|
||||
/* ================= USERS ================= */
|
||||
|
||||
{
|
||||
path: 'users',
|
||||
name: 'user-gateway',
|
||||
component: () => import('pages/UserGateway.vue'),
|
||||
meta: { permission: 'user:view' }
|
||||
},
|
||||
|
||||
{
|
||||
path: 'users/list',
|
||||
name: 'user-list',
|
||||
component: () => import('pages/UserList.vue'),
|
||||
meta: { permission: 'user:view' }
|
||||
},
|
||||
|
||||
{
|
||||
path: 'users/new',
|
||||
name: 'user-new',
|
||||
component: () => import('pages/UserDetail.vue'),
|
||||
meta: {
|
||||
mode: 'new',
|
||||
permission: 'user:insert'
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
path: 'users/edit/:id',
|
||||
name: 'user-edit',
|
||||
component: () => import('pages/UserDetail.vue'),
|
||||
props: true,
|
||||
meta: {
|
||||
mode: 'edit',
|
||||
permission: 'user:update'
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
path: 'users/view/:id',
|
||||
name: 'user-view',
|
||||
component: () => import('pages/UserDetail.vue'),
|
||||
props: true,
|
||||
meta: {
|
||||
mode: 'view',
|
||||
permission: 'user:view'
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/* ================= LOGS ================= */
|
||||
|
||||
{
|
||||
path: 'activity-logs',
|
||||
name: 'activity-logs',
|
||||
component: () => import('pages/ActivityLogs.vue'),
|
||||
meta: { permission: 'user:view' }
|
||||
},
|
||||
|
||||
|
||||
/* ================= TEST MAIL ================= */
|
||||
|
||||
{
|
||||
path: 'test-mail',
|
||||
name: 'test-mail',
|
||||
component: () => import('pages/TestMail.vue'),
|
||||
meta: { permission: 'user:insert' }
|
||||
},
|
||||
|
||||
|
||||
/* ================= ORDERS ================= */
|
||||
|
||||
{
|
||||
path: 'order-gateway',
|
||||
name: 'order-gateway',
|
||||
component: () => import('pages/OrderGateway.vue'),
|
||||
meta: { permission: 'order:view' }
|
||||
},
|
||||
|
||||
{
|
||||
path: 'order-entry/:orderHeaderID',
|
||||
name: 'order-entry',
|
||||
component: () => import('pages/OrderEntry.vue'),
|
||||
props: true,
|
||||
meta: {
|
||||
mode: 'new',
|
||||
permission: 'order:insert'
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
path: 'order-edit/:orderHeaderID',
|
||||
name: 'order-edit',
|
||||
component: () => import('pages/OrderEntry.vue'),
|
||||
props: true,
|
||||
meta: {
|
||||
mode: 'edit',
|
||||
permission: 'order:update'
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
path: 'order-list',
|
||||
name: 'order-list',
|
||||
component: () => import('pages/OrderList.vue'),
|
||||
meta: { permission: 'order:view' }
|
||||
},
|
||||
|
||||
{
|
||||
path: 'order-pdf/:id',
|
||||
name: 'order-pdf',
|
||||
component: () => import('pages/OrderPdf.vue'),
|
||||
props: true,
|
||||
meta: { permission: 'order:export' }
|
||||
},
|
||||
|
||||
|
||||
/* ================= PASSWORD ================= */
|
||||
|
||||
{
|
||||
path: 'change-password',
|
||||
name: 'change-password',
|
||||
component: () => import('pages/ChangePassword.vue')
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
|
||||
/* ==========================================================
|
||||
❌ 404
|
||||
========================================================== */
|
||||
{
|
||||
path: '/:catchAll(.*)*',
|
||||
component: () => import('pages/ErrorNotFound.vue')
|
||||
}
|
||||
]
|
||||
|
||||
export default routes
|
||||
Reference in New Issue
Block a user