ปรับปรุง Permission
This commit is contained in:
parent
c45db89839
commit
1d79416a7f
@ -7,56 +7,79 @@ from datetime import timedelta
|
|||||||
|
|
||||||
from api.models import InferenceAuditLog
|
from api.models import InferenceAuditLog
|
||||||
from api.serializers.audit_serializer import InferenceAuditLogSerializer
|
from api.serializers.audit_serializer import InferenceAuditLogSerializer
|
||||||
from permissions.permission_classes import IsAdminOrManager # ใช้สิทธิ์เดียวกันกับ Model Registry
|
# นำเข้า Permission ที่จำเป็น
|
||||||
|
from permissions.permission_classes import IsAdminOrOperator, IsViewerOrHigher
|
||||||
|
|
||||||
class AuditLogViewSet(viewsets.ReadOnlyModelViewSet):
|
class AuditLogViewSet(viewsets.ReadOnlyModelViewSet):
|
||||||
"""
|
"""
|
||||||
API สำหรับการเข้าถึง Inference Audit Log และสถิติรวม
|
API สำหรับการเข้าถึง Inference Audit Log และสถิติรวม (รวมการดึง Summary ด้วย)
|
||||||
"""
|
"""
|
||||||
queryset = InferenceAuditLog.objects.all()
|
queryset = InferenceAuditLog.objects.all()
|
||||||
serializer_class = InferenceAuditLogSerializer
|
serializer_class = InferenceAuditLogSerializer
|
||||||
permission_classes = [permissions.IsAuthenticated] # อนุญาตให้เข้าถึงเมื่อล็อกอินแล้ว
|
permission_classes = [permissions.IsAuthenticated]
|
||||||
|
|
||||||
# ใช้ 'id' เป็นฟิลด์ค้นหา
|
|
||||||
lookup_field = 'id'
|
lookup_field = 'id'
|
||||||
|
|
||||||
# ใช้ 'id' เป็นชื่อพารามิเตอร์ใน URL
|
|
||||||
lookup_url_kwarg = 'id'
|
lookup_url_kwarg = 'id'
|
||||||
|
|
||||||
def retrieve(self, request, *args, **kwargs):
|
def retrieve(self, request, *args, **kwargs):
|
||||||
# บังคับให้ Lookup Key (pk) เป็น String
|
# บังคับให้ Lookup Key (pk) เป็น String
|
||||||
kwargs[self.lookup_url_kwarg] = str(kwargs[self.lookup_url_kwarg])
|
kwargs[self.lookup_url_kwarg] = str(kwargs[self.lookup_url_kwarg])
|
||||||
|
|
||||||
return super().retrieve(request, *args, **kwargs)
|
return super().retrieve(request, *args, **kwargs)
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
# คืน Log ล่าสุด 10 รายการ (สำหรับ Recent Events ใน Dashboard)
|
# คืน Log ล่าสุด 10 รายการ (สำหรับ Recent Events ใน Dashboard)
|
||||||
return self.queryset.select_related('user')[:10]
|
return self.queryset.select_related('user').order_by('-timestamp')[:10] # 📌 เพิ่ม order_by เพื่อให้ได้ล่าสุดจริง ๆ
|
||||||
|
|
||||||
# -----------------------------------------------
|
# -----------------------------------------------
|
||||||
# Custom Action: ดึงสถิติรวมสำหรับ Dashboard
|
# Custom Action: ดึงสถิติรวม Dynamic (สำหรับ Viewer, Operator, Admin)
|
||||||
# Endpoint: GET /api/v1/audit/inference-summary/
|
# Endpoint: GET /api/v1/audit/summary/
|
||||||
# -----------------------------------------------
|
# -----------------------------------------------
|
||||||
@action(detail=False, methods=['get'], url_path='inference-summary')
|
@action(
|
||||||
|
detail=False,
|
||||||
|
methods=['get'],
|
||||||
|
url_path='summary',
|
||||||
|
# อนุญาตให้เข้าถึงสำหรับทุก Role ใน Dashboard
|
||||||
|
permission_classes=[IsViewerOrHigher]
|
||||||
|
)
|
||||||
def get_summary(self, request):
|
def get_summary(self, request):
|
||||||
one_day_ago = timezone.now() - timedelta(hours=24)
|
user = request.user
|
||||||
|
|
||||||
# 1. คำนวณสถิติรวม (Global Metrics)
|
# 1. ตรวจสอบ Role เพื่อกำหนดขอบเขตข้อมูลที่จะแสดง (Dynamic View)
|
||||||
metrics = self.queryset.filter(timestamp__gte=one_day_ago).aggregate(
|
# ตรวจสอบว่าผู้ใช้เป็น ADMIN หรือ OPERATOR หรือไม่
|
||||||
|
is_high_privilege = user.role in ['ADMIN', 'OPERATOR']
|
||||||
|
|
||||||
|
# กำหนดขีดจำกัดและข้อมูลที่จะรวม
|
||||||
|
log_limit = 10 if is_high_privilege else 5
|
||||||
|
include_latency = is_high_privilege # Latency เป็นข้อมูลเชิงเทคนิค
|
||||||
|
|
||||||
|
one_day_ago = timezone.now() - timedelta(hours=24)
|
||||||
|
queryset = self.queryset.filter(timestamp__gte=one_day_ago)
|
||||||
|
|
||||||
|
# 2. คำนวณสถิติพื้นฐาน (ใช้ร่วมกัน)
|
||||||
|
metrics = queryset.aggregate(
|
||||||
total_runs=Count('id'),
|
total_runs=Count('id'),
|
||||||
success_count=Count('id', filter=Q(is_success=True)),
|
success_count=Count('id', filter=Q(is_success=True)),
|
||||||
avg_latency_ms=Avg('latency_ms')
|
# คำนวณ Avg Latency เฉพาะถ้าผู้ใช้มีสิทธิ์สูง
|
||||||
|
avg_latency_ms=Avg('latency_ms') if include_latency else None
|
||||||
)
|
)
|
||||||
|
|
||||||
# 2. คำนวณ Success Rate
|
# 3. คำนวณ Success Rate
|
||||||
total = metrics.get('total_runs', 0)
|
total = metrics.get('total_runs', 0)
|
||||||
success = metrics.get('success_count', 0)
|
success = metrics.get('success_count', 0)
|
||||||
success_rate = (success / total) * 100 if total > 0 else 0
|
success_rate = (success / total) * 100 if total > 0 else 0
|
||||||
|
|
||||||
return Response({
|
# 4. ดึง Log ล่าสุดตามสิทธิ์
|
||||||
|
last_logs = self.queryset.select_related('user').order_by('-timestamp')[:log_limit]
|
||||||
|
|
||||||
|
response_data = {
|
||||||
"time_window": "24 hours",
|
"time_window": "24 hours",
|
||||||
"total_runs": total,
|
"total_runs": total,
|
||||||
"success_rate": round(success_rate, 2),
|
"success_rate": round(success_rate, 2),
|
||||||
"avg_latency_ms": round(metrics.get('avg_latency_ms', 0) or 0, 2),
|
"last_logs": InferenceAuditLogSerializer(last_logs, many=True).data
|
||||||
"last_logs": InferenceAuditLogSerializer(self.get_queryset(), many=True).data
|
}
|
||||||
})
|
|
||||||
|
# 5. เพิ่ม Latency เข้าไปใน Response เฉพาะ Role ที่มีสิทธิ์สูง
|
||||||
|
if include_latency:
|
||||||
|
response_data['avg_latency_ms'] = round(metrics.get('avg_latency_ms', 0) or 0, 2)
|
||||||
|
|
||||||
|
return Response(response_data)
|
||||||
@ -1,5 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
URL configuration for cremation_backend project.
|
URL configuration for backend project.
|
||||||
|
|
||||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||||
https://docs.djangoproject.com/en/5.2/topics/http/urls/
|
https://docs.djangoproject.com/en/5.2/topics/http/urls/
|
||||||
|
|||||||
@ -1,56 +1,57 @@
|
|||||||
from rest_framework import permissions
|
from rest_framework import permissions
|
||||||
from rest_framework.exceptions import PermissionDenied
|
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------
|
# ----------------------------------------------------
|
||||||
# Base Class เพื่อใช้ Logic การตรวจสอบสิทธิ์ร่วมกัน
|
# Global Role Definition
|
||||||
# ----------------------------------------------------
|
# ----------------------------------------------------
|
||||||
class BaseRolePermission(permissions.BasePermission):
|
ROLES = {
|
||||||
|
"ADMIN": "ADMIN",
|
||||||
|
"OPERATOR": "OPERATOR",
|
||||||
|
"VIEWER": "VIEWER",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------
|
||||||
|
# Base Role Permission (Clean Code + Robustness)
|
||||||
|
# ----------------------------------------------------
|
||||||
|
class RolePermission(permissions.BasePermission):
|
||||||
"""
|
"""
|
||||||
Base class สำหรับตรวจสอบสิทธิ์ตาม is_superuser และ is_staff
|
Base class สำหรับตรวจสอบสิทธิ์ตามฟิลด์ role (ADMIN, OPERATOR, VIEWER)
|
||||||
"""
|
"""
|
||||||
def is_admin(self, user):
|
|
||||||
return user.is_authenticated and user.is_superuser
|
|
||||||
|
|
||||||
def is_manager(self, user):
|
allowed_roles = []
|
||||||
# ผู้จัดการคือ is_staff แต่ไม่ใช่ superuser
|
|
||||||
return user.is_authenticated and user.is_staff and not user.is_superuser
|
|
||||||
|
|
||||||
# หรือเพิ่มฟังก์ชัน is_model_owner(user) ถ้าจำเป็น
|
|
||||||
# ถ้าใช้ฟิลด์ 'role' ที่คุณเพิ่มใน Serializer ควรใช้:
|
|
||||||
# return user.is_authenticated and getattr(user, 'role', '').upper() == 'ADMIN'
|
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------
|
|
||||||
# 1. Permission: อนุญาตเฉพาะ Admin เท่านั้น
|
|
||||||
# ----------------------------------------------------
|
|
||||||
class IsAdmin(BaseRolePermission):
|
|
||||||
message = "คุณไม่มีสิทธิ์เข้าถึงหน้านี้ (Admin Required)."
|
|
||||||
|
|
||||||
def has_permission(self, request, view):
|
def has_permission(self, request, view):
|
||||||
# Admin คือ is_superuser
|
user = request.user
|
||||||
return self.is_admin(request.user)
|
|
||||||
|
if not user or not user.is_authenticated:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# กรณีที่ไม่มี attribute ชื่อ role ก็จะไม่ทำให้เกิด AttributeError
|
||||||
|
user_role = getattr(user, 'role', '').upper()
|
||||||
|
|
||||||
|
return user_role in self.allowed_roles
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------
|
# ----------------------------------------------------
|
||||||
# 2. Permission: อนุญาตเฉพาะ Admin หรือ Manager เท่านั้น
|
# Specific Permission Classes
|
||||||
# ----------------------------------------------------
|
# ----------------------------------------------------
|
||||||
class IsAdminOrManager(BaseRolePermission):
|
class IsAdmin(RolePermission):
|
||||||
message = "คุณต้องมีสิทธิ์ Admin หรือ Manager."
|
message = "คุณไม่มีสิทธิ์เข้าถึงหน้านี้ (ADMIN Required)."
|
||||||
|
allowed_roles = [ROLES["ADMIN"]]
|
||||||
def has_permission(self, request, view):
|
|
||||||
# Admin หรือ Manager (is_staff)
|
|
||||||
return self.is_admin(request.user) or self.is_manager(request.user)
|
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------
|
class IsAdminOrOperator(RolePermission):
|
||||||
# 3. Permission: อนุญาตเฉพาะผู้ใช้ที่ผ่านการยืนยันตัวตนแล้ว (IsAuthenticated)
|
allowed_roles = [ROLES["ADMIN"], ROLES["OPERATOR"]]
|
||||||
# และเป็น Viewer ขึ้นไป
|
|
||||||
# ----------------------------------------------------
|
|
||||||
# ไม่จำเป็นต้องสร้างคลาสนี้ถ้าใช้ DRF's IsAuthenticated โดยตรง
|
class IsViewerOrHigher(RolePermission):
|
||||||
# แต่ถ้าต้องการเพิ่ม Logic อื่นๆ ในอนาคต ควรใช้คลาสนี้
|
allowed_roles = [
|
||||||
class IsAuthenticatedViewer(permissions.BasePermission):
|
ROLES["ADMIN"],
|
||||||
|
ROLES["OPERATOR"],
|
||||||
|
ROLES["VIEWER"],
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class IsAuthenticatedAccess(permissions.IsAuthenticated):
|
||||||
message = "คุณต้องเข้าสู่ระบบก่อน"
|
message = "คุณต้องเข้าสู่ระบบก่อน"
|
||||||
|
|
||||||
def has_permission(self, request, view):
|
|
||||||
return request.user.is_authenticated
|
|
||||||
@ -1,35 +1,37 @@
|
|||||||
// src/components/SidebarSubmenu.jsx (ปรับปรุงสำหรับ RBAC)
|
|
||||||
|
|
||||||
import ChevronDownIcon from '@heroicons/react/24/outline/ChevronDownIcon';
|
import ChevronDownIcon from '@heroicons/react/24/outline/ChevronDownIcon';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { NavLink, useLocation } from 'react-router-dom';
|
import { NavLink, useLocation } from 'react-router-dom';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
|
|
||||||
// Component นี้คาดหวังว่าฟังก์ชัน canAccess จะถูกส่งมาจาก MainLayout หรือสร้างขึ้นใหม่
|
|
||||||
|
|
||||||
function SidebarSubmenu({ submenu, name, icon, closeMobileSidebar }) {
|
function SidebarSubmenu({ submenu, name, icon, closeMobileSidebar }) {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const [isExpanded, setIsExpanded] = useState(false);
|
const [isExpanded, setIsExpanded] = useState(false);
|
||||||
const role = useSelector(state => state.auth.role); // ดึง Role จาก Redux
|
// ดึง Role จาก Redux
|
||||||
|
const userRole = useSelector(state => state.auth.role);
|
||||||
|
|
||||||
// ฟังก์ชันตรวจสอบสิทธิ์ (ควรมาจาก MainLayout แต่เราจะใช้ Logic พื้นฐาน)
|
// ฟังก์ชันตรวจสอบสิทธิ์
|
||||||
const canAccess = (requiredRole) => {
|
const canAccess = (requiredRole) => {
|
||||||
if (role === 'admin') return true;
|
// userRole ที่มาจาก Redux ควรเป็นตัวพิมพ์ใหญ่แล้ว
|
||||||
if (!requiredRole) return true;
|
if (!requiredRole) return true;
|
||||||
const allowedRoles = Array.isArray(requiredRole) ? requiredRole : [requiredRole];
|
|
||||||
return allowedRoles.includes(role);
|
const allowedRoles = Array.isArray(requiredRole)
|
||||||
|
? requiredRole
|
||||||
|
: [requiredRole];
|
||||||
|
|
||||||
|
return allowedRoles.includes(userRole);
|
||||||
};
|
};
|
||||||
|
|
||||||
// เปิด Submenu หาก Path ปัจจุบันอยู่ใน Submenu นั้น
|
// เปิด Submenu หาก Path ปัจจุบันอยู่ใน Submenu นั้น
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// ตรวจสอบว่ามีเมนูย่อยใดที่ผู้ใช้เข้าถึงได้และเป็น Path ปัจจุบันหรือไม่
|
// ตรวจสอบว่ามีเมนูย่อยใดที่ผู้ใช้เข้าถึงได้และเป็น Path ปัจจุบันหรือไม่
|
||||||
const isActive = submenu.some(
|
const isActive = submenu.some(
|
||||||
|
// ใช้ userRole ใน canAccess
|
||||||
m => canAccess(m.requiredRole) && m.path === location.pathname
|
m => canAccess(m.requiredRole) && m.path === location.pathname
|
||||||
);
|
);
|
||||||
if (isActive) {
|
if (isActive) {
|
||||||
setIsExpanded(true);
|
setIsExpanded(true);
|
||||||
}
|
}
|
||||||
}, [location.pathname, submenu, role]); // เพิ่ม role ใน dependency array
|
}, [location.pathname, submenu, userRole]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-col'>
|
<div className='flex flex-col'>
|
||||||
|
|||||||
@ -5,6 +5,7 @@ const routes = [
|
|||||||
path: '/dashboard',
|
path: '/dashboard',
|
||||||
icon: <FaTachometerAlt className="w-5 h-5 flex-shrink-0" />,
|
icon: <FaTachometerAlt className="w-5 h-5 flex-shrink-0" />,
|
||||||
name: 'แดชบอร์ด/ภาพรวม',
|
name: 'แดชบอร์ด/ภาพรวม',
|
||||||
|
requiredRole: ['VIEWER', 'OPERATOR', 'ADMIN'],
|
||||||
},
|
},
|
||||||
|
|
||||||
// ----------------================--
|
// ----------------================--
|
||||||
@ -14,17 +15,17 @@ const routes = [
|
|||||||
path: '',
|
path: '',
|
||||||
icon: <FaFlask className="w-5 h-5 flex-shrink-0" />,
|
icon: <FaFlask className="w-5 h-5 flex-shrink-0" />,
|
||||||
name: 'AI Model Operations',
|
name: 'AI Model Operations',
|
||||||
requiredRole: ['viewer', 'admin', 'manager'],
|
requiredRole: ['VIEWER', 'OPERATOR', 'ADMIN'],
|
||||||
submenu: [
|
submenu: [
|
||||||
{
|
{
|
||||||
path: '/dashboard/inference-run',
|
path: '/dashboard/inference-run',
|
||||||
name: 'AI Inference (Run)',
|
name: 'AI Inference (Run)',
|
||||||
requiredRole: ['viewer', 'admin', 'manager'],
|
requiredRole: ['VIEWER', 'OPERATOR', 'ADMIN'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/dashboard/model-registry',
|
path: '/dashboard/model-registry',
|
||||||
name: 'Model Registry & Control',
|
name: 'Model Registry & Control',
|
||||||
requiredRole: ['manager', 'admin'],
|
requiredRole: ['OPERATOR', 'ADMIN'],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -34,7 +35,7 @@ const routes = [
|
|||||||
path: '/dashboard/profile',
|
path: '/dashboard/profile',
|
||||||
icon: <FaUserCircle className="w-5 h-5 flex-shrink-0" />,
|
icon: <FaUserCircle className="w-5 h-5 flex-shrink-0" />,
|
||||||
name: 'การจัดการโปรไฟล์',
|
name: 'การจัดการโปรไฟล์',
|
||||||
requiredRole: ['viewer', 'admin', 'manager'], // ทุก Role ควรเข้าถึงได้
|
requiredRole: ['VIEWER', 'OPERATOR', 'ADMIN'], // ทุก Role ควรเข้าถึงได้
|
||||||
},
|
},
|
||||||
|
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
@ -44,13 +45,13 @@ const routes = [
|
|||||||
path: '/dashboard/health',
|
path: '/dashboard/health',
|
||||||
icon: <FaHeartbeat className="w-5 h-5 flex-shrink-0" />,
|
icon: <FaHeartbeat className="w-5 h-5 flex-shrink-0" />,
|
||||||
name: 'สถานะระบบ (Health)',
|
name: 'สถานะระบบ (Health)',
|
||||||
requiredRole: ['viewer', 'admin', 'manager'],
|
requiredRole: ['VIEWER', 'OPERATOR', 'ADMIN'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/dashboard/guide',
|
path: '/dashboard/guide',
|
||||||
icon: <FaCog className="w-5 h-5 flex-shrink-0" />,
|
icon: <FaCog className="w-5 h-5 flex-shrink-0" />,
|
||||||
name: 'คู่มือการใช้งาน (Guide)',
|
name: 'คู่มือการใช้งาน (Guide)',
|
||||||
requiredRole: ['viewer', 'manager', 'admin'],
|
requiredRole: ['VIEWER', 'OPERATOR', 'ADMIN'],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@ -5,23 +5,24 @@ import { createSlice } from '@reduxjs/toolkit';
|
|||||||
// ----------------------------------------------------
|
// ----------------------------------------------------
|
||||||
const determineRole = (user) => {
|
const determineRole = (user) => {
|
||||||
if (!user || !user.id) {
|
if (!user || !user.id) {
|
||||||
return 'guest';
|
return 'GUEST';
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. ใช้ฟิลด์ 'role' ที่ส่งมาจาก Backend โดยตรง (ถ้ามี)
|
// 1. ใช้ฟิลด์ 'role' ที่ส่งมาจาก Backend โดยตรง
|
||||||
if (user.role) {
|
if (user.role) {
|
||||||
return user.role.toLowerCase(); // เช่น 'ADMIN' -> 'admin'
|
return user.role.toUpperCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. ใช้ฟิลด์ is_superuser/is_staff เป็น Fallback
|
// 2. ใช้ฟิลด์ is_superuser/is_staff เป็น Fallback
|
||||||
if (user.is_superuser) {
|
if (user.is_superuser) {
|
||||||
return 'admin';
|
return 'ADMIN'; //
|
||||||
}
|
}
|
||||||
if (user.is_staff) {
|
if (user.is_staff) {
|
||||||
return 'manager';
|
return 'OPERATOR';
|
||||||
}
|
}
|
||||||
|
|
||||||
// ผู้ใช้ทั่วไป
|
// ผู้ใช้ทั่วไป
|
||||||
return 'viewer';
|
return 'VIEWER';
|
||||||
};
|
};
|
||||||
|
|
||||||
// ----------------------------------------------------
|
// ----------------------------------------------------
|
||||||
@ -29,15 +30,14 @@ const determineRole = (user) => {
|
|||||||
// ----------------------------------------------------
|
// ----------------------------------------------------
|
||||||
const getInitialAuthState = () => {
|
const getInitialAuthState = () => {
|
||||||
const token = localStorage.getItem('token');
|
const token = localStorage.getItem('token');
|
||||||
|
// อ่าน user object
|
||||||
const user = JSON.parse(localStorage.getItem('user'));
|
const user = JSON.parse(localStorage.getItem('user'));
|
||||||
|
|
||||||
// ดึง Role
|
|
||||||
const role = determineRole(user);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isAuthenticated: !!token,
|
isAuthenticated: !!token,
|
||||||
user: user || null,
|
user: user || null,
|
||||||
role: role,
|
// คำนวณ Role จาก user object ที่โหลดมา เพื่อให้เป็น Single Source of Truth
|
||||||
|
role: determineRole(user),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -49,16 +49,17 @@ const authSlice = createSlice({
|
|||||||
reducers: {
|
reducers: {
|
||||||
// Reducer สำหรับการล็อกอินสำเร็จ
|
// Reducer สำหรับการล็อกอินสำเร็จ
|
||||||
loginSuccess: (state, action) => {
|
loginSuccess: (state, action) => {
|
||||||
|
const user = action.payload.user;
|
||||||
state.isAuthenticated = true;
|
state.isAuthenticated = true;
|
||||||
state.user = action.payload.user;
|
state.user = user;
|
||||||
state.role = action.payload.role;
|
state.role = determineRole(user); // คำนวณ role ทุกครั้ง
|
||||||
},
|
},
|
||||||
|
|
||||||
// Reducer สำหรับการออกจากระบบ
|
// Reducer สำหรับการออกจากระบบ
|
||||||
logout: (state) => {
|
logout: (state) => {
|
||||||
state.isAuthenticated = false;
|
state.isAuthenticated = false;
|
||||||
state.user = null;
|
state.user = null;
|
||||||
state.role = 'guest';
|
state.role = 'GUEST';
|
||||||
// ลบข้อมูลจาก localStorage
|
// ลบข้อมูลจาก localStorage
|
||||||
localStorage.removeItem('token');
|
localStorage.removeItem('token');
|
||||||
localStorage.removeItem('user');
|
localStorage.removeItem('user');
|
||||||
@ -66,10 +67,11 @@ const authSlice = createSlice({
|
|||||||
|
|
||||||
// Reducer สำหรับอัปเดตข้อมูลผู้ใช้ (เช่น หลังอัปเดตโปรไฟล์)
|
// Reducer สำหรับอัปเดตข้อมูลผู้ใช้ (เช่น หลังอัปเดตโปรไฟล์)
|
||||||
updateUser: (state, action) => {
|
updateUser: (state, action) => {
|
||||||
state.user = action.payload.user;
|
const user = action.payload.user;
|
||||||
state.role = action.payload.role; // อัปเดต Role ด้วย (เผื่อมีการอัปเดตสิทธิ์)
|
state.user = user;
|
||||||
localStorage.setItem('user', JSON.stringify(action.payload.user));
|
state.role = determineRole(user); // คำนวณ Role ใหม่จาก User Object ที่อัปเดต
|
||||||
localStorage.setItem('role', action.payload.role);
|
localStorage.setItem('user', JSON.stringify(user));
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,21 +1,20 @@
|
|||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
import axiosClient from './axiosClient';
|
import axiosClient from './axiosClient';
|
||||||
|
|
||||||
const STALE_TIME = 60000; // 1 นาที
|
const STALE_TIME = 60000;
|
||||||
|
|
||||||
/**
|
|
||||||
* Hook สำหรับดึงสรุปสถิติ Inference (Total Runs, Success Rate, Avg Latency)
|
|
||||||
* Endpoint: GET /api/v1/audit/inference-summary/
|
|
||||||
*/
|
|
||||||
export const useInferenceSummary = () => {
|
export const useInferenceSummary = () => {
|
||||||
|
const userRole = useSelector((state) => state.auth.userRole); // ดึง Role จาก Redux
|
||||||
|
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: ['inferenceSummary'],
|
queryKey: ['inferenceSummary', userRole], // แยก Cache ตาม Role
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const response = await axiosClient.get('/api/v1/audit/inference-summary/');
|
const res = await axiosClient.get('/api/v1/audit/summary/');
|
||||||
return response.data;
|
return res.data;
|
||||||
},
|
},
|
||||||
staleTime: STALE_TIME,
|
staleTime: STALE_TIME,
|
||||||
// ดึงข้อมูลใหม่ทุก 30 วินาทีเพื่ออัปเดตสถิติ Dashboard
|
refetchInterval: 30000, // อัปเดตทุก 30 วินาที
|
||||||
refetchInterval: 30000,
|
enabled: !!userRole, // เรียก API ก็ต่อเมื่อ Role พร้อม
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
Loading…
x
Reference in New Issue
Block a user