mirror of
https://github.com/fosrl/pangolin.git
synced 2026-03-03 17:26:38 +00:00
Filtering on all tables
This commit is contained in:
@@ -1920,5 +1920,6 @@
|
|||||||
"reason": "Reason",
|
"reason": "Reason",
|
||||||
"requestLogs": "Request Logs",
|
"requestLogs": "Request Logs",
|
||||||
"host": "Host",
|
"host": "Host",
|
||||||
"location": "Location"
|
"location": "Location",
|
||||||
|
"actionLogs": "Action Logs"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ export const queryAccessAuditLogsQuery = z.object({
|
|||||||
.optional(),
|
.optional(),
|
||||||
actor: z.string().optional(),
|
actor: z.string().optional(),
|
||||||
type: z.string().optional(),
|
type: z.string().optional(),
|
||||||
|
location: z.string().optional(),
|
||||||
limit: z
|
limit: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
@@ -91,6 +92,7 @@ function getWhere(data: Q) {
|
|||||||
? eq(accessAuditLog.actorType, data.actorType)
|
? eq(accessAuditLog.actorType, data.actorType)
|
||||||
: undefined,
|
: undefined,
|
||||||
data.actorId ? eq(accessAuditLog.actorId, data.actorId) : undefined,
|
data.actorId ? eq(accessAuditLog.actorId, data.actorId) : undefined,
|
||||||
|
data.location ? eq(accessAuditLog.location, data.location) : undefined,
|
||||||
data.type ? eq(accessAuditLog.type, data.type) : undefined,
|
data.type ? eq(accessAuditLog.type, data.type) : undefined,
|
||||||
data.action !== undefined
|
data.action !== undefined
|
||||||
? eq(accessAuditLog.action, data.action)
|
? eq(accessAuditLog.action, data.action)
|
||||||
|
|||||||
@@ -103,6 +103,38 @@ export function countActionQuery(data: Q) {
|
|||||||
return countQuery;
|
return countQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function queryUniqueFilterAttributes(
|
||||||
|
timeStart: number,
|
||||||
|
timeEnd: number,
|
||||||
|
orgId: string
|
||||||
|
) {
|
||||||
|
const baseConditions = and(
|
||||||
|
gt(actionAuditLog.timestamp, timeStart),
|
||||||
|
lt(actionAuditLog.timestamp, timeEnd),
|
||||||
|
eq(actionAuditLog.orgId, orgId)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get unique actors
|
||||||
|
const uniqueActors = await db
|
||||||
|
.selectDistinct({
|
||||||
|
actor: actionAuditLog.actor
|
||||||
|
})
|
||||||
|
.from(actionAuditLog)
|
||||||
|
.where(baseConditions);
|
||||||
|
|
||||||
|
const uniqueActions = await db
|
||||||
|
.selectDistinct({
|
||||||
|
action: actionAuditLog.action
|
||||||
|
})
|
||||||
|
.from(actionAuditLog)
|
||||||
|
.where(baseConditions);
|
||||||
|
|
||||||
|
return {
|
||||||
|
actors: uniqueActors.map(row => row.actor).filter((actor): actor is string => actor !== null),
|
||||||
|
actions: uniqueActions.map(row => row.action).filter((action): action is string => action !== null),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
registry.registerPath({
|
registry.registerPath({
|
||||||
method: "get",
|
method: "get",
|
||||||
path: "/org/{orgId}/logs/action",
|
path: "/org/{orgId}/logs/action",
|
||||||
@@ -149,6 +181,12 @@ export async function queryActionAuditLogs(
|
|||||||
const totalCountResult = await countActionQuery(data);
|
const totalCountResult = await countActionQuery(data);
|
||||||
const totalCount = totalCountResult[0].count;
|
const totalCount = totalCountResult[0].count;
|
||||||
|
|
||||||
|
const filterAttributes = await queryUniqueFilterAttributes(
|
||||||
|
data.timeStart,
|
||||||
|
data.timeEnd,
|
||||||
|
data.orgId
|
||||||
|
);
|
||||||
|
|
||||||
return response<QueryActionAuditLogResponse>(res, {
|
return response<QueryActionAuditLogResponse>(res, {
|
||||||
data: {
|
data: {
|
||||||
log: log,
|
log: log,
|
||||||
@@ -156,7 +194,8 @@ export async function queryActionAuditLogs(
|
|||||||
total: totalCount,
|
total: totalCount,
|
||||||
limit: data.limit,
|
limit: data.limit,
|
||||||
offset: data.offset
|
offset: data.offset
|
||||||
}
|
},
|
||||||
|
filterAttributes
|
||||||
},
|
},
|
||||||
success: true,
|
success: true,
|
||||||
error: false,
|
error: false,
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ export const queryAccessAuditLogsQuery = z.object({
|
|||||||
.pipe(z.number().int().positive())
|
.pipe(z.number().int().positive())
|
||||||
.optional(),
|
.optional(),
|
||||||
actor: z.string().optional(),
|
actor: z.string().optional(),
|
||||||
|
location: z.string().optional(),
|
||||||
host: z.string().optional(),
|
host: z.string().optional(),
|
||||||
path: z.string().optional(),
|
path: z.string().optional(),
|
||||||
limit: z
|
limit: z
|
||||||
@@ -82,6 +83,7 @@ function getWhere(data: Q) {
|
|||||||
data.method ? eq(requestAuditLog.method, data.method) : undefined,
|
data.method ? eq(requestAuditLog.method, data.method) : undefined,
|
||||||
data.reason ? eq(requestAuditLog.reason, data.reason) : undefined,
|
data.reason ? eq(requestAuditLog.reason, data.reason) : undefined,
|
||||||
data.host ? eq(requestAuditLog.host, data.host) : undefined,
|
data.host ? eq(requestAuditLog.host, data.host) : undefined,
|
||||||
|
data.location ? eq(requestAuditLog.location, data.location) : undefined,
|
||||||
data.path ? eq(requestAuditLog.path, data.path) : undefined,
|
data.path ? eq(requestAuditLog.path, data.path) : undefined,
|
||||||
data.action !== undefined
|
data.action !== undefined
|
||||||
? eq(requestAuditLog.action, data.action)
|
? eq(requestAuditLog.action, data.action)
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ export type QueryActionAuditLogResponse = {
|
|||||||
limit: number;
|
limit: number;
|
||||||
offset: number;
|
offset: number;
|
||||||
};
|
};
|
||||||
|
filterAttributes: {
|
||||||
|
actors: string[];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export type QueryRequestAuditLogResponse = {
|
export type QueryRequestAuditLogResponse = {
|
||||||
|
|||||||
@@ -80,10 +80,12 @@ async function makeApiRequest<T>(
|
|||||||
|
|
||||||
const headersList = await reqHeaders();
|
const headersList = await reqHeaders();
|
||||||
const host = headersList.get("host");
|
const host = headersList.get("host");
|
||||||
|
const xForwardedFor = headersList.get("x-forwarded-for");
|
||||||
|
|
||||||
const headers: Record<string, string> = {
|
const headers: Record<string, string> = {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
"X-CSRF-Token": "x-csrf-protection",
|
"X-CSRF-Token": "x-csrf-protection",
|
||||||
|
...(xForwardedFor ? { "X-Forwarded-For": xForwardedFor } : {}),
|
||||||
...(cookieHeader && { Cookie: cookieHeader }),
|
...(cookieHeader && { Cookie: cookieHeader }),
|
||||||
...additionalHeaders
|
...additionalHeaders
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -142,8 +142,6 @@ export default function GeneralPage() {
|
|||||||
filterType: keyof typeof filters,
|
filterType: keyof typeof filters,
|
||||||
value: string | undefined
|
value: string | undefined
|
||||||
) => {
|
) => {
|
||||||
console.log(`${filterType} filter changed:`, value);
|
|
||||||
|
|
||||||
// Create new filters object with updated value
|
// Create new filters object with updated value
|
||||||
const newFilters = {
|
const newFilters = {
|
||||||
...filters,
|
...filters,
|
||||||
@@ -193,6 +191,9 @@ export default function GeneralPage() {
|
|||||||
filtersParam?: {
|
filtersParam?: {
|
||||||
action?: string;
|
action?: string;
|
||||||
type?: string;
|
type?: string;
|
||||||
|
resourceId?: string;
|
||||||
|
location?: string;
|
||||||
|
actor?: string;
|
||||||
}
|
}
|
||||||
) => {
|
) => {
|
||||||
console.log("Date range changed:", { startDate, endDate, page, size });
|
console.log("Date range changed:", { startDate, endDate, page, size });
|
||||||
@@ -403,7 +404,7 @@ export default function GeneralPage() {
|
|||||||
<span className="flex items-center gap-1">
|
<span className="flex items-center gap-1">
|
||||||
{row.original.location ? (
|
{row.original.location ? (
|
||||||
<span className="text-muted-foreground text-xs">
|
<span className="text-muted-foreground text-xs">
|
||||||
({row.original.location})
|
{row.original.location}
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<span className="text-muted-foreground text-xs">
|
<span className="text-muted-foreground text-xs">
|
||||||
@@ -482,7 +483,12 @@ export default function GeneralPage() {
|
|||||||
},
|
},
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
// should be capitalized first letter
|
// should be capitalized first letter
|
||||||
return <span>{row.original.type.charAt(0).toUpperCase() + row.original.type.slice(1) || "-"}</span>;
|
return (
|
||||||
|
<span>
|
||||||
|
{row.original.type.charAt(0).toUpperCase() +
|
||||||
|
row.original.type.slice(1) || "-"}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,12 +4,13 @@ import { toast } from "@app/hooks/useToast";
|
|||||||
import { useState, useRef, useEffect } from "react";
|
import { useState, useRef, useEffect } from "react";
|
||||||
import { createApiClient } from "@app/lib/api";
|
import { createApiClient } from "@app/lib/api";
|
||||||
import { useEnvContext } from "@app/hooks/useEnvContext";
|
import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||||
import { useParams, useRouter } from "next/navigation";
|
import { useParams, useRouter, useSearchParams } from "next/navigation";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { LogDataTable } from "@app/components/LogDataTable";
|
import { LogDataTable } from "@app/components/LogDataTable";
|
||||||
import { ColumnDef } from "@tanstack/react-table";
|
import { ColumnDef } from "@tanstack/react-table";
|
||||||
import { DateTimeValue } from "@app/components/DateTimePicker";
|
import { DateTimeValue } from "@app/components/DateTimePicker";
|
||||||
import { Key, User } from "lucide-react";
|
import { Key, User } from "lucide-react";
|
||||||
|
import { ColumnFilter } from "@app/components/ColumnFilter";
|
||||||
|
|
||||||
export default function GeneralPage() {
|
export default function GeneralPage() {
|
||||||
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
||||||
@@ -18,10 +19,27 @@ export default function GeneralPage() {
|
|||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
const { env } = useEnvContext();
|
const { env } = useEnvContext();
|
||||||
const { orgId } = useParams();
|
const { orgId } = useParams();
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
|
||||||
const [rows, setRows] = useState<any[]>([]);
|
const [rows, setRows] = useState<any[]>([]);
|
||||||
const [isRefreshing, setIsRefreshing] = useState(false);
|
const [isRefreshing, setIsRefreshing] = useState(false);
|
||||||
const [isExporting, setIsExporting] = useState(false);
|
const [isExporting, setIsExporting] = useState(false);
|
||||||
|
const [filterAttributes, setFilterAttributes] = useState<{
|
||||||
|
actors: string[];
|
||||||
|
actions: string[];
|
||||||
|
}>({
|
||||||
|
actors: [],
|
||||||
|
actions: []
|
||||||
|
});
|
||||||
|
|
||||||
|
// Filter states - unified object for all filters
|
||||||
|
const [filters, setFilters] = useState<{
|
||||||
|
action?: string;
|
||||||
|
actor?: string;
|
||||||
|
}>({
|
||||||
|
action: searchParams.get("action") || undefined,
|
||||||
|
actor: searchParams.get("actor") || undefined
|
||||||
|
});
|
||||||
|
|
||||||
// Pagination state
|
// Pagination state
|
||||||
const [totalCount, setTotalCount] = useState<number>(0);
|
const [totalCount, setTotalCount] = useState<number>(0);
|
||||||
@@ -31,6 +49,20 @@ export default function GeneralPage() {
|
|||||||
|
|
||||||
// Set default date range to last 24 hours
|
// Set default date range to last 24 hours
|
||||||
const getDefaultDateRange = () => {
|
const getDefaultDateRange = () => {
|
||||||
|
// if the time is in the url params, use that instead
|
||||||
|
const startParam = searchParams.get("start");
|
||||||
|
const endParam = searchParams.get("end");
|
||||||
|
if (startParam && endParam) {
|
||||||
|
return {
|
||||||
|
startDate: {
|
||||||
|
date: new Date(startParam)
|
||||||
|
},
|
||||||
|
endDate: {
|
||||||
|
date: new Date(endParam)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const yesterday = new Date(now.getTime() - 24 * 60 * 60 * 1000);
|
const yesterday = new Date(now.getTime() - 24 * 60 * 60 * 1000);
|
||||||
|
|
||||||
@@ -52,7 +84,12 @@ export default function GeneralPage() {
|
|||||||
// Trigger search with default values on component mount
|
// Trigger search with default values on component mount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const defaultRange = getDefaultDateRange();
|
const defaultRange = getDefaultDateRange();
|
||||||
queryDateTime(defaultRange.startDate, defaultRange.endDate);
|
queryDateTime(
|
||||||
|
defaultRange.startDate,
|
||||||
|
defaultRange.endDate,
|
||||||
|
0,
|
||||||
|
pageSize
|
||||||
|
);
|
||||||
}, [orgId]); // Re-run if orgId changes
|
}, [orgId]); // Re-run if orgId changes
|
||||||
|
|
||||||
const handleDateRangeChange = (
|
const handleDateRangeChange = (
|
||||||
@@ -61,6 +98,12 @@ export default function GeneralPage() {
|
|||||||
) => {
|
) => {
|
||||||
setDateRange({ startDate, endDate });
|
setDateRange({ startDate, endDate });
|
||||||
setCurrentPage(0); // Reset to first page when filtering
|
setCurrentPage(0); // Reset to first page when filtering
|
||||||
|
// put the search params in the url for the time
|
||||||
|
updateUrlParamsForAllFilters({
|
||||||
|
start: startDate.date?.toISOString() || "",
|
||||||
|
end: endDate.date?.toISOString() || ""
|
||||||
|
});
|
||||||
|
|
||||||
queryDateTime(startDate, endDate, 0, pageSize);
|
queryDateTime(startDate, endDate, 0, pageSize);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -82,20 +125,74 @@ export default function GeneralPage() {
|
|||||||
queryDateTime(dateRange.startDate, dateRange.endDate, 0, newPageSize);
|
queryDateTime(dateRange.startDate, dateRange.endDate, 0, newPageSize);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Handle filter changes generically
|
||||||
|
const handleFilterChange = (
|
||||||
|
filterType: keyof typeof filters,
|
||||||
|
value: string | undefined
|
||||||
|
) => {
|
||||||
|
// Create new filters object with updated value
|
||||||
|
const newFilters = {
|
||||||
|
...filters,
|
||||||
|
[filterType]: value
|
||||||
|
};
|
||||||
|
|
||||||
|
setFilters(newFilters);
|
||||||
|
setCurrentPage(0); // Reset to first page when filtering
|
||||||
|
|
||||||
|
// Update URL params
|
||||||
|
updateUrlParamsForAllFilters(newFilters);
|
||||||
|
|
||||||
|
// Trigger new query with updated filters (pass directly to avoid async state issues)
|
||||||
|
queryDateTime(
|
||||||
|
dateRange.startDate,
|
||||||
|
dateRange.endDate,
|
||||||
|
0,
|
||||||
|
pageSize,
|
||||||
|
newFilters
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateUrlParamsForAllFilters = (
|
||||||
|
newFilters:
|
||||||
|
| typeof filters
|
||||||
|
| {
|
||||||
|
start: string;
|
||||||
|
end: string;
|
||||||
|
}
|
||||||
|
) => {
|
||||||
|
const params = new URLSearchParams(searchParams);
|
||||||
|
Object.entries(newFilters).forEach(([key, value]) => {
|
||||||
|
if (value) {
|
||||||
|
params.set(key, value);
|
||||||
|
} else {
|
||||||
|
params.delete(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
router.replace(`?${params.toString()}`, { scroll: false });
|
||||||
|
};
|
||||||
|
|
||||||
const queryDateTime = async (
|
const queryDateTime = async (
|
||||||
startDate: DateTimeValue,
|
startDate: DateTimeValue,
|
||||||
endDate: DateTimeValue,
|
endDate: DateTimeValue,
|
||||||
page: number = currentPage,
|
page: number = currentPage,
|
||||||
size: number = pageSize
|
size: number = pageSize,
|
||||||
|
filtersParam?: {
|
||||||
|
action?: string;
|
||||||
|
actor?: string;
|
||||||
|
}
|
||||||
) => {
|
) => {
|
||||||
console.log("Date range changed:", { startDate, endDate, page, size });
|
console.log("Date range changed:", { startDate, endDate, page, size });
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Use the provided filters or fall back to current state
|
||||||
|
const activeFilters = filtersParam || filters;
|
||||||
|
|
||||||
// Convert the date/time values to API parameters
|
// Convert the date/time values to API parameters
|
||||||
let params: any = {
|
let params: any = {
|
||||||
limit: size,
|
limit: size,
|
||||||
offset: page * size
|
offset: page * size,
|
||||||
|
...activeFilters
|
||||||
};
|
};
|
||||||
|
|
||||||
if (startDate?.date) {
|
if (startDate?.date) {
|
||||||
@@ -133,6 +230,7 @@ export default function GeneralPage() {
|
|||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
setRows(res.data.data.log || []);
|
setRows(res.data.data.log || []);
|
||||||
setTotalCount(res.data.data.pagination?.total || 0);
|
setTotalCount(res.data.data.pagination?.total || 0);
|
||||||
|
setFilterAttributes(res.data.data.filterAttributes);
|
||||||
console.log("Fetched logs:", res.data);
|
console.log("Fetched logs:", res.data);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -171,16 +269,21 @@ export default function GeneralPage() {
|
|||||||
const exportData = async () => {
|
const exportData = async () => {
|
||||||
try {
|
try {
|
||||||
setIsExporting(true);
|
setIsExporting(true);
|
||||||
|
|
||||||
|
// Prepare query params for export
|
||||||
|
let params: any = {
|
||||||
|
timeStart: dateRange.startDate?.date
|
||||||
|
? new Date(dateRange.startDate.date).toISOString()
|
||||||
|
: undefined,
|
||||||
|
timeEnd: dateRange.endDate?.date
|
||||||
|
? new Date(dateRange.endDate.date).toISOString()
|
||||||
|
: undefined,
|
||||||
|
...filters
|
||||||
|
};
|
||||||
|
|
||||||
const response = await api.get(`/org/${orgId}/logs/action/export`, {
|
const response = await api.get(`/org/${orgId}/logs/action/export`, {
|
||||||
responseType: "blob",
|
responseType: "blob",
|
||||||
params: {
|
params
|
||||||
timeStart: dateRange.startDate?.date
|
|
||||||
? new Date(dateRange.startDate.date).toISOString()
|
|
||||||
: undefined,
|
|
||||||
timeEnd: dateRange.endDate?.date
|
|
||||||
? new Date(dateRange.endDate.date).toISOString()
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create a URL for the blob and trigger a download
|
// Create a URL for the blob and trigger a download
|
||||||
@@ -224,9 +327,27 @@ export default function GeneralPage() {
|
|||||||
{
|
{
|
||||||
accessorKey: "action",
|
accessorKey: "action",
|
||||||
header: ({ column }) => {
|
header: ({ column }) => {
|
||||||
return t("action");
|
return (
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span>{t("action")}</span>
|
||||||
|
<ColumnFilter
|
||||||
|
options={filterAttributes.actions.map((action) => ({
|
||||||
|
label:
|
||||||
|
action.charAt(0).toUpperCase() +
|
||||||
|
action.slice(1),
|
||||||
|
value: action
|
||||||
|
}))}
|
||||||
|
selectedValue={filters.action}
|
||||||
|
onValueChange={(value) =>
|
||||||
|
handleFilterChange("action", value)
|
||||||
|
}
|
||||||
|
// placeholder=""
|
||||||
|
searchPlaceholder="Search..."
|
||||||
|
emptyMessage="None found"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
},
|
},
|
||||||
// make the value capitalized
|
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
return (
|
return (
|
||||||
<span className="hitespace-nowrap">
|
<span className="hitespace-nowrap">
|
||||||
@@ -239,7 +360,24 @@ export default function GeneralPage() {
|
|||||||
{
|
{
|
||||||
accessorKey: "actor",
|
accessorKey: "actor",
|
||||||
header: ({ column }) => {
|
header: ({ column }) => {
|
||||||
return t("actor");
|
return (
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span>{t("actor")}</span>
|
||||||
|
<ColumnFilter
|
||||||
|
options={filterAttributes.actors.map((actor) => ({
|
||||||
|
value: actor,
|
||||||
|
label: actor
|
||||||
|
}))}
|
||||||
|
selectedValue={filters.actor}
|
||||||
|
onValueChange={(value) =>
|
||||||
|
handleFilterChange("actor", value)
|
||||||
|
}
|
||||||
|
// placeholder=""
|
||||||
|
searchPlaceholder="Search..."
|
||||||
|
emptyMessage="None found"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
},
|
},
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
return (
|
return (
|
||||||
@@ -276,7 +414,13 @@ export default function GeneralPage() {
|
|||||||
<div>
|
<div>
|
||||||
<strong>Metadata:</strong>
|
<strong>Metadata:</strong>
|
||||||
<pre className="text-muted-foreground mt-1 text-xs bg-background p-2 rounded border overflow-auto">
|
<pre className="text-muted-foreground mt-1 text-xs bg-background p-2 rounded border overflow-auto">
|
||||||
{row.metadata ? JSON.stringify(JSON.parse(row.metadata), null, 2) : "N/A"}
|
{row.metadata
|
||||||
|
? JSON.stringify(
|
||||||
|
JSON.parse(row.metadata),
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
)
|
||||||
|
: "N/A"}
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export default function GeneralPage() {
|
|||||||
resources: [],
|
resources: [],
|
||||||
locations: [],
|
locations: [],
|
||||||
hosts: [],
|
hosts: [],
|
||||||
paths: []
|
paths: []
|
||||||
});
|
});
|
||||||
|
|
||||||
// Filter states - unified object for all filters
|
// Filter states - unified object for all filters
|
||||||
@@ -457,7 +457,7 @@ export default function GeneralPage() {
|
|||||||
<span className="flex items-center gap-1">
|
<span className="flex items-center gap-1">
|
||||||
{row.original.location ? (
|
{row.original.location ? (
|
||||||
<span className="text-muted-foreground text-xs">
|
<span className="text-muted-foreground text-xs">
|
||||||
({row.original.location})
|
{row.original.location}
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<span className="text-muted-foreground text-xs">
|
<span className="text-muted-foreground text-xs">
|
||||||
@@ -564,7 +564,7 @@ export default function GeneralPage() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// {
|
// {
|
||||||
@@ -588,8 +588,7 @@ export default function GeneralPage() {
|
|||||||
{ value: "PATCH", label: "PATCH" },
|
{ value: "PATCH", label: "PATCH" },
|
||||||
{ value: "HEAD", label: "HEAD" },
|
{ value: "HEAD", label: "HEAD" },
|
||||||
{ value: "OPTIONS", label: "OPTIONS" }
|
{ value: "OPTIONS", label: "OPTIONS" }
|
||||||
]
|
]}
|
||||||
}
|
|
||||||
selectedValue={filters.method}
|
selectedValue={filters.method}
|
||||||
onValueChange={(value) =>
|
onValueChange={(value) =>
|
||||||
handleFilterChange("method", value)
|
handleFilterChange("method", value)
|
||||||
@@ -600,7 +599,7 @@ export default function GeneralPage() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: "reason",
|
accessorKey: "reason",
|
||||||
@@ -622,7 +621,10 @@ export default function GeneralPage() {
|
|||||||
{ value: "202", label: t("resourceBlocked") },
|
{ value: "202", label: t("resourceBlocked") },
|
||||||
{ value: "203", label: t("droppedByRule") },
|
{ value: "203", label: t("droppedByRule") },
|
||||||
{ value: "204", label: t("noSessions") },
|
{ value: "204", label: t("noSessions") },
|
||||||
{ value: "205", label: t("temporaryRequestToken") },
|
{
|
||||||
|
value: "205",
|
||||||
|
label: t("temporaryRequestToken")
|
||||||
|
},
|
||||||
{ value: "299", label: t("noMoreAuthMethods") }
|
{ value: "299", label: t("noMoreAuthMethods") }
|
||||||
]}
|
]}
|
||||||
selectedValue={filters.reason}
|
selectedValue={filters.reason}
|
||||||
@@ -712,14 +714,24 @@ export default function GeneralPage() {
|
|||||||
<div>
|
<div>
|
||||||
<strong>Metadata:</strong>
|
<strong>Metadata:</strong>
|
||||||
<pre className="text-muted-foreground mt-1 text-xs bg-background p-2 rounded border overflow-auto">
|
<pre className="text-muted-foreground mt-1 text-xs bg-background p-2 rounded border overflow-auto">
|
||||||
{row.metadata ? JSON.stringify(JSON.parse(row.metadata), null, 2) : "N/A"}
|
{row.metadata
|
||||||
|
? JSON.stringify(
|
||||||
|
JSON.parse(row.metadata),
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
)
|
||||||
|
: "N/A"}
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
{row.headers && (
|
{row.headers && (
|
||||||
<div className="md:col-span-2">
|
<div className="md:col-span-2">
|
||||||
<strong>Headers:</strong>
|
<strong>Headers:</strong>
|
||||||
<pre className="text-muted-foreground mt-1 text-xs bg-background p-2 rounded border overflow-auto">
|
<pre className="text-muted-foreground mt-1 text-xs bg-background p-2 rounded border overflow-auto">
|
||||||
{JSON.stringify(JSON.parse(row.headers), null, 2)}
|
{JSON.stringify(
|
||||||
|
JSON.parse(row.headers),
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
)}
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user