This commit is contained in:
2026-02-11 17:46:22 +03:00
commit eacfacb13b
266 changed files with 51337 additions and 0 deletions

96
ui/src/router/index.js Normal file
View 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
View 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
View 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