mirror of
https://github.com/9technologygroup/patchmon.net.git
synced 2025-11-02 04:53:40 +00:00
Fixed Issues with RHEL based systems not sending their repos to PatchMon
This commit is contained in:
@@ -574,14 +574,118 @@ get_yum_repositories() {
|
|||||||
local -n first_ref=$2
|
local -n first_ref=$2
|
||||||
|
|
||||||
# Parse yum/dnf repository configuration
|
# Parse yum/dnf repository configuration
|
||||||
|
local repo_info=""
|
||||||
if command -v dnf >/dev/null 2>&1; then
|
if command -v dnf >/dev/null 2>&1; then
|
||||||
local repo_info=$(dnf repolist all --verbose 2>/dev/null | grep -E "^Repo-id|^Repo-baseurl|^Repo-name|^Repo-status")
|
repo_info=$(dnf repolist all --verbose 2>/dev/null | grep -E "^Repo-id|^Repo-baseurl|^Repo-mirrors|^Repo-name|^Repo-status")
|
||||||
elif command -v yum >/dev/null 2>&1; then
|
elif command -v yum >/dev/null 2>&1; then
|
||||||
local repo_info=$(yum repolist all -v 2>/dev/null | grep -E "^Repo-id|^Repo-baseurl|^Repo-name|^Repo-status")
|
repo_info=$(yum repolist all -v 2>/dev/null | grep -E "^Repo-id|^Repo-baseurl|^Repo-mirrors|^Repo-name|^Repo-status")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# This is a simplified implementation - would need more work for full YUM support
|
if [[ -z "$repo_info" ]]; then
|
||||||
# For now, return empty for non-APT systems
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Parse repository information
|
||||||
|
local current_repo=""
|
||||||
|
local repo_id=""
|
||||||
|
local repo_name=""
|
||||||
|
local repo_url=""
|
||||||
|
local repo_mirrors=""
|
||||||
|
local repo_status=""
|
||||||
|
|
||||||
|
while IFS= read -r line; do
|
||||||
|
if [[ "$line" =~ ^Repo-id[[:space:]]+:[[:space:]]+(.+)$ ]]; then
|
||||||
|
# Process previous repository if we have one
|
||||||
|
if [[ -n "$current_repo" ]]; then
|
||||||
|
process_yum_repo repos_ref first_ref "$repo_id" "$repo_name" "$repo_url" "$repo_mirrors" "$repo_status"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Start new repository
|
||||||
|
repo_id="${BASH_REMATCH[1]}"
|
||||||
|
repo_name="$repo_id"
|
||||||
|
repo_url=""
|
||||||
|
repo_mirrors=""
|
||||||
|
repo_status=""
|
||||||
|
current_repo="$repo_id"
|
||||||
|
|
||||||
|
elif [[ "$line" =~ ^Repo-name[[:space:]]+:[[:space:]]+(.+)$ ]]; then
|
||||||
|
repo_name="${BASH_REMATCH[1]}"
|
||||||
|
|
||||||
|
elif [[ "$line" =~ ^Repo-baseurl[[:space:]]+:[[:space:]]+(.+)$ ]]; then
|
||||||
|
repo_url="${BASH_REMATCH[1]}"
|
||||||
|
|
||||||
|
elif [[ "$line" =~ ^Repo-mirrors[[:space:]]+:[[:space:]]+(.+)$ ]]; then
|
||||||
|
repo_mirrors="${BASH_REMATCH[1]}"
|
||||||
|
|
||||||
|
elif [[ "$line" =~ ^Repo-status[[:space:]]+:[[:space:]]+(.+)$ ]]; then
|
||||||
|
repo_status="${BASH_REMATCH[1]}"
|
||||||
|
fi
|
||||||
|
done <<< "$repo_info"
|
||||||
|
|
||||||
|
# Process the last repository
|
||||||
|
if [[ -n "$current_repo" ]]; then
|
||||||
|
process_yum_repo repos_ref first_ref "$repo_id" "$repo_name" "$repo_url" "$repo_mirrors" "$repo_status"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Process a single YUM repository and add it to the JSON
|
||||||
|
process_yum_repo() {
|
||||||
|
local -n _repos_ref=$1
|
||||||
|
local -n _first_ref=$2
|
||||||
|
local repo_id="$3"
|
||||||
|
local repo_name="$4"
|
||||||
|
local repo_url="$5"
|
||||||
|
local repo_mirrors="$6"
|
||||||
|
local repo_status="$7"
|
||||||
|
|
||||||
|
# Skip if we don't have essential info
|
||||||
|
if [[ -z "$repo_id" ]]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Determine if repository is enabled
|
||||||
|
local is_enabled=false
|
||||||
|
if [[ "$repo_status" == "enabled" ]]; then
|
||||||
|
is_enabled=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Use baseurl if available, otherwise use mirrors URL
|
||||||
|
local final_url=""
|
||||||
|
if [[ -n "$repo_url" ]]; then
|
||||||
|
# Extract first URL if multiple are listed
|
||||||
|
final_url=$(echo "$repo_url" | head -n 1 | awk '{print $1}')
|
||||||
|
elif [[ -n "$repo_mirrors" ]]; then
|
||||||
|
final_url="$repo_mirrors"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Skip if we don't have any URL
|
||||||
|
if [[ -z "$final_url" ]]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Determine if repository uses HTTPS
|
||||||
|
local is_secure=false
|
||||||
|
if [[ "$final_url" =~ ^https:// ]]; then
|
||||||
|
is_secure=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Generate repository name if not provided
|
||||||
|
if [[ -z "$repo_name" ]]; then
|
||||||
|
repo_name="$repo_id"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Clean up repository name and URL - escape quotes and backslashes
|
||||||
|
repo_name=$(echo "$repo_name" | sed 's/\\/\\\\/g' | sed 's/"/\\"/g')
|
||||||
|
final_url=$(echo "$final_url" | sed 's/\\/\\\\/g' | sed 's/"/\\"/g')
|
||||||
|
|
||||||
|
# Add to JSON
|
||||||
|
if [[ "$_first_ref" == true ]]; then
|
||||||
|
_first_ref=false
|
||||||
|
else
|
||||||
|
_repos_ref+=","
|
||||||
|
fi
|
||||||
|
|
||||||
|
_repos_ref+="{\"name\":\"$repo_name\",\"url\":\"$final_url\",\"distribution\":\"$OS_VERSION\",\"components\":\"main\",\"repoType\":\"rpm\",\"isEnabled\":$is_enabled,\"isSecure\":$is_secure}"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Get package information based on OS
|
# Get package information based on OS
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
Clock,
|
Clock,
|
||||||
Copy,
|
Copy,
|
||||||
Cpu,
|
Cpu,
|
||||||
|
Database,
|
||||||
Eye,
|
Eye,
|
||||||
EyeOff,
|
EyeOff,
|
||||||
HardDrive,
|
HardDrive,
|
||||||
@@ -31,6 +32,7 @@ import {
|
|||||||
dashboardAPI,
|
dashboardAPI,
|
||||||
formatDate,
|
formatDate,
|
||||||
formatRelativeTime,
|
formatRelativeTime,
|
||||||
|
repositoryAPI,
|
||||||
settingsAPI,
|
settingsAPI,
|
||||||
} from "../utils/api";
|
} from "../utils/api";
|
||||||
import { OSIcon } from "../utils/osIcons.jsx";
|
import { OSIcon } from "../utils/osIcons.jsx";
|
||||||
@@ -64,6 +66,15 @@ const HostDetail = () => {
|
|||||||
refetchOnWindowFocus: false, // Don't refetch when window regains focus
|
refetchOnWindowFocus: false, // Don't refetch when window regains focus
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Fetch repository count for this host
|
||||||
|
const { data: repositories, isLoading: isLoadingRepos } = useQuery({
|
||||||
|
queryKey: ["host-repositories", hostId],
|
||||||
|
queryFn: () => repositoryAPI.getByHost(hostId).then((res) => res.data),
|
||||||
|
staleTime: 5 * 60 * 1000, // 5 minutes - data stays fresh longer
|
||||||
|
refetchOnWindowFocus: false, // Don't refetch when window regains focus
|
||||||
|
enabled: !!hostId,
|
||||||
|
});
|
||||||
|
|
||||||
// Tab change handler
|
// Tab change handler
|
||||||
const handleTabChange = (tabName) => {
|
const handleTabChange = (tabName) => {
|
||||||
setActiveTab(tabName);
|
setActiveTab(tabName);
|
||||||
@@ -290,7 +301,7 @@ const HostDetail = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Package Statistics Cards */}
|
{/* Package Statistics Cards */}
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4 mb-6">
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => navigate(`/packages?host=${hostId}`)}
|
onClick={() => navigate(`/packages?host=${hostId}`)}
|
||||||
@@ -347,6 +358,25 @@ const HostDetail = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => navigate(`/repositories?host=${hostId}`)}
|
||||||
|
className="card p-4 cursor-pointer hover:shadow-card-hover dark:hover:shadow-card-hover-dark transition-shadow duration-200 text-left w-full"
|
||||||
|
title="View repositories for this host"
|
||||||
|
>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<Database className="h-5 w-5 text-blue-600 mr-2" />
|
||||||
|
<div>
|
||||||
|
<p className="text-sm text-secondary-500 dark:text-white">
|
||||||
|
Repos
|
||||||
|
</p>
|
||||||
|
<p className="text-xl font-semibold text-secondary-900 dark:text-white">
|
||||||
|
{isLoadingRepos ? "..." : repositories?.length || 0}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Main Content - Full Width */}
|
{/* Main Content - Full Width */}
|
||||||
|
|||||||
@@ -18,21 +18,31 @@ import {
|
|||||||
Unlock,
|
Unlock,
|
||||||
X,
|
X,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate, useSearchParams } from "react-router-dom";
|
||||||
import { repositoryAPI } from "../utils/api";
|
import { dashboardAPI, repositoryAPI } from "../utils/api";
|
||||||
|
|
||||||
const Repositories = () => {
|
const Repositories = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const [searchParams] = useSearchParams();
|
||||||
const [searchTerm, setSearchTerm] = useState("");
|
const [searchTerm, setSearchTerm] = useState("");
|
||||||
const [filterType, setFilterType] = useState("all"); // all, secure, insecure
|
const [filterType, setFilterType] = useState("all"); // all, secure, insecure
|
||||||
const [filterStatus, setFilterStatus] = useState("all"); // all, active, inactive
|
const [filterStatus, setFilterStatus] = useState("all"); // all, active, inactive
|
||||||
|
const [hostFilter, setHostFilter] = useState("");
|
||||||
const [sortField, setSortField] = useState("name");
|
const [sortField, setSortField] = useState("name");
|
||||||
const [sortDirection, setSortDirection] = useState("asc");
|
const [sortDirection, setSortDirection] = useState("asc");
|
||||||
const [showColumnSettings, setShowColumnSettings] = useState(false);
|
const [showColumnSettings, setShowColumnSettings] = useState(false);
|
||||||
const [deleteModalData, setDeleteModalData] = useState(null);
|
const [deleteModalData, setDeleteModalData] = useState(null);
|
||||||
|
|
||||||
|
// Handle host filter from URL parameter
|
||||||
|
useEffect(() => {
|
||||||
|
const hostParam = searchParams.get("host");
|
||||||
|
if (hostParam) {
|
||||||
|
setHostFilter(hostParam);
|
||||||
|
}
|
||||||
|
}, [searchParams]);
|
||||||
|
|
||||||
// Column configuration
|
// Column configuration
|
||||||
const [columnConfig, setColumnConfig] = useState(() => {
|
const [columnConfig, setColumnConfig] = useState(() => {
|
||||||
const defaultConfig = [
|
const defaultConfig = [
|
||||||
@@ -82,6 +92,17 @@ const Repositories = () => {
|
|||||||
queryFn: () => repositoryAPI.getStats().then((res) => res.data),
|
queryFn: () => repositoryAPI.getStats().then((res) => res.data),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Fetch host information when filtering by host
|
||||||
|
const { data: hosts } = useQuery({
|
||||||
|
queryKey: ["hosts"],
|
||||||
|
queryFn: () => dashboardAPI.getHosts().then((res) => res.data),
|
||||||
|
staleTime: 5 * 60 * 1000,
|
||||||
|
enabled: !!hostFilter,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get the filtered host information
|
||||||
|
const filteredHost = hosts?.find((host) => host.id === hostFilter);
|
||||||
|
|
||||||
// Delete repository mutation
|
// Delete repository mutation
|
||||||
const deleteRepositoryMutation = useMutation({
|
const deleteRepositoryMutation = useMutation({
|
||||||
mutationFn: (repositoryId) => repositoryAPI.delete(repositoryId),
|
mutationFn: (repositoryId) => repositoryAPI.delete(repositoryId),
|
||||||
@@ -202,7 +223,11 @@ const Repositories = () => {
|
|||||||
(filterStatus === "active" && repo.is_active === true) ||
|
(filterStatus === "active" && repo.is_active === true) ||
|
||||||
(filterStatus === "inactive" && repo.is_active === false);
|
(filterStatus === "inactive" && repo.is_active === false);
|
||||||
|
|
||||||
return matchesSearch && matchesType && matchesStatus;
|
// Filter by host if hostFilter is set
|
||||||
|
const matchesHost =
|
||||||
|
!hostFilter || repo.hosts?.some((host) => host.id === hostFilter);
|
||||||
|
|
||||||
|
return matchesSearch && matchesType && matchesStatus && matchesHost;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Sort repositories
|
// Sort repositories
|
||||||
@@ -237,6 +262,7 @@ const Repositories = () => {
|
|||||||
filterStatus,
|
filterStatus,
|
||||||
sortField,
|
sortField,
|
||||||
sortDirection,
|
sortDirection,
|
||||||
|
hostFilter,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
@@ -421,6 +447,31 @@ const Repositories = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Host Filter Indicator */}
|
||||||
|
{hostFilter && filteredHost && (
|
||||||
|
<div className="flex items-center gap-2 px-3 py-2 bg-primary-50 dark:bg-primary-900 border border-primary-200 dark:border-primary-700 rounded-md">
|
||||||
|
<Server className="h-4 w-4 text-primary-600 dark:text-primary-400" />
|
||||||
|
<span className="text-sm text-primary-700 dark:text-primary-300">
|
||||||
|
Filtered by: {filteredHost.friendly_name}
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => {
|
||||||
|
setHostFilter("");
|
||||||
|
// Update URL to remove host parameter
|
||||||
|
const newSearchParams = new URLSearchParams(searchParams);
|
||||||
|
newSearchParams.delete("host");
|
||||||
|
navigate(`/repositories?${newSearchParams.toString()}`, {
|
||||||
|
replace: true,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
className="text-primary-500 hover:text-primary-700 dark:text-primary-400 dark:hover:text-primary-200"
|
||||||
|
>
|
||||||
|
<X className="h-4 w-4" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Security Filter */}
|
{/* Security Filter */}
|
||||||
<div className="sm:w-48">
|
<div className="sm:w-48">
|
||||||
<select
|
<select
|
||||||
|
|||||||
Reference in New Issue
Block a user