add pagenation for domain list

This commit is contained in:
oiov
2025-05-21 19:15:10 +08:00
parent a5f5312476
commit 91d3f06f38
5 changed files with 55 additions and 49 deletions

View File

@@ -16,6 +16,7 @@ import {
CardDescription,
CardHeader,
} from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Modal } from "@/components/ui/modal";
import { Skeleton } from "@/components/ui/skeleton";
import { Switch } from "@/components/ui/switch";
@@ -88,7 +89,7 @@ export default function DomainList({ user, action }: DomainListProps) {
total: number;
list: DomainFormData[];
}>(
`${action}?page=${currentPage}&size=${pageSize}&slug=${searchParams.slug}&userName=${searchParams.userName}&target=${searchParams.target}`,
`${action}?page=${currentPage}&size=${pageSize}&target=${searchParams.target}`,
fetcher,
);
@@ -129,10 +130,14 @@ export default function DomainList({ user, action }: DomainListProps) {
<>
<Card className="xl:col-span-2">
<CardHeader className="flex flex-row items-center">
<CardDescription className="text-balance text-lg font-bold">
<div className="flex items-center gap-1 text-balance text-lg font-bold">
<span>Total Domains:</span>{" "}
<span className="font-bold">{data && data.total}</span>
</CardDescription>
{isLoading ? (
<Skeleton className="h-6 w-16" />
) : (
<span className="font-bold">{data && data.total}</span>
)}
</div>
<div className="ml-auto flex items-center justify-end gap-3">
<Button
@@ -161,30 +166,32 @@ export default function DomainList({ user, action }: DomainListProps) {
</div>
</CardHeader>
<CardContent>
{/* <div className="mb-2 flex-row items-center gap-2 space-y-2 sm:flex sm:space-y-0">
<div className="mb-2 flex-row items-center gap-2 space-y-2 sm:flex sm:space-y-0">
<div className="relative w-full">
<Input
className="h-8 text-xs md:text-xs"
placeholder="Search by slug..."
value={searchParams.slug}
placeholder="Search by domain name..."
value={searchParams.target}
onChange={(e) => {
setSearchParams({
...searchParams,
slug: e.target.value,
target: e.target.value,
});
}}
/>
{searchParams.slug && (
{searchParams.target && (
<Button
className="absolute right-2 top-1/2 h-6 -translate-y-1/2 rounded-full px-1 text-gray-500 hover:text-gray-700"
onClick={() => setSearchParams({ ...searchParams, slug: "" })}
onClick={() =>
setSearchParams({ ...searchParams, target: "" })
}
variant={"ghost"}
>
<Icons.close className="size-3" />
</Button>
)}
</div>
</div> */}
</div>
<Table>
<TableHeader className="bg-gray-100/50 dark:bg-primary-foreground">

View File

@@ -4,7 +4,6 @@ import {
createDomain,
deleteDomain,
getAllDomains,
invalidateDomainConfigCache,
updateDomain,
} from "@/lib/dto/domains";
import { checkUserStatus } from "@/lib/dto/user";
@@ -19,13 +18,18 @@ export async function GET(req: NextRequest) {
return Response.json("Unauthorized", { status: 401 });
}
// TODO: Add pagination
const domains = await getAllDomains();
const url = new URL(req.url);
const page = url.searchParams.get("page");
const size = url.searchParams.get("size");
const target = url.searchParams.get("target") || "";
return Response.json(
{ list: domains, total: domains.length },
{ status: 200 },
const data = await getAllDomains(
Number(page || "1"),
Number(size || "10"),
target,
);
return Response.json(data, { status: 200 });
} catch (error) {
console.error("[Error]", error);
return Response.json(error.message || "Server error", { status: 500 });
@@ -61,8 +65,6 @@ export async function POST(req: NextRequest) {
active: true,
});
invalidateDomainConfigCache();
return Response.json(newDomain, { status: 200 });
} catch (error) {
console.error("[Error]", error);
@@ -112,8 +114,6 @@ export async function PUT(req: NextRequest) {
max_dns_records,
});
invalidateDomainConfigCache();
return Response.json(updatedDomain, { status: 200 });
} catch (error) {
console.error("[Error]", error);
@@ -137,8 +137,6 @@ export async function DELETE(req: NextRequest) {
const deletedDomain = await deleteDomain(domain_name);
invalidateDomainConfigCache();
return Response.json(deletedDomain, { status: 200 });
} catch (error) {
console.error("[Error]", error);

View File

@@ -1,4 +1,4 @@
import { getAllDomains, invalidateDomainConfigCache } from "@/lib/dto/domains";
import { getAllDomains } from "@/lib/dto/domains";
export async function getDomainConfig() {
return await getAllDomains();
@@ -7,7 +7,7 @@ export async function getDomainConfig() {
export async function getCloudflareCredentials(domain_name: string) {
try {
const domains = await getAllDomains();
const domain = domains.find((d) => d.domain_name === domain_name);
const domain = domains.list.find((d) => d.domain_name === domain_name);
if (!domain || !domain.cf_api_key || !domain.cf_email) {
throw new Error(
`No Cloudflare credentials found for domain: ${domain_name}`,
@@ -32,5 +32,3 @@ export async function getCloudflareCredentials(domain_name: string) {
function decrypt(encryptedKey: string) {
return encryptedKey; // Replace with actual decryption logic
}
export { invalidateDomainConfigCache };

View File

@@ -5,7 +5,7 @@ import { prisma } from "../db";
// In-memory cache
let domainConfigCache: Domain[] | null = null;
let lastCacheUpdate = 0;
const CACHE_DURATION = 60 * 1000; // Cache for 1 minute in memory
const CACHE_DURATION = 60 * 1000;
export const FeatureMap = {
short: "enable_short_link",
@@ -34,20 +34,33 @@ export interface DomainFormData extends DomainConfig {
updatedAt: Date;
}
export async function getAllDomains() {
export async function getAllDomains(page = 1, size = 10, target: string = "") {
try {
const now = Date.now();
if (domainConfigCache && now - lastCacheUpdate < CACHE_DURATION) {
return domainConfigCache;
let option: any;
if (target) {
option = {
domain_name: {
contains: target,
},
};
}
const domains = await prisma.domain.findMany({
// where: { active: true },
});
const [total, list] = await prisma.$transaction([
prisma.domain.count({
where: option,
}),
prisma.domain.findMany({
where: option,
skip: (page - 1) * size,
take: size,
orderBy: {
updatedAt: "desc",
},
}),
]);
domainConfigCache = domains;
lastCacheUpdate = now;
return domains;
return { list, total };
} catch (error) {
throw new Error(`Failed to fetch domain config: ${error.message}`);
}
@@ -58,11 +71,6 @@ export async function getDomainsByFeature(
admin: boolean = false,
) {
try {
const now = Date.now();
if (domainConfigCache && now - lastCacheUpdate < CACHE_DURATION) {
return domainConfigCache;
}
const domains = await prisma.domain.findMany({
where: { [feature]: true },
select: {
@@ -129,8 +137,3 @@ export async function deleteDomain(domain_name: string) {
throw new Error(`Failed to delete domain`);
}
}
export function invalidateDomainConfigCache() {
domainConfigCache = null;
lastCacheUpdate = 0;
}

File diff suppressed because one or more lines are too long