Skip to content
Snippets Groups Projects
Commit 7a50bd54 authored by 한동현's avatar 한동현
Browse files

feat: 인증서 등록 페이지 API 연동

parent 59f25fea
No related branches found
No related tags found
No related merge requests found
import { z } from 'zod'; import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/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 { 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 { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
...@@ -11,6 +11,7 @@ import { Separator } from '@/components/ui/separator'; ...@@ -11,6 +11,7 @@ import { Separator } from '@/components/ui/separator';
import { toast } from 'sonner'; import { toast } from 'sonner';
import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/form'; import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/form';
import { Badge } from '@/components/ui/badge'; import { Badge } from '@/components/ui/badge';
import { useAuthStore } from '@/stores/authStore';
const formSchema = z const formSchema = z
.object({ .object({
...@@ -24,32 +25,61 @@ const formSchema = z ...@@ -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,}$/, { .regex(/^(?=.{1,253}$)(?:(?:\*\.)?[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/, {
message: '올바른 도메인 주소를 입력해주세요', message: '올바른 도메인 주소를 입력해주세요',
}), }),
api_token: z.string(), apiToken: z.string(),
dns_challenge: z.boolean(), 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 토큰을 입력해주세요', 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를 사용해야 합니다', message: '와일드카드 도메인 인증서는 DNS Challenge를 사용해야 합니다',
path: ['domain'], path: ['domain'],
}); });
export default function CertificateCreate() { export default function CertificateCreate() {
const navigate = useNavigate();
const { authFetch, selectedProject } = useAuthStore();
const form = useForm<z.infer<typeof formSchema>>({ const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema), resolver: zodResolver(formSchema),
defaultValues: { defaultValues: {
email: '', email: '',
domain: '', domain: '',
dns_challenge: false, dnsChallenge: false,
api_token: '', apiToken: '',
}, },
}); });
function onSubmit(values: z.infer<typeof formSchema>) { async function onSubmit(values: z.infer<typeof formSchema>) {
console.log('제출된 데이터:', values); const { email, domain, apiToken, dnsChallenge } = values;
toast.warning('신규 SSL 인증서를 등록합니다'); 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 ( return (
...@@ -108,7 +138,7 @@ export default function CertificateCreate() { ...@@ -108,7 +138,7 @@ export default function CertificateCreate() {
<CardContent className="space-y-4"> <CardContent className="space-y-4">
<FormField <FormField
control={form.control} control={form.control}
name="dns_challenge" name="dnsChallenge"
render={({ field }) => ( render={({ field }) => (
<FormItem className="flex flex-row items-center justify-between"> <FormItem className="flex flex-row items-center justify-between">
<div className="space-y-0.5"> <div className="space-y-0.5">
...@@ -137,12 +167,12 @@ export default function CertificateCreate() { ...@@ -137,12 +167,12 @@ export default function CertificateCreate() {
)} )}
/> />
{form.watch('dns_challenge') && ( {form.watch('dnsChallenge') && (
<> <>
<Separator /> <Separator />
<FormField <FormField
control={form.control} control={form.control}
name="api_token" name="apiToken"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel required>Cloudflare DNS API 토큰</FormLabel> <FormLabel required>Cloudflare DNS API 토큰</FormLabel>
...@@ -171,7 +201,10 @@ export default function CertificateCreate() { ...@@ -171,7 +201,10 @@ export default function CertificateCreate() {
<Button variant="outline" asChild> <Button variant="outline" asChild>
<Link to="..">취소</Link> <Link to="..">취소</Link>
</Button> </Button>
<Button type="submit">저장</Button> <Button type="submit" disabled={form.formState.isSubmitting}>
{form.formState.isSubmitting && <LoaderCircle className="animate-spin" />}
저장
</Button>
</div> </div>
</form> </form>
</Form> </Form>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment