diff --git a/src/lib/models/access-lists.ts b/src/lib/models/access-lists.ts index 42265258..00780de6 100644 --- a/src/lib/models/access-lists.ts +++ b/src/lib/models/access-lists.ts @@ -3,7 +3,7 @@ import db, { nowIso, toIso } from "../db"; import { applyCaddyConfig } from "../caddy"; import { logAuditEvent } from "../audit"; import { accessListEntries, accessLists } from "../db/schema"; -import { asc, eq, inArray } from "drizzle-orm"; +import { asc, eq, inArray, count } from "drizzle-orm"; export type AccessListEntry = { id: number; @@ -78,6 +78,36 @@ export async function listAccessLists(): Promise { return lists.map((list) => toAccessList(list, entriesByList.get(list.id) ?? [])); } +export async function countAccessLists(): Promise { + const [row] = await db.select({ value: count() }).from(accessLists); + return row?.value ?? 0; +} + +export async function listAccessListsPaginated(limit: number, offset: number): Promise { + const lists = await db.query.accessLists.findMany({ + orderBy: (table) => asc(table.name), + limit, + offset, + }); + + if (lists.length === 0) return []; + + const listIds = lists.map((list) => list.id); + const entries = await db + .select() + .from(accessListEntries) + .where(inArray(accessListEntries.accessListId, listIds)); + + const entriesByList = new Map(); + for (const entry of entries) { + const bucket = entriesByList.get(entry.accessListId) ?? []; + bucket.push(entry); + entriesByList.set(entry.accessListId, bucket); + } + + return lists.map((list) => toAccessList(list, entriesByList.get(list.id) ?? [])); +} + export async function getAccessList(id: number): Promise { const list = await db.query.accessLists.findFirst({ where: (table, operators) => operators.eq(table.id, id) diff --git a/src/lib/models/audit.ts b/src/lib/models/audit.ts index ffb750de..8f44e9e8 100644 --- a/src/lib/models/audit.ts +++ b/src/lib/models/audit.ts @@ -1,6 +1,6 @@ import db, { toIso, nowIso } from "../db"; import { auditEvents } from "../db/schema"; -import { desc } from "drizzle-orm"; +import { desc, ilike, or, count } from "drizzle-orm"; export type AuditEvent = { id: number; @@ -12,12 +12,37 @@ export type AuditEvent = { created_at: string; }; -export async function listAuditEvents(limit = 100): Promise { +export async function countAuditEvents(search?: string): Promise { + const where = search + ? or( + ilike(auditEvents.summary, `%${search}%`), + ilike(auditEvents.action, `%${search}%`), + ilike(auditEvents.entityType, `%${search}%`) + ) + : undefined; + const [row] = await db.select({ value: count() }).from(auditEvents).where(where); + return row?.value ?? 0; +} + +export async function listAuditEvents( + limit = 100, + offset = 0, + search?: string +): Promise { + const where = search + ? or( + ilike(auditEvents.summary, `%${search}%`), + ilike(auditEvents.action, `%${search}%`), + ilike(auditEvents.entityType, `%${search}%`) + ) + : undefined; const events = await db .select() .from(auditEvents) + .where(where) .orderBy(desc(auditEvents.createdAt)) - .limit(limit); + .limit(limit) + .offset(offset); return events.map((event) => ({ id: event.id, @@ -26,7 +51,7 @@ export async function listAuditEvents(limit = 100): Promise { entity_type: event.entityType, entity_id: event.entityId, summary: event.summary, - created_at: toIso(event.createdAt)! + created_at: toIso(event.createdAt)!, })); } diff --git a/src/lib/models/proxy-hosts.ts b/src/lib/models/proxy-hosts.ts index 9170ac62..df38331a 100644 --- a/src/lib/models/proxy-hosts.ts +++ b/src/lib/models/proxy-hosts.ts @@ -2,7 +2,7 @@ import db, { nowIso, toIso } from "../db"; import { applyCaddyConfig } from "../caddy"; import { logAuditEvent } from "../audit"; import { proxyHosts } from "../db/schema"; -import { desc, eq } from "drizzle-orm"; +import { desc, eq, count } from "drizzle-orm"; import { getAuthentikSettings, getGeoBlockSettings, GeoBlockSettings } from "../settings"; const DEFAULT_AUTHENTIK_HEADERS = [ @@ -1329,6 +1329,21 @@ export async function listProxyHosts(): Promise { return hosts.map(parseProxyHost); } +export async function countProxyHosts(): Promise { + const [row] = await db.select({ value: count() }).from(proxyHosts); + return row?.value ?? 0; +} + +export async function listProxyHostsPaginated(limit: number, offset: number): Promise { + const hosts = await db + .select() + .from(proxyHosts) + .orderBy(desc(proxyHosts.createdAt)) + .limit(limit) + .offset(offset); + return hosts.map(parseProxyHost); +} + export async function createProxyHost(input: ProxyHostInput, actorUserId: number) { if (!input.domains || input.domains.length === 0) { throw new Error("At least one domain must be specified");