294 lines
12 KiB
TypeScript
294 lines
12 KiB
TypeScript
import React, { useState } from "react";
|
||
import {
|
||
Shield,
|
||
Refresh,
|
||
InfoCircle,
|
||
TickCircle,
|
||
CloseCircle
|
||
} from "iconsax-react";
|
||
import Button from "../../components/Button";
|
||
import Pagination from "../../components/Pagination";
|
||
import TitleLine from "../../components/TitleLine";
|
||
import PageLoading from "../../components/PageLoading";
|
||
import FilterPanel from "./components/FilterPanel";
|
||
import AccessLogsTable from "./components/AccessLogsTable";
|
||
import LogDetailsModal from "./components/LogDetailsModal";
|
||
import { useErrorLogs, useAccessLogDetail } from "./hooks/useAccessLogs";
|
||
import { AccessLog } from "./types/AccessLogTypes";
|
||
|
||
const PermissionLogs: React.FC = () => {
|
||
const {
|
||
errorLogs,
|
||
loading,
|
||
error,
|
||
total,
|
||
filters,
|
||
updateFilters,
|
||
refetch,
|
||
} = useErrorLogs();
|
||
|
||
const { log: selectedLog, fetchLog, clearLog } = useAccessLogDetail();
|
||
const [isDetailsModalOpen, setIsDetailsModalOpen] = useState(false);
|
||
|
||
const handleViewDetails = async (log: AccessLog) => {
|
||
await fetchLog(log.id);
|
||
setIsDetailsModalOpen(true);
|
||
};
|
||
|
||
const handleCloseDetailsModal = () => {
|
||
setIsDetailsModalOpen(false);
|
||
clearLog();
|
||
};
|
||
|
||
const handleSort = (field: string, order: "ASC" | "DESC") => {
|
||
updateFilters({ sortBy: field, sortOrder: order });
|
||
};
|
||
|
||
const handlePageChange = (page: number) => {
|
||
updateFilters({ page });
|
||
};
|
||
|
||
const resetFilters = () => {
|
||
updateFilters({
|
||
page: 1,
|
||
limit: 20,
|
||
sortBy: "createdAt",
|
||
sortOrder: "DESC",
|
||
operationType: undefined,
|
||
userType: undefined,
|
||
entityName: "",
|
||
entityId: "",
|
||
userId: "",
|
||
ipAddress: "",
|
||
endpoint: "",
|
||
httpMethod: undefined,
|
||
statusCode: undefined,
|
||
startDate: "",
|
||
endDate: "",
|
||
search: "",
|
||
minExecutionTime: undefined,
|
||
maxExecutionTime: undefined,
|
||
});
|
||
};
|
||
|
||
// Filter permission-related logs (assuming they have specific operation types or endpoints)
|
||
const permissionLogs = (errorLogs || []).filter((log: AccessLog) => {
|
||
return log.operationType?.includes('permission') ||
|
||
log.endpoint?.includes('permission') ||
|
||
log.entityName?.includes('role') ||
|
||
log.entityName?.includes('permission');
|
||
});
|
||
|
||
// Count permission actions
|
||
const permissionStats = permissionLogs.reduce((acc: Record<string, number>, log: AccessLog) => {
|
||
const action = log.operationType || 'unknown';
|
||
acc[action] = (acc[action] || 0) + 1;
|
||
return acc;
|
||
}, {} as Record<string, number>);
|
||
|
||
// Count recent permission changes (last 24 hours)
|
||
const recentPermissions = permissionLogs.filter((log: AccessLog) => {
|
||
const logDate = new Date(log.createdAt);
|
||
const oneDayAgo = new Date(Date.now() - 24 * 60 * 60 * 1000);
|
||
return logDate > oneDayAgo;
|
||
}).length;
|
||
|
||
if (error) {
|
||
return (
|
||
<div className="p-6">
|
||
<TitleLine title="لاگهای مجوز" />
|
||
<div className="bg-red-50 border border-red-200 rounded-lg p-4">
|
||
<div className="flex items-center justify-between">
|
||
<p className="text-red-800">{error}</p>
|
||
<Button
|
||
onClick={refetch}
|
||
className="bg-red-600 text-white hover:bg-red-700"
|
||
>
|
||
تلاش مجدد
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<div className="p-6 space-y-6">
|
||
{/* Header */}
|
||
<div className="flex items-center justify-between">
|
||
<div>
|
||
<TitleLine title="لاگهای مجوز" />
|
||
<p className="text-gray-600 mt-2">
|
||
مشاهده و تحلیل تغییرات مجوزها و نقشهای کاربری
|
||
</p>
|
||
</div>
|
||
|
||
<div className="flex items-center gap-3">
|
||
<Button
|
||
onClick={refetch}
|
||
disabled={loading}
|
||
className="flex items-center gap-2 border border-gray-300 bg-white text-gray-700 hover:bg-gray-50"
|
||
>
|
||
<Refresh size={16} className={loading ? "animate-spin" : ""} />
|
||
بروزرسانی
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Permission Stats */}
|
||
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-4">
|
||
<div className="flex items-center gap-3">
|
||
<div className="flex-shrink-0 p-2 bg-blue-100 rounded-lg">
|
||
<Shield size={20} className="text-blue-600" />
|
||
</div>
|
||
<div>
|
||
<p className="text-sm font-medium text-gray-600">کل تغییرات مجوز</p>
|
||
<p className="text-xl font-bold text-gray-900">
|
||
{permissionLogs.length.toLocaleString('fa-IR')}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-4">
|
||
<div className="flex items-center gap-3">
|
||
<div className="flex-shrink-0 p-2 bg-green-100 rounded-lg">
|
||
<TickCircle size={20} className="text-green-600" />
|
||
</div>
|
||
<div>
|
||
<p className="text-sm font-medium text-gray-600">مجوزهای اعطا شده</p>
|
||
<p className="text-xl font-bold text-gray-900">
|
||
{(permissionStats['grant'] || 0).toLocaleString('fa-IR')}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-4">
|
||
<div className="flex items-center gap-3">
|
||
<div className="flex-shrink-0 p-2 bg-red-100 rounded-lg">
|
||
<CloseCircle size={20} className="text-red-600" />
|
||
</div>
|
||
<div>
|
||
<p className="text-sm font-medium text-gray-600">مجوزهای لغو شده</p>
|
||
<p className="text-xl font-bold text-gray-900">
|
||
{(permissionStats['revoke'] || 0).toLocaleString('fa-IR')}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-4">
|
||
<div className="flex items-center gap-3">
|
||
<div className="flex-shrink-0 p-2 bg-yellow-100 rounded-lg">
|
||
<InfoCircle size={20} className="text-yellow-600" />
|
||
</div>
|
||
<div>
|
||
<p className="text-sm font-medium text-gray-600">24 ساعت گذشته</p>
|
||
<p className="text-xl font-bold text-gray-900">
|
||
{recentPermissions.toLocaleString('fa-IR')}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Security Alert */}
|
||
{recentPermissions > 5 && (
|
||
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-4">
|
||
<div className="flex items-center gap-3">
|
||
<Shield size={20} className="text-yellow-600" />
|
||
<div>
|
||
<h3 className="text-sm font-medium text-yellow-900">تغییرات زیاد مجوز</h3>
|
||
<p className="text-sm text-yellow-700 mt-1">
|
||
{recentPermissions} تغییر مجوز در 24 ساعت گذشته رخ داده است.
|
||
لطفاً این تغییرات را بررسی کنید.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* Filter Panel */}
|
||
<FilterPanel
|
||
filters={filters}
|
||
onFiltersChange={updateFilters}
|
||
onReset={resetFilters}
|
||
loading={loading}
|
||
/>
|
||
|
||
{/* Loading State */}
|
||
{loading && <PageLoading />}
|
||
|
||
{/* Table */}
|
||
{!loading && (
|
||
<>
|
||
<AccessLogsTable
|
||
logs={permissionLogs}
|
||
loading={loading}
|
||
onViewDetails={handleViewDetails}
|
||
onSort={handleSort}
|
||
sortBy={filters.sortBy}
|
||
sortOrder={filters.sortOrder}
|
||
/>
|
||
|
||
{/* Pagination */}
|
||
{total > (filters.limit || 20) && (
|
||
<div className="flex justify-center">
|
||
<Pagination
|
||
currentPage={filters.page || 1}
|
||
totalPages={Math.ceil(total / (filters.limit || 20))}
|
||
onPageChange={handlePageChange}
|
||
/>
|
||
</div>
|
||
)}
|
||
</>
|
||
)}
|
||
|
||
{/* Details Modal */}
|
||
<LogDetailsModal
|
||
log={selectedLog}
|
||
isOpen={isDetailsModalOpen}
|
||
onClose={handleCloseDetailsModal}
|
||
/>
|
||
|
||
{/* Empty State Info */}
|
||
{!loading && (!permissionLogs || permissionLogs.length === 0) && !error && (
|
||
<div className="bg-green-50 border border-green-200 rounded-lg p-6">
|
||
<div className="flex items-center gap-3">
|
||
<InfoCircle size={24} className="text-green-600" />
|
||
<div>
|
||
<h3 className="text-lg font-medium text-green-900">هیچ تغییر مجوزی یافت نشد</h3>
|
||
<p className="text-green-700 mt-1">
|
||
در بازه زمانی و فیلترهای انتخاب شده، هیچ تغییر مجوزی ثبت نشده است.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* Permission Analysis Tips */}
|
||
{!loading && permissionLogs && permissionLogs.length > 0 && (
|
||
<div className="bg-blue-50 border border-blue-200 rounded-lg p-6">
|
||
<div className="flex items-start gap-3">
|
||
<InfoCircle size={24} className="text-blue-600 flex-shrink-0 mt-0.5" />
|
||
<div>
|
||
<h3 className="text-lg font-medium text-blue-900 mb-2">راهنمای تحلیل مجوزها</h3>
|
||
<div className="text-blue-700 space-y-2">
|
||
<p>• <strong>تغییرات نقش:</strong> بررسی تغییرات نقشهای کاربری و مجوزهای مربوطه</p>
|
||
<p>• <strong>دسترسیهای حساس:</strong> نظارت بر دسترسیهای مهم و حساس سیستم</p>
|
||
<p>• <strong>الگوهای دسترسی:</strong> شناسایی الگوهای غیرعادی در دسترسیها</p>
|
||
<p>• <strong>زمانبندی:</strong> توجه به زمان تغییرات مجوزها</p>
|
||
<p>• <strong>کاربران:</strong> شناسایی کاربرانی که تغییرات مجوز زیادی دارند</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default PermissionLogs;
|