diff --git a/api/src/Kurs.Platform.DbMigrator/Seeds/SeederData.json b/api/src/Kurs.Platform.DbMigrator/Seeds/SeederData.json
index f3d01b68..abe70758 100644
--- a/api/src/Kurs.Platform.DbMigrator/Seeds/SeederData.json
+++ b/api/src/Kurs.Platform.DbMigrator/Seeds/SeederData.json
@@ -2497,6 +2497,12 @@
"en": "Access Failed Count",
"tr": "Başarısız Erişim Sayısı"
},
+ {
+ "resourceName": "Platform",
+ "key": "Abp.Identity.Ai",
+ "en": "AI Assistant",
+ "tr": "Yapay Zeka Asistanı"
+ },
{
"resourceName": "Platform",
"key": "Abp.Identity.Profile",
@@ -6352,7 +6358,7 @@
{
"resourceName": "Platform",
"key": "Public.notFound.button",
- "tr": "Ana Sayfaya Dön",
+ "tr": "Ana Sayfa'ya Dön",
"en": "Return to Homepage"
},
{
@@ -10602,6 +10608,14 @@
"IsEnabled": true,
"MultiTenancySide": 3
},
+ {
+ "GroupName": "AbpIdentity",
+ "Name": "Abp.Identity.Ai",
+ "ParentName": null,
+ "DisplayName": "Abp.Identity.Ai",
+ "IsEnabled": true,
+ "MultiTenancySide": 3
+ },
{
"GroupName": "AbpIdentity",
"Name": "Abp.Identity.PermissionGroups",
@@ -25519,14 +25533,14 @@
"path": "/admin/menuManager",
"componentPath": "@/views/menu/MenuManager",
"routeType": "protected",
- "authority": []
+ "authority": ["App.Menus.Manager"]
},
{
"key": "admin.listFormManagement.wizard",
"path": "/admin/listform/wizard",
"componentPath": "@/views/admin/listForm/Wizard",
"routeType": "protected",
- "authority": []
+ "authority": ["App.Listforms.Wizard"]
},
{
"key": "admin.listFormManagement.edit",
@@ -25547,14 +25561,14 @@
"path": "/admin/forumManagement",
"componentPath": "@/views/forum/Management",
"routeType": "protected",
- "authority": []
+ "authority": ["App.ForumManagement"]
},
{
"key": "admin.ai",
"path": "/admin/ai",
"componentPath": "@/views/ai/Assistant",
"routeType": "protected",
- "authority": []
+ "authority": ["Abp.Identity.Ai"]
},
{
"key": "admin.profile.general",
@@ -25596,28 +25610,28 @@
"path": "/admin/settings",
"componentPath": "@/views/settings/Settings",
"routeType": "protected",
- "authority": []
+ "authority": ["App.Setting"]
},
{
"key": "admin.identity.user.detail",
"path": "/admin/users/detail/:userId",
"componentPath": "@/views/admin/user-management/Details",
"routeType": "protected",
- "authority": []
+ "authority": ["AbpIdentity.Users.Update"]
},
{
"key": "admin.identity.ous",
"path": "/admin/ous",
"componentPath": "@/views/admin/organization-unit/OrganizationUnits",
"routeType": "protected",
- "authority": []
+ "authority": ["Abp.Identity.OrganizationUnits"]
},
{
"key": "admin.forum",
"path": "/admin/forum",
"componentPath": "@/views/forum/Forum",
"routeType": "protected",
- "authority": []
+ "authority": ["App.ForumManagement.Publish"]
},
{
"key": "admin.list",
@@ -25666,70 +25680,70 @@
"path": "/admin/developerkit",
"componentPath": "@/views/developerKit/DashboardPage",
"routeType": "protected",
- "authority": []
+ "authority": ["App.DeveloperKit"]
},
{
"key": "admin.developerkit.entities",
"path": "/admin/developerkit/entities",
"componentPath": "@/views/developerKit/EntityPage",
"routeType": "protected",
- "authority": []
+ "authority": ["App.DeveloperKit.Entity"]
},
{
"key": "admin.developerkit.entities.new",
"path": "/admin/developerkit/entities/new",
"componentPath": "@/views/developerKit/EntityDetailPage",
"routeType": "protected",
- "authority": []
+ "authority": ["App.DeveloperKit.Entity"]
},
{
"key": "admin.developerkit.entities.edit",
"path": "/admin/developerkit/entities/edit/:id",
"componentPath": "@/views/developerKit/EntityDetailPage",
"routeType": "protected",
- "authority": []
+ "authority": ["App.DeveloperKit.Entity"]
},
{
"key": "admin.developerkit.migrations",
"path": "/admin/developerkit/migrations",
"componentPath": "@/views/developerKit/MigrationPage",
"routeType": "protected",
- "authority": []
+ "authority": ["App.DeveloperKit.Migrations"]
},
{
"key": "admin.developerkit.endpoints",
"path": "/admin/developerkit/endpoints",
"componentPath": "@/views/developerKit/EndpointPage",
"routeType": "protected",
- "authority": []
+ "authority": ["App.DeveloperKit.Endpoints"]
},
{
"key": "admin.developerkit.components",
"path": "/admin/developerkit/components",
"componentPath": "@/views/developerKit/ComponentPage",
"routeType": "protected",
- "authority": []
+ "authority": ["App.DeveloperKit.Components"]
},
{
"key": "admin.developerkit.components.new",
"path": "/admin/developerkit/components/new",
"componentPath": "@/views/developerKit/ComponentDetailPage",
"routeType": "protected",
- "authority": []
+ "authority": ["App.DeveloperKit.Components"]
},
{
"key": "admin.developerkit.components.view",
"path": "/admin/developerkit/components/view/:id",
"componentPath": "@/views/developerKit/ComponentDetailPage",
"routeType": "protected",
- "authority": []
+ "authority": ["App.DeveloperKit.Components"]
},
{
"key": "admin.developerkit.components.edit",
"path": "/admin/developerkit/components/edit/:id",
"componentPath": "@/views/developerKit/CodePage",
"routeType": "protected",
- "authority": []
+ "authority": ["App.DeveloperKit.Components" ]
}
],
"CustomEndpoints": [
diff --git a/ui/dev-dist/sw.js b/ui/dev-dist/sw.js
index d9e48480..88827ea8 100644
--- a/ui/dev-dist/sw.js
+++ b/ui/dev-dist/sw.js
@@ -82,7 +82,7 @@ define(['./workbox-54d0af47'], (function (workbox) { 'use strict';
"revision": "3ca0b8505b4bec776b69afdba2768812"
}, {
"url": "index.html",
- "revision": "0.0ii4qr2m5co"
+ "revision": "0.rs1dfl4jqho"
}], {});
workbox.cleanupOutdatedCaches();
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {
diff --git a/ui/src/components/layouts/Layouts.tsx b/ui/src/components/layouts/Layouts.tsx
index 054fde96..1771f0bc 100644
--- a/ui/src/components/layouts/Layouts.tsx
+++ b/ui/src/components/layouts/Layouts.tsx
@@ -38,58 +38,58 @@ const PublicLayout = lazy(() => import('./PublicLayout'))
const Layout = () => {
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 { authenticated } = useAuth()
- useDirection() // Her zaman çağrılmalı
- useLocale() // Her zaman çağrılmalı
+ useDirection()
+ useLocale()
const currentPath = location.pathname
+ const isAdminPath = currentPath.startsWith('/admin')
const route = useMemo(() => {
if (!routes || routes.length === 0) return undefined
-
const matched = routes.find((route) => {
if (!route.path.includes(':')) return route.path === currentPath
-
const regexPath = route.path.replace(/:[^/]+/g, '[^/]+')
const regex = new RegExp(`^${regexPath}$`)
return regex.test(currentPath)
})
-
return matched
}, [routes, currentPath])
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]
}
- if (route?.routeType === 'authenticated' || hasSubdomain()) {
- return AuthLayout
+ // 2) Admin değil ve route bulunamadı -> PublicLayout
+ if (!route) {
+ return PublicLayout
}
+ // 3) Mevcut kurallar
+ if (authenticated && route.routeType === 'protected') {
+ return layouts[layoutType]
+ }
+ if (route.routeType === 'authenticated' || hasSubdomain()) {
+ return AuthLayout
+ }
return PublicLayout
- }, [layoutType, authenticated, route])
+ }, [isAdminPath, route, layoutType, authenticated])
- // ❗ KOŞUL BURADA OLMALI: HOOK’LARDAN SONRA
if (loading) {
return (
-
+
)
}
return (
-
-
-
- }
- >
+ }>
)
diff --git a/ui/src/components/route/AuthorityGuard.tsx b/ui/src/components/route/AuthorityGuard.tsx
index 3d18a353..983ebb51 100644
--- a/ui/src/components/route/AuthorityGuard.tsx
+++ b/ui/src/components/route/AuthorityGuard.tsx
@@ -1,18 +1,25 @@
+// AuthorityGuard.tsx
import { PropsWithChildren } from 'react'
-import { Navigate } from 'react-router-dom'
+import { Navigate, useLocation } from 'react-router-dom'
import useAuthority from '@/utils/hooks/useAuthority'
+import { getAccessDeniedPath } from '@/utils/routing'
type AuthorityGuardProps = PropsWithChildren<{
- userAuthority?: string[]
- authority?: string[]
+ userAuthority?: string[]
+ authority?: string[]
}>
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
+ }
- return <>{roleMatched ? children : }>
+ return <>{children}>
}
export default AuthorityGuard
diff --git a/ui/src/components/route/PermissionGuard.tsx b/ui/src/components/route/PermissionGuard.tsx
index 5faba93d..88d96cd8 100644
--- a/ui/src/components/route/PermissionGuard.tsx
+++ b/ui/src/components/route/PermissionGuard.tsx
@@ -1,18 +1,24 @@
+// PermissionGuard.tsx
import { usePermission } from '@/utils/hooks/usePermission'
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<{
permissions?: string[]
}>
-const PermissionGuard = (props: PermissionGuardProps) => {
- const { permissions = [], children } = props
-
+const PermissionGuard = ({ permissions = [], children }: PermissionGuardProps) => {
const { checkPermissions } = usePermission()
const permissionsMatched = checkPermissions(permissions)
+ const location = useLocation()
- return <>{permissionsMatched ? children : }>
+ if (!permissionsMatched) {
+ const to = getAccessDeniedPath(location.pathname)
+ return
+ }
+
+ return <>{children}>
}
export default PermissionGuard
diff --git a/ui/src/components/template/AiAssistant.tsx b/ui/src/components/template/AiAssistant.tsx
index 5eaba1e7..02af8f58 100644
--- a/ui/src/components/template/AiAssistant.tsx
+++ b/ui/src/components/template/AiAssistant.tsx
@@ -1,6 +1,7 @@
import Tooltip from '@/components/ui/Tooltip'
import { ROUTES_ENUM } from '@/routes/route.constant'
import { useLocalization } from '@/utils/hooks/useLocalization'
+import { usePermission } from '@/utils/hooks/usePermission'
import { Helmet } from 'react-helmet'
import { FcAssistant } from 'react-icons/fc'
import { useNavigate } from 'react-router-dom'
@@ -8,15 +9,17 @@ import { useNavigate } from 'react-router-dom'
const AiAssistant = () => {
const { translate } = useLocalization()
const navigate = useNavigate()
+ const { checkPermissions } = usePermission()
+ const canViewAi = checkPermissions(['Abp.Identity.Ai'])
+
+ if (!canViewAi) {
+ return null
+ }
+
return (
<>
-
-
+
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"
diff --git a/ui/src/routes/dynamicRouter.tsx b/ui/src/routes/dynamicRouter.tsx
index 8cb2eca7..66a87d87 100644
--- a/ui/src/routes/dynamicRouter.tsx
+++ b/ui/src/routes/dynamicRouter.tsx
@@ -1,4 +1,5 @@
-import React, { useMemo } from 'react'
+// DynamicRouter.tsx
+import React from 'react'
import { Routes, Route, Navigate } from 'react-router-dom'
import { mapDynamicRoutes, loadComponent } from './dynamicRouteLoader'
import { useDynamicRoutes } from './dynamicRoutesContext'
@@ -8,17 +9,20 @@ import PageContainer from '@/components/template/PageContainer'
import { ROUTES_ENUM } from './route.constant'
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 = () => {
const { routes, loading, error } = useDynamicRoutes()
-
- const dynamicRoutes = useMemo(() => mapDynamicRoutes(routes), [routes])
- const NotFoundComponent = useMemo(() => loadComponent('views/NotFound'), [])
+ const dynamicRoutes = React.useMemo(() => mapDynamicRoutes(routes), [routes])
if (loading) return
Loading...
if (error) return
Hata: {error}
return (
+ {/* ADMIN */}
}>
{dynamicRoutes
.filter((r) => r.routeType === 'protected')
@@ -27,7 +31,7 @@ export const DynamicRouter: React.FC = () => {
return (
@@ -40,9 +44,36 @@ export const DynamicRouter: React.FC = () => {
/>
)
})}
+
+ {/* admin default */}
} />
+
+ {/* admin access denied (statik) */}
+
+ Loading... }>
+
+
+
+ }
+ />
+
+ {/* admin not found (statik) */}
+
+ Loading...}>
+
+
+
+ }
+ />
+ {/* Auth/Public dinamik rotalar */}
{dynamicRoutes
.filter((r) =>
hasSubdomain() ? r.routeType === 'authenticated' : r.routeType !== 'protected',
@@ -62,6 +93,7 @@ export const DynamicRouter: React.FC = () => {
)
})}
+ {/* root redirect */}
{
}
/>
+ {/* public access denied (statik) */}
+ Loading...}>
+
+
+ }
+ />
+
+ {/* public not found (statik) */}
- ) : (
- Loading...}>
-
-
- )
+ Loading...}>
+
+
}
/>
diff --git a/ui/src/routes/route.constant.ts b/ui/src/routes/route.constant.ts
index 7afef0d6..ce5fb0f6 100644
--- a/ui/src/routes/route.constant.ts
+++ b/ui/src/routes/route.constant.ts
@@ -7,6 +7,7 @@ export const ROUTES_ENUM = {
demo: '/demo',
blog: '/blog',
contact: '/contact',
+ accessDenied: '/access-denied',
},
authenticated: {
login: '/login',
@@ -67,5 +68,6 @@ export const ROUTES_ENUM = {
chart: '/admin/chart/:chartCode',
pivot: '/admin/pivot/:listFormCode',
},
+ accessDenied: '/admin/access-denied',
},
}
diff --git a/ui/src/utils/routing.ts b/ui/src/utils/routing.ts
new file mode 100644
index 00000000..1985c349
--- /dev/null
+++ b/ui/src/utils/routing.ts
@@ -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
diff --git a/ui/src/views/AccessDenied.tsx b/ui/src/views/AccessDenied.tsx
index ef048744..f555eb30 100644
--- a/ui/src/views/AccessDenied.tsx
+++ b/ui/src/views/AccessDenied.tsx
@@ -10,19 +10,27 @@ const AccessDenied = () => {
const navigate = useNavigate()
const { translate } = useLocalization()
+ const currentPath = location.pathname
+ const isAdminPath = currentPath.startsWith('/admin')
+
return (
-
+
-
{ translate('::AccessDenied')}
-
{ translate('::AccessDeniedMessage')}
+
{translate('::AccessDenied')}
+
{translate('::AccessDeniedMessage')}
-
diff --git a/ui/src/views/NotFound.tsx b/ui/src/views/NotFound.tsx
index a121b692..c0853557 100644
--- a/ui/src/views/NotFound.tsx
+++ b/ui/src/views/NotFound.tsx
@@ -1,25 +1,30 @@
import { ROUTES_ENUM } from '@/routes/route.constant'
+import { useLocalization } from '@/utils/hooks/useLocalization'
import { useNavigate } from 'react-router-dom'
const NotFoundPage = () => {
const navigate = useNavigate()
+ const { translate } = useLocalization()
+
+ const currentPath = location.pathname
+ const isAdminPath = currentPath.startsWith('/admin')
return (
-
+
- Aradığınız sayfa bulunamadı.
+ {translate('::Public.notFound.message')}
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"
>
- Ana Sayfa'ya Dön
+ {translate('::Public.notFound.button')}
diff --git a/ui/src/views/Views.tsx b/ui/src/views/Views.tsx
index 825dd776..5c5b0450 100644
--- a/ui/src/views/Views.tsx
+++ b/ui/src/views/Views.tsx
@@ -10,6 +10,7 @@ import DialogProvider from './shared/DialogContext'
import DialogShowComponent from './shared/DialogContext/DialogShowComponent'
import UiDialog from './shared/UiDialog'
import { DynamicRouter } from '@/routes/dynamicRouter'
+import { getAccessDeniedPath } from '@/utils/routing'
interface ViewsProps {
pageContainerType?: 'default' | 'gutterless' | 'contained'
@@ -48,7 +49,11 @@ const Views = (props: ViewsProps) => {
)}
{errors?.some((e) => e.statusCode === '403' || e.statusCode === '401') && (
-
+
)}
{errors?.map((e) => (
{
- if (!permissionResults) {
- return
- }
+ if (!permissionResults) return
- if (
- (mode === 'new' && !permissionResults.c) ||
- (mode === 'edit' && !permissionResults.u) ||
- (mode === 'view' && !permissionResults.r)
- ) {
- navigate('/access-denied')
+ const noCreate = mode === 'new' && !permissionResults.c
+ const noUpdate = mode === 'edit' && !permissionResults.u
+ const noRead = mode === 'view' && !permissionResults.r
+
+ if (noCreate || noUpdate || noRead) {
+ navigate(getAccessDeniedPath(location.pathname), { replace: true, state: { from: location } })
}
}, [permissionResults])