homelab framework module init (everything is a mess)
Some checks failed
Test / tests (push) Has been cancelled
/ OpenTofu (push) Has been cancelled

This commit is contained in:
plasmagoat 2025-07-28 02:05:13 +02:00
parent 0347f4d325
commit bcbcc8b17b
94 changed files with 7289 additions and 436 deletions

View file

@ -0,0 +1,304 @@
# modules/motd/default.nix
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.homelab.motd;
globalCfg = config.homelab.global;
enabledServices = filterAttrs (name: service: service.enable) globalCfg.services;
homelab-motd = pkgs.writeShellScriptBin "homelab-motd" ''
#! /usr/bin/env bash
source /etc/os-release
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
CYAN='\033[0;36m'
WHITE='\033[1;37m'
NC='\033[0m' # No Color
BOLD='\033[1m'
# Helper functions
print_header() {
echo -e "''${BOLD}''${BLUE}''${NC}"
echo -e "''${BOLD}''${BLUE}''${NC}''${WHITE} 🏠 HOMELAB STATUS ''${NC}''${BOLD}''${BLUE}''${NC}"
echo -e "''${BOLD}''${BLUE}''${NC}"
}
print_section() {
echo -e "\n''${BOLD}''${CYAN} $1''${NC}"
echo -e "''${CYAN}''${NC}"
}
get_service_status() {
local service="$1"
if ${pkgs.systemd}/bin/systemctl is-active --quiet "$service" 2>/dev/null; then
echo -e "''${GREEN}''${NC} Active"
elif ${pkgs.systemd}/bin/systemctl is-enabled --quiet "$service" 2>/dev/null; then
echo -e "''${YELLOW}''${NC} Inactive"
else
echo -e "''${RED}''${NC} Disabled"
fi
}
get_timer_status() {
local timer="$1"
if ${pkgs.systemd}/bin/systemctl is-active --quiet "$timer" 2>/dev/null; then
local next_run=$(${pkgs.systemd}/bin/systemctl show "$timer" --property=NextElapseUSecRealtime --value 2>/dev/null || echo "0")
if [[ "$next_run" != "0" && "$next_run" != "n/a" ]]; then
local next_readable=$(${pkgs.systemd}/bin/systemctl list-timers --no-pager "$timer" 2>/dev/null | tail -n +2 | head -n 1 | awk '{print $1, $2}' || echo "Unknown")
echo -e "''${GREEN}''${NC} Next: ''${next_readable}"
else
echo -e "''${GREEN}''${NC} Active"
fi
else
echo -e "''${RED}''${NC} Inactive"
fi
}
# Main script
${optionalString cfg.clearScreen "clear"}
print_header
# Check if global config exists
CONFIG_FILE="/etc/homelab/config.json"
if [[ ! -f "$CONFIG_FILE" ]]; then
echo -e "''${RED} Global homelab configuration not found at $CONFIG_FILE''${NC}"
exit 1
fi
# Parse global configuration
HOSTNAME=$(${pkgs.jq}/bin/jq -r '.hostname' "$CONFIG_FILE" 2>/dev/null || hostname)
DOMAIN=$(${pkgs.jq}/bin/jq -r '.domain' "$CONFIG_FILE" 2>/dev/null || echo "unknown")
ENVIRONMENT=$(${pkgs.jq}/bin/jq -r '.environment' "$CONFIG_FILE" 2>/dev/null || echo "unknown")
LOCATION=$(${pkgs.jq}/bin/jq -r '.location' "$CONFIG_FILE" 2>/dev/null || echo "unknown")
TAGS=$(${pkgs.jq}/bin/jq -r '.tags[]?' "$CONFIG_FILE" 2>/dev/null | tr '\n' ' ' || echo "none")
print_section "SYSTEM INFO"
echo -e " ''${BOLD}Hostname:''${NC} $HOSTNAME"
echo -e " ''${BOLD}Domain:''${NC} $DOMAIN"
echo -e " ''${BOLD}Environment:''${NC} $ENVIRONMENT"
echo -e " ''${BOLD}Location:''${NC} $LOCATION"
echo -e " ''${BOLD}Tags:''${NC} ''${TAGS:-none}"
echo -e " ''${BOLD}Uptime:''${NC} $(${pkgs.procps}/bin/uptime -p)"
echo -e " ''${BOLD}Load:''${NC} $(${pkgs.procps}/bin/uptime | awk -F'load average:' '{print $2}' | xargs)"
${optionalString cfg.showServices ''
# Enabled services from homelab config
print_section "HOMELAB SERVICES"
${concatStringsSep "\n" (mapAttrsToList (name: service: ''
status=$(get_service_status "${service.systemdService}")
printf " %-25s %s\n" "${name}" "$status"
'')
cfg.services)}
''}
${optionalString cfg.showMonitoring ''
# Monitoring endpoints
print_section "MONITORING ENDPOINTS"
ENDPOINTS=$(${pkgs.jq}/bin/jq -c '.monitoring.endpoints[]?' "$CONFIG_FILE" 2>/dev/null || echo "")
if [[ -n "$ENDPOINTS" ]]; then
while IFS= read -r endpoint; do
name=$(echo "$endpoint" | ${pkgs.jq}/bin/jq -r '.name')
port=$(echo "$endpoint" | ${pkgs.jq}/bin/jq -r '.port')
path=$(echo "$endpoint" | ${pkgs.jq}/bin/jq -r '.path')
job=$(echo "$endpoint" | ${pkgs.jq}/bin/jq -r '.jobName')
# Check if port is accessible
if ${pkgs.netcat}/bin/nc -z localhost "$port" 2>/dev/null; then
status="''${GREEN}''${NC}"
else
status="''${RED}''${NC}"
fi
printf " %-20s %s %s:%s%s (job: %s)\n" "$name" "$status" "$HOSTNAME" "$port" "$path" "$job"
done <<< "$ENDPOINTS"
else
echo -e " ''${YELLOW}No monitoring endpoints configured''${NC}"
fi
''}
${optionalString cfg.showBackups ''
# Backup jobs status
print_section "BACKUP JOBS"
BACKUP_JOBS=$(${pkgs.jq}/bin/jq -c '.backups.jobs[]?' "$CONFIG_FILE" 2>/dev/null || echo "")
if [[ -n "$BACKUP_JOBS" ]]; then
while IFS= read -r job; do
name=$(echo "$job" | ${pkgs.jq}/bin/jq -r '.name')
backend=$(echo "$job" | ${pkgs.jq}/bin/jq -r '.backend')
schedule=$(echo "$job" | ${pkgs.jq}/bin/jq -r '.schedule')
service_name="backup-''${name}"
timer_name="''${service_name}.timer"
timer_status=$(get_timer_status "$timer_name")
# Get last backup info
last_run="Unknown"
if ${pkgs.systemd}/bin/systemctl show "$service_name" --property=ExecMainStartTimestamp --value 2>/dev/null | grep -q "^[^n]"; then
last_run=$(${pkgs.systemd}/bin/systemctl show "$service_name" --property=ExecMainStartTimestamp --value 2>/dev/null | head -1)
if [[ "$last_run" != "n/a" && -n "$last_run" ]]; then
last_run=$(${pkgs.coreutils}/bin/date -d "$last_run" "+%Y-%m-%d %H:%M" 2>/dev/null || echo "Unknown")
fi
fi
printf " %-20s %s (%s, %s) Last: %s\n" "$name" "$timer_status" "$backend" "$schedule" "$last_run"
done <<< "$BACKUP_JOBS"
# Show backup-status command output if available
if command -v backup-status >/dev/null 2>&1; then
echo -e "\n ''${BOLD}Quick Status:''${NC}"
backup-status 2>/dev/null | tail -n +3 | head -10 | sed 's/^/ /'
fi
else
echo -e " ''${YELLOW}No backup jobs configured''${NC}"
fi
''}
${optionalString cfg.showReverseProxy ''
# Reverse proxy entries
print_section "REVERSE PROXY ENTRIES"
PROXY_ENTRIES=$(${pkgs.jq}/bin/jq -c '.reverseProxy.entries[]?' "$CONFIG_FILE" 2>/dev/null || echo "")
if [[ -n "$PROXY_ENTRIES" ]]; then
while IFS= read -r entry; do
subdomain=$(echo "$entry" | ${pkgs.jq}/bin/jq -r '.subdomain')
port=$(echo "$entry" | ${pkgs.jq}/bin/jq -r '.port')
domain=$(echo "$entry" | ${pkgs.jq}/bin/jq -r '.domain')
auth=$(echo "$entry" | ${pkgs.jq}/bin/jq -r '.enableAuth')
ssl=$(echo "$entry" | ${pkgs.jq}/bin/jq -r '.enableSSL')
# Check if service is running on the port
if ${pkgs.netcat}/bin/nc -z localhost "$port" 2>/dev/null; then
status="''${GREEN}''${NC}"
else
status="''${RED}''${NC}"
fi
auth_indicator=""
[[ "$auth" == "true" ]] && auth_indicator=" 🔐"
ssl_indicator=""
[[ "$ssl" == "true" ]] && ssl_indicator=" 🔒"
printf " %-25s %s :%s %s%s%s\n" "''${domain}" "$status" "$port" "$domain" "$auth_indicator" "$ssl_indicator"
done <<< "$PROXY_ENTRIES"
else
echo -e " ''${YELLOW}No reverse proxy entries configured''${NC}"
fi
''}
${optionalString cfg.showResources ''
# Resource usage
print_section "RESOURCE USAGE"
echo -e " ''${BOLD}Memory:''${NC} $(${pkgs.procps}/bin/free -h | awk '/^Mem:/ {printf "%s/%s (%.1f%%)", $3, $2, ($3/$2)*100}')"
echo -e " ''${BOLD}Disk (root):''${NC} $(${pkgs.coreutils}/bin/df -h / | awk 'NR==2 {printf "%s/%s (%s)", $3, $2, $5}')"
echo -e " ''${BOLD}CPU Usage:''${NC} $(${pkgs.procps}/bin/top -bn1 | grep "Cpu(s)" | awk '{printf "%.1f%%", $2+$4}' | sed 's/%us,//')%"
''}
${optionalString cfg.showRecentIssues ''
# Recent logs (errors only)
print_section "RECENT ISSUES"
error_count=$(${pkgs.systemd}/bin/journalctl --since "24 hours ago" --priority=err --no-pager -q | wc -l)
if [[ "$error_count" -gt 0 ]]; then
echo -e " ''${RED} $error_count errors in last 24h''${NC}"
${pkgs.systemd}/bin/journalctl --since "24 hours ago" --priority=err --no-pager -q | tail -3 | sed 's/^/ /'
else
echo -e " ''${GREEN} No critical errors in last 24h''${NC}"
fi
''}
echo -e "\n''${BOLD}''${BLUE}''${NC}"
echo -e "''${BOLD}''${BLUE}''${NC} ''${WHITE}Run 'backup-status' for detailed backup info ''${NC}''${BOLD}''${BLUE}''${NC}"
echo -e "''${BOLD}''${BLUE}''${NC} ''${WHITE}Config: /etc/homelab/config.json ''${NC}''${BOLD}''${BLUE}''${NC}"
echo -e "''${BOLD}''${BLUE}''${NC}"
echo
'';
in {
options.homelab.motd = {
enable = mkEnableOption "Dynamic homelab MOTD";
clearScreen = mkOption {
type = types.bool;
default = true;
description = "Clear screen before showing MOTD";
};
showServices = mkOption {
type = types.bool;
default = true;
description = "Show enabled homelab services";
};
showMonitoring = mkOption {
type = types.bool;
default = true;
description = "Show monitoring endpoints";
};
showBackups = mkOption {
type = types.bool;
default = true;
description = "Show backup jobs status";
};
showReverseProxy = mkOption {
type = types.bool;
default = true;
description = "Show reverse proxy entries";
};
showResources = mkOption {
type = types.bool;
default = true;
description = "Show system resource usage";
};
showRecentIssues = mkOption {
type = types.bool;
default = true;
description = "Show recent system issues";
};
services = mkOption {
type = types.attrsOf (types.submodule {
options = {
systemdService = mkOption {
type = types.str;
description = "Name of the systemd service to monitor";
};
description = mkOption {
type = types.str;
default = "";
description = "Human-readable description of the service";
};
};
});
default = {};
description = "Homelab services to monitor in MOTD";
};
};
config = mkIf (cfg.enable && globalCfg.enable) {
# Register services with MOTD
homelab.motd.services =
mapAttrs (name: service: {
systemdService = name;
description = service.description;
})
enabledServices;
# Create a command to manually run the MOTD
environment.systemPackages = with pkgs; [
jq
netcat
homelab-motd
];
};
}