feats(record): support check cf configs access

This commit is contained in:
oiov
2025-05-27 15:54:33 +08:00
parent 6505d1876a
commit 157c07c747
4 changed files with 105 additions and 11 deletions
+24
View File
@@ -0,0 +1,24 @@
import { NextRequest } from "next/server";
import { getZoneDetail } from "@/lib/cloudflare";
import { checkUserStatus } from "@/lib/dto/user";
import { getCurrentUser } from "@/lib/session";
export async function GET(req: NextRequest) {
try {
const user = checkUserStatus(await getCurrentUser());
if (user instanceof Response) return user;
const url = new URL(req.url);
const zone_id = url.searchParams.get("zone_id") || "";
const api_key = url.searchParams.get("api_key") || "";
const email = url.searchParams.get("email") || "";
const res = await getZoneDetail(zone_id, api_key, email);
if (res === 200) return Response.json(200, { status: 200 });
else return Response.json(400, { status: 400 });
} catch (error) {
return Response.json(500, { status: 500 });
}
}
+54 -10
View File
@@ -1,17 +1,21 @@
"use client";
import { Dispatch, SetStateAction, useState, useTransition } from "react";
import {
Dispatch,
SetStateAction,
useEffect,
useState,
useTransition,
} from "react";
import Link from "next/link";
import { zodResolver } from "@hookform/resolvers/zod";
import { User } from "@prisma/client";
import { Sparkles } from "lucide-react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { siteConfig } from "@/config/site";
import { getZoneDetail } from "@/lib/cloudflare";
import { DomainFormData } from "@/lib/dto/domains";
import { EXPIRATION_ENUMS } from "@/lib/enums";
import { generateUrlSuffix } from "@/lib/utils";
import { cn } from "@/lib/utils";
import { createDomainSchema } from "@/lib/validations/domain";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
@@ -50,9 +54,11 @@ export function DomainForm({
}: DomainFormProps) {
const [isPending, startTransition] = useTransition();
const [isDeleting, startDeleteTransition] = useTransition();
const [isChecking, startCheckTransition] = useTransition();
const [currentRecordStatus, setCurrentRecordStatus] = useState(
initData?.enable_dns || false,
);
const [isChecked, setIsChecked] = useState(false);
const {
handleSubmit,
@@ -152,10 +158,47 @@ export function DomainForm({
}
};
const handleCheckAccess = async (event) => {
event?.stopPropagation();
if (!currentRecordStatus) return;
if (isChecked) {
setIsChecked(false);
}
startCheckTransition(async () => {
const values = getValues(["cf_zone_id", "cf_api_key", "cf_email"]);
const res = await fetch(
`/api/domain/access-check?zone_id=${values[0]}&api_key=${values[1]}&email=${values[2]}`,
);
if (res.ok) {
const data = await res.json();
if (data === 200) {
setIsChecked(true);
return;
}
}
setIsChecked(false);
toast.error("Access Failed", {
description: "Please check your Cloudflare settings and try again.",
});
});
};
const ReadyBadge = (
<Badge className="text-xs font-semibold" variant="green">
<Icons.check className="mr-1 size-3" />
Ready
<Badge
className={cn(
"ml-auto text-xs font-semibold",
!currentRecordStatus && "text-muted-foreground",
)}
variant={
currentRecordStatus ? (isChecked ? "green" : "default") : "outline"
}
onClick={(event) => handleCheckAccess(event)}
>
{isChecking && <Icons.spinner className="mr-1 size-3 animate-spin" />}
{isChecked && !isChecking && <Icons.check className="mr-1 size-3" />}
{isChecked ? "Ready" : "Access Check"}
</Badge>
);
@@ -257,10 +300,11 @@ export function DomainForm({
<Collapsible className="relative mt-2 rounded-md bg-neutral-100 p-4 dark:bg-neutral-800">
<CollapsibleTrigger className="flex w-full items-center justify-between">
<h2 className="absolute left-2 top-4 text-xs font-semibold text-neutral-400">
<h2 className="absolute left-2 top-5 text-xs font-semibold text-neutral-400">
Cloudflare Configs(Optional)
</h2>
<Icons.chevronDown className="ml-auto size-4" />
{ReadyBadge}
<Icons.chevronDown className="ml-2 size-4" />
</CollapsibleTrigger>
<CollapsibleContent>
{!currentRecordStatus && (
+26
View File
@@ -223,3 +223,29 @@ export const getDNSRecordDetail = async (
throw error;
}
};
export const getZoneDetail = async (
zoneId: string,
apiKey: string,
email: string,
) => {
try {
const url = `${CLOUDFLARE_API_URL}/zones/${zoneId}`;
const headers = {
"X-Auth-Email": email,
"X-Auth-Key": apiKey,
};
const response = await fetch(url, {
method: "GET",
headers,
});
console.log(response.status);
return response.status;
} catch (error) {
throw error;
}
};
+1 -1
View File
File diff suppressed because one or more lines are too long