fix pages viewing

This commit is contained in:
fuomag9
2026-02-27 19:49:11 +01:00
parent 3d88f565ca
commit a45a156068
3 changed files with 53 additions and 32 deletions
@@ -1,6 +1,7 @@
"use client";
import { useMemo, useState } from "react";
import { useEffect, useRef, useState } from "react";
import { useRouter, usePathname, useSearchParams } from "next/navigation";
import { IconButton, Stack, Switch, Tooltip, Typography } from "@mui/material";
import EditIcon from "@mui/icons-material/Edit";
import DeleteIcon from "@mui/icons-material/Delete";
@@ -22,36 +23,39 @@ type Props = {
accessLists: AccessList[];
authentikDefaults: AuthentikSettings | null;
pagination: { total: number; page: number; perPage: number };
initialSearch: string;
};
export default function ProxyHostsClient({ hosts, certificates, accessLists, authentikDefaults, pagination }: Props) {
export default function ProxyHostsClient({ hosts, certificates, accessLists, authentikDefaults, pagination, initialSearch }: Props) {
const [createOpen, setCreateOpen] = useState(false);
const [duplicateHost, setDuplicateHost] = useState<ProxyHost | null>(null);
const [editHost, setEditHost] = useState<ProxyHost | null>(null);
const [deleteHost, setDeleteHost] = useState<ProxyHost | null>(null);
const [searchTerm, setSearchTerm] = useState("");
const [searchTerm, setSearchTerm] = useState(initialSearch);
const filteredHosts = useMemo(() => {
if (!searchTerm.trim()) return hosts;
const router = useRouter();
const pathname = usePathname();
const searchParams = useSearchParams();
const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const search = searchTerm.toLowerCase();
return hosts.filter((host) => {
// Search in name
if (host.name.toLowerCase().includes(search)) return true;
// Search in domains
if (host.domains.some(domain => domain.toLowerCase().includes(search))) return true;
// Search in upstreams
if (host.upstreams.some(upstream => upstream.toLowerCase().includes(search))) return true;
useEffect(() => {
setSearchTerm(initialSearch);
}, [initialSearch]);
const certificate = host.certificate_id
? certificates.find(c => c.id === host.certificate_id)
: null;
const certName = certificate?.name ?? "Managed by Caddy (Auto)";
if (certName.toLowerCase().includes(search)) return true;
return false;
});
}, [hosts, certificates, searchTerm]);
function handleSearchChange(value: string) {
setSearchTerm(value);
if (debounceRef.current) clearTimeout(debounceRef.current);
debounceRef.current = setTimeout(() => {
const params = new URLSearchParams(searchParams.toString());
if (value.trim()) {
params.set("search", value.trim());
} else {
params.delete("search");
}
params.set("page", "1");
router.push(`${pathname}?${params.toString()}`);
}, 400);
}
const handleToggleEnabled = async (id: number, enabled: boolean) => {
await toggleProxyHostAction(id, enabled);
@@ -151,13 +155,13 @@ export default function ProxyHostsClient({ hosts, certificates, accessLists, aut
<SearchField
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
onChange={(e) => handleSearchChange(e.target.value)}
placeholder="Search hosts..."
/>
<DataTable
columns={columns}
data={filteredHosts}
data={hosts}
keyField="id"
emptyMessage={searchTerm ? "No hosts match your search" : "No proxy hosts found"}
pagination={pagination}
+6 -4
View File
@@ -8,18 +8,19 @@ import { requireAdmin } from "@/src/lib/auth";
const PER_PAGE = 25;
interface PageProps {
searchParams: Promise<{ page?: string }>;
searchParams: Promise<{ page?: string; search?: string }>;
}
export default async function ProxyHostsPage({ searchParams }: PageProps) {
await requireAdmin();
const { page: pageParam } = await searchParams;
const { page: pageParam, search: searchParam } = await searchParams;
const page = Math.max(1, parseInt(pageParam ?? "1", 10) || 1);
const search = searchParam?.trim() || undefined;
const offset = (page - 1) * PER_PAGE;
const [hosts, total, certificates, accessLists, authentikDefaults] = await Promise.all([
listProxyHostsPaginated(PER_PAGE, offset),
countProxyHosts(),
listProxyHostsPaginated(PER_PAGE, offset, search),
countProxyHosts(search),
listCertificates(),
listAccessLists(),
getAuthentikSettings(),
@@ -32,6 +33,7 @@ export default async function ProxyHostsPage({ searchParams }: PageProps) {
accessLists={accessLists}
authentikDefaults={authentikDefaults}
pagination={{ total, page, perPage: PER_PAGE }}
initialSearch={search ?? ""}
/>
);
}
+19 -4
View File
@@ -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, count } from "drizzle-orm";
import { desc, eq, count, ilike, or } from "drizzle-orm";
import { getAuthentikSettings, getGeoBlockSettings, GeoBlockSettings } from "../settings";
const DEFAULT_AUTHENTIK_HEADERS = [
@@ -1329,15 +1329,30 @@ export async function listProxyHosts(): Promise<ProxyHost[]> {
return hosts.map(parseProxyHost);
}
export async function countProxyHosts(): Promise<number> {
const [row] = await db.select({ value: count() }).from(proxyHosts);
export async function countProxyHosts(search?: string): Promise<number> {
const where = search
? or(
ilike(proxyHosts.name, `%${search}%`),
ilike(proxyHosts.domains, `%${search}%`),
ilike(proxyHosts.upstreams, `%${search}%`)
)
: undefined;
const [row] = await db.select({ value: count() }).from(proxyHosts).where(where);
return row?.value ?? 0;
}
export async function listProxyHostsPaginated(limit: number, offset: number): Promise<ProxyHost[]> {
export async function listProxyHostsPaginated(limit: number, offset: number, search?: string): Promise<ProxyHost[]> {
const where = search
? or(
ilike(proxyHosts.name, `%${search}%`),
ilike(proxyHosts.domains, `%${search}%`),
ilike(proxyHosts.upstreams, `%${search}%`)
)
: undefined;
const hosts = await db
.select()
.from(proxyHosts)
.where(where)
.orderBy(desc(proxyHosts.createdAt))
.limit(limit)
.offset(offset);