fix: manual host creation and improve host identification

- Add machine_id support for manual host creation from GUI
- Generate temporary 'pending-{uuid}' machine_id for new hosts
- Agent now collects and sends machine_id on every update
- Backend replaces pending machine_id with real one on first agent connection
- Remove unnecessary duplicate name check (friendly_name can be duplicated)
- Add get_machine_id() function to agent (reads from /etc/machine-id, /var/lib/dbus/machine-id, or generates fallback)
- Display IP address in Network tab on host details page
- Fix network tab visibility conditions to include host.ip

This ensures proper host identification using machine_id while maintaining backwards compatibility with API credentials as the primary authentication method.
This commit is contained in:
Muhammad Ibrahim
2025-10-04 09:39:47 +01:00
parent 35d3c28ae5
commit dd28e741d4
3 changed files with 51 additions and 11 deletions

View File

@@ -56,6 +56,28 @@ warning() {
log "WARNING: $1"
}
# Get or generate machine ID
get_machine_id() {
# Try standard locations for machine-id
if [[ -f /etc/machine-id ]]; then
cat /etc/machine-id
elif [[ -f /var/lib/dbus/machine-id ]]; then
cat /var/lib/dbus/machine-id
else
# Fallback: generate from hardware UUID or hostname+MAC
if command -v dmidecode &> /dev/null; then
local uuid=$(dmidecode -s system-uuid 2>/dev/null | tr -d ' -' | tr '[:upper:]' '[:lower:]')
if [[ -n "$uuid" && "$uuid" != "notpresent" ]]; then
echo "$uuid"
return
fi
fi
# Last resort: hash hostname + primary MAC address
local primary_mac=$(ip link show | grep -oP '(?<=link/ether\s)[0-9a-f:]+' | head -1 | tr -d ':')
echo "$HOSTNAME-$primary_mac" | sha256sum | cut -d' ' -f1 | cut -c1-32
fi
}
# Check if running as root
check_root() {
if [[ $EUID -ne 0 ]]; then
@@ -865,6 +887,9 @@ send_update() {
# Merge all JSON objects into one
local merged_json=$(echo "$hardware_json $network_json $system_json" | jq -s '.[0] * .[1] * .[2]')
# Get machine ID
local machine_id=$(get_machine_id)
# Create the base payload and merge with system info
local base_payload=$(cat <<EOF
{
@@ -875,7 +900,8 @@ send_update() {
"hostname": "$HOSTNAME",
"ip": "$IP_ADDRESS",
"architecture": "$ARCHITECTURE",
"agentVersion": "$AGENT_VERSION"
"agentVersion": "$AGENT_VERSION",
"machineId": "$machine_id"
}
EOF
)

View File

@@ -172,15 +172,6 @@ router.post(
// Generate unique API credentials for this host
const { apiId, apiKey } = generateApiCredentials();
// Check if host already exists
const existingHost = await prisma.hosts.findUnique({
where: { friendly_name: friendly_name },
});
if (existingHost) {
return res.status(409).json({ error: "Host already exists" });
}
// If hostGroupId is provided, verify the group exists
if (hostGroupId) {
const hostGroup = await prisma.host_groups.findUnique({
@@ -196,6 +187,7 @@ router.post(
const host = await prisma.hosts.create({
data: {
id: uuidv4(),
machine_id: `pending-${uuidv4()}`, // Temporary placeholder until agent connects with real machine_id
friendly_name: friendly_name,
os_type: "unknown", // Will be updated when agent connects
os_version: "unknown", // Will be updated when agent connects
@@ -321,6 +313,10 @@ router.post(
.optional()
.isArray()
.withMessage("Load average must be an array"),
body("machineId")
.optional()
.isString()
.withMessage("Machine ID must be a string"),
],
async (req, res) => {
try {
@@ -338,6 +334,11 @@ router.post(
updated_at: new Date(),
};
// Update machine_id if provided and current one is a placeholder
if (req.body.machineId && host.machine_id.startsWith("pending-")) {
updateData.machine_id = req.body.machineId;
}
// Basic system info
if (req.body.osType) updateData.os_type = req.body.osType;
if (req.body.osVersion) updateData.os_version = req.body.osVersion;

View File

@@ -466,11 +466,23 @@ const HostDetail = () => {
{/* Network Information */}
{activeTab === "network" &&
(host.gateway_ip ||
(host.ip ||
host.gateway_ip ||
host.dns_servers ||
host.network_interfaces) && (
<div className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{host.ip && (
<div>
<p className="text-xs text-secondary-500 dark:text-secondary-300">
IP Address
</p>
<p className="font-medium text-secondary-900 dark:text-white font-mono text-sm">
{host.ip}
</p>
</div>
)}
{host.gateway_ip && (
<div>
<p className="text-xs text-secondary-500 dark:text-secondary-300">
@@ -802,6 +814,7 @@ const HostDetail = () => {
{activeTab === "network" &&
!(
host.ip ||
host.gateway_ip ||
host.dns_servers ||
host.network_interfaces