Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,27 +1,15 @@
import { NextRequest } from 'next/server';
import { auditLogsApi } from '../../../api';
import { getToken } from 'next-auth/jwt';
import { AuditLogRequestModel } from '@/src/models/audit-log';
import { parseNumber } from '@/src/utils/number';
import { mapSearchParamsToAuditLogRequest } from '@/src/utils/audit-logs';

export const dynamic = 'force-dynamic';

export async function GET(req: NextRequest) {
const token = await getToken({ req });

const { searchParams } = new URL(req.url);

const params: AuditLogRequestModel = {
limit: parseNumber(searchParams.get('limit')),
offset: parseNumber(searchParams.get('offset')),
entity_type: searchParams.get('entity_type') ?? undefined,
action_type: searchParams.get('action_type') ?? undefined,
item_id: parseNumber(searchParams.get('item_id')),
entity_id: searchParams.get('entity_id') ?? undefined,
performed_by: searchParams.get('performed_by') ?? undefined,
created_at_from: searchParams.get('created_at_from') ?? undefined,
created_at_to: searchParams.get('created_at_to') ?? undefined,
};
const params = mapSearchParamsToAuditLogRequest(searchParams);

return await auditLogsApi.export(token, params);
}
17 changes: 2 additions & 15 deletions apps/statgpt-admin-frontend/src/app/api/v1/audit-logs/route.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,15 @@
import { NextRequest } from 'next/server';
import { auditLogsApi } from '../../api';
import { getToken } from 'next-auth/jwt';
import { AuditLogRequestModel } from '@/src/models/audit-log';
import { parseNumber } from '@/src/utils/number';
import { mapSearchParamsToAuditLogRequest } from '@/src/utils/audit-logs';

export const dynamic = 'force-dynamic';

export async function GET(req: NextRequest) {
const token = await getToken({ req });

const { searchParams } = new URL(req.url);

const params: AuditLogRequestModel = {
limit: parseNumber(searchParams.get('limit')),
offset: parseNumber(searchParams.get('offset')),
entity_type: searchParams.get('entity_type') ?? undefined,
action_type: searchParams.get('action_type') ?? undefined,
item_id: parseNumber(searchParams.get('item_id')),
entity_id: searchParams.get('entity_id') ?? undefined,
performed_by: searchParams.get('performed_by') ?? undefined,
created_at_from: searchParams.get('created_at_from') ?? undefined,
created_at_to: searchParams.get('created_at_to') ?? undefined,
};

const params = mapSearchParamsToAuditLogRequest(searchParams);
const data = await auditLogsApi.getAuditLogs(token, params);

return Response.json(data);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ import type { RequestData } from '@/src/models/request-data';
import { sendGetRequest } from '@/src/server/api';
import { DEFAULT_GRID_PAGE_SIZE } from '@/src/constants/columns/grid';
import { useAuditLogFiltersInUrl } from '@/src/hooks/use-audit-logs-filters-in-url';
import { auditLogRequestToQueryString } from '@/src/utils/audit-logs';
import { getEnumFilterValue, getTextEquals } from '@/src/utils/client/grid';
import { mapAuditLogRequestToQueryString } from '@/src/utils/audit-logs';
import { getEnumFilterValue, getTextContains } from '@/src/utils/client/grid';
import { ListContent } from '../ListView/ListContent/ListContent';
import { getAuditLogsColumns } from '@/src/constants/columns/audit-logs';

Expand All @@ -45,7 +45,13 @@ export function AuditLogsListView({ enums }: AuditLogsListViewProps) {
const entity_type = getEnumFilterValue(args.filterModel, 'entity_type');
const action_type = getEnumFilterValue(args.filterModel, 'action_type');

const entity_id = getTextEquals(args.filterModel, 'entity_id');
const trace_id = getTextContains(args.filterModel, 'trace_id');
const entity_id = getTextContains(args.filterModel, 'entity_id');
const entity_name = getTextContains(args.filterModel, 'entity_name');
const performed_by_name = getTextContains(
args.filterModel,
'performed_by_name',
);

const request: AuditLogRequestModel = {
offset: args.offset,
Expand All @@ -54,10 +60,13 @@ export function AuditLogsListView({ enums }: AuditLogsListViewProps) {
created_at_to,
...(entity_type ? { entity_type } : {}),
...(action_type ? { action_type } : {}),
...(trace_id ? { trace_id } : {}),
...(entity_id ? { entity_id } : {}),
...(entity_name ? { entity_name } : {}),
...(performed_by_name ? { performed_by_name } : {}),
};

const query = auditLogRequestToQueryString(request);
const query = mapAuditLogRequestToQueryString(request);

const payload = await sendGetRequest<any, RequestData<AuditLogDetails>>(
`/api/v1/audit-logs?${query}`,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
'use client';

import { EnumSelectFilterModel } from '@/src/models/grid';
import { GridEnumSelectFilterModel } from '@/src/models/grid';
import { normalizeEnumValues } from '@/src/utils/client/grid';
import React, { useMemo } from 'react';

export interface EnumSelectFilterProps {
model: EnumSelectFilterModel;
onModelChange: (model: EnumSelectFilterModel) => void;
model: GridEnumSelectFilterModel;
onModelChange: (model: GridEnumSelectFilterModel) => void;
values: readonly string[];
allLabel?: string;
formatValue?: (v: string) => string;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
'use client';

import { EnumSelectFilterModel } from '@/src/models/grid';
import { GridEnumSelectFilterModel } from '@/src/models/grid';
import { normalizeEnumValues } from '@/src/utils/client/grid';
import React, { useMemo } from 'react';

export interface EnumSelectFloatingFilterProps {
model: EnumSelectFilterModel;
onModelChange: (model: EnumSelectFilterModel) => void;
model: GridEnumSelectFilterModel;
onModelChange: (model: GridEnumSelectFilterModel) => void;
values: readonly string[];
allLabel?: string;
formatValue?: (v: string) => string;
Expand Down
16 changes: 11 additions & 5 deletions apps/statgpt-admin-frontend/src/constants/columns/audit-logs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import { EnumSelectFloatingFilter } from '@/src/components/GridView/CustomFilter
import { AuditLog, AuditLogEnumValues } from '@/src/models/audit-log';
import { ColDef } from 'ag-grid-community';

const EQUALS_ONLY_FILTER = {
filterOptions: ['equals'],
defaultOption: 'equals',
suppressAndOrCondition: true,
const CONTAINS_TEXT_FILTER = {
filterOptions: ['contains'],
defaultOption: 'contains',
maxNumConditions: 1,
debounceMs: 400,
};

Expand Down Expand Up @@ -46,22 +46,28 @@ export const getAuditLogsColumns = ({
field: 'entity_id',
headerName: 'Entity ID',
filter: 'agTextColumnFilter',
filterParams: EQUALS_ONLY_FILTER,
filterParams: CONTAINS_TEXT_FILTER,
},
{
field: 'entity_name',
headerName: 'Entity name',
filter: 'agTextColumnFilter',
filterParams: CONTAINS_TEXT_FILTER,
},
{
field: 'performed_by_name',
headerName: 'Initiated',
filter: 'agTextColumnFilter',
filterParams: CONTAINS_TEXT_FILTER,
valueGetter: ({ data }: { data: AuditLog }) => {
return data?.performed_by_name ?? data?.performed_by ?? '';
},
},
{
field: 'trace_id',
headerName: 'Activity ID',
filter: 'agTextColumnFilter',
filterParams: CONTAINS_TEXT_FILTER,
},
{
field: 'created_at',
Expand Down
3 changes: 3 additions & 0 deletions apps/statgpt-admin-frontend/src/models/audit-log.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,11 @@ export interface AuditLogRequestModel extends AuditLogTimeRange {
entity_type?: string;
action_type?: string;
item_id?: number;
trace_id?: string;
entity_id?: string;
entity_name?: string;
performed_by?: string;
performed_by_name?: string;
}

export interface AuditLogEnumValues {
Expand Down
10 changes: 6 additions & 4 deletions apps/statgpt-admin-frontend/src/models/grid.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
export interface AgTextEqualsFilterModel {
export type GridEnumSelectFilterModel = { value: string } | null;

export type GridTextFilterType = 'equals' | 'contains';

export interface GridTextFilterModel {
filterType: 'text';
type: 'equals';
type: GridTextFilterType;
filter?: string;
}

export type EnumSelectFilterModel = { value: string } | null;
6 changes: 3 additions & 3 deletions apps/statgpt-admin-frontend/src/server/audit-logs-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { JWT } from 'next-auth/jwt';
import { RequestData } from '@/src/models/request-data';
import { MAIN_API } from './api';
import { BaseApi } from './base-api';
import { auditLogRequestToQueryString } from '../utils/audit-logs';
import { mapAuditLogRequestToQueryString } from '../utils/audit-logs';

const AUDIT_LOGS_URL = `${MAIN_API}/audit-logs`;
const AUDIT_LOGS_ID_URL = (id?: string | number): string =>
Expand All @@ -24,7 +24,7 @@ export class AuditLogsApi extends BaseApi {
let url = AUDIT_LOGS_URL;

if (params) {
const queryString = auditLogRequestToQueryString(params);
const queryString = mapAuditLogRequestToQueryString(params);
if (queryString) {
url += `?${queryString}`;
}
Expand All @@ -44,7 +44,7 @@ export class AuditLogsApi extends BaseApi {
let url = EXPORT_URL;

if (params) {
const queryString = auditLogRequestToQueryString(params);
const queryString = mapAuditLogRequestToQueryString(params);
if (queryString) {
url += `?${queryString}`;
}
Expand Down
24 changes: 23 additions & 1 deletion apps/statgpt-admin-frontend/src/utils/audit-logs.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { AuditLogRequestModel } from '../models/audit-log';
import { parseNumber } from './number';

export function auditLogRequestToQueryString(
export function mapAuditLogRequestToQueryString(
params: AuditLogRequestModel,
): string {
const searchParams = new URLSearchParams();
Expand All @@ -13,3 +14,24 @@ export function auditLogRequestToQueryString(

return searchParams.toString();
}

export function mapSearchParamsToAuditLogRequest(
searchParams: URLSearchParams,
): AuditLogRequestModel {
const params: AuditLogRequestModel = {
limit: parseNumber(searchParams.get('limit')),
offset: parseNumber(searchParams.get('offset')),
entity_type: searchParams.get('entity_type') ?? undefined,
action_type: searchParams.get('action_type') ?? undefined,
item_id: parseNumber(searchParams.get('item_id')),
trace_id: searchParams.get('trace_id') ?? undefined,
entity_id: searchParams.get('entity_id') ?? undefined,
entity_name: searchParams.get('entity_name') ?? undefined,
performed_by: searchParams.get('performed_by') ?? undefined,
performed_by_name: searchParams.get('performed_by_name') ?? undefined,
created_at_from: searchParams.get('created_at_from') ?? undefined,
created_at_to: searchParams.get('created_at_to') ?? undefined,
};

return params;
}
26 changes: 21 additions & 5 deletions apps/statgpt-admin-frontend/src/utils/client/grid.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,35 @@
import { AgTextEqualsFilterModel } from '@/src/models/grid';
import { GridTextFilterModel, GridTextFilterType } from '@/src/models/grid';

export function getTextEquals(
function getGridTextFilterValue(
filterModel: unknown,
field: string,
expectedType: GridTextFilterType,
): string | undefined {
const model = filterModel as Record<string, any> | undefined;
const filter = model?.[field] as Partial<AgTextEqualsFilterModel> | undefined;
const model = filterModel as Record<string, unknown> | undefined;
const filter = model?.[field] as Partial<GridTextFilterModel> | undefined;

if (!filter || filter.filterType !== 'text' || filter.type !== 'equals')
if (!filter || filter.filterType !== 'text' || filter.type !== expectedType) {
return undefined;
}

const value = typeof filter.filter === 'string' ? filter.filter.trim() : '';
return value.length ? value : undefined;
}

export function getTextEquals(
filterModel: unknown,
field: string,
): string | undefined {
return getGridTextFilterValue(filterModel, field, 'equals');
}

export function getTextContains(
filterModel: unknown,
field: string,
): string | undefined {
return getGridTextFilterValue(filterModel, field, 'contains');
}

export function getNumberEquals(
filterModel: unknown,
field: string,
Expand Down
Loading