Fixes platform->host->agent logic

main
Yasen Pramatarov 2025-01-22 17:46:08 +02:00
parent e3f839bc56
commit fc1ed97499
3 changed files with 93 additions and 113 deletions

View File

@ -24,29 +24,32 @@ class Agent {
/** /**
* Retrieves details of a specified agent ID (or all agents) in a specified platform. * Retrieves details of agents for a specified host.
* *
* @param int $platform_id The platform ID to filter agents by. * @param int $host_id The host ID to filter agents by.
* @param int $agent_id The agent ID to filter by. If empty, all agents are returned. * @param int $agent_id Optional agent ID to filter by.
* *
* @return array The list of agent details. * @return array The list of agent details.
*/ */
public function getAgentDetails($platform_id, $agent_id = '') { public function getAgentDetails($host_id, $agent_id = '') {
$sql = 'SELECT $sql = 'SELECT
ja.id, ja.id,
ja.platform_id, ja.host_id,
ja.agent_type_id, ja.agent_type_id,
ja.url, ja.url,
ja.secret_key, ja.secret_key,
ja.check_period, ja.check_period,
jat.description AS agent_description, jat.description AS agent_description,
jat.endpoint AS agent_endpoint jat.endpoint AS agent_endpoint,
h.platform_id
FROM FROM
jilo_agents ja jilo_agents ja
JOIN JOIN
jilo_agent_types jat ON ja.agent_type_id = jat.id jilo_agent_types jat ON ja.agent_type_id = jat.id
JOIN
hosts h ON ja.host_id = h.id
WHERE WHERE
platform_id = :platform_id'; ja.host_id = :host_id';
if ($agent_id !== '') { if ($agent_id !== '') {
$sql .= ' AND ja.id = :agent_id'; $sql .= ' AND ja.id = :agent_id';
@ -54,7 +57,7 @@ class Agent {
$query = $this->db->prepare($sql); $query = $this->db->prepare($sql);
$query->bindParam(':platform_id', $platform_id); $query->bindParam(':host_id', $host_id);
if ($agent_id !== '') { if ($agent_id !== '') {
$query->bindParam(':agent_id', $agent_id); $query->bindParam(':agent_id', $agent_id);
} }
@ -75,17 +78,20 @@ class Agent {
public function getAgentIDDetails($agent_id) { public function getAgentIDDetails($agent_id) {
$sql = 'SELECT $sql = 'SELECT
ja.id, ja.id,
ja.platform_id, ja.host_id,
ja.agent_type_id, ja.agent_type_id,
ja.url, ja.url,
ja.secret_key, ja.secret_key,
ja.check_period, ja.check_period,
jat.description AS agent_description, jat.description AS agent_description,
jat.endpoint AS agent_endpoint jat.endpoint AS agent_endpoint,
h.platform_id
FROM FROM
jilo_agents ja jilo_agents ja
JOIN JOIN
jilo_agent_types jat ON ja.agent_type_id = jat.id jilo_agent_types jat ON ja.agent_type_id = jat.id
JOIN
hosts h ON ja.host_id = h.id
WHERE WHERE
ja.id = :agent_id'; ja.id = :agent_id';
@ -114,22 +120,22 @@ class Agent {
/** /**
* Retrieves agent types already configured for a specific platform. * Retrieves agent types already configured for a specific host.
* *
* @param int $platform_id The platform ID to filter agents by. * @param int $host_id The host ID to filter agents by.
* *
* @return array List of agent types configured for the platform. * @return array List of agent types configured for the host.
*/ */
public function getPlatformAgentTypes($platform_id) { public function getHostAgentTypes($host_id) {
$sql = 'SELECT $sql = 'SELECT
id, id,
agent_type_id agent_type_id
FROM FROM
jilo_agents jilo_agents
WHERE WHERE
platform_id = :platform_id'; host_id = :host_id';
$query = $this->db->prepare($sql); $query = $this->db->prepare($sql);
$query->bindParam(':platform_id', $platform_id); $query->bindParam(':host_id', $host_id);
$query->execute(); $query->execute();
return $query->fetchAll(PDO::FETCH_ASSOC); return $query->fetchAll(PDO::FETCH_ASSOC);
@ -137,23 +143,23 @@ class Agent {
/** /**
* Adds a new agent to the platform. * Add a new agent to the database.
* *
* @param int $platform_id The platform ID where the agent is to be added. * @param int $host_id The host ID to add the agent to.
* @param array $newAgent The new agent details to add. * @param array $newAgent An associative array containing the details of the agent to be added.
* *
* @return bool|string Returns true on success or an error message on failure. * @return bool|string True if the agent was added successfully, otherwise error message.
*/ */
public function addAgent($platform_id, $newAgent) { public function addAgent($host_id, $newAgent) {
try { try {
$sql = 'INSERT INTO jilo_agents $sql = 'INSERT INTO jilo_agents
(platform_id, agent_type_id, url, secret_key, check_period) (host_id, agent_type_id, url, secret_key, check_period)
VALUES VALUES
(:platform_id, :agent_type_id, :url, :secret_key, :check_period)'; (:host_id, :agent_type_id, :url, :secret_key, :check_period)';
$query = $this->db->prepare($sql); $query = $this->db->prepare($sql);
$query->execute([ $query->execute([
':platform_id' => $platform_id, ':host_id' => $host_id,
':agent_type_id' => $newAgent['type_id'], ':agent_type_id' => $newAgent['type_id'],
':url' => $newAgent['url'], ':url' => $newAgent['url'],
':secret_key' => $newAgent['secret_key'], ':secret_key' => $newAgent['secret_key'],
@ -169,33 +175,29 @@ class Agent {
/** /**
* Edits an existing agent's details. * Edit an existing agent in the database.
* *
* @param int $platform_id The platform ID where the agent exists. * @param int $agent_id The ID of the agent to edit.
* @param array $updatedAgent The updated agent details. * @param array $updatedAgent An associative array containing the updated details of the agent.
* *
* @return bool|string Returns true on success or an error message on failure. * @return bool|string True if the agent was updated successfully, otherwise error message.
*/ */
public function editAgent($platform_id, $updatedAgent) { public function editAgent($agent_id, $updatedAgent) {
try { try {
$sql = 'UPDATE jilo_agents SET $sql = 'UPDATE jilo_agents
agent_type_id = :agent_type_id, SET
url = :url, url = :url,
secret_key = :secret_key, secret_key = :secret_key,
check_period = :check_period check_period = :check_period
WHERE WHERE
id = :agent_id id = :agent_id';
AND
platform_id = :platform_id';
$query = $this->db->prepare($sql); $query = $this->db->prepare($sql);
$query->execute([ $query->execute([
':agent_type_id' => $updatedAgent['agent_type_id'], ':agent_id' => $agent_id,
':url' => $updatedAgent['url'], ':url' => $updatedAgent['url'],
':secret_key' => $updatedAgent['secret_key'], ':secret_key' => $updatedAgent['secret_key'],
':check_period' => $updatedAgent['check_period'], ':check_period' => $updatedAgent['check_period'],
':agent_id' => $updatedAgent['id'],
':platform_id' => $platform_id,
]); ]);
return true; return true;
@ -207,7 +209,7 @@ class Agent {
/** /**
* Deletes an agent from the platform. * Deletes an agent from the database.
* *
* @param int $agent_id The agent ID to delete. * @param int $agent_id The agent ID to delete.
* *
@ -398,15 +400,15 @@ class Agent {
} }
/** /**
* Retrieves the latest stored data for a specific platform, agent type, and metric type. * Retrieves the latest stored data for a specific host, agent type, and metric type.
* *
* @param int $platform_id The platform ID. * @param int $host_id The host ID.
* @param string $agent_type The agent type. * @param string $agent_type The agent type.
* @param string $metric_type The metric type to filter by. * @param string $metric_type The metric type to filter by.
* *
* @return mixed The latest stored data. * @return mixed The latest stored data.
*/ */
public function getLatestData($platform_id, $agent_type, $metric_type) { public function getLatestData($host_id, $agent_type, $metric_type) {
$sql = 'SELECT $sql = 'SELECT
jac.timestamp, jac.timestamp,
jac.response_content, jac.response_content,
@ -418,8 +420,10 @@ class Agent {
jilo_agents ja ON jac.agent_id = ja.id jilo_agents ja ON jac.agent_id = ja.id
JOIN JOIN
jilo_agent_types jat ON ja.agent_type_id = jat.id jilo_agent_types jat ON ja.agent_type_id = jat.id
JOIN
hosts h ON ja.host_id = h.id
WHERE WHERE
ja.platform_id = :platform_id h.id = :host_id
AND jat.description = :agent_type AND jat.description = :agent_type
AND jac.status_code = 200 AND jac.status_code = 200
ORDER BY ORDER BY
@ -428,7 +432,7 @@ class Agent {
$query = $this->db->prepare($sql); $query = $this->db->prepare($sql);
$query->execute([ $query->execute([
':platform_id' => $platform_id, ':host_id' => $host_id,
':agent_type' => $agent_type ':agent_type' => $agent_type
]); ]);
@ -497,14 +501,14 @@ class Agent {
/** /**
* Gets historical data for a specific metric from agent checks * Gets historical data for a specific metric from agent checks
* *
* @param int $platform_id The platform ID * @param int $host_id The host ID
* @param string $agent_type The type of agent (e.g., 'jvb', 'jicofo') * @param string $agent_type The type of agent (e.g., 'jvb', 'jicofo')
* @param string $metric_type The type of metric to retrieve * @param string $metric_type The type of metric to retrieve
* @param string $from_time Start time in Y-m-d format * @param string $from_time Start time in Y-m-d format
* @param string $until_time End time in Y-m-d format * @param string $until_time End time in Y-m-d format
* @return array Array with the dataset from agent checks * @return array Array with the dataset from agent checks
*/ */
public function getHistoricalData($platform_id, $agent_type, $metric_type, $from_time, $until_time) { public function getHistoricalData($host_id, $agent_type, $metric_type, $from_time, $until_time) {
// Get data from agent checks // Get data from agent checks
$sql = 'SELECT $sql = 'SELECT
DATE(jac.timestamp) as date, DATE(jac.timestamp) as date,
@ -516,8 +520,10 @@ class Agent {
jilo_agents ja ON jac.agent_id = ja.id jilo_agents ja ON jac.agent_id = ja.id
JOIN JOIN
jilo_agent_types jat ON ja.agent_type_id = jat.id jilo_agent_types jat ON ja.agent_type_id = jat.id
JOIN
hosts h ON ja.host_id = h.id
WHERE WHERE
ja.platform_id = :platform_id h.id = :host_id
AND jat.description = :agent_type AND jat.description = :agent_type
AND jac.status_code = 200 AND jac.status_code = 200
AND DATE(jac.timestamp) BETWEEN :from_time AND :until_time AND DATE(jac.timestamp) BETWEEN :from_time AND :until_time
@ -528,7 +534,7 @@ class Agent {
$query = $this->db->prepare($sql); $query = $this->db->prepare($sql);
$query->execute([ $query->execute([
':platform_id' => $platform_id, ':host_id' => $host_id,
':agent_type' => $agent_type, ':agent_type' => $agent_type,
':from_time' => $from_time, ':from_time' => $from_time,
':until_time' => $until_time ':until_time' => $until_time

View File

@ -97,23 +97,20 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST') {
'secret_key' => $_POST['secret_key'], 'secret_key' => $_POST['secret_key'],
'check_period' => $_POST['check_period'], 'check_period' => $_POST['check_period'],
]; ];
$result = $agentObject->addAgent($_POST['platform'], $newAgent); $result = $agentObject->addAgent($_POST['host'], $newAgent);
if ($result === true) { if ($result === true) {
$_SESSION['notice'] = "New Jilo Agent added."; $_SESSION['notice'] = "New Jilo agent added.";
} else { } else {
$_SESSION['error'] = "Adding the agent failed. Error: $result"; $_SESSION['error'] = "Adding the agent failed. Error: $result";
} }
} else { // This is an edit of existing agent } else { // This is an edit of existing agent
$agent_id = $_POST['agent']; $agent_id = $_POST['agent'];
$platform_id = $_POST['platform'];
$updatedAgent = [ $updatedAgent = [
'id' => $agent_id,
'agent_type_id' => $_POST['agent_type_id'],
'url' => $_POST['url'], 'url' => $_POST['url'],
'secret_key' => $_POST['secret_key'], 'secret_key' => $_POST['secret_key'],
'check_period' => $_POST['check_period'] 'check_period' => $_POST['check_period'],
]; ];
$result = $agentObject->editAgent($platform_id, $updatedAgent); $result = $agentObject->editAgent($agent_id, $updatedAgent);
if ($result === true) { if ($result === true) {
$_SESSION['notice'] = "Agent edited."; $_SESSION['notice'] = "Agent edited.";
} else { } else {

View File

@ -120,9 +120,7 @@
<?php if (!empty($hosts)): ?> <?php if (!empty($hosts)): ?>
<?php foreach ($hosts as $host): ?> <?php foreach ($hosts as $host): ?>
<?php <?php
$hostAgents = array_filter($agents, function($agent) use ($host) { $hostAgents = $agentObject->getAgentDetails($host['id']);
return isset($agent['host_id']) && $agent['host_id'] === $host['id'];
});
?> ?>
<div class="card mt-5 host-details" data-host-id="<?= htmlspecialchars($host['id']) ?>"> <div class="card mt-5 host-details" data-host-id="<?= htmlspecialchars($host['id']) ?>">
<div class="card-header bg-light d-flex justify-content-between align-items-center"> <div class="card-header bg-light d-flex justify-content-between align-items-center">
@ -181,16 +179,17 @@
<div class="card-body"> <div class="card-body">
<!-- Agents section --> <!-- Agents section -->
<?php $hostAgents = $agentObject->getAgentDetails($platform['id']); ?>
<div class="d-flex justify-content-between align-items-center mb-3"> <div class="d-flex justify-content-between align-items-center mb-3">
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
<i class="fas fa-robot me-2 text-secondary"></i> <i class="fas fa-robot me-2 text-secondary"></i>
<span class="text-secondary"> <span class="text-secondary">
<?= htmlspecialchars(count($hostAgents)) ?> <?= count($hostAgents) === 1 ? 'agent' : 'agents' ?> <?php
for this host $hostAgents = $agentObject->getAgentDetails($host['id']);
echo count($hostAgents) . ' ' . (count($hostAgents) === 1 ? 'agent' : 'agents') . ' for host "' . htmlspecialchars($host['name']) . '"';
?>
</span> </span>
</div> </div>
<button class="btn btn-sm btn-primary" onclick="showAddAgentModal(<?= htmlspecialchars($platform['id']) ?>, <?= htmlspecialchars($host['id']) ?>)"> <button class="btn btn-primary" onclick="showAddAgentModal(<?= htmlspecialchars($host['id']) ?>)">
<i class="fas fa-plus me-2"></i>Add new agent <i class="fas fa-plus me-2"></i>Add new agent
</button> </button>
</div> </div>
@ -202,8 +201,8 @@
<tr> <tr>
<th>Agent type</th> <th>Agent type</th>
<th>Endpoint URL</th> <th>Endpoint URL</th>
<th>Check period (minutes)</th> <th>Check period</th>
<th class="text-end">Actions</th> <th>Actions</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -215,32 +214,14 @@
<span class="agent-view-mode"> <span class="agent-view-mode">
<?= htmlspecialchars($agent['agent_description']) ?> <?= htmlspecialchars($agent['agent_description']) ?>
</span> </span>
<div class="agent-edit-mode" style="display: none;">
<select class="form-select form-select-sm" name="agent_type_id" required>
<?php foreach ($agentObject->getAgentTypes() as $type): ?>
<option value="<?= htmlspecialchars($type['id']) ?>"
data-endpoint="<?= htmlspecialchars($type['endpoint']) ?>"
<?= $type['id'] === $agent['agent_type_id'] ? 'selected' : '' ?>>
<?= htmlspecialchars($type['description']) ?>
</option>
<?php endforeach; ?>
</select>
</div>
</div> </div>
</td> </td>
<td class="text-break"> <td>
<span class="agent-view-mode"> <span class="agent-view-mode">
<?= htmlspecialchars($agent['url'].$agent['agent_endpoint']) ?> <?= htmlspecialchars($agent['url']) ?>
</span> </span>
<div class="agent-edit-mode" style="display: none;"> <div class="agent-edit-mode" style="display: none;">
<label class="form-label small text-muted">URL</label> <input type="text" class="form-control" name="url" value="<?= htmlspecialchars($agent['url']) ?>" required>
<input type="text" class="form-control form-control-sm text-break mb-2" name="url"
value="<?= htmlspecialchars($agent['url']) ?>"
placeholder="e.g., http://localhost:8080" required>
<label class="form-label small text-muted">Secret key</label>
<input type="text" class="form-control form-control-sm text-break" name="secret_key"
value="<?= htmlspecialchars($agent['secret_key']) ?>"
placeholder="Secret key for authentication" required>
</div> </div>
</td> </td>
<td> <td>
@ -248,31 +229,29 @@
<?php if (isset($agent['check_period']) && $agent['check_period'] !== 0): ?> <?php if (isset($agent['check_period']) && $agent['check_period'] !== 0): ?>
<?= htmlspecialchars($agent['check_period']) ?> <?= ($agent['check_period'] == 1 ? 'minute' : 'minutes') ?> <?= htmlspecialchars($agent['check_period']) ?> <?= ($agent['check_period'] == 1 ? 'minute' : 'minutes') ?>
<?php else: ?> <?php else: ?>
<span class="text-muted">-</span> Not monitored
<?php endif; ?> <?php endif; ?>
</span> </span>
<div class="agent-edit-mode" style="display: none;"> <div class="agent-edit-mode" style="display: none;">
<input type="number" class="form-control form-control-sm" name="check_period" <input type="number" class="form-control" name="check_period" value="<?= htmlspecialchars($agent['check_period']) ?>" min="0">
value="<?= htmlspecialchars($agent['check_period']) ?>"
min="0" placeholder="Check interval in minutes">
</div> </div>
</td> </td>
<td class="text-end"> <td>
<div class="btn-group agent-actions" data-agent-id="<?= htmlspecialchars($agent['id']) ?>" <div class="btn-group agent-actions" data-agent-id="<?= htmlspecialchars($agent['id']) ?>"
data-platform-id="<?= htmlspecialchars($platform['id']) ?>"
data-host-id="<?= htmlspecialchars($host['id']) ?>"> data-host-id="<?= htmlspecialchars($host['id']) ?>">
<button type="button" class="btn btn-outline-primary btn-sm edit-agent agent-view-mode"> <button type="button" class="btn btn-outline-primary btn-sm edit-agent agent-view-mode">
<i class="fas fa-edit me-1"></i>Edit <i class="fas fa-edit me-1"></i>Edit agent
</button> </button>
<button type="button" class="btn btn-outline-primary btn-sm save-agent agent-edit-mode" style="display: none;"> <button type="button" class="btn btn-outline-danger btn-sm delete-agent agent-view-mode"
onclick="showDeleteAgentModal(<?= htmlspecialchars($agent['id']) ?>, '<?= htmlspecialchars($agent['agent_description']) ?>', '<?= htmlspecialchars($agent['url']) ?>')">
<i class="fas fa-trash-alt me-1"></i>Delete
</button>
<button type="button" class="btn btn-outline-success btn-sm save-agent agent-edit-mode" style="display: none;">
<i class="fas fa-save me-1"></i>Save <i class="fas fa-save me-1"></i>Save
</button> </button>
<button type="button" class="btn btn-outline-secondary btn-sm cancel-agent-edit agent-edit-mode" style="display: none;"> <button type="button" class="btn btn-outline-secondary btn-sm cancel-agent-edit agent-edit-mode" style="display: none;">
<i class="fas fa-times me-1"></i>Cancel <i class="fas fa-times me-1"></i>Cancel
</button> </button>
<button type="button" class="btn btn-outline-danger btn-sm agent-view-mode" onclick="showDeleteAgentModal(<?= htmlspecialchars($platform['id']) ?>, <?= htmlspecialchars($host['id']) ?>, <?= htmlspecialchars($agent['id']) ?>, '<?= htmlspecialchars(addslashes($agent['agent_description'])) ?>')">
<i class="fas fa-trash me-1"></i>Delete
</button>
</div> </div>
</td> </td>
</tr> </tr>
@ -484,11 +463,11 @@
<div id="deleteHostWarning"></div> <div id="deleteHostWarning"></div>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label class="form-label">Host name</label> <label class="form-label small text-muted">Host name</label>
<div id="deleteHostName" class="form-control-plaintext"></div> <div id="deleteHostName" class="form-control-plaintext"></div>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label class="form-label">Address</label> <label class="form-label small text-muted">Address</label>
<div id="deleteHostAddress" class="form-control-plaintext"></div> <div id="deleteHostAddress" class="form-control-plaintext"></div>
</div> </div>
<div class="mb-3"> <div class="mb-3">
@ -902,7 +881,7 @@ $(function() {
}); });
}); });
// Delete Platform Modal // Run the delete platform modal
function showDeletePlatformModal(platformId, name, url, database) { function showDeletePlatformModal(platformId, name, url, database) {
document.getElementById('deletePlatformId').value = platformId; document.getElementById('deletePlatformId').value = platformId;
document.getElementById('deletePlatformName').textContent = name; document.getElementById('deletePlatformName').textContent = name;
@ -948,7 +927,7 @@ $(function() {
$('#deletePlatformModal').modal(); $('#deletePlatformModal').modal();
} }
// Delete host modal // run the delete host modal
function showDeleteHostModal(platformId, hostId, name, address) { function showDeleteHostModal(platformId, hostId, name, address) {
document.getElementById('deleteHostPlatformId').value = platformId; document.getElementById('deleteHostPlatformId').value = platformId;
document.getElementById('deleteHostId').value = hostId; document.getElementById('deleteHostId').value = hostId;
@ -987,7 +966,7 @@ $(function() {
$('#deleteHostModal').modal(); $('#deleteHostModal').modal();
} }
// Delete agent modal // Run the delete agent modal
function showDeleteAgentModal(platformId, hostId, agentId, type) { function showDeleteAgentModal(platformId, hostId, agentId, type) {
document.getElementById('deleteAgentPlatformId').value = platformId; document.getElementById('deleteAgentPlatformId').value = platformId;
document.getElementById('deleteAgentHostId').value = hostId; document.getElementById('deleteAgentHostId').value = hostId;
@ -1025,21 +1004,20 @@ $(function() {
$('[data-toggle="tooltip"]').tooltip(); $('[data-toggle="tooltip"]').tooltip();
}); });
// Platform modal // Show add platform modal
function showAddPlatformModal() { function showAddPlatformModal() {
$('#addPlatformModal').modal('show'); $('#addPlatformModal').modal('show');
} }
// Host modal // Show add host modal
function showAddHostModal(platformId) { function showAddHostModal(platformId) {
document.getElementById('hostPlatformId').value = platformId; document.getElementById('hostPlatformId').value = platformId;
document.getElementById('addHostModalLabel').textContent = 'Add new host to platform #' + platformId; document.getElementById('addHostModalLabel').textContent = 'Add new host to platform #' + platformId;
$('#addHostModal').modal('show'); $('#addHostModal').modal('show');
} }
// Agent modal // Show add agent modal
function showAddAgentModal(platformId, hostId) { function showAddAgentModal(hostId) {
document.getElementById('agentPlatformId').value = platformId;
document.getElementById('agentHostId').value = hostId; document.getElementById('agentHostId').value = hostId;
document.getElementById('addAgentModalLabel').textContent = 'Add new agent to host #' + hostId; document.getElementById('addAgentModalLabel').textContent = 'Add new agent to host #' + hostId;
@ -1073,12 +1051,11 @@ document.addEventListener('DOMContentLoaded', function() {
// Add Agent buttons for each host // Add Agent buttons for each host
document.querySelectorAll('.host-card').forEach(card => { document.querySelectorAll('.host-card').forEach(card => {
const platformId = card.closest('.platform-card').dataset.platformId;
const hostId = card.dataset.hostId; const hostId = card.dataset.hostId;
const addAgentBtn = document.createElement('button'); const addAgentBtn = document.createElement('button');
addAgentBtn.className = 'btn btn-outline-primary btn-sm ms-2'; addAgentBtn.className = 'btn btn-outline-primary btn-sm ms-2';
addAgentBtn.innerHTML = '<i class="fas fa-plus me-1"></i>Add agent'; addAgentBtn.innerHTML = '<i class="fas fa-plus me-1"></i>Add agent';
addAgentBtn.onclick = () => showAddAgentModal(platformId, hostId); addAgentBtn.onclick = () => showAddAgentModal(hostId);
card.querySelector('.card-header').appendChild(addAgentBtn); card.querySelector('.card-header').appendChild(addAgentBtn);
}); });
}); });