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:
DDD1542
2026-03-19 15:07:07 +09:00
parent 7f781b0177
commit 43cf91e748
41 changed files with 4020 additions and 3155 deletions

View File

@@ -1,118 +1,31 @@
"use client";
/**
* 스크립트 실행 액션 노드
* Python, Shell, PowerShell 등 외부 스크립트를 실행하는 노드
*/
import { memo } from "react";
import { Handle, Position, NodeProps } from "reactflow";
import { Terminal, FileCode, Play } from "lucide-react";
import type { ScriptActionNodeData } from "@/types/node-editor";
import { NodeProps } from "reactflow";
import { Terminal } from "lucide-react";
import { CompactNodeShell } from "./CompactNodeShell";
// 스크립트 타입별 아이콘 색상
const SCRIPT_TYPE_COLORS: Record<string, { bg: string; text: string; label: string }> = {
python: { bg: "bg-amber-100", text: "text-yellow-700", label: "Python" },
shell: { bg: "bg-emerald-100", text: "text-emerald-700", label: "Shell" },
powershell: { bg: "bg-primary/10", text: "text-primary", label: "PowerShell" },
node: { bg: "bg-emerald-100", text: "text-emerald-700", label: "Node.js" },
executable: { bg: "bg-muted", text: "text-foreground", label: "실행파일" },
};
export const ScriptActionNode = memo(({ data, selected }: NodeProps<ScriptActionNodeData>) => {
const scriptTypeInfo = SCRIPT_TYPE_COLORS[data.scriptType] || SCRIPT_TYPE_COLORS.executable;
const hasScript = data.executionMode === "inline" ? !!data.inlineScript : !!data.scriptPath;
export const ScriptActionNode = memo(({ data, selected }: NodeProps<any>) => {
const scriptType = data.scriptType || "python";
const summary = data.inlineScript
? `${scriptType} 스크립트 (${data.inlineScript.split("\n").length}줄)`
: "스크립트를 작성해 주세요";
return (
<div
className={`min-w-[250px] rounded-lg border-2 bg-white shadow-md transition-all ${
selected ? "border-emerald-500 shadow-lg" : "border-border"
}`}
<CompactNodeShell
color="#10B981"
label={data.displayName || "스크립트 실행"}
summary={summary}
icon={<Terminal 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-emerald-500"
/>
{/* 헤더 */}
<div className="flex items-center gap-2 rounded-t-lg bg-emerald-500 px-3 py-2 text-white">
<Terminal className="h-4 w-4" />
<div className="flex-1">
<div className="text-sm font-semibold">{data.displayName || "스크립트 실행"}</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-medium ${scriptTypeInfo.bg} ${scriptTypeInfo.text}`}>
{scriptTypeInfo.label}
</span>
<span className="rounded bg-muted px-2 py-0.5 text-xs text-muted-foreground">
{data.executionMode === "inline" ? "인라인" : "파일"}
</span>
</div>
{/* 스크립트 정보 */}
<div className="flex items-center gap-2 text-xs">
{data.executionMode === "inline" ? (
<>
<FileCode className="h-3 w-3 text-muted-foreground/70" />
<span className="text-muted-foreground">
{hasScript ? (
<span className="text-emerald-600">
{data.inlineScript!.split("\n").length}
</span>
) : (
<span className="text-amber-500"> </span>
)}
</span>
</>
) : (
<>
<Play className="h-3 w-3 text-muted-foreground/70" />
<span className="text-muted-foreground">
{hasScript ? (
<span className="truncate text-emerald-600">{data.scriptPath}</span>
) : (
<span className="text-amber-500"> </span>
)}
</span>
</>
)}
</div>
{/* 입력 방식 */}
<div className="text-xs">
<span className="text-muted-foreground">: </span>
<span className="text-foreground">
{data.inputMethod === "stdin" && "표준입력 (stdin)"}
{data.inputMethod === "args" && "명령줄 인자"}
{data.inputMethod === "env" && "환경변수"}
{data.inputMethod === "file" && "파일"}
</span>
</div>
{/* 타임아웃 */}
{data.options?.timeout && (
<div className="text-xs text-muted-foreground">
: {Math.round(data.options.timeout / 1000)}
</div>
)}
</div>
{/* 출력 핸들 */}
<Handle
type="source"
position={Position.Right}
className="!h-3 !w-3 !border-2 !border-white !bg-emerald-500"
/>
</div>
{data.scriptType && (
<span className="rounded bg-emerald-500/20 px-1 py-0.5 font-mono text-[9px] font-semibold text-emerald-400">
{scriptType}
</span>
)}
</CompactNodeShell>
);
});
ScriptActionNode.displayName = "ScriptActionNode";