feat: show agent token, revoke, regenerate
Some checks failed
Deploy Web Client / deploy (push) Failing after 35s
Some checks failed
Deploy Web Client / deploy (push) Failing after 35s
This commit is contained in:
parent
652a863706
commit
621db2e29c
@ -237,6 +237,10 @@ async def update_member(slug: str, request: Request, user: dict = Depends(get_cu
|
||||
async def regenerate_token(slug: str, user: dict = Depends(get_current_user)):
|
||||
return await tracker_post(f"/api/v1/members/{slug}/regenerate-token")
|
||||
|
||||
@app.post("/api/v1/members/{slug}/revoke-token")
|
||||
async def revoke_token(slug: str, user: dict = Depends(get_current_user)):
|
||||
return await tracker_post(f"/api/v1/members/{slug}/revoke-token")
|
||||
|
||||
|
||||
# --- Health ---
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { Member, updateMember, regenerateToken } from "@/lib/api";
|
||||
import { Member, updateMember, regenerateToken, revokeToken } from "@/lib/api";
|
||||
|
||||
interface Props {
|
||||
agent: Member;
|
||||
@ -19,9 +19,10 @@ export default function AgentModal({ agent, onClose, onUpdated }: Props) {
|
||||
const [prompt, setPrompt] = useState(agent.agent_config?.prompt || "");
|
||||
const [model, setModel] = useState(agent.agent_config?.model || "");
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [newToken, setNewToken] = useState<string | null>(null);
|
||||
const [currentToken, setCurrentToken] = useState<string | null>(agent.token || null);
|
||||
const [copied, setCopied] = useState(false);
|
||||
const [confirmRegen, setConfirmRegen] = useState(false);
|
||||
const [confirmRevoke, setConfirmRevoke] = useState(false);
|
||||
|
||||
const handleSave = async () => {
|
||||
setSaving(true);
|
||||
@ -49,16 +50,26 @@ export default function AgentModal({ agent, onClose, onUpdated }: Props) {
|
||||
const handleRegenerate = async () => {
|
||||
try {
|
||||
const { token } = await regenerateToken(agent.slug);
|
||||
setNewToken(token);
|
||||
setCurrentToken(token);
|
||||
setConfirmRegen(false);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRevoke = async () => {
|
||||
try {
|
||||
await revokeToken(agent.slug);
|
||||
setCurrentToken(null);
|
||||
setConfirmRevoke(false);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
|
||||
const copyToken = () => {
|
||||
if (newToken) {
|
||||
navigator.clipboard.writeText(newToken);
|
||||
if (currentToken) {
|
||||
navigator.clipboard.writeText(currentToken);
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
}
|
||||
@ -157,45 +168,68 @@ export default function AgentModal({ agent, onClose, onUpdated }: Props) {
|
||||
{/* Token section */}
|
||||
<div className="pt-3 border-t border-[var(--border)]">
|
||||
<div className="text-sm text-[var(--muted)] mb-2">Токен доступа</div>
|
||||
{newToken ? (
|
||||
{currentToken ? (
|
||||
<div>
|
||||
<div className="mb-2 p-2 bg-yellow-500/10 border border-yellow-500/30 rounded text-yellow-400 text-xs">
|
||||
⚠️ Сохраните токен — он больше не будет показан!
|
||||
</div>
|
||||
<div className="p-3 bg-[var(--bg)] border border-[var(--border)] rounded-lg font-mono text-xs break-all select-all mb-2">
|
||||
{newToken}
|
||||
{currentToken}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
onClick={copyToken}
|
||||
className="px-3 py-1.5 bg-[var(--accent)] text-white rounded text-xs hover:opacity-90"
|
||||
>
|
||||
{copied ? "Скопировано!" : "Скопировать"}
|
||||
</button>
|
||||
{confirmRegen ? (
|
||||
<>
|
||||
<button
|
||||
onClick={handleRegenerate}
|
||||
className="px-3 py-1.5 bg-yellow-500/20 text-yellow-400 rounded text-xs hover:bg-yellow-500/30"
|
||||
>
|
||||
Да, новый
|
||||
</button>
|
||||
<button onClick={() => setConfirmRegen(false)} className="text-xs text-[var(--muted)]">
|
||||
Отмена
|
||||
</button>
|
||||
</>
|
||||
) : (
|
||||
<button
|
||||
onClick={() => setConfirmRegen(true)}
|
||||
className="text-xs text-[var(--muted)] hover:text-[var(--fg)]"
|
||||
>
|
||||
Перегенерировать
|
||||
</button>
|
||||
)}
|
||||
{confirmRevoke ? (
|
||||
<>
|
||||
<button
|
||||
onClick={handleRevoke}
|
||||
className="px-3 py-1.5 bg-red-500/20 text-red-400 rounded text-xs hover:bg-red-500/30"
|
||||
>
|
||||
Да, отозвать
|
||||
</button>
|
||||
<button onClick={() => setConfirmRevoke(false)} className="text-xs text-[var(--muted)]">
|
||||
Отмена
|
||||
</button>
|
||||
</>
|
||||
) : (
|
||||
<button
|
||||
onClick={() => setConfirmRevoke(true)}
|
||||
className="text-xs text-red-400/60 hover:text-red-400"
|
||||
>
|
||||
Отозвать
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
onClick={copyToken}
|
||||
className="px-3 py-1.5 bg-[var(--accent)] text-white rounded text-xs hover:opacity-90"
|
||||
>
|
||||
{copied ? "Скопировано!" : "Скопировать"}
|
||||
</button>
|
||||
</div>
|
||||
) : confirmRegen ? (
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-xs text-red-400">Старый токен перестанет работать!</span>
|
||||
<button
|
||||
onClick={handleRegenerate}
|
||||
className="px-3 py-1.5 bg-red-500/20 text-red-400 rounded text-xs hover:bg-red-500/30"
|
||||
>
|
||||
Да, перегенерировать
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setConfirmRegen(false)}
|
||||
className="px-3 py-1.5 text-xs text-[var(--muted)]"
|
||||
>
|
||||
Отмена
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-xs text-[var(--muted)]">Токен скрыт</span>
|
||||
<span className="text-xs text-[var(--muted)]">Токен отозван</span>
|
||||
<button
|
||||
onClick={() => setConfirmRegen(true)}
|
||||
onClick={handleRegenerate}
|
||||
className="text-xs text-[var(--accent)] hover:underline"
|
||||
>
|
||||
Перегенерировать
|
||||
Сгенерировать новый
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -50,6 +50,7 @@ export interface Member {
|
||||
status: string;
|
||||
avatar_url: string | null;
|
||||
agent_config: AgentConfig | null;
|
||||
token?: string | null;
|
||||
}
|
||||
|
||||
export interface MemberCreateResponse extends Member {
|
||||
@ -238,3 +239,7 @@ export async function updateMember(slug: string, data: {
|
||||
export async function regenerateToken(slug: string): Promise<{ token: string }> {
|
||||
return request(`/api/v1/members/${slug}/regenerate-token`, { method: "POST" });
|
||||
}
|
||||
|
||||
export async function revokeToken(slug: string): Promise<void> {
|
||||
await request(`/api/v1/members/${slug}/revoke-token`, { method: "POST" });
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user