fixup
This commit is contained in:
@@ -41,12 +41,21 @@ export default function LoginPage() {
|
||||
<UserAuthForm />
|
||||
</Suspense>
|
||||
<p className="px-8 text-center text-sm text-muted-foreground">
|
||||
By clicking continue, you agree to our{" "}
|
||||
<Link
|
||||
href="/register"
|
||||
href="/terms"
|
||||
className="hover:text-brand underline underline-offset-4"
|
||||
>
|
||||
Don't have an account? Sign Up
|
||||
Terms of Service
|
||||
</Link>{" "}
|
||||
and{" "}
|
||||
<Link
|
||||
href="/privacy"
|
||||
className="hover:text-brand underline underline-offset-4"
|
||||
>
|
||||
Privacy Policy
|
||||
</Link>
|
||||
.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,8 +4,8 @@ import { useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { UserRecordFormData } from "@/actions/cloudflare-dns-record";
|
||||
import { User } from "@prisma/client";
|
||||
import { ArrowUpRight } from "lucide-react";
|
||||
import useSWR from "swr";
|
||||
import { ArrowUpRight, DotSquareIcon, RefreshCwIcon } from "lucide-react";
|
||||
import useSWR, { useSWRConfig } from "swr";
|
||||
|
||||
import { TTL_ENUMS } from "@/lib/cloudflare";
|
||||
import { fetcher } from "@/lib/utils";
|
||||
@@ -66,6 +66,7 @@ export default function UserRecordsList({ user }: RecordListProps) {
|
||||
const [currentEditRecord, setCurrentEditRecord] =
|
||||
useState<UserRecordFormData | null>(null);
|
||||
|
||||
const { mutate } = useSWRConfig();
|
||||
const { data, error, isLoading } = useSWR<UserRecordFormData[]>(
|
||||
"/api/record",
|
||||
fetcher,
|
||||
@@ -74,6 +75,10 @@ export default function UserRecordsList({ user }: RecordListProps) {
|
||||
},
|
||||
);
|
||||
|
||||
const handleRefresh = () => {
|
||||
mutate("/api/record", undefined);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card className="xl:col-span-2">
|
||||
@@ -84,18 +89,31 @@ export default function UserRecordsList({ user }: RecordListProps) {
|
||||
All Dns Records
|
||||
</CardDescription>
|
||||
</div>
|
||||
<Button
|
||||
className="ml-auto w-[120px] shrink-0 gap-1"
|
||||
variant="default"
|
||||
onClick={() => {
|
||||
setCurrentEditRecord(null);
|
||||
setShowForm(false);
|
||||
setFormType("add");
|
||||
setShowForm(!isShowForm);
|
||||
}}
|
||||
>
|
||||
Add record
|
||||
</Button>
|
||||
<div className="ml-auto flex items-center justify-end gap-3">
|
||||
<Button
|
||||
variant={"outline"}
|
||||
onClick={() => handleRefresh()}
|
||||
disabled={isLoading}
|
||||
>
|
||||
{isLoading ? (
|
||||
<RefreshCwIcon className="h-4 w-4 animate-spin" />
|
||||
) : (
|
||||
<RefreshCwIcon className="h-4 w-4" />
|
||||
)}
|
||||
</Button>
|
||||
<Button
|
||||
className="w-[120px] shrink-0 gap-1"
|
||||
variant="default"
|
||||
onClick={() => {
|
||||
setCurrentEditRecord(null);
|
||||
setShowForm(false);
|
||||
setFormType("add");
|
||||
setShowForm(!isShowForm);
|
||||
}}
|
||||
>
|
||||
Add record
|
||||
</Button>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{isShowForm && (
|
||||
@@ -105,6 +123,7 @@ export default function UserRecordsList({ user }: RecordListProps) {
|
||||
setShowForm={setShowForm}
|
||||
type={formType}
|
||||
initData={currentEditRecord}
|
||||
onRefresh={handleRefresh}
|
||||
/>
|
||||
)}
|
||||
<Table>
|
||||
@@ -179,7 +198,18 @@ export default function UserRecordsList({ user }: RecordListProps) {
|
||||
<EmptyPlaceholder.Description>
|
||||
You don't have any record yet. Start creating record.
|
||||
</EmptyPlaceholder.Description>
|
||||
<Button>Add Record</Button>
|
||||
<Button
|
||||
className="w-[120px] shrink-0 gap-1"
|
||||
variant="default"
|
||||
onClick={() => {
|
||||
setCurrentEditRecord(null);
|
||||
setShowForm(false);
|
||||
setFormType("add");
|
||||
setShowForm(!isShowForm);
|
||||
}}
|
||||
>
|
||||
Add record
|
||||
</Button>
|
||||
</EmptyPlaceholder>
|
||||
)}
|
||||
</TableBody>
|
||||
|
||||
@@ -7,11 +7,14 @@ import {
|
||||
import { env } from "@/env.mjs";
|
||||
import { createDNSRecord } from "@/lib/cloudflare";
|
||||
import { getCurrentUser } from "@/lib/session";
|
||||
import { checkUserStatus } from "@/lib/user";
|
||||
import { generateSecret } from "@/lib/utils";
|
||||
|
||||
export async function POST(req: Request) {
|
||||
try {
|
||||
const user = await getCurrentUser();
|
||||
const user = checkUserStatus(await getCurrentUser());
|
||||
if (user instanceof Response) return user;
|
||||
|
||||
const { records } = await req.json();
|
||||
const {
|
||||
CLOUDFLARE_ZONE_ID,
|
||||
@@ -20,13 +23,6 @@ export async function POST(req: Request) {
|
||||
NEXT_PUBLIC_FREE_RECORD_QUOTA,
|
||||
} = env;
|
||||
|
||||
if (!user?.id) {
|
||||
return Response.json("Unauthorized", {
|
||||
status: 401,
|
||||
statusText: "Unauthorized",
|
||||
});
|
||||
}
|
||||
|
||||
if (!CLOUDFLARE_ZONE_ID || !CLOUDFLARE_API_KEY || !CLOUDFLARE_EMAIL) {
|
||||
return Response.json("API key、zone iD and email are required", {
|
||||
status: 400,
|
||||
@@ -106,9 +102,9 @@ export async function POST(req: Request) {
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return Response.json(error, {
|
||||
status: 500,
|
||||
statusText: "Server error",
|
||||
return Response.json(error?.statusText || error, {
|
||||
status: error?.status || 500,
|
||||
statusText: error?.statusText || "Server error",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,38 @@
|
||||
import { deleteUserRecord } from "@/actions/cloudflare-dns-record";
|
||||
|
||||
import { env } from "@/env.mjs";
|
||||
import { deleteDNSRecord } from "@/lib/cloudflare";
|
||||
import { getCurrentUser } from "@/lib/session";
|
||||
import { checkUserStatus } from "@/lib/user";
|
||||
|
||||
export async function POST(req: Request) {
|
||||
try {
|
||||
const user = await getCurrentUser();
|
||||
if (!user?.id) {
|
||||
return Response.json("Unauthorized", {
|
||||
status: 401,
|
||||
statusText: "Unauthorized",
|
||||
});
|
||||
}
|
||||
const user = checkUserStatus(await getCurrentUser());
|
||||
if (user instanceof Response) return user;
|
||||
|
||||
const { record_id, zone_id, active } = await req.json();
|
||||
const { CLOUDFLARE_ZONE_ID, CLOUDFLARE_API_KEY, CLOUDFLARE_EMAIL } = env;
|
||||
|
||||
await deleteUserRecord(user.id, record_id, zone_id, active);
|
||||
// await
|
||||
// Delete cf dns record first.
|
||||
const res = await deleteDNSRecord(
|
||||
CLOUDFLARE_ZONE_ID,
|
||||
CLOUDFLARE_API_KEY,
|
||||
CLOUDFLARE_EMAIL,
|
||||
record_id,
|
||||
);
|
||||
if (res && res.result?.id) {
|
||||
// Then delete user record.
|
||||
await deleteUserRecord(user.id, record_id, zone_id, active);
|
||||
return Response.json({
|
||||
status: 200,
|
||||
statusText: "success",
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return Response.json(error, {
|
||||
status: 500,
|
||||
statusText: "Server error",
|
||||
return Response.json(error?.statusText || error, {
|
||||
status: error.status || 500,
|
||||
statusText: error.statusText || "Server error",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+6
-11
@@ -2,26 +2,21 @@ import { getUserRecords } from "@/actions/cloudflare-dns-record";
|
||||
|
||||
import { env } from "@/env.mjs";
|
||||
import { getCurrentUser } from "@/lib/session";
|
||||
import { checkUserStatus } from "@/lib/user";
|
||||
|
||||
export async function GET(req: Request) {
|
||||
try {
|
||||
const user = await getCurrentUser();
|
||||
if (!user?.id) {
|
||||
return Response.json("Unauthorized", {
|
||||
status: 401,
|
||||
statusText: "Unauthorized",
|
||||
});
|
||||
}
|
||||
|
||||
const user = checkUserStatus(await getCurrentUser());
|
||||
if (user instanceof Response) return user;
|
||||
// const { CLOUDFLARE_ZONE_ID, CLOUDFLARE_API_KEY, CLOUDFLARE_EMAIL } = env;
|
||||
|
||||
const user_records = await getUserRecords(user.id, 1);
|
||||
|
||||
return Response.json(user_records);
|
||||
} catch (error) {
|
||||
return Response.json(error, {
|
||||
status: 500,
|
||||
statusText: "Server error",
|
||||
return Response.json(error?.statusText || error, {
|
||||
status: error.status || 500,
|
||||
statusText: error.statusText || "Server error",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { env } from "@/env.mjs";
|
||||
import { getCurrentUser } from "@/lib/session";
|
||||
import { checkUserStatus } from "@/lib/user";
|
||||
|
||||
export async function POST(req: Request) {
|
||||
try {
|
||||
const user = checkUserStatus(await getCurrentUser());
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return Response.json(error, {
|
||||
|
||||
@@ -38,6 +38,7 @@ export interface RecordFormProps {
|
||||
setShowForm: Dispatch<SetStateAction<boolean>>;
|
||||
type: FormType;
|
||||
initData?: UserRecordFormData | null;
|
||||
onRefresh: () => void;
|
||||
}
|
||||
|
||||
export function RecordForm({
|
||||
@@ -46,8 +47,10 @@ export function RecordForm({
|
||||
setShowForm,
|
||||
type,
|
||||
initData,
|
||||
onRefresh,
|
||||
}: RecordFormProps) {
|
||||
const [isPending, startTransition] = useTransition();
|
||||
const [isDeleting, startDeleteTransition] = useTransition();
|
||||
|
||||
const {
|
||||
handleSubmit,
|
||||
@@ -66,68 +69,79 @@ export function RecordForm({
|
||||
});
|
||||
|
||||
const onSubmit = handleSubmit((data) => {
|
||||
startTransition(async () => {
|
||||
if (type === "add") {
|
||||
handleCreateRecord(data);
|
||||
} else if (type === "edit") {
|
||||
handleUpdateRecord(data);
|
||||
}
|
||||
});
|
||||
if (type === "add") {
|
||||
handleCreateRecord(data);
|
||||
} else if (type === "edit") {
|
||||
handleUpdateRecord(data);
|
||||
}
|
||||
});
|
||||
|
||||
const handleCreateRecord = async (data: CreateDNSRecord) => {
|
||||
const response = await fetch("/api/record/add", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
records: [data],
|
||||
}),
|
||||
});
|
||||
if (!response.ok || response.status !== 200) {
|
||||
toast.error("Created Failed!", {
|
||||
description: response.statusText,
|
||||
startTransition(async () => {
|
||||
const response = await fetch("/api/record/add", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
records: [data],
|
||||
}),
|
||||
});
|
||||
} else {
|
||||
const res = await response.json();
|
||||
toast.success(`Created successfully!`);
|
||||
setShowForm(false);
|
||||
}
|
||||
if (!response.ok || response.status !== 200) {
|
||||
toast.error("Created Failed!", {
|
||||
description: response.statusText,
|
||||
});
|
||||
} else {
|
||||
const res = await response.json();
|
||||
toast.success(`Created successfully!`);
|
||||
setShowForm(false);
|
||||
onRefresh();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleUpdateRecord = async (data: CreateDNSRecord) => {
|
||||
const response = await fetch("/api/record/update", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
records: [data],
|
||||
}),
|
||||
startTransition(async () => {
|
||||
if (type === "edit") {
|
||||
const response = await fetch("/api/record/update", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
records: [data],
|
||||
}),
|
||||
});
|
||||
if (!response.ok || response.status !== 200) {
|
||||
toast.error("Update Failed", {
|
||||
description: response.statusText,
|
||||
});
|
||||
} else {
|
||||
const res = await response.json();
|
||||
toast.success(`Update successfully!`);
|
||||
setShowForm(false);
|
||||
onRefresh();
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!response.ok || response.status !== 200) {
|
||||
toast.error("Update Failed", {
|
||||
description: response.statusText,
|
||||
});
|
||||
} else {
|
||||
const res = await response.json();
|
||||
toast.success(`Update successfully!`);
|
||||
setShowForm(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeleteRecord = async () => {
|
||||
const response = await fetch("/api/record/delete", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
record_id: initData?.record_id,
|
||||
zone_id: initData?.zone_id,
|
||||
active: initData?.active,
|
||||
}),
|
||||
});
|
||||
if (!response.ok || response.status !== 200) {
|
||||
toast.error("Delete Failed", {
|
||||
description: response.statusText,
|
||||
if (type === "edit") {
|
||||
startTransition(async () => {
|
||||
const response = await fetch("/api/record/delete", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
record_id: initData?.record_id,
|
||||
zone_id: initData?.zone_id,
|
||||
active: initData?.active,
|
||||
}),
|
||||
});
|
||||
if (!response.ok || response.status !== 200) {
|
||||
toast.error("Delete Failed", {
|
||||
description: response.statusText,
|
||||
});
|
||||
} else {
|
||||
await response.json();
|
||||
toast.success(`Success`);
|
||||
setShowForm(false);
|
||||
onRefresh();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
await response.json();
|
||||
toast.success(`Delete successfully!`);
|
||||
setShowForm(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -261,16 +275,22 @@ export function RecordForm({
|
||||
<div className="flex justify-end gap-3">
|
||||
{type === "edit" && (
|
||||
<Button
|
||||
variant={"destructive"}
|
||||
type="button"
|
||||
variant="destructive"
|
||||
className="mr-auto w-[80px] px-0"
|
||||
onClick={() => handleDeleteRecord()}
|
||||
disabled={isDeleting}
|
||||
>
|
||||
Delete
|
||||
{isDeleting ? (
|
||||
<Icons.spinner className="size-4 animate-spin" />
|
||||
) : (
|
||||
<p>Delete</p>
|
||||
)}
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
type="reset"
|
||||
variant={"outline"}
|
||||
variant="outline"
|
||||
className="w-[80px] px-0"
|
||||
onClick={() => setShowForm(false)}
|
||||
>
|
||||
|
||||
+17
-1
@@ -26,4 +26,20 @@ export const getUserById = async (id: string) => {
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export function checkUserStatus(user: any) {
|
||||
if (!user?.id) {
|
||||
throw new Response("Unauthorized", {
|
||||
status: 401,
|
||||
statusText: "Unauthorized",
|
||||
});
|
||||
}
|
||||
if (user.active === 0) {
|
||||
throw new Response("Forbidden", {
|
||||
status: 403,
|
||||
statusText: "Forbidden",
|
||||
});
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user