From 7a50bd542dc6aab4bb1067c0abbf08816b80a526 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=95=9C=EB=8F=99=ED=98=84?= <hando1220@ajou.ac.kr> Date: Tue, 6 May 2025 00:22:54 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=9D=B8=EC=A6=9D=EC=84=9C=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20=ED=8E=98=EC=9D=B4=EC=A7=80=20API=20=EC=97=B0?= =?UTF-8?q?=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/certificate/Create.tsx | 65 ++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 16 deletions(-) diff --git a/src/pages/certificate/Create.tsx b/src/pages/certificate/Create.tsx index 0570ca9..d6a2192 100644 --- a/src/pages/certificate/Create.tsx +++ b/src/pages/certificate/Create.tsx @@ -1,8 +1,8 @@ import { z } from 'zod'; import { zodResolver } from '@hookform/resolvers/zod'; -import { Link } from 'react-router'; +import { Link, useNavigate } from 'react-router'; import { useForm } from 'react-hook-form'; -import { Globe, HardDrive } from 'lucide-react'; +import { Globe, HardDrive, LoaderCircle } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Input } from '@/components/ui/input'; @@ -11,6 +11,7 @@ import { Separator } from '@/components/ui/separator'; import { toast } from 'sonner'; import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/form'; import { Badge } from '@/components/ui/badge'; +import { useAuthStore } from '@/stores/authStore'; const formSchema = z .object({ @@ -24,32 +25,61 @@ const formSchema = z .regex(/^(?=.{1,253}$)(?:(?:\*\.)?[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/, { message: '올바른 도메인 주소를 입력해주세요', }), - api_token: z.string(), - dns_challenge: z.boolean(), + apiToken: z.string(), + dnsChallenge: z.boolean(), }) - .refine((data) => !data.dns_challenge || (data.dns_challenge && data.api_token !== ''), { + .refine((data) => !data.dnsChallenge || (data.dnsChallenge && data.apiToken !== ''), { message: 'Cloudflare API 토큰을 입력해주세요', - path: ['api_token'], + path: ['apiToken'], }) - .refine((data) => !data.domain.startsWith('*.') || data.dns_challenge, { + .refine((data) => !data.domain.startsWith('*.') || data.dnsChallenge, { message: '와일드카드 도메인 인증서는 DNS Challenge를 사용해야 합니다', path: ['domain'], }); export default function CertificateCreate() { + const navigate = useNavigate(); + const { authFetch, selectedProject } = useAuthStore(); const form = useForm<z.infer<typeof formSchema>>({ resolver: zodResolver(formSchema), defaultValues: { email: '', domain: '', - dns_challenge: false, - api_token: '', + dnsChallenge: false, + apiToken: '', }, }); - function onSubmit(values: z.infer<typeof formSchema>) { - console.log('제출된 데이터:', values); - toast.warning('신규 SSL 인증서를 등록합니다'); + async function onSubmit(values: z.infer<typeof formSchema>) { + const { email, domain, apiToken, dnsChallenge } = values; + const challenge = dnsChallenge ? 'dns_cloudflare' : 'http'; + + const response = await authFetch(`/api/certificate?projectId=${selectedProject?.id}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + email, + domain, + challenge, + apiToken, + }), + }); + + if (!response.ok) { + console.error(response); + + const { code } = await response.json(); + if (code == 'UNAUTHORIZED_USER') { + toast.error('권한이 없는 사용자입니다'); + } else { + toast.error('인증서 등록에 실패했습니다'); + } + } else { + toast.success('SSL 인증서가 등록되었습니다'); + navigate('/certificate'); + } } return ( @@ -108,7 +138,7 @@ export default function CertificateCreate() { <CardContent className="space-y-4"> <FormField control={form.control} - name="dns_challenge" + name="dnsChallenge" render={({ field }) => ( <FormItem className="flex flex-row items-center justify-between"> <div className="space-y-0.5"> @@ -137,12 +167,12 @@ export default function CertificateCreate() { )} /> - {form.watch('dns_challenge') && ( + {form.watch('dnsChallenge') && ( <> <Separator /> <FormField control={form.control} - name="api_token" + name="apiToken" render={({ field }) => ( <FormItem> <FormLabel required>Cloudflare DNS API 토큰</FormLabel> @@ -171,7 +201,10 @@ export default function CertificateCreate() { <Button variant="outline" asChild> <Link to="..">취소</Link> </Button> - <Button type="submit">저장</Button> + <Button type="submit" disabled={form.formState.isSubmitting}> + {form.formState.isSubmitting && <LoaderCircle className="animate-spin" />} + 저장 + </Button> </div> </form> </Form> -- GitLab