mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-07 08:26:51 +00:00
Add user names to audit log displays (#58)
Include the email in the detail view.
This commit is contained in:
parent
65e38dee4d
commit
db2d8d3d50
8 changed files with 49 additions and 38 deletions
|
|
@ -5,7 +5,7 @@ import { eq, gte, and, desc } from 'drizzle-orm';
|
|||
import { logger } from '@/lib/server';
|
||||
import {
|
||||
type DatabaseOrTransaction,
|
||||
type AuditLog,
|
||||
type AuditLogWithUser,
|
||||
type CreateAuditLog,
|
||||
client as db,
|
||||
auditLogs,
|
||||
|
|
@ -19,7 +19,7 @@ export const getAuditLogs = async ({
|
|||
orgId?: string | null;
|
||||
limit?: number;
|
||||
timePeriod?: number;
|
||||
}): Promise<AuditLog[]> => {
|
||||
}): Promise<AuditLogWithUser[]> => {
|
||||
if (!orgId) {
|
||||
return [];
|
||||
}
|
||||
|
|
@ -30,12 +30,14 @@ export const getAuditLogs = async ({
|
|||
}
|
||||
|
||||
if (timePeriod === undefined) {
|
||||
return await db
|
||||
.select()
|
||||
.from(auditLogs)
|
||||
.where(eq(auditLogs.orgId, orgId))
|
||||
.orderBy(desc(auditLogs.createdAt))
|
||||
.limit(limit);
|
||||
return await db.query.auditLogs.findMany({
|
||||
where: eq(auditLogs.orgId, orgId),
|
||||
with: {
|
||||
user: true,
|
||||
},
|
||||
orderBy: desc(auditLogs.createdAt),
|
||||
limit: limit,
|
||||
});
|
||||
} else {
|
||||
if (isNaN(timePeriod) || timePeriod <= 0) {
|
||||
throw new Error('timePeriod must be a positive number');
|
||||
|
|
@ -44,14 +46,17 @@ export const getAuditLogs = async ({
|
|||
const cutoff = new Date();
|
||||
cutoff.setDate(cutoff.getDate() - timePeriod);
|
||||
|
||||
return await db
|
||||
.select()
|
||||
.from(auditLogs)
|
||||
.where(
|
||||
and(eq(auditLogs.orgId, orgId), gte(auditLogs.createdAt, cutoff)),
|
||||
)
|
||||
.orderBy(desc(auditLogs.createdAt))
|
||||
.limit(limit);
|
||||
return await db.query.auditLogs.findMany({
|
||||
where: and(
|
||||
eq(auditLogs.orgId, orgId),
|
||||
gte(auditLogs.createdAt, cutoff),
|
||||
),
|
||||
with: {
|
||||
user: true,
|
||||
},
|
||||
orderBy: desc(auditLogs.createdAt),
|
||||
limit: limit,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error({
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { useAuth } from '@clerk/nextjs';
|
|||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { type TimePeriod, timePeriods } from '@/types';
|
||||
import type { AuditLog } from '@/db';
|
||||
import type { AuditLogWithUser } from '@/db';
|
||||
import { getAuditLogs } from '@/actions/auditLogs';
|
||||
import {
|
||||
Button,
|
||||
|
|
@ -29,7 +29,7 @@ export const AuditLogs = () => {
|
|||
enabled: !!orgId,
|
||||
});
|
||||
|
||||
const [selectedLog, setSelectedLog] = useState<AuditLog | null>(null);
|
||||
const [selectedLog, setSelectedLog] = useState<AuditLogWithUser | null>(null);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
@ -61,11 +61,11 @@ export const AuditLogs = () => {
|
|||
<Skeleton className="h-[64px] w-full" />
|
||||
</>
|
||||
) : logs.length > 0 ? (
|
||||
logs.map((log: AuditLog) => (
|
||||
logs.map((log: AuditLogWithUser) => (
|
||||
<AuditLogEntry
|
||||
key={log.id}
|
||||
log={log}
|
||||
onClick={(log: AuditLog) => setSelectedLog(log)}
|
||||
onClick={(log: AuditLogWithUser) => setSelectedLog(log)}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { useAuth } from '@clerk/nextjs';
|
|||
import { useQuery } from '@tanstack/react-query';
|
||||
import { ArrowRightIcon } from 'lucide-react';
|
||||
|
||||
import type { AuditLog } from '@/db';
|
||||
import type { AuditLogWithUser } from '@/db';
|
||||
import { getAuditLogs } from '@/actions/auditLogs';
|
||||
import {
|
||||
Card,
|
||||
|
|
@ -33,7 +33,7 @@ export function AuditLogCard() {
|
|||
enabled: !!orgId,
|
||||
});
|
||||
|
||||
const [selectedLog, setSelectedLog] = useState<AuditLog | null>(null);
|
||||
const [selectedLog, setSelectedLog] = useState<AuditLogWithUser | null>(null);
|
||||
const path = usePathname();
|
||||
|
||||
return (
|
||||
|
|
@ -70,11 +70,11 @@ export function AuditLogCard() {
|
|||
<Skeleton className="h-[64px] w-full" />
|
||||
</>
|
||||
) : logs.length > 0 ? (
|
||||
logs.map((log: AuditLog) => (
|
||||
logs.map((log: AuditLogWithUser) => (
|
||||
<AuditLogEntry
|
||||
key={log.id}
|
||||
log={log}
|
||||
onClick={(log: AuditLog) => setSelectedLog(log)}
|
||||
onClick={(log: AuditLogWithUser) => setSelectedLog(log)}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import Link from 'next/link';
|
||||
import { ArrowRight, Calendar, Clock, User } from 'lucide-react';
|
||||
|
||||
import type { AuditLog } from '@/db';
|
||||
import type { AuditLogWithUser } from '@/db';
|
||||
|
||||
export const AuditLogDetails = ({ log }: { log: AuditLog }) => (
|
||||
export const AuditLogDetails = ({ log }: { log: AuditLogWithUser }) => (
|
||||
<div className="space-y-6">
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center space-x-2 text-sm text-muted-foreground">
|
||||
|
|
@ -14,7 +14,9 @@ export const AuditLogDetails = ({ log }: { log: AuditLog }) => (
|
|||
</div>
|
||||
<div className="flex items-center space-x-2 text-sm text-muted-foreground">
|
||||
<User className="size-4" />
|
||||
<span>{log.userId}</span>
|
||||
<span>
|
||||
{log.user.name} ({log.user.email})
|
||||
</span>
|
||||
</div>
|
||||
{log.targetId && (
|
||||
<Link
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { AuditLog } from '@/db';
|
||||
import type { AuditLogWithUser } from '@/db';
|
||||
import {
|
||||
Drawer,
|
||||
DrawerContent,
|
||||
|
|
@ -8,7 +8,7 @@ import {
|
|||
import { AuditLogDetails } from '@/components/audit-logs';
|
||||
|
||||
type AuditLogDrawerProps = {
|
||||
selectedLog: AuditLog | null;
|
||||
selectedLog: AuditLogWithUser | null;
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import { formatDistance } from 'date-fns';
|
||||
import { Settings, Sliders, Users } from 'lucide-react';
|
||||
|
||||
import { type AuditLog, AuditLogTargetType } from '@/db';
|
||||
import { type AuditLogWithUser, AuditLogTargetType } from '@/db';
|
||||
|
||||
type AuditLogEntryProps = {
|
||||
log: AuditLog;
|
||||
onClick: (log: AuditLog) => void;
|
||||
log: AuditLogWithUser;
|
||||
onClick: (log: AuditLogWithUser) => void;
|
||||
};
|
||||
|
||||
export const AuditLogEntry = ({ log, onClick }: AuditLogEntryProps) => (
|
||||
|
|
@ -18,7 +18,7 @@ export const AuditLogEntry = ({ log, onClick }: AuditLogEntryProps) => (
|
|||
<div className="flex flex-row justify-between items-center gap-2 flex-1">
|
||||
<div className="flex flex-col gap-1">
|
||||
<p className="text-sm text-foreground">{log.description}</p>
|
||||
<p className="text-xs text-muted-foreground">{log.userId}</p>
|
||||
<p className="text-xs text-muted-foreground">{log.user.name}</p>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground hidden sm:block">
|
||||
{formatDistance(log.createdAt, new Date(), { addSuffix: true })}
|
||||
|
|
|
|||
10
src/db/db.ts
10
src/db/db.ts
|
|
@ -2,10 +2,13 @@ import { drizzle } from 'drizzle-orm/postgres-js';
|
|||
import postgres from 'postgres';
|
||||
|
||||
import { Env } from '@/lib/server';
|
||||
import * as schema from './schema';
|
||||
|
||||
const pgClient = postgres(Env.DATABASE_URL, { prepare: false });
|
||||
|
||||
let testDb: ReturnType<typeof drizzle> | undefined = undefined;
|
||||
const client = drizzle({ client: pgClient, schema });
|
||||
|
||||
let testDb: typeof client | undefined = undefined;
|
||||
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
if (
|
||||
|
|
@ -15,12 +18,9 @@ if (process.env.NODE_ENV === 'test') {
|
|||
throw new Error('DATABASE_URL is not a test database');
|
||||
}
|
||||
|
||||
testDb = drizzle({ client: pgClient });
|
||||
testDb = client;
|
||||
}
|
||||
|
||||
const client =
|
||||
process.env.NODE_ENV === 'test' ? testDb! : drizzle({ client: pgClient });
|
||||
|
||||
const disconnect = async () => {
|
||||
await pgClient.end();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -36,3 +36,7 @@ export type CreateOrgSettings = Omit<
|
|||
export type AuditLog = typeof auditLogs.$inferSelect;
|
||||
|
||||
export type CreateAuditLog = Omit<typeof auditLogs.$inferInsert, Generated>;
|
||||
|
||||
export type AuditLogWithUser = AuditLog & {
|
||||
user: User;
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue