feat: add create agents
ci-front / build (push) Successful in 2m8s

This commit is contained in:
2026-04-04 01:24:45 +03:00
parent 476499515d
commit 57f12f792c
5 changed files with 756 additions and 1 deletions
+224
View File
@@ -0,0 +1,224 @@
import React, { useState } from "react";
import { SSHAgentForm } from "../modules/agent/ui/SSHAgentForm";
import { FiPlusCircle, FiSend } from "react-icons/fi";
interface SSHAgentConfig {
user: string;
ip: string;
authMethod: string;
sshKey?: string;
password?: string;
extraFields: { key: string; value: string }[];
deployType: string;
}
const createEmptyAgentConfig = (): SSHAgentConfig => ({
user: "",
ip: "",
authMethod: "key",
sshKey: "",
password: "",
extraFields: [],
deployType: "docker",
});
export const AddAgentsPage: React.FC = () => {
const [agents, setAgents] = useState<SSHAgentConfig[]>([
createEmptyAgentConfig(),
]);
const [isSubmitting, setIsSubmitting] = useState(false);
const [submitMessage, setSubmitMessage] = useState<string | null>(null);
const [submitError, setSubmitError] = useState<string | null>(null);
const handleAgentChange = (index: number, config: SSHAgentConfig) => {
const newAgents = [...agents];
newAgents[index] = config;
setAgents(newAgents);
};
const handleAgentRemove = (index: number) => {
const newAgents = agents.filter((_, i) => i !== index);
setAgents(newAgents);
};
const handleAddAgent = () => {
setAgents([...agents, createEmptyAgentConfig()]);
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
// Валидация
const isValid = agents.every((agent) => {
if (!agent.user || !agent.ip) return false;
if (agent.authMethod === "key" && !agent.sshKey) return false;
if (agent.authMethod === "password" && !agent.password) return false;
return true;
});
if (!isValid) {
setSubmitError("Пожалуйста, заполните все обязательные поля");
return;
}
setIsSubmitting(true);
setSubmitMessage(null);
setSubmitError(null);
try {
// TODO: Реальный API вызов для развертывания агентов
console.log("Deploying agents:", agents);
// Имитация задержки API
await new Promise((resolve) => setTimeout(resolve, 1500));
setSubmitMessage(
`Успешно отправлено ${agents.length} сервер(ов) на развертывание`,
);
setAgents([createEmptyAgentConfig()]);
} catch (error) {
setSubmitError("Ошибка при развертывании на серверах");
} finally {
setIsSubmitting(false);
}
};
return (
<div
className="min-h-screen py-8 px-4"
style={{ backgroundColor: "var(--bg-primary)" }}
>
<div style={{ maxWidth: "900px", margin: "0 auto" }}>
{/* Header */}
<div className="mb-8">
<div className="flex items-center gap-4 mb-4">
<div
className="w-14 h-14 rounded-xl flex items-center justify-center"
style={{ backgroundColor: "var(--bg-secondary)" }}
>
<FiSend className="w-7 h-7" style={{ color: "var(--accent)" }} />
</div>
<div>
<h1
className="text-3xl font-bold mb-1"
style={{ color: "var(--text-primary)" }}
>
Развертывание агентов по SSH
</h1>
<p style={{ color: "var(--text-secondary)", fontSize: "16px" }}>
Настройте SSH-серверы и разверните агенты
</p>
</div>
</div>
</div>
<form onSubmit={handleSubmit}>
{/* Agent Forms */}
<div className="space-y-5">
{agents.map((agent, index) => (
<SSHAgentForm
key={index}
index={index}
config={agent}
onChange={handleAgentChange}
onRemove={handleAgentRemove}
canRemove={agents.length > 1}
/>
))}
</div>
{/* Add Agent Button */}
<button
type="button"
onClick={handleAddAgent}
className="w-full flex items-center justify-center gap-2 py-3.5 px-4 rounded-xl border-2 border-dashed transition-all mb-6 font-medium"
style={{
borderColor: "var(--border)",
backgroundColor: "transparent",
color: "var(--accent)",
fontSize: "15px",
cursor: "pointer",
}}
onMouseEnter={(e) => {
e.currentTarget.style.borderColor = "var(--accent)";
e.currentTarget.style.backgroundColor = "var(--accent)10";
}}
onMouseLeave={(e) => {
e.currentTarget.style.borderColor = "var(--border)";
e.currentTarget.style.backgroundColor = "transparent";
}}
>
<FiPlusCircle size={18} />
Добавить ещё один сервер
</button>
{/* Messages */}
{submitMessage && (
<div
className="mb-6 p-4 rounded-lg border text-sm"
style={{
backgroundColor: "var(--success-bg)",
borderColor: "var(--success-border)",
color: "var(--success-text)",
}}
>
{submitMessage}
</div>
)}
{submitError && (
<div
className="mb-6 p-4 rounded-lg border text-sm"
style={{
backgroundColor: "var(--error-bg)",
borderColor: "var(--error-border)",
color: "var(--error-text)",
}}
>
{submitError}
</div>
)}
{/* Submit Button */}
<button
type="submit"
disabled={isSubmitting}
className="w-full flex items-center justify-center gap-2 px-4 py-3.5 rounded-xl transition-all disabled:opacity-50 disabled:cursor-not-allowed font-medium text-base"
style={{
backgroundColor: isSubmitting
? "var(--bg-secondary)"
: "var(--button-primary)",
color: "var(--button-primary-text)",
boxShadow: isSubmitting
? "none"
: "0 4px 14px var(--shadow-color)",
}}
onMouseEnter={(e) => {
if (!isSubmitting) {
e.currentTarget.style.backgroundColor =
"var(--button-primary-hover)";
}
}}
onMouseLeave={(e) => {
e.currentTarget.style.backgroundColor = isSubmitting
? "var(--bg-secondary)"
: "var(--button-primary)";
}}
>
{isSubmitting ? (
<>
<div className="w-5 h-5 border-2 border-current border-t-transparent rounded-full animate-spin" />
Подключение к серверам...
</>
) : (
<>
<FiSend size={18} />
Развернуть на {agents.length} сервер(ах)
</>
)}
</button>
</form>
</div>
</div>
);
};