Loading packages/admin/src/components/sidebar/Sidebar.tsx +7 −0 Original line number Diff line number Diff line Loading @@ -9,6 +9,7 @@ import { faCog, faHashtag, faHome, faList, faServer, faShieldHalved, faSquare, Loading Loading @@ -54,6 +55,12 @@ export const SidebarWrapper = () => { isActive={pathname === "/"} href="/" /> <SidebarItem title="Audit Log" icon={<FontAwesomeIcon icon={faList} />} isActive={pathname === "/audit"} href="/audit" /> <CollapseItems icon={<FontAwesomeIcon icon={faChartBar} />} title="Stats" Loading packages/admin/src/main.tsx +5 −0 Original line number Diff line number Diff line Loading @@ -9,6 +9,7 @@ import { HomePage } from "./pages/Home/page.tsx"; import { AccountsPage } from "./pages/Accounts/Accounts/page.tsx"; import { ServiceSettingsPage } from "./pages/Service/settings.tsx"; import { ToastContainer } from "react-toastify"; import { AuditLog } from "./pages/AuditLog/auditlog.tsx"; const router = createBrowserRouter( [ Loading @@ -28,6 +29,10 @@ const router = createBrowserRouter( path: "/service/settings", element: <ServiceSettingsPage />, }, { path: "/audit", element: <AuditLog />, }, ], }, ], Loading packages/admin/src/pages/AuditLog/auditlog.tsx 0 → 100644 +77 −0 Original line number Diff line number Diff line import { useEffect, useState } from "react"; import { api, handleError } from "../../lib/utils"; import { Table, TableBody, TableCell, TableColumn, TableHeader, TableRow, } from "@nextui-org/react"; type AuditLogAction = "BAN_CREATE" | "BAN_UPDATE" | "BAN_DELETE"; type AuditLog = { id: number; userId: string; action: AuditLogAction; reason?: string; comment?: string; banId?: number; createdAt: string; updatedAt?: string; }; export const AuditLog = () => { const [auditLogs, setAuditLogs] = useState<AuditLog[]>([]); useEffect(() => { api<{ auditLogs: AuditLog[] }>("/api/admin/audit", "GET").then( ({ status, data }) => { if (status === 200) { if (data.success) { setAuditLogs(data.auditLogs); } else { handleError(status, data); } } else { handleError(status, data); } } ); }, []); return ( <> <h4 className="text-l font-semibold">Audit Log</h4> <div className="relative"> <Table> <TableHeader> <TableColumn>ID</TableColumn> <TableColumn>User ID</TableColumn> <TableColumn>Action</TableColumn> <TableColumn>Reason</TableColumn> <TableColumn>Comment</TableColumn> <TableColumn>Created At / Updated At</TableColumn> </TableHeader> <TableBody> {auditLogs.map((log) => ( <TableRow key={log.id}> <TableCell>{log.id}</TableCell> <TableCell>{log.userId}</TableCell> <TableCell>{log.action}</TableCell> <TableCell>{log.reason}</TableCell> <TableCell>{log.comment}</TableCell> <TableCell> {log.createdAt} / {log.updatedAt} </TableCell> </TableRow> ))} </TableBody> </Table> </div> </> ); }; packages/server/prisma/dbml/schema.dbml +28 −1 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ Table User { pixels Pixel [not null] FactionMember FactionMember [not null] Ban Ban AuditLog AuditLog [not null] } Table Instance { Loading Loading @@ -109,8 +110,30 @@ Table Ban { privateNote String publicNote String expiresAt DateTime [not null] createdAt DateTime [default: `now()`, not null] updatedAt DateTime user User instance Instance AuditLog AuditLog [not null] } Table AuditLog { id Int [pk, increment] userId String action AuditLogAction [not null] reason String comment String banId Int createdAt DateTime [default: `now()`, not null] updatedAt DateTime user User ban Ban } Enum AuditLogAction { BAN_CREATE BAN_UPDATE BAN_DELETE } Ref: Pixel.userId > User.sub Loading @@ -130,3 +153,7 @@ Ref: FactionSetting.factionId > Faction.id Ref: Ban.userId - User.sub Ref: Ban.instanceId - Instance.id Ref: AuditLog.userId > User.sub Ref: AuditLog.banId > Ban.id No newline at end of file packages/server/prisma/migrations/20240707193624_add_audit_log_model/migration.sql 0 → 100644 +22 −0 Original line number Diff line number Diff line -- CreateEnum CREATE TYPE "AuditLogAction" AS ENUM ('BAN_CREATE', 'BAN_UPDATE', 'BAN_DELETE'); -- CreateTable CREATE TABLE "AuditLog" ( "id" SERIAL NOT NULL, "userId" TEXT, "action" "AuditLogAction" NOT NULL, "reason" TEXT, "comment" TEXT, "banId" INTEGER, "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "updatedAt" TIMESTAMP(3), CONSTRAINT "AuditLog_pkey" PRIMARY KEY ("id") ); -- AddForeignKey ALTER TABLE "AuditLog" ADD CONSTRAINT "AuditLog_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("sub") ON DELETE SET NULL ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "AuditLog" ADD CONSTRAINT "AuditLog_banId_fkey" FOREIGN KEY ("banId") REFERENCES "Ban"("id") ON DELETE SET NULL ON UPDATE CASCADE; Loading
packages/admin/src/components/sidebar/Sidebar.tsx +7 −0 Original line number Diff line number Diff line Loading @@ -9,6 +9,7 @@ import { faCog, faHashtag, faHome, faList, faServer, faShieldHalved, faSquare, Loading Loading @@ -54,6 +55,12 @@ export const SidebarWrapper = () => { isActive={pathname === "/"} href="/" /> <SidebarItem title="Audit Log" icon={<FontAwesomeIcon icon={faList} />} isActive={pathname === "/audit"} href="/audit" /> <CollapseItems icon={<FontAwesomeIcon icon={faChartBar} />} title="Stats" Loading
packages/admin/src/main.tsx +5 −0 Original line number Diff line number Diff line Loading @@ -9,6 +9,7 @@ import { HomePage } from "./pages/Home/page.tsx"; import { AccountsPage } from "./pages/Accounts/Accounts/page.tsx"; import { ServiceSettingsPage } from "./pages/Service/settings.tsx"; import { ToastContainer } from "react-toastify"; import { AuditLog } from "./pages/AuditLog/auditlog.tsx"; const router = createBrowserRouter( [ Loading @@ -28,6 +29,10 @@ const router = createBrowserRouter( path: "/service/settings", element: <ServiceSettingsPage />, }, { path: "/audit", element: <AuditLog />, }, ], }, ], Loading
packages/admin/src/pages/AuditLog/auditlog.tsx 0 → 100644 +77 −0 Original line number Diff line number Diff line import { useEffect, useState } from "react"; import { api, handleError } from "../../lib/utils"; import { Table, TableBody, TableCell, TableColumn, TableHeader, TableRow, } from "@nextui-org/react"; type AuditLogAction = "BAN_CREATE" | "BAN_UPDATE" | "BAN_DELETE"; type AuditLog = { id: number; userId: string; action: AuditLogAction; reason?: string; comment?: string; banId?: number; createdAt: string; updatedAt?: string; }; export const AuditLog = () => { const [auditLogs, setAuditLogs] = useState<AuditLog[]>([]); useEffect(() => { api<{ auditLogs: AuditLog[] }>("/api/admin/audit", "GET").then( ({ status, data }) => { if (status === 200) { if (data.success) { setAuditLogs(data.auditLogs); } else { handleError(status, data); } } else { handleError(status, data); } } ); }, []); return ( <> <h4 className="text-l font-semibold">Audit Log</h4> <div className="relative"> <Table> <TableHeader> <TableColumn>ID</TableColumn> <TableColumn>User ID</TableColumn> <TableColumn>Action</TableColumn> <TableColumn>Reason</TableColumn> <TableColumn>Comment</TableColumn> <TableColumn>Created At / Updated At</TableColumn> </TableHeader> <TableBody> {auditLogs.map((log) => ( <TableRow key={log.id}> <TableCell>{log.id}</TableCell> <TableCell>{log.userId}</TableCell> <TableCell>{log.action}</TableCell> <TableCell>{log.reason}</TableCell> <TableCell>{log.comment}</TableCell> <TableCell> {log.createdAt} / {log.updatedAt} </TableCell> </TableRow> ))} </TableBody> </Table> </div> </> ); };
packages/server/prisma/dbml/schema.dbml +28 −1 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ Table User { pixels Pixel [not null] FactionMember FactionMember [not null] Ban Ban AuditLog AuditLog [not null] } Table Instance { Loading Loading @@ -109,8 +110,30 @@ Table Ban { privateNote String publicNote String expiresAt DateTime [not null] createdAt DateTime [default: `now()`, not null] updatedAt DateTime user User instance Instance AuditLog AuditLog [not null] } Table AuditLog { id Int [pk, increment] userId String action AuditLogAction [not null] reason String comment String banId Int createdAt DateTime [default: `now()`, not null] updatedAt DateTime user User ban Ban } Enum AuditLogAction { BAN_CREATE BAN_UPDATE BAN_DELETE } Ref: Pixel.userId > User.sub Loading @@ -130,3 +153,7 @@ Ref: FactionSetting.factionId > Faction.id Ref: Ban.userId - User.sub Ref: Ban.instanceId - Instance.id Ref: AuditLog.userId > User.sub Ref: AuditLog.banId > Ban.id No newline at end of file
packages/server/prisma/migrations/20240707193624_add_audit_log_model/migration.sql 0 → 100644 +22 −0 Original line number Diff line number Diff line -- CreateEnum CREATE TYPE "AuditLogAction" AS ENUM ('BAN_CREATE', 'BAN_UPDATE', 'BAN_DELETE'); -- CreateTable CREATE TABLE "AuditLog" ( "id" SERIAL NOT NULL, "userId" TEXT, "action" "AuditLogAction" NOT NULL, "reason" TEXT, "comment" TEXT, "banId" INTEGER, "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "updatedAt" TIMESTAMP(3), CONSTRAINT "AuditLog_pkey" PRIMARY KEY ("id") ); -- AddForeignKey ALTER TABLE "AuditLog" ADD CONSTRAINT "AuditLog_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("sub") ON DELETE SET NULL ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "AuditLog" ADD CONSTRAINT "AuditLog_banId_fkey" FOREIGN KEY ("banId") REFERENCES "Ban"("id") ON DELETE SET NULL ON UPDATE CASCADE;