From 627e77e11b2cd65e2e69704f3c2c8ea5e368433b 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: Sun, 2 Mar 2025 00:39:51 +0900
Subject: [PATCH] =?UTF-8?q?feat:=20SSH=20=ED=8F=AC=ED=8A=B8=ED=8F=AC?=
 =?UTF-8?q?=EC=9B=8C=EB=94=A9=20=EB=AA=A9=EB=A1=9D=20=ED=8E=98=EC=9D=B4?=
 =?UTF-8?q?=EC=A7=80=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/pages/forwarding/List.tsx | 118 ++++++++++++++++++++++++++++++++++
 src/routes.tsx                |   4 ++
 2 files changed, 122 insertions(+)
 create mode 100644 src/pages/forwarding/List.tsx

diff --git a/src/pages/forwarding/List.tsx b/src/pages/forwarding/List.tsx
new file mode 100644
index 0000000..30dcb86
--- /dev/null
+++ b/src/pages/forwarding/List.tsx
@@ -0,0 +1,118 @@
+import { Filter, Plus, Pencil, Trash } from 'lucide-react';
+import { Button } from '@/components/ui/button';
+import { Card, CardContent } from '@/components/ui/card';
+import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
+import { HoverCard, HoverCardContent, HoverCardTrigger } from '@/components/ui/hover-card';
+import { Input } from '@/components/ui/input';
+import { Link } from 'react-router';
+
+const forwardings = [
+  {
+    id: 1,
+    name: '아올다 프록시 매니저 콘솔',
+    created_at: '2021-09-01 11:43:00',
+    updated_at: '2021-09-01 12:00:00',
+    port: 20444,
+    instance_ip: '10.16.0.10',
+  },
+  {
+    id: 2,
+    name: '아올다 블로그',
+    created_at: '2021-09-01 12:00:00',
+    updated_at: '2021-09-01 12:01:00',
+    port: 22222,
+    instance_ip: '10.16.0.11',
+  },
+  {
+    id: 3,
+    name: '개인 블로그',
+    created_at: '2021-09-01 12:01:00',
+    updated_at: '2021-09-01 13:00:00',
+    port: 20411,
+    instance_ip: '10.16.3.23',
+  },
+  {
+    id: 4,
+    name: '아올다 테스트 서버',
+    created_at: '2021-09-01 13:00:00',
+    updated_at: '2021-09-02 12:00:00',
+    port: 20432,
+    instance_ip: '10.16.32.1',
+  },
+];
+
+export default function ForwardingList() {
+  return (
+    <div className="flex flex-1 flex-col gap-4 p-6">
+      <div className="flex justify-between mb-2">
+        <div>
+          <h1 className="scroll-m-20 text-3xl font-semibold first:mt-0">SSH 포트포워딩 설정</h1>
+          <p className="mt-1 text-base text-gray-500">현재 4개의 SSH 포트포워딩이 설정되어 있습니다.</p>
+        </div>
+        <Button className="ml-2" asChild>
+          <Link to="./create">
+            <Plus className="h-4 w-4" /> 새 포트포워딩 추가
+          </Link>
+        </Button>
+      </div>
+      <Card>
+        <CardContent>
+          <div className="flex w-full items-center space-x-2 mb-4">
+            <Filter className="mr-3" />
+            <Input placeholder="이름, 포트, 인스턴스 IP로 검색..." />
+            <Button variant="secondary">검색</Button>
+          </div>
+          <Table>
+            <TableHeader>
+              <TableRow>
+                <TableHead className="w-48">이름</TableHead>
+                <TableHead>포트</TableHead>
+                <TableHead>인스턴스 IP</TableHead>
+                <TableHead className="w-32 min-w-32 text-center">작업</TableHead>
+              </TableRow>
+            </TableHeader>
+            <TableBody>
+              {forwardings.map((forwarding) => (
+                <TableRow key={forwarding.id}>
+                  <TableCell className="truncate max-w-48">
+                    <HoverCard>
+                      <HoverCardTrigger>{forwarding.name}</HoverCardTrigger>
+                      <HoverCardContent className="w-80 whitespace-normal">
+                        <div className="flex justify-between space-x-4">
+                          <div className="space-y-1">
+                            <p className="text-sm font-semibold">{forwarding.name}</p>
+                            <p className="text-sm">
+                              ssh.aoldacloud.com:{forwarding.port} ↔ {forwarding.instance_ip}:22
+                            </p>
+                            <p className="text-xs text-muted-foreground mt-2">{forwarding.created_at} 생성</p>
+                            <p className="text-xs text-muted-foreground">{forwarding.updated_at} 수정</p>
+                          </div>
+                        </div>
+                      </HoverCardContent>
+                    </HoverCard>
+                  </TableCell>
+                  <TableCell>{forwarding.port}</TableCell>
+                  <TableCell>{forwarding.instance_ip}</TableCell>
+                  <TableCell>
+                    <div className="flex justify-center items-center gap-2">
+                      <Button variant="secondary" className="size-8">
+                        <Link to={`./edit/${forwarding.id}`}>
+                          <Pencil />
+                        </Link>
+                      </Button>
+                      <Button variant="secondary" className="size-8">
+                        <Link to={`./delete/${forwarding.id}`}>
+                          <Trash />
+                        </Link>
+                      </Button>
+                    </div>
+                  </TableCell>
+                </TableRow>
+              ))}
+            </TableBody>
+          </Table>
+        </CardContent>
+      </Card>
+    </div>
+  );
+}
diff --git a/src/routes.tsx b/src/routes.tsx
index 2630ccf..0ffff99 100644
--- a/src/routes.tsx
+++ b/src/routes.tsx
@@ -5,6 +5,7 @@ import Home from '@/pages/Home';
 import Login from '@/pages/Login';
 import RoutingList from '@/pages/routing/List';
 import RoutingCreate from '@/pages/routing/Create';
+import ForwardingList from '@/pages/forwarding/List';
 
 export default function AppRoutes() {
   return (
@@ -16,6 +17,9 @@ export default function AppRoutes() {
           <Route index element={<RoutingList />} />
           <Route path="create" element={<RoutingCreate />} />
         </Route>
+        <Route path="forwarding">
+          <Route index element={<ForwardingList />} />
+        </Route>
         <Route path="*" element={<NotFound />} />
       </Route>
     </Routes>
-- 
GitLab