forked from baron/baron-sso
클라이언트 상세 페이지 비밀키 재발급 기능 연동
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import type { AxiosError } from "axios";
|
||||
import { AlertCircle, Copy, Eye, EyeOff, Link2, Shield, Workflow, Save } from "lucide-react";
|
||||
import { AlertCircle, Copy, Eye, EyeOff, Link2, Shield, Workflow, Save, RefreshCw } from "lucide-react";
|
||||
import { Link, useParams } from "react-router-dom";
|
||||
import { Badge } from "../../components/ui/badge";
|
||||
import { Button } from "../../components/ui/button";
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
} from "../../components/ui/table";
|
||||
import { Textarea } from "../../components/ui/textarea";
|
||||
import { Label } from "../../components/ui/label";
|
||||
import { fetchClient, updateClient } from "../../lib/devApi";
|
||||
import { fetchClient, updateClient, rotateClientSecret } from "../../lib/devApi";
|
||||
import { cn } from "../../lib/utils";
|
||||
import { CopyButton } from "../../components/ui/copy-button";
|
||||
import { toast } from "../../components/ui/use-toast";
|
||||
@@ -57,6 +57,24 @@ function ClientDetailsPage() {
|
||||
},
|
||||
});
|
||||
|
||||
const rotateMutation = useMutation({
|
||||
mutationFn: () => rotateClientSecret(clientId),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["client", clientId] });
|
||||
toast("Client Secret이 재발급되었습니다.");
|
||||
setShowSecret(true); // 재발급 후 바로 보여줌
|
||||
},
|
||||
onError: (err) => {
|
||||
toast(`재발급 실패: ${(err as Error).message}`, "error");
|
||||
},
|
||||
});
|
||||
|
||||
const handleRotateSecret = () => {
|
||||
if (window.confirm("경고: Client Secret을 재발급하면 기존 시크릿은 즉시 무효화됩니다.\n연동된 애플리케이션이 중단될 수 있습니다. 계속하시겠습니까?")) {
|
||||
rotateMutation.mutate();
|
||||
}
|
||||
};
|
||||
|
||||
if (!clientId) {
|
||||
return <div className="p-8 text-center">Client ID가 필요합니다.</div>;
|
||||
}
|
||||
@@ -176,14 +194,20 @@ function ClientDetailsPage() {
|
||||
>
|
||||
{showSecret ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="icon"
|
||||
onClick={handleRotateSecret}
|
||||
disabled={rotateMutation.isPending}
|
||||
title="비밀키 재발급 (Rotate)"
|
||||
>
|
||||
<RefreshCw className={cn("h-4 w-4", rotateMutation.isPending && "animate-spin")} />
|
||||
</Button>
|
||||
<CopyButton
|
||||
value={clientSecret}
|
||||
disabled={!showSecret && clientSecret === "SECRET_NOT_AVAILABLE"}
|
||||
onCopy={() => toast("Client Secret이 복사되었습니다.")}
|
||||
/>
|
||||
<Button variant="outline" size="icon" className="border-amber-500/50 text-amber-500">
|
||||
<AlertCircle className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -136,6 +136,13 @@ export async function updateClient(
|
||||
return data;
|
||||
}
|
||||
|
||||
export async function rotateClientSecret(clientId: string) {
|
||||
const { data } = await apiClient.post<ClientDetailResponse>(
|
||||
`/dev/clients/${clientId}/secret/rotate`
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
export async function deleteClient(clientId: string) {
|
||||
await apiClient.delete(`/dev/clients/${clientId}`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user