Yetki düzenlemesi, Access Denied ve NotFound
This commit is contained in:
parent
821806a8db
commit
e97d560761
13 changed files with 180 additions and 87 deletions
|
|
@ -2497,6 +2497,12 @@
|
||||||
"en": "Access Failed Count",
|
"en": "Access Failed Count",
|
||||||
"tr": "Başarısız Erişim Sayısı"
|
"tr": "Başarısız Erişim Sayısı"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"resourceName": "Platform",
|
||||||
|
"key": "Abp.Identity.Ai",
|
||||||
|
"en": "AI Assistant",
|
||||||
|
"tr": "Yapay Zeka Asistanı"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"resourceName": "Platform",
|
"resourceName": "Platform",
|
||||||
"key": "Abp.Identity.Profile",
|
"key": "Abp.Identity.Profile",
|
||||||
|
|
@ -6352,7 +6358,7 @@
|
||||||
{
|
{
|
||||||
"resourceName": "Platform",
|
"resourceName": "Platform",
|
||||||
"key": "Public.notFound.button",
|
"key": "Public.notFound.button",
|
||||||
"tr": "Ana Sayfaya Dön",
|
"tr": "Ana Sayfa'ya Dön",
|
||||||
"en": "Return to Homepage"
|
"en": "Return to Homepage"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -10602,6 +10608,14 @@
|
||||||
"IsEnabled": true,
|
"IsEnabled": true,
|
||||||
"MultiTenancySide": 3
|
"MultiTenancySide": 3
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"GroupName": "AbpIdentity",
|
||||||
|
"Name": "Abp.Identity.Ai",
|
||||||
|
"ParentName": null,
|
||||||
|
"DisplayName": "Abp.Identity.Ai",
|
||||||
|
"IsEnabled": true,
|
||||||
|
"MultiTenancySide": 3
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"GroupName": "AbpIdentity",
|
"GroupName": "AbpIdentity",
|
||||||
"Name": "Abp.Identity.PermissionGroups",
|
"Name": "Abp.Identity.PermissionGroups",
|
||||||
|
|
@ -25519,14 +25533,14 @@
|
||||||
"path": "/admin/menuManager",
|
"path": "/admin/menuManager",
|
||||||
"componentPath": "@/views/menu/MenuManager",
|
"componentPath": "@/views/menu/MenuManager",
|
||||||
"routeType": "protected",
|
"routeType": "protected",
|
||||||
"authority": []
|
"authority": ["App.Menus.Manager"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "admin.listFormManagement.wizard",
|
"key": "admin.listFormManagement.wizard",
|
||||||
"path": "/admin/listform/wizard",
|
"path": "/admin/listform/wizard",
|
||||||
"componentPath": "@/views/admin/listForm/Wizard",
|
"componentPath": "@/views/admin/listForm/Wizard",
|
||||||
"routeType": "protected",
|
"routeType": "protected",
|
||||||
"authority": []
|
"authority": ["App.Listforms.Wizard"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "admin.listFormManagement.edit",
|
"key": "admin.listFormManagement.edit",
|
||||||
|
|
@ -25547,14 +25561,14 @@
|
||||||
"path": "/admin/forumManagement",
|
"path": "/admin/forumManagement",
|
||||||
"componentPath": "@/views/forum/Management",
|
"componentPath": "@/views/forum/Management",
|
||||||
"routeType": "protected",
|
"routeType": "protected",
|
||||||
"authority": []
|
"authority": ["App.ForumManagement"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "admin.ai",
|
"key": "admin.ai",
|
||||||
"path": "/admin/ai",
|
"path": "/admin/ai",
|
||||||
"componentPath": "@/views/ai/Assistant",
|
"componentPath": "@/views/ai/Assistant",
|
||||||
"routeType": "protected",
|
"routeType": "protected",
|
||||||
"authority": []
|
"authority": ["Abp.Identity.Ai"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "admin.profile.general",
|
"key": "admin.profile.general",
|
||||||
|
|
@ -25596,28 +25610,28 @@
|
||||||
"path": "/admin/settings",
|
"path": "/admin/settings",
|
||||||
"componentPath": "@/views/settings/Settings",
|
"componentPath": "@/views/settings/Settings",
|
||||||
"routeType": "protected",
|
"routeType": "protected",
|
||||||
"authority": []
|
"authority": ["App.Setting"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "admin.identity.user.detail",
|
"key": "admin.identity.user.detail",
|
||||||
"path": "/admin/users/detail/:userId",
|
"path": "/admin/users/detail/:userId",
|
||||||
"componentPath": "@/views/admin/user-management/Details",
|
"componentPath": "@/views/admin/user-management/Details",
|
||||||
"routeType": "protected",
|
"routeType": "protected",
|
||||||
"authority": []
|
"authority": ["AbpIdentity.Users.Update"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "admin.identity.ous",
|
"key": "admin.identity.ous",
|
||||||
"path": "/admin/ous",
|
"path": "/admin/ous",
|
||||||
"componentPath": "@/views/admin/organization-unit/OrganizationUnits",
|
"componentPath": "@/views/admin/organization-unit/OrganizationUnits",
|
||||||
"routeType": "protected",
|
"routeType": "protected",
|
||||||
"authority": []
|
"authority": ["Abp.Identity.OrganizationUnits"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "admin.forum",
|
"key": "admin.forum",
|
||||||
"path": "/admin/forum",
|
"path": "/admin/forum",
|
||||||
"componentPath": "@/views/forum/Forum",
|
"componentPath": "@/views/forum/Forum",
|
||||||
"routeType": "protected",
|
"routeType": "protected",
|
||||||
"authority": []
|
"authority": ["App.ForumManagement.Publish"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "admin.list",
|
"key": "admin.list",
|
||||||
|
|
@ -25666,70 +25680,70 @@
|
||||||
"path": "/admin/developerkit",
|
"path": "/admin/developerkit",
|
||||||
"componentPath": "@/views/developerKit/DashboardPage",
|
"componentPath": "@/views/developerKit/DashboardPage",
|
||||||
"routeType": "protected",
|
"routeType": "protected",
|
||||||
"authority": []
|
"authority": ["App.DeveloperKit"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "admin.developerkit.entities",
|
"key": "admin.developerkit.entities",
|
||||||
"path": "/admin/developerkit/entities",
|
"path": "/admin/developerkit/entities",
|
||||||
"componentPath": "@/views/developerKit/EntityPage",
|
"componentPath": "@/views/developerKit/EntityPage",
|
||||||
"routeType": "protected",
|
"routeType": "protected",
|
||||||
"authority": []
|
"authority": ["App.DeveloperKit.Entity"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "admin.developerkit.entities.new",
|
"key": "admin.developerkit.entities.new",
|
||||||
"path": "/admin/developerkit/entities/new",
|
"path": "/admin/developerkit/entities/new",
|
||||||
"componentPath": "@/views/developerKit/EntityDetailPage",
|
"componentPath": "@/views/developerKit/EntityDetailPage",
|
||||||
"routeType": "protected",
|
"routeType": "protected",
|
||||||
"authority": []
|
"authority": ["App.DeveloperKit.Entity"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "admin.developerkit.entities.edit",
|
"key": "admin.developerkit.entities.edit",
|
||||||
"path": "/admin/developerkit/entities/edit/:id",
|
"path": "/admin/developerkit/entities/edit/:id",
|
||||||
"componentPath": "@/views/developerKit/EntityDetailPage",
|
"componentPath": "@/views/developerKit/EntityDetailPage",
|
||||||
"routeType": "protected",
|
"routeType": "protected",
|
||||||
"authority": []
|
"authority": ["App.DeveloperKit.Entity"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "admin.developerkit.migrations",
|
"key": "admin.developerkit.migrations",
|
||||||
"path": "/admin/developerkit/migrations",
|
"path": "/admin/developerkit/migrations",
|
||||||
"componentPath": "@/views/developerKit/MigrationPage",
|
"componentPath": "@/views/developerKit/MigrationPage",
|
||||||
"routeType": "protected",
|
"routeType": "protected",
|
||||||
"authority": []
|
"authority": ["App.DeveloperKit.Migrations"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "admin.developerkit.endpoints",
|
"key": "admin.developerkit.endpoints",
|
||||||
"path": "/admin/developerkit/endpoints",
|
"path": "/admin/developerkit/endpoints",
|
||||||
"componentPath": "@/views/developerKit/EndpointPage",
|
"componentPath": "@/views/developerKit/EndpointPage",
|
||||||
"routeType": "protected",
|
"routeType": "protected",
|
||||||
"authority": []
|
"authority": ["App.DeveloperKit.Endpoints"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "admin.developerkit.components",
|
"key": "admin.developerkit.components",
|
||||||
"path": "/admin/developerkit/components",
|
"path": "/admin/developerkit/components",
|
||||||
"componentPath": "@/views/developerKit/ComponentPage",
|
"componentPath": "@/views/developerKit/ComponentPage",
|
||||||
"routeType": "protected",
|
"routeType": "protected",
|
||||||
"authority": []
|
"authority": ["App.DeveloperKit.Components"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "admin.developerkit.components.new",
|
"key": "admin.developerkit.components.new",
|
||||||
"path": "/admin/developerkit/components/new",
|
"path": "/admin/developerkit/components/new",
|
||||||
"componentPath": "@/views/developerKit/ComponentDetailPage",
|
"componentPath": "@/views/developerKit/ComponentDetailPage",
|
||||||
"routeType": "protected",
|
"routeType": "protected",
|
||||||
"authority": []
|
"authority": ["App.DeveloperKit.Components"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "admin.developerkit.components.view",
|
"key": "admin.developerkit.components.view",
|
||||||
"path": "/admin/developerkit/components/view/:id",
|
"path": "/admin/developerkit/components/view/:id",
|
||||||
"componentPath": "@/views/developerKit/ComponentDetailPage",
|
"componentPath": "@/views/developerKit/ComponentDetailPage",
|
||||||
"routeType": "protected",
|
"routeType": "protected",
|
||||||
"authority": []
|
"authority": ["App.DeveloperKit.Components"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "admin.developerkit.components.edit",
|
"key": "admin.developerkit.components.edit",
|
||||||
"path": "/admin/developerkit/components/edit/:id",
|
"path": "/admin/developerkit/components/edit/:id",
|
||||||
"componentPath": "@/views/developerKit/CodePage",
|
"componentPath": "@/views/developerKit/CodePage",
|
||||||
"routeType": "protected",
|
"routeType": "protected",
|
||||||
"authority": []
|
"authority": ["App.DeveloperKit.Components" ]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"CustomEndpoints": [
|
"CustomEndpoints": [
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ define(['./workbox-54d0af47'], (function (workbox) { 'use strict';
|
||||||
"revision": "3ca0b8505b4bec776b69afdba2768812"
|
"revision": "3ca0b8505b4bec776b69afdba2768812"
|
||||||
}, {
|
}, {
|
||||||
"url": "index.html",
|
"url": "index.html",
|
||||||
"revision": "0.0ii4qr2m5co"
|
"revision": "0.rs1dfl4jqho"
|
||||||
}], {});
|
}], {});
|
||||||
workbox.cleanupOutdatedCaches();
|
workbox.cleanupOutdatedCaches();
|
||||||
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {
|
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {
|
||||||
|
|
|
||||||
|
|
@ -38,58 +38,58 @@ const PublicLayout = lazy(() => import('./PublicLayout'))
|
||||||
|
|
||||||
const Layout = () => {
|
const Layout = () => {
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
const layoutType = useStoreState((state) => state.theme.layout.type) as LayoutType
|
const layoutType = useStoreState((s) => s.theme.layout.type) as LayoutType
|
||||||
const { routes, loading } = useDynamicRoutes()
|
const { routes, loading } = useDynamicRoutes()
|
||||||
const { authenticated } = useAuth()
|
const { authenticated } = useAuth()
|
||||||
|
|
||||||
useDirection() // Her zaman çağrılmalı
|
useDirection()
|
||||||
useLocale() // Her zaman çağrılmalı
|
useLocale()
|
||||||
|
|
||||||
const currentPath = location.pathname
|
const currentPath = location.pathname
|
||||||
|
const isAdminPath = currentPath.startsWith('/admin')
|
||||||
|
|
||||||
const route = useMemo(() => {
|
const route = useMemo(() => {
|
||||||
if (!routes || routes.length === 0) return undefined
|
if (!routes || routes.length === 0) return undefined
|
||||||
|
|
||||||
const matched = routes.find((route) => {
|
const matched = routes.find((route) => {
|
||||||
if (!route.path.includes(':')) return route.path === currentPath
|
if (!route.path.includes(':')) return route.path === currentPath
|
||||||
|
|
||||||
const regexPath = route.path.replace(/:[^/]+/g, '[^/]+')
|
const regexPath = route.path.replace(/:[^/]+/g, '[^/]+')
|
||||||
const regex = new RegExp(`^${regexPath}$`)
|
const regex = new RegExp(`^${regexPath}$`)
|
||||||
return regex.test(currentPath)
|
return regex.test(currentPath)
|
||||||
})
|
})
|
||||||
|
|
||||||
return matched
|
return matched
|
||||||
}, [routes, currentPath])
|
}, [routes, currentPath])
|
||||||
|
|
||||||
const AppLayout = useMemo(() => {
|
const AppLayout = useMemo(() => {
|
||||||
if (authenticated && route?.routeType === 'protected') {
|
// 1) Admin path ise, route bulunmasa bile admin layout'u göster
|
||||||
|
if (isAdminPath) {
|
||||||
return layouts[layoutType]
|
return layouts[layoutType]
|
||||||
}
|
}
|
||||||
|
|
||||||
if (route?.routeType === 'authenticated' || hasSubdomain()) {
|
// 2) Admin değil ve route bulunamadı -> PublicLayout
|
||||||
return AuthLayout
|
if (!route) {
|
||||||
|
return PublicLayout
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 3) Mevcut kurallar
|
||||||
|
if (authenticated && route.routeType === 'protected') {
|
||||||
|
return layouts[layoutType]
|
||||||
|
}
|
||||||
|
if (route.routeType === 'authenticated' || hasSubdomain()) {
|
||||||
|
return AuthLayout
|
||||||
|
}
|
||||||
return PublicLayout
|
return PublicLayout
|
||||||
}, [layoutType, authenticated, route])
|
}, [isAdminPath, route, layoutType, authenticated])
|
||||||
|
|
||||||
// ❗ KOŞUL BURADA OLMALI: HOOK’LARDAN SONRA
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-auto flex-col h-[100vh]">
|
<div className="flex flex-auto flex-col h-[100vh]">
|
||||||
<Loading loading={true} />
|
<Loading loading />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Suspense
|
<Suspense fallback={<div className="flex flex-auto flex-col h-[100vh]"><Loading loading /></div>}>
|
||||||
fallback={
|
|
||||||
<div className="flex flex-auto flex-col h-[100vh]">
|
|
||||||
<Loading loading={true} />
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<AppLayout />
|
<AppLayout />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,25 @@
|
||||||
|
// AuthorityGuard.tsx
|
||||||
import { PropsWithChildren } from 'react'
|
import { PropsWithChildren } from 'react'
|
||||||
import { Navigate } from 'react-router-dom'
|
import { Navigate, useLocation } from 'react-router-dom'
|
||||||
import useAuthority from '@/utils/hooks/useAuthority'
|
import useAuthority from '@/utils/hooks/useAuthority'
|
||||||
|
import { getAccessDeniedPath } from '@/utils/routing'
|
||||||
|
|
||||||
type AuthorityGuardProps = PropsWithChildren<{
|
type AuthorityGuardProps = PropsWithChildren<{
|
||||||
userAuthority?: string[]
|
userAuthority?: string[]
|
||||||
authority?: string[]
|
authority?: string[]
|
||||||
}>
|
}>
|
||||||
|
|
||||||
const AuthorityGuard = (props: AuthorityGuardProps) => {
|
const AuthorityGuard = (props: AuthorityGuardProps) => {
|
||||||
const { userAuthority = [], authority = [], children } = props
|
const { userAuthority = [], authority = [], children } = props
|
||||||
|
const roleMatched = useAuthority(userAuthority, authority)
|
||||||
|
const location = useLocation()
|
||||||
|
|
||||||
const roleMatched = useAuthority(userAuthority, authority)
|
if (!roleMatched) {
|
||||||
|
const to = getAccessDeniedPath(location.pathname)
|
||||||
|
return <Navigate to={to} replace state={{ from: location }} />
|
||||||
|
}
|
||||||
|
|
||||||
return <>{roleMatched ? children : <Navigate to="/access-denied" />}</>
|
return <>{children}</>
|
||||||
}
|
}
|
||||||
|
|
||||||
export default AuthorityGuard
|
export default AuthorityGuard
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,24 @@
|
||||||
|
// PermissionGuard.tsx
|
||||||
import { usePermission } from '@/utils/hooks/usePermission'
|
import { usePermission } from '@/utils/hooks/usePermission'
|
||||||
import { PropsWithChildren } from 'react'
|
import { PropsWithChildren } from 'react'
|
||||||
import { Navigate } from 'react-router-dom'
|
import { Navigate, useLocation } from 'react-router-dom'
|
||||||
|
import { getAccessDeniedPath } from '@/utils/routing'
|
||||||
|
|
||||||
type PermissionGuardProps = PropsWithChildren<{
|
type PermissionGuardProps = PropsWithChildren<{
|
||||||
permissions?: string[]
|
permissions?: string[]
|
||||||
}>
|
}>
|
||||||
|
|
||||||
const PermissionGuard = (props: PermissionGuardProps) => {
|
const PermissionGuard = ({ permissions = [], children }: PermissionGuardProps) => {
|
||||||
const { permissions = [], children } = props
|
|
||||||
|
|
||||||
const { checkPermissions } = usePermission()
|
const { checkPermissions } = usePermission()
|
||||||
const permissionsMatched = checkPermissions(permissions)
|
const permissionsMatched = checkPermissions(permissions)
|
||||||
|
const location = useLocation()
|
||||||
|
|
||||||
return <>{permissionsMatched ? children : <Navigate to="/access-denied" />}</>
|
if (!permissionsMatched) {
|
||||||
|
const to = getAccessDeniedPath(location.pathname)
|
||||||
|
return <Navigate to={to} replace state={{ from: location }} />
|
||||||
|
}
|
||||||
|
|
||||||
|
return <>{children}</>
|
||||||
}
|
}
|
||||||
|
|
||||||
export default PermissionGuard
|
export default PermissionGuard
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import Tooltip from '@/components/ui/Tooltip'
|
import Tooltip from '@/components/ui/Tooltip'
|
||||||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
import { ROUTES_ENUM } from '@/routes/route.constant'
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
|
import { usePermission } from '@/utils/hooks/usePermission'
|
||||||
import { Helmet } from 'react-helmet'
|
import { Helmet } from 'react-helmet'
|
||||||
import { FcAssistant } from 'react-icons/fc'
|
import { FcAssistant } from 'react-icons/fc'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
|
|
@ -8,15 +9,17 @@ import { useNavigate } from 'react-router-dom'
|
||||||
const AiAssistant = () => {
|
const AiAssistant = () => {
|
||||||
const { translate } = useLocalization()
|
const { translate } = useLocalization()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
const { checkPermissions } = usePermission()
|
||||||
|
|
||||||
|
const canViewAi = checkPermissions(['Abp.Identity.Ai'])
|
||||||
|
|
||||||
|
if (!canViewAi) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Helmet
|
<Tooltip title={translate('::Abp.Identity.Ai')}>
|
||||||
titleTemplate="%s | Kurs Platform"
|
|
||||||
title={translate('::' + 'AI Assistant')}
|
|
||||||
defaultTitle="Kurs Platform"
|
|
||||||
></Helmet>
|
|
||||||
<Tooltip title="AI Asistan">
|
|
||||||
<div
|
<div
|
||||||
onClick={() => navigate(ROUTES_ENUM.protected.admin.ai)}
|
onClick={() => navigate(ROUTES_ENUM.protected.admin.ai)}
|
||||||
className="flex items-center justify-center w-9 h-9 m-2 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 cursor-pointer transition-colors duration-200"
|
className="flex items-center justify-center w-9 h-9 m-2 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 cursor-pointer transition-colors duration-200"
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import React, { useMemo } from 'react'
|
// DynamicRouter.tsx
|
||||||
|
import React from 'react'
|
||||||
import { Routes, Route, Navigate } from 'react-router-dom'
|
import { Routes, Route, Navigate } from 'react-router-dom'
|
||||||
import { mapDynamicRoutes, loadComponent } from './dynamicRouteLoader'
|
import { mapDynamicRoutes, loadComponent } from './dynamicRouteLoader'
|
||||||
import { useDynamicRoutes } from './dynamicRoutesContext'
|
import { useDynamicRoutes } from './dynamicRoutesContext'
|
||||||
|
|
@ -8,17 +9,20 @@ import PageContainer from '@/components/template/PageContainer'
|
||||||
import { ROUTES_ENUM } from './route.constant'
|
import { ROUTES_ENUM } from './route.constant'
|
||||||
import { hasSubdomain } from '@/utils/subdomain'
|
import { hasSubdomain } from '@/utils/subdomain'
|
||||||
|
|
||||||
|
// AccessDenied ve NotFound'u dinamiklikten çıkarıyoruz
|
||||||
|
const AccessDenied = React.lazy(() => import('@/views/AccessDenied'))
|
||||||
|
const NotFound = React.lazy(() => import('@/views/NotFound'))
|
||||||
|
|
||||||
export const DynamicRouter: React.FC = () => {
|
export const DynamicRouter: React.FC = () => {
|
||||||
const { routes, loading, error } = useDynamicRoutes()
|
const { routes, loading, error } = useDynamicRoutes()
|
||||||
|
const dynamicRoutes = React.useMemo(() => mapDynamicRoutes(routes), [routes])
|
||||||
const dynamicRoutes = useMemo(() => mapDynamicRoutes(routes), [routes])
|
|
||||||
const NotFoundComponent = useMemo(() => loadComponent('views/NotFound'), [])
|
|
||||||
|
|
||||||
if (loading) return <div>Loading...</div>
|
if (loading) return <div>Loading...</div>
|
||||||
if (error) return <div>Hata: {error}</div>
|
if (error) return <div>Hata: {error}</div>
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Routes>
|
<Routes>
|
||||||
|
{/* ADMIN */}
|
||||||
<Route path="/admin/*" element={<ProtectedRoute />}>
|
<Route path="/admin/*" element={<ProtectedRoute />}>
|
||||||
{dynamicRoutes
|
{dynamicRoutes
|
||||||
.filter((r) => r.routeType === 'protected')
|
.filter((r) => r.routeType === 'protected')
|
||||||
|
|
@ -27,7 +31,7 @@ export const DynamicRouter: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<Route
|
<Route
|
||||||
key={route.key}
|
key={route.key}
|
||||||
path={route.path.replace(/^\/admin\/?/, '') || '.'} // Remove /admin prefix for nested routes
|
path={route.path.replace(/^\/admin\/?/, '') || '.'}
|
||||||
element={
|
element={
|
||||||
<PermissionGuard permissions={route.authority}>
|
<PermissionGuard permissions={route.authority}>
|
||||||
<PageContainer>
|
<PageContainer>
|
||||||
|
|
@ -40,9 +44,36 @@ export const DynamicRouter: React.FC = () => {
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
|
||||||
|
{/* admin default */}
|
||||||
<Route index element={<Navigate to={ROUTES_ENUM.protected.dashboard} replace />} />
|
<Route index element={<Navigate to={ROUTES_ENUM.protected.dashboard} replace />} />
|
||||||
|
|
||||||
|
{/* admin access denied (statik) */}
|
||||||
|
<Route
|
||||||
|
path="access-denied"
|
||||||
|
element={
|
||||||
|
<PageContainer>
|
||||||
|
<React.Suspense fallback={<div>Loading...</div>}>
|
||||||
|
<AccessDenied />
|
||||||
|
</React.Suspense>
|
||||||
|
</PageContainer>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* admin not found (statik) */}
|
||||||
|
<Route
|
||||||
|
path="*"
|
||||||
|
element={
|
||||||
|
<PageContainer>
|
||||||
|
<React.Suspense fallback={<div>Loading...</div>}>
|
||||||
|
<NotFound />
|
||||||
|
</React.Suspense>
|
||||||
|
</PageContainer>
|
||||||
|
}
|
||||||
|
/>
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
|
{/* Auth/Public dinamik rotalar */}
|
||||||
{dynamicRoutes
|
{dynamicRoutes
|
||||||
.filter((r) =>
|
.filter((r) =>
|
||||||
hasSubdomain() ? r.routeType === 'authenticated' : r.routeType !== 'protected',
|
hasSubdomain() ? r.routeType === 'authenticated' : r.routeType !== 'protected',
|
||||||
|
|
@ -62,6 +93,7 @@ export const DynamicRouter: React.FC = () => {
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
|
||||||
|
{/* root redirect */}
|
||||||
<Route
|
<Route
|
||||||
path="/"
|
path="/"
|
||||||
element={
|
element={
|
||||||
|
|
@ -72,16 +104,23 @@ export const DynamicRouter: React.FC = () => {
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* public access denied (statik) */}
|
||||||
|
<Route
|
||||||
|
path="/access-denied"
|
||||||
|
element={
|
||||||
|
<React.Suspense fallback={<div>Loading...</div>}>
|
||||||
|
<AccessDenied />
|
||||||
|
</React.Suspense>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* public not found (statik) */}
|
||||||
<Route
|
<Route
|
||||||
path="*"
|
path="*"
|
||||||
element={
|
element={
|
||||||
hasSubdomain() ? (
|
<React.Suspense fallback={<div>Loading...</div>}>
|
||||||
<Navigate to={ROUTES_ENUM.authenticated.login} replace />
|
<NotFound />
|
||||||
) : (
|
</React.Suspense>
|
||||||
<React.Suspense fallback={<div>Loading...</div>}>
|
|
||||||
<NotFoundComponent />
|
|
||||||
</React.Suspense>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Routes>
|
</Routes>
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ export const ROUTES_ENUM = {
|
||||||
demo: '/demo',
|
demo: '/demo',
|
||||||
blog: '/blog',
|
blog: '/blog',
|
||||||
contact: '/contact',
|
contact: '/contact',
|
||||||
|
accessDenied: '/access-denied',
|
||||||
},
|
},
|
||||||
authenticated: {
|
authenticated: {
|
||||||
login: '/login',
|
login: '/login',
|
||||||
|
|
@ -67,5 +68,6 @@ export const ROUTES_ENUM = {
|
||||||
chart: '/admin/chart/:chartCode',
|
chart: '/admin/chart/:chartCode',
|
||||||
pivot: '/admin/pivot/:listFormCode',
|
pivot: '/admin/pivot/:listFormCode',
|
||||||
},
|
},
|
||||||
|
accessDenied: '/admin/access-denied',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
5
ui/src/utils/routing.ts
Normal file
5
ui/src/utils/routing.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
import { ROUTES_ENUM } from "@/routes/route.constant";
|
||||||
|
|
||||||
|
// src/utils/routing.ts
|
||||||
|
export const getAccessDeniedPath = (pathname: string) =>
|
||||||
|
pathname.startsWith('/admin') ? ROUTES_ENUM.protected.accessDenied : ROUTES_ENUM.public.accessDenied
|
||||||
|
|
@ -10,19 +10,27 @@ const AccessDenied = () => {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const { translate } = useLocalization()
|
const { translate } = useLocalization()
|
||||||
|
|
||||||
|
const currentPath = location.pathname
|
||||||
|
const isAdminPath = currentPath.startsWith('/admin')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container className="h-full">
|
<Container className="h-full">
|
||||||
<div className="h-full flex flex-col items-center justify-center p-24">
|
<div className="h-full flex flex-col items-center justify-center p-28">
|
||||||
<DoubleSidedImage
|
<DoubleSidedImage
|
||||||
src="/img/others/img-2.png"
|
src="/img/others/img-2.png"
|
||||||
darkModeSrc="/img/others/img-2-dark.png"
|
darkModeSrc="/img/others/img-2-dark.png"
|
||||||
alt={ translate('::AccessDenied')}
|
alt={translate('::AccessDenied')}
|
||||||
/>
|
/>
|
||||||
<div className="mt-6 text-center">
|
<div className="mt-6 text-center">
|
||||||
<h3 className="mb-2">{ translate('::AccessDenied')}</h3>
|
<h3 className="mb-2">{translate('::AccessDenied')}</h3>
|
||||||
<p className="text-base">{ translate('::AccessDeniedMessage')}</p>
|
<p className="text-base">{translate('::AccessDeniedMessage')}</p>
|
||||||
</div>
|
</div>
|
||||||
<Button size="xs" className="mt-2" variant="default" onClick={() => navigate(ROUTES_ENUM.protected.dashboard)}>
|
<Button
|
||||||
|
size="xs"
|
||||||
|
className="mt-2"
|
||||||
|
variant="default"
|
||||||
|
onClick={() => navigate(isAdminPath ? ROUTES_ENUM.protected.dashboard : ROUTES_ENUM.public.home)}
|
||||||
|
>
|
||||||
<MdArrowBack />
|
<MdArrowBack />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,30 @@
|
||||||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
import { ROUTES_ENUM } from '@/routes/route.constant'
|
||||||
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
|
|
||||||
const NotFoundPage = () => {
|
const NotFoundPage = () => {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
const { translate } = useLocalization()
|
||||||
|
|
||||||
|
const currentPath = location.pathname
|
||||||
|
const isAdminPath = currentPath.startsWith('/admin')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-20">
|
<div className="p-28">
|
||||||
<div className="flex items-center justify-center font-inter">
|
<div className="flex items-center justify-center font-inter">
|
||||||
<div className="text-[8rem] sm:text-[10rem] md:text-[12rem] font-bold bg-gradient-to-br from-primary to-secondary bg-clip-text animate-pulse">
|
<div className="text-[8rem] sm:text-[10rem] md:text-[12rem] font-bold bg-gradient-to-br from-primary to-secondary bg-clip-text animate-pulse">
|
||||||
404
|
404
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className="flex items-center justify-center text-xl mb-6 text-gray-600">
|
<p className="flex items-center justify-center text-xl mb-6 text-gray-600">
|
||||||
Aradığınız sayfa bulunamadı.
|
{translate('::Public.notFound.message')}
|
||||||
</p>
|
</p>
|
||||||
<div className="flex items-center justify-center font-inter">
|
<div className="flex items-center justify-center font-inter">
|
||||||
<button
|
<button
|
||||||
onClick={() => navigate(ROUTES_ENUM.protected.dashboard)}
|
onClick={() => navigate(isAdminPath ? ROUTES_ENUM.protected.dashboard : ROUTES_ENUM.public.home)}
|
||||||
className="px-6 py-3 bg-primary rounded-xl shadow hover:bg-secondary transition"
|
className="px-6 py-3 bg-primary rounded-xl shadow hover:bg-secondary transition"
|
||||||
>
|
>
|
||||||
Ana Sayfa'ya Dön
|
{translate('::Public.notFound.button')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import DialogProvider from './shared/DialogContext'
|
||||||
import DialogShowComponent from './shared/DialogContext/DialogShowComponent'
|
import DialogShowComponent from './shared/DialogContext/DialogShowComponent'
|
||||||
import UiDialog from './shared/UiDialog'
|
import UiDialog from './shared/UiDialog'
|
||||||
import { DynamicRouter } from '@/routes/dynamicRouter'
|
import { DynamicRouter } from '@/routes/dynamicRouter'
|
||||||
|
import { getAccessDeniedPath } from '@/utils/routing'
|
||||||
|
|
||||||
interface ViewsProps {
|
interface ViewsProps {
|
||||||
pageContainerType?: 'default' | 'gutterless' | 'contained'
|
pageContainerType?: 'default' | 'gutterless' | 'contained'
|
||||||
|
|
@ -48,7 +49,11 @@ const Views = (props: ViewsProps) => {
|
||||||
</Alert>
|
</Alert>
|
||||||
)}
|
)}
|
||||||
{errors?.some((e) => e.statusCode === '403' || e.statusCode === '401') && (
|
{errors?.some((e) => e.statusCode === '403' || e.statusCode === '401') && (
|
||||||
<Navigate to="/access-denied" replace />
|
<Navigate
|
||||||
|
to={getAccessDeniedPath(location.pathname)}
|
||||||
|
replace
|
||||||
|
state={{ from: location }}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
{errors?.map((e) => (
|
{errors?.map((e) => (
|
||||||
<UiDialog
|
<UiDialog
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import { GridColumnData } from '../list/GridColumnData'
|
||||||
import { addCss, addJs } from '../list/Utils'
|
import { addCss, addJs } from '../list/Utils'
|
||||||
import { PermissionResults, RowMode, SimpleItemWithColData } from './types'
|
import { PermissionResults, RowMode, SimpleItemWithColData } from './types'
|
||||||
import { EditingFormItemDto, GridDto, PlatformEditorTypes } from '@/proxy/form/models'
|
import { EditingFormItemDto, GridDto, PlatformEditorTypes } from '@/proxy/form/models'
|
||||||
|
import { getAccessDeniedPath } from '@/utils/routing'
|
||||||
|
|
||||||
const useGridData = (props: {
|
const useGridData = (props: {
|
||||||
mode: RowMode
|
mode: RowMode
|
||||||
|
|
@ -287,16 +288,14 @@ const useGridData = (props: {
|
||||||
|
|
||||||
// Auth check
|
// Auth check
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!permissionResults) {
|
if (!permissionResults) return
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
const noCreate = mode === 'new' && !permissionResults.c
|
||||||
(mode === 'new' && !permissionResults.c) ||
|
const noUpdate = mode === 'edit' && !permissionResults.u
|
||||||
(mode === 'edit' && !permissionResults.u) ||
|
const noRead = mode === 'view' && !permissionResults.r
|
||||||
(mode === 'view' && !permissionResults.r)
|
|
||||||
) {
|
if (noCreate || noUpdate || noRead) {
|
||||||
navigate('/access-denied')
|
navigate(getAccessDeniedPath(location.pathname), { replace: true, state: { from: location } })
|
||||||
}
|
}
|
||||||
}, [permissionResults])
|
}, [permissionResults])
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue