From 06993c49b5a0a5619729251320aa4a08cf666352 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: Sat, 29 Mar 2025 00:12:30 +0900
Subject: [PATCH] =?UTF-8?q?feat:=20=EC=82=AC=EC=9A=A9=EC=9E=90=20=EA=B6=8C?=
 =?UTF-8?q?=ED=95=9C=EB=B3=84=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/pages/certificate/List.tsx  |  9 ++++++---
 src/pages/forwarding/Create.tsx |  2 ++
 src/pages/forwarding/List.tsx   |  5 +++--
 src/pages/routing/Create.tsx    |  2 ++
 src/pages/routing/List.tsx      | 15 ++++++++++-----
 5 files changed, 23 insertions(+), 10 deletions(-)

diff --git a/src/pages/certificate/List.tsx b/src/pages/certificate/List.tsx
index cd223a0..170943d 100644
--- a/src/pages/certificate/List.tsx
+++ b/src/pages/certificate/List.tsx
@@ -1,11 +1,12 @@
+import { Link } from 'react-router';
 import { Filter, Plus, Trash, HardDrive, Globe } 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';
 import { Badge } from '@/components/ui/badge';
+import { useAuthStore } from '@/stores/authStore';
 
 const certificates = [
   {
@@ -47,6 +48,8 @@ const certificates = [
 ];
 
 export default function CertificateList() {
+  const { selectedProject } = useAuthStore();
+
   return (
     <div className="flex flex-1 flex-col gap-4 p-6">
       <div className="flex flex-col sm:flex-row gap-4 justify-between mb-2">
@@ -55,7 +58,7 @@ export default function CertificateList() {
           <p className="mt-1 text-base text-gray-500">현재 {certificates.length}개의 SSL 인증서가 등록되어 있습니다.</p>
         </div>
         <Button asChild>
-          <Link to="./create">
+          <Link to="./create" className={selectedProject?.role !== 'admin' ? 'opacity-50 pointer-events-none' : ''}>
             <Plus className="h-4 w-4" /> 새 인증서 추가
           </Link>
         </Button>
@@ -125,7 +128,7 @@ export default function CertificateList() {
                   </TableCell>
                   <TableCell>
                     <div className="flex justify-center items-center gap-2">
-                      <Button variant="secondary" className="size-8">
+                      <Button disabled={selectedProject?.role !== 'admin'} variant="secondary" className="size-8">
                         <Link to={`./delete/${certificate.id}`}>
                           <Trash />
                         </Link>
diff --git a/src/pages/forwarding/Create.tsx b/src/pages/forwarding/Create.tsx
index a1d9a56..7c44fcb 100644
--- a/src/pages/forwarding/Create.tsx
+++ b/src/pages/forwarding/Create.tsx
@@ -61,6 +61,8 @@ export default function ForwardingCreate() {
       } else if (code == 'DUPLICATED_SERVER_PORT') {
         form.setError('serverPort', { type: 'custom' });
         toast.error('이미 사용 중인 포트 번호입니다');
+      } else if (code == 'UNAUTHORIZED_USER') {
+        toast.error('권한이 없는 사용자입니다');
       } else {
         toast.error('포트포워딩 설정 등록에 실패했습니다');
       }
diff --git a/src/pages/forwarding/List.tsx b/src/pages/forwarding/List.tsx
index 94371e1..446bd0c 100644
--- a/src/pages/forwarding/List.tsx
+++ b/src/pages/forwarding/List.tsx
@@ -74,7 +74,7 @@ export default function ForwardingList() {
           )}
         </div>
         <Button asChild>
-          <Link to="./create">
+          <Link to="./create" className={selectedProject?.role !== 'admin' ? 'opacity-50 pointer-events-none' : ''}>
             <Plus className="h-4 w-4" /> 새 포트포워딩 추가
           </Link>
         </Button>
@@ -145,12 +145,13 @@ export default function ForwardingList() {
                     <TableCell>{forwarding.instanceIp}</TableCell>
                     <TableCell>
                       <div className="flex justify-center items-center gap-2">
-                        <Button variant="secondary" className="size-8">
+                        <Button disabled={selectedProject?.role !== 'admin'} variant="secondary" className="size-8">
                           <Link to={`./edit/${forwarding.id}`}>
                             <Pencil />
                           </Link>
                         </Button>
                         <Button
+                          disabled={selectedProject?.role !== 'admin'}
                           variant="secondary"
                           className="size-8"
                           onClick={() => setSelectedForwarding(forwarding)}
diff --git a/src/pages/routing/Create.tsx b/src/pages/routing/Create.tsx
index c60b5f1..44150f9 100644
--- a/src/pages/routing/Create.tsx
+++ b/src/pages/routing/Create.tsx
@@ -109,6 +109,8 @@ export default function RoutingCreate() {
       if (code == 'DUPLICATED_DOMAIN_NAME') {
         form.setError('domain', { type: 'custom', message: '이미 사용중인 도메인입니다' });
         toast.error('이미 사용중인 도메인입니다');
+      } else if (code == 'UNAUTHORIZED_USER') {
+        toast.error('권한이 없는 사용자입니다');
       } else {
         toast.error('라우팅 설정을 등록할 수 없습니다');
       }
diff --git a/src/pages/routing/List.tsx b/src/pages/routing/List.tsx
index 0e232c7..ab57550 100644
--- a/src/pages/routing/List.tsx
+++ b/src/pages/routing/List.tsx
@@ -9,8 +9,6 @@ import { HoverCard, HoverCardContent, HoverCardTrigger } from '@/components/ui/h
 import { Input } from '@/components/ui/input';
 import { Badge } from '@/components/ui/badge';
 import { Skeleton } from '@/components/ui/skeleton';
-import { useAuthStore } from '@/stores/authStore';
-import { Routing } from '@/types/routing';
 import {
   AlertDialog,
   AlertDialogContent,
@@ -21,6 +19,8 @@ import {
   AlertDialogDescription,
   AlertDialogFooter,
 } from '@/components/ui/alert-dialog';
+import { useAuthStore } from '@/stores/authStore';
+import { Routing } from '@/types/routing';
 
 export default function RoutingList() {
   const { authFetch, selectedProject } = useAuthStore();
@@ -75,7 +75,7 @@ export default function RoutingList() {
           )}
         </div>
         <Button asChild>
-          <Link to="./create">
+          <Link to="./create" className={selectedProject?.role !== 'admin' ? 'opacity-50 pointer-events-none' : ''}>
             <Plus className="h-4 w-4" /> 새 프록시 추가
           </Link>
         </Button>
@@ -174,12 +174,17 @@ export default function RoutingList() {
                     </TableCell>
                     <TableCell>
                       <div className="flex justify-center items-center gap-2">
-                        <Button variant="secondary" className="size-8">
+                        <Button disabled={selectedProject?.role !== 'admin'} variant="secondary" className="size-8">
                           <Link to={`./edit/${routing.id}`}>
                             <Pencil />
                           </Link>
                         </Button>
-                        <Button variant="secondary" className="size-8" onClick={() => setSelectedRouting(routing)}>
+                        <Button
+                          disabled={selectedProject?.role !== 'admin'}
+                          variant="secondary"
+                          className="size-8"
+                          onClick={() => setSelectedRouting(routing)}
+                        >
                           <Trash />
                         </Button>
                       </div>
-- 
GitLab