({
+ agentLabel: "",
user: "",
ip: "",
port: 22,
@@ -53,7 +54,8 @@ export const AddAgentsPage: React.FC = () => {
// Валидация
const isValid = agents.every((agent) => {
- if (!agent.user || !agent.ip || !agent.port) return false;
+ if (!agent.agentLabel || !agent.user || !agent.ip || !agent.port)
+ return false;
if (agent.port < 1 || agent.port > 65535) return false;
if (agent.authMethod === "key" && !agent.sshKey) return false;
if (agent.authMethod === "password" && !agent.password) return false;
@@ -73,7 +75,7 @@ export const AddAgentsPage: React.FC = () => {
// Преобразуем данные из формы в формат API
const deployData: DeployAgentsRequest = {
servers: agents.map((agent) => ({
- agentLabel: `${agent.ip}-${agent.user}`,
+ agentLabel: agent.agentLabel,
ip: agent.ip,
user: agent.user,
port: agent.port,
diff --git a/frontend/src/pages/registration.page.tsx b/frontend/src/pages/registration.page.tsx
new file mode 100644
index 0000000..ecf464a
--- /dev/null
+++ b/frontend/src/pages/registration.page.tsx
@@ -0,0 +1,423 @@
+import React, { useState } from "react";
+import { agentApiService } from "@/modules/agent/api/agent.api.service";
+import { FiKey, FiPlus, FiTrash2, FiCopy, FiCheck, FiX } from "react-icons/fi";
+
+interface RegistrationTokenForm {
+ label: string;
+}
+
+interface RegistrationResult {
+ label: string;
+ token: string;
+}
+
+export const RegistrationTokenPage: React.FC = () => {
+ const [tokens, setTokens] = useState
([
+ { label: "" },
+ ]);
+ const [results, setResults] = useState([]);
+ const [isSubmitting, setIsSubmitting] = useState(false);
+ const [error, setError] = useState(null);
+ const [successMessage, setSuccessMessage] = useState(null);
+ const [copiedIndex, setCopiedIndex] = useState(null);
+
+ const handleTokenChange = (index: number, label: string) => {
+ const newTokens = [...tokens];
+ newTokens[index] = { label };
+ setTokens(newTokens);
+ };
+
+ const handleAddToken = () => {
+ setTokens([...tokens, { label: "" }]);
+ };
+
+ const handleRemoveToken = (index: number) => {
+ const newTokens = tokens.filter((_, i) => i !== index);
+ setTokens(newTokens);
+ };
+
+ const handleCopyToken = async (token: string, index: number) => {
+ await navigator.clipboard.writeText(token);
+ setCopiedIndex(index);
+ setTimeout(() => setCopiedIndex(null), 2000);
+ };
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault();
+
+ // Валидация
+ const validTokens = tokens.filter((t) => t.label.trim());
+ if (validTokens.length === 0) {
+ setError("Введите хотя бы одну метку для токена");
+ return;
+ }
+
+ setIsSubmitting(true);
+ setError(null);
+ setSuccessMessage(null);
+ setResults([]);
+
+ try {
+ const createdTokens: RegistrationResult[] = [];
+
+ for (const tokenData of validTokens) {
+ const response = await agentApiService.createRegistrationToken({
+ label: tokenData.label,
+ });
+
+ // API возвращает объект с токеном
+ const token = response.token || Object.values(response)[0] as string;
+
+ createdTokens.push({
+ label: tokenData.label,
+ token,
+ });
+ }
+
+ setResults(createdTokens);
+ setSuccessMessage(
+ `Успешно создано ${createdTokens.length} токен(ов)`
+ );
+ setTokens([{ label: "" }]);
+ } catch (err) {
+ setError(
+ err instanceof Error
+ ? err.message
+ : "Ошибка при создании токенов"
+ );
+ } finally {
+ setIsSubmitting(false);
+ }
+ };
+
+ const inputStyle: React.CSSProperties = {
+ width: "100%",
+ padding: "10px 12px",
+ border: "1px solid var(--border)",
+ borderRadius: "8px",
+ backgroundColor: "var(--input-bg)",
+ color: "var(--text-primary)",
+ fontSize: "14px",
+ transition: "border-color 0.2s, box-shadow 0.2s",
+ };
+
+ const labelStyle: React.CSSProperties = {
+ display: "block",
+ marginBottom: "8px",
+ color: "var(--text-secondary)",
+ fontSize: "14px",
+ fontWeight: 500,
+ };
+
+ return (
+
+
+ {/* Header */}
+
+
+
+
+
+
+
+ Регистрация токенов для агентов
+
+
+ Создайте токены для регистрации новых агентов
+
+
+
+
+
+
+
+
+ );
+};