Enhance batch management functionality by adding node flow execution support and improving batch configuration creation. Introduce new API endpoint for retrieving node flows and update existing batch services to handle execution types. Update frontend components to support new scheduling options and node flow selection.
This commit is contained in:
@@ -1,124 +1,34 @@
|
||||
"use client";
|
||||
|
||||
/**
|
||||
* HTTP 요청 액션 노드
|
||||
* REST API를 호출하는 노드
|
||||
*/
|
||||
|
||||
import { memo } from "react";
|
||||
import { Handle, Position, NodeProps } from "reactflow";
|
||||
import { Globe, Lock, Unlock } from "lucide-react";
|
||||
import type { HttpRequestActionNodeData } from "@/types/node-editor";
|
||||
import { NodeProps } from "reactflow";
|
||||
import { Send } from "lucide-react";
|
||||
import { CompactNodeShell } from "./CompactNodeShell";
|
||||
|
||||
// HTTP 메서드별 색상
|
||||
const METHOD_COLORS: Record<string, { bg: string; text: string }> = {
|
||||
GET: { bg: "bg-emerald-100", text: "text-emerald-700" },
|
||||
POST: { bg: "bg-primary/10", text: "text-primary" },
|
||||
PUT: { bg: "bg-amber-100", text: "text-orange-700" },
|
||||
PATCH: { bg: "bg-amber-100", text: "text-yellow-700" },
|
||||
DELETE: { bg: "bg-destructive/10", text: "text-destructive" },
|
||||
HEAD: { bg: "bg-muted", text: "text-foreground" },
|
||||
OPTIONS: { bg: "bg-purple-100", text: "text-purple-700" },
|
||||
};
|
||||
|
||||
export const HttpRequestActionNode = memo(({ data, selected }: NodeProps<HttpRequestActionNodeData>) => {
|
||||
const methodColor = METHOD_COLORS[data.method] || METHOD_COLORS.GET;
|
||||
const hasUrl = data.url && data.url.trim().length > 0;
|
||||
const hasAuth = data.authentication?.type && data.authentication.type !== "none";
|
||||
|
||||
// URL에서 도메인 추출
|
||||
const getDomain = (url: string) => {
|
||||
try {
|
||||
const urlObj = new URL(url);
|
||||
return urlObj.hostname;
|
||||
} catch {
|
||||
return url;
|
||||
}
|
||||
};
|
||||
export const HttpRequestActionNode = memo(({ data, selected }: NodeProps<any>) => {
|
||||
const method = data.method || "GET";
|
||||
const summary = data.url
|
||||
? `${method} ${data.url}`
|
||||
: "요청 URL을 입력해 주세요";
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`min-w-[250px] rounded-lg border-2 bg-white shadow-md transition-all ${
|
||||
selected ? "border-cyan-500 shadow-lg" : "border-border"
|
||||
}`}
|
||||
<CompactNodeShell
|
||||
color="#06B6D4"
|
||||
label={data.displayName || "HTTP 요청"}
|
||||
summary={summary}
|
||||
icon={<Send className="h-3.5 w-3.5" />}
|
||||
selected={selected}
|
||||
>
|
||||
{/* 입력 핸들 */}
|
||||
<Handle
|
||||
type="target"
|
||||
position={Position.Left}
|
||||
className="!h-3 !w-3 !border-2 !border-white !bg-cyan-500"
|
||||
/>
|
||||
|
||||
{/* 헤더 */}
|
||||
<div className="flex items-center gap-2 rounded-t-lg bg-cyan-500 px-3 py-2 text-white">
|
||||
<Globe className="h-4 w-4" />
|
||||
<div className="flex-1">
|
||||
<div className="text-sm font-semibold">{data.displayName || "HTTP 요청"}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 본문 */}
|
||||
<div className="space-y-2 p-3">
|
||||
{/* 메서드 & 인증 */}
|
||||
<div className="flex items-center gap-2">
|
||||
<span className={`rounded px-2 py-0.5 text-xs font-bold ${methodColor.bg} ${methodColor.text}`}>
|
||||
{data.method}
|
||||
{data.url && (
|
||||
<div className="flex items-center gap-1.5">
|
||||
<span className="rounded bg-cyan-500/20 px-1 py-0.5 font-mono text-[9px] font-semibold text-cyan-400">
|
||||
{method}
|
||||
</span>
|
||||
{hasAuth ? (
|
||||
<span className="flex items-center gap-1 rounded bg-emerald-100 px-1.5 py-0.5 text-xs text-emerald-700">
|
||||
<Lock className="h-3 w-3" />
|
||||
{data.authentication?.type}
|
||||
</span>
|
||||
) : (
|
||||
<span className="flex items-center gap-1 rounded bg-muted px-1.5 py-0.5 text-xs text-muted-foreground">
|
||||
<Unlock className="h-3 w-3" />
|
||||
인증없음
|
||||
</span>
|
||||
)}
|
||||
<span className="break-all font-mono">{data.url}</span>
|
||||
</div>
|
||||
|
||||
{/* URL */}
|
||||
<div className="text-xs">
|
||||
<span className="text-muted-foreground">URL: </span>
|
||||
{hasUrl ? (
|
||||
<span className="truncate text-foreground" title={data.url}>
|
||||
{getDomain(data.url)}
|
||||
</span>
|
||||
) : (
|
||||
<span className="text-amber-500">URL 설정 필요</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 바디 타입 */}
|
||||
{data.bodyType && data.bodyType !== "none" && (
|
||||
<div className="text-xs">
|
||||
<span className="text-muted-foreground">Body: </span>
|
||||
<span className="rounded bg-muted px-1.5 py-0.5 text-muted-foreground">
|
||||
{data.bodyType.toUpperCase()}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 타임아웃 & 재시도 */}
|
||||
<div className="flex gap-2 text-xs text-muted-foreground">
|
||||
{data.options?.timeout && (
|
||||
<span>타임아웃: {Math.round(data.options.timeout / 1000)}초</span>
|
||||
)}
|
||||
{data.options?.retryCount && data.options.retryCount > 0 && (
|
||||
<span>재시도: {data.options.retryCount}회</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 출력 핸들 */}
|
||||
<Handle
|
||||
type="source"
|
||||
position={Position.Right}
|
||||
className="!h-3 !w-3 !border-2 !border-white !bg-cyan-500"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</CompactNodeShell>
|
||||
);
|
||||
});
|
||||
|
||||
HttpRequestActionNode.displayName = "HttpRequestActionNode";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user