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

feat: 포트포워딩 수정 페이지 API 연동

parent f99d9645
No related branches found
No related tags found
No related merge requests found
import { z } from 'zod';
import { useEffect, useState, useRef } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import { Link, useNavigate, useParams } from 'react-router';
import { useForm } from 'react-hook-form';
import { toast } from 'sonner';
import { Skeleton } from '@/components/ui/skeleton';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Input } from '@/components/ui/input';
import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/form';
import { useAuthStore } from '@/stores/authStore';
const formSchema = z.object({
name: z.string({ required_error: '서버 이름을 입력해주세요' }).min(1, { message: '서버 이름을 입력해주세요' }),
serverPort: z
.number({ required_error: '도메인 주소를 입력해주세요' })
.min(20000, { message: '포트 번호는 20000 이상이어야 합니다' })
.max(29999, { message: '포트 번호는 29999 이하여야 합니다' }),
instanceIp: z
.string({ required_error: '인스턴스 IP를 입력해주세요' })
.regex(/^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$/, {
message: '올바른 IP 주소를 입력해주세요',
})
.startsWith('10.16.', { message: '인스턴스 IP는 10.16.0.0/16 대역을 사용해야 합니다' }),
});
export default function ForwardingEdit() {
const navigate = useNavigate();
const { id } = useParams();
const { authFetch } = useAuthStore();
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
});
const initialData = useRef<z.infer<typeof formSchema> | null>(null);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
authFetch(`/api/forwarding?forwardingId=${id}`)
.then((response) => {
if (!response.ok) {
console.error(response);
throw Error();
}
return response.json();
})
.then(({ name, serverPort, instanceIp }) => {
initialData.current = { name, serverPort: parseInt(serverPort), instanceIp };
form.setValue('name', name);
form.setValue('serverPort', parseInt(serverPort));
form.setValue('instanceIp', instanceIp);
setIsLoading(false);
})
.catch((error) => {
console.error(error);
toast.error('포트포워딩 정보를 조회할 수 없습니다.');
});
});
async function onSubmit(values: z.infer<typeof formSchema>) {
if (!initialData.current) return;
const payload: Partial<z.infer<typeof formSchema>> = {};
if (values.name !== initialData.current.name) payload.name = values.name;
if (values.serverPort !== initialData.current.serverPort) payload.serverPort = values.serverPort;
if (values.instanceIp !== initialData.current.instanceIp) payload.instanceIp = values.instanceIp;
const response = await authFetch(`/api/forwarding?forwardingId=${id}`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
});
if (!response.ok) {
console.error(response);
const { code } = await response.json();
if (code == 'DUPLICATED_INSTANCE_INFO') {
form.setError('instanceIp', { type: 'custom' });
toast.error('이미 존재하는 포트포워딩 설정입니다');
} else if (code == 'DUPLICATED_SERVER_PORT') {
form.setError('serverPort', { type: 'custom' });
toast.error('이미 사용 중인 포트 번호입니다');
} else {
toast.error('포트포워딩 설정 수정에 실패했습니다');
}
} else {
toast.success('포트포워딩 설정이 수정되었습니다');
navigate('/forwarding');
}
}
return (
<div className="flex flex-1 flex-col gap-4 p-6">
<div className="mb-2">
<h1 className="scroll-m-20 text-3xl font-semibold first:mt-0">포트포워딩 설정 수정</h1>
<p className="mt-1 text-base text-gray-500">기존 포트포워딩 설정을 수정합니다.</p>
</div>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<Card className="mb-4">
<CardHeader>
{isLoading ? (
<Skeleton className="h-[1.75rem] w-[16rem]" />
) : (
<CardTitle className="text-xl">SSH 포트포워딩 설정</CardTitle>
)}
</CardHeader>
<CardContent className="space-y-4">
{isLoading ? (
<Skeleton className="h-[16rem] w-full" />
) : (
<div className="grid grid-cols-1 gap-4">
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel required>서버 이름</FormLabel>
<FormControl>
<Input placeholder="웹 서버 이름" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="serverPort"
render={({ field }) => (
<FormItem>
<FormLabel required>포트 번호</FormLabel>
<FormControl>
<Input
type="number"
min={20000}
max={29999}
placeholder="20000 ~ 29999"
{...field}
onChange={(e) => field.onChange(+e.target.value)}
/>
</FormControl>
<FormDescription>ssh.aoldacloud.com:&lt;포트번호&gt;로 접속 가능합니다</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="instanceIp"
render={({ field }) => (
<FormItem>
<FormLabel required>인스턴스 IP</FormLabel>
<FormControl>
<Input placeholder="10.16.x.x" {...field} />
</FormControl>
<FormDescription>인스턴스 IP는 10.16.0.0/16 대역을 사용합니다</FormDescription>
<FormMessage />
</FormItem>
)}
/>
</div>
)}
</CardContent>
</Card>
<div className="flex justify-end gap-2">
<Button variant="outline" asChild>
<Link to="..">취소</Link>
</Button>
<Button type="submit">저장</Button>
</div>
</form>
</Form>
</div>
);
}
......@@ -9,6 +9,7 @@ import CertificateList from './pages/certificate/List';
import CertificateCreate from './pages/certificate/Create';
import ForwardingList from '@/pages/forwarding/List';
import ForwardingCreate from './pages/forwarding/Create';
import ForwardingEdit from './pages/forwarding/Edit';
export default function AppRoutes() {
return (
......@@ -27,6 +28,7 @@ export default function AppRoutes() {
<Route path="forwarding">
<Route index element={<ForwardingList />} />
<Route path="create" element={<ForwardingCreate />} />
<Route path="edit/:id" element={<ForwardingEdit />} />
</Route>
<Route path="*" element={<NotFound />} />
</Route>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment