homelab/modules/homelab/motd/default.nix
plasmagoat bcbcc8b17b
Some checks failed
Test / tests (push) Has been cancelled
/ OpenTofu (push) Has been cancelled
homelab framework module init (everything is a mess)
2025-07-28 02:05:13 +02:00

397 lines
16 KiB
Nix
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# modules/motd/default.nix
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.homelab.motd;
homelab-motd = pkgs.writeShellScriptBin "homelab-motd" ''
#! /usr/bin/env bash
# Colors for output
RED="\e[31m"
GREEN="\e[32m"
YELLOW="\e[33m"
BLUE='\e[0;34m'
CYAN='\e[0;36m'
WHITE='\e[1;37m'
NC='\e[0m' # No Color
BOLD='\e[1m'
# Helper functions
print_header() {
echo -e "''${BOLD}''${BLUE}''${NC}"
echo -e "''${BOLD}''${BLUE}''${NC}''${WHITE} 🏠 $(hostname -s) HOMELAB ''${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}"
elif ${pkgs.systemd}/bin/systemctl is-enabled --quiet "$service" 2>/dev/null; then
echo -e "''${YELLOW}''${NC}"
else
echo -e "''${RED}×''${NC}"
fi
}
check_backup_issues() {
local issues=0
# Check for failed backup services in the last 24 hours
if ${pkgs.systemd}/bin/journalctl --since "24 hours ago" --unit="*backup*" --unit="restic*" --unit="borgbackup*" --priority=err --no-pager -q 2>/dev/null | grep -q .; then
issues=$((issues + 1))
fi
# Check for failed backup timers
local failed_timers=$(${pkgs.systemd}/bin/systemctl list-timers --failed --no-pager --no-legend 2>/dev/null | grep -E "(backup|restic|borgbackup)" | wc -l)
issues=$((issues + failed_timers))
echo $issues
}
# Main script
${optionalString cfg.clearScreen "clear"}
print_header
# System info
print_section "SYSTEM"
echo -e " ''${BOLD}Uptime:''${NC} $(${pkgs.procps}/bin/uptime -p | sed 's/up //')"
echo -e " ''${BOLD}Load:''${NC} $(${pkgs.procps}/bin/uptime | awk -F'load average:' '{print $2}' | xargs)"
echo -e " ''${BOLD}Memory:''${NC} $(${pkgs.procps}/bin/free -h | awk '/^Mem:/ {printf "%s/%s", $3, $2}')"
echo -e " ''${BOLD}Disk:''${NC} $(${pkgs.coreutils}/bin/df -h / | awk 'NR==2 {printf "%s/%s (%s)", $3, $2, $5}')"
${optionalString cfg.showServices ''
# Local homelab services (auto-detected + manual)
print_section "HOMELAB SERVICES"
# Auto-detect services from homelab configuration
${optionalString (config.homelab.services.gatus.enable or false) ''
status=$(get_service_status "gatus")
printf " %-20s %b %s\n" "gatus" "$status" "Uptime monitoring"
''}
${optionalString (config.homelab.services.prometheus.enable or false) ''
status=$(get_service_status "prometheus")
printf " %-20s %b %s\n" "prometheus" "$status" "Metrics collection"
''}
${optionalString (config.homelab.services.grafana.enable or false) ''
status=$(get_service_status "grafana")
printf " %-20s %b %s\n" "grafana" "$status" "Monitoring dashboard"
''}
${optionalString (config.homelab.services.alertmanager.enable or false) ''
status=$(get_service_status "alertmanager")
printf " %-20s %b %s\n" "alertmanager" "$status" "Alert routing"
''}
${optionalString (config.services.nginx.enable or false) ''
status=$(get_service_status "nginx")
printf " %-20s %b %s\n" "nginx" "$status" "Web server/proxy"
''}
${optionalString (config.services.postgresql.enable or false) ''
status=$(get_service_status "postgresql")
printf " %-20s %b %s\n" "postgresql" "$status" "Database server"
''}
${optionalString (config.services.redis.server.enable or false) ''
status=$(get_service_status "redis")
printf " %-20s %b %s\n" "redis" "$status" "Key-value store"
''}
# Manual services from configuration
${concatStringsSep "\n" (mapAttrsToList (name: service: ''
status=$(get_service_status "${service.systemdService}")
printf " %-20s %b %s\n" "${name}" "$status" "${service.description}"
'')
cfg.services)}
# Show legend
echo -e "\n ''${GREEN}''${NC} Active ''${YELLOW}''${NC} Inactive ''${RED}×''${NC} Disabled"
''}
# Quick backup check
backup_issues=$(check_backup_issues)
if [[ $backup_issues -gt 0 ]]; then
echo -e "\n''${BOLD}''${RED} WARNING: $backup_issues backup issues detected!''${NC}"
echo -e " Run ''${BOLD}homelab-backup-status''${NC} for details"
fi
# Recent critical issues
error_count=$(${pkgs.systemd}/bin/journalctl --since "24 hours ago" --priority=err --no-pager -q 2>/dev/null | wc -l || echo 0)
if [[ "$error_count" -gt 0 ]]; then
echo -e "\n''${BOLD}''${YELLOW} $error_count system errors in last 24h''${NC}"
echo -e " Run ''${BOLD}journalctl --priority=err --since='24 hours ago' ''${NC} for details"
fi
# Helpful commands
echo -e "\n''${BOLD}''${BLUE}''${NC}"
echo -e "''${BOLD}''${BLUE}''${NC} ''${WHITE}Useful commands: ''${NC}''${BOLD}''${BLUE}''${NC}"
echo -e "''${BOLD}''${BLUE}''${NC} ''${CYAN}homelab-monitor-status''${NC} - Monitoring overview ''${BOLD}''${BLUE}''${NC}"
echo -e "''${BOLD}''${BLUE}''${NC} ''${CYAN}homelab-backup-status''${NC} - Backup jobs status ''${BOLD}''${BLUE}''${NC}"
echo -e "''${BOLD}''${BLUE}''${NC} ''${CYAN}homelab-proxy-status''${NC} - Reverse proxy entries ''${BOLD}''${BLUE}''${NC}"
echo -e "''${BOLD}''${BLUE}''${NC} ''${CYAN}systemctl status <srv>''${NC} - Check specific service ''${BOLD}''${BLUE}''${NC}"
echo -e "''${BOLD}''${BLUE}''${NC}"
echo
'';
# Helper script for monitoring status
homelab-monitor-status = pkgs.writeShellScriptBin "homelab-monitor-status" ''
#! /usr/bin/env bash
# Colors
RED="\e[31m"
GREEN="\e[32m"
YELLOW="\e[33m"
BLUE='\e[0;34m'
CYAN='\e[0;36m'
WHITE='\e[1;37m'
NC='\e[0m'
BOLD='\e[1m'
CONFIG_FILE="/etc/homelab/config.json"
if [[ ! -f "$CONFIG_FILE" ]]; then
echo -e "''${RED} Global homelab configuration not found''${NC}"
exit 1
fi
echo -e "''${BOLD}''${BLUE}📊 Homelab Monitoring Status''${NC}"
echo -e "''${BLUE}=============================''${NC}"
# Show metrics endpoints
echo -e "\n''${BOLD}''${CYAN}Metrics Endpoints:''${NC}"
metrics_count=$(${pkgs.jq}/bin/jq '.monitoring.metrics | length' "$CONFIG_FILE" 2>/dev/null || echo 0)
if [[ $metrics_count -gt 0 ]]; then
${pkgs.jq}/bin/jq -r '.monitoring.metrics[]? | " ''${GREEN}''${NC} \(.name): ''${BOLD}\(.host):\(.port)''${NC}\(.path) ''${YELLOW}(job: \(.jobName))''${NC}"' "$CONFIG_FILE" 2>/dev/null
echo -e "\n ''${BOLD}Total: ''${metrics_count} endpoints''${NC}"
else
echo -e " ''${YELLOW}No metrics endpoints configured''${NC}"
fi
# Show health checks by group
echo -e "\n''${BOLD}''${CYAN}Health Checks:''${NC}"
health_count=$(${pkgs.jq}/bin/jq '.monitoring.healthChecks | length' "$CONFIG_FILE" 2>/dev/null || echo 0)
if [[ $health_count -gt 0 ]]; then
# Group health checks
${pkgs.jq}/bin/jq -r '
.monitoring.healthChecks |
group_by(.group // "default") |
.[] |
"''${BOLD} \(.[0].group // "default" | ascii_upcase) Group:''${NC}" as $header |
($header, (
.[] |
" ''${if .enabled // true then "''${GREEN}" else "''${YELLOW}" end}''${NC} \(.name): ''${BOLD}\(.protocol)://\(.host)\(if .port then ":\(.port)" else "" end)''${NC}\(.path)"
))
' "$CONFIG_FILE" 2>/dev/null
echo -e "\n ''${BOLD}Total: ''${health_count} health checks''${NC}"
else
echo -e " ''${YELLOW}No health checks configured''${NC}"
fi
echo -e "\n''${CYAN}Run ''${BOLD}homelab-proxy-status''${NC}''${CYAN} and ''${BOLD}homelab-backup-status''${NC}''${CYAN} for more details.''${NC}"
'';
# Helper script for backup status
homelab-backup-status = pkgs.writeShellScriptBin "homelab-backup-status" ''
#! /usr/bin/env bash
# Colors
RED="\e[31m"
GREEN="\e[32m"
YELLOW="\e[33m"
BLUE='\e[0;34m'
CYAN='\e[0;36m'
WHITE='\e[1;37m'
NC='\e[0m'
BOLD='\e[1m'
echo -e "''${BOLD}''${BLUE}💾 Backup Status''${NC}"
echo -e "''${BLUE}===============''${NC}"
# Check backup timers
echo -e "\n''${BOLD}''${CYAN}Backup Timers:''${NC}"
backup_timers=$(${pkgs.systemd}/bin/systemctl list-timers --no-pager --no-legend 2>/dev/null | grep -E "(backup|restic|borgbackup)")
if [[ -n "$backup_timers" ]]; then
while IFS= read -r line; do
if [[ -n "$line" ]]; then
next=$(echo "$line" | awk '{print $1, $2}')
left=$(echo "$line" | awk '{print $3}')
timer=$(echo "$line" | awk '{print $5}')
service=$(echo "$line" | awk '{print $6}')
# Color code based on time left
if [[ "$left" == "n/a" ]]; then
color="''${RED}"
status=""
elif echo "$left" | grep -qE "(sec|min|[0-9]h)"; then
color="''${YELLOW}"
status=""
else
color="''${GREEN}"
status=""
fi
printf " %b%s%b %-25s Next: %s (%s)\n" "$color" "$status" "$NC" "$(basename "$timer" .timer)" "$next" "$left"
fi
done <<< "$backup_timers"
else
echo -e " ''${YELLOW}No backup timers found''${NC}"
fi
# Check recent backup activity (last 3 days, summarized)
echo -e "\n''${BOLD}''${CYAN}Recent Activity (3 days):''${NC}"
# Count successful vs failed backups
success_count=$(${pkgs.systemd}/bin/journalctl --since "3 days ago" --unit="*backup*" --unit="restic*" --unit="borgbackup*" --no-pager -q 2>/dev/null | grep -iE "(completed|success|finished)" | wc -l)
error_count=$(${pkgs.systemd}/bin/journalctl --since "3 days ago" --unit="*backup*" --unit="restic*" --unit="borgbackup*" --priority=err --no-pager -q 2>/dev/null | wc -l)
if [[ $success_count -gt 0 ]]; then
echo -e " ''${GREEN} $success_count successful backups''${NC}"
fi
if [[ $error_count -gt 0 ]]; then
echo -e " ''${RED} $error_count failed backups''${NC}"
echo -e "\n''${BOLD}''${RED}Recent Failures:''${NC}"
${pkgs.systemd}/bin/journalctl --since "3 days ago" --unit="*backup*" --unit="restic*" --unit="borgbackup*" --priority=err --no-pager --lines=3 2>/dev/null | while read -r line; do
# Extract just the important parts
timestamp=$(echo "$line" | awk '{print $1, $2, $3}')
service=$(echo "$line" | grep -oE "(restic-backups-[^[]+|borgbackup-job-[^[]+|[^[]*backup[^[]*)" | head -1)
message=$(echo "$line" | sed -E 's/.*\]: //' | cut -c1-60)
echo -e " ''${YELLOW}$timestamp''${NC} ''${BOLD}$service''${NC}: $message..."
done
elif [[ $success_count -eq 0 ]]; then
echo -e " ''${YELLOW} No backup activity in last 3 days''${NC}"
else
echo -e " ''${GREEN} All backups completed successfully''${NC}"
fi
# Show backup summary from global config if available
CONFIG_FILE="/etc/homelab/config.json"
if [[ -f "$CONFIG_FILE" ]]; then
total_jobs=$(${pkgs.jq}/bin/jq -r '.backups.summary.totalJobs // 0' "$CONFIG_FILE" 2>/dev/null)
backends=$(${pkgs.jq}/bin/jq -r '.backups.summary.backendsInUse[]?' "$CONFIG_FILE" 2>/dev/null | tr '\n' ' ')
if [[ $total_jobs -gt 0 ]]; then
echo -e "\n''${BOLD}''${CYAN}Configuration:''${NC}"
echo -e " ''${BOLD}Total jobs:''${NC} $total_jobs"
if [[ -n "$backends" ]]; then
echo -e " ''${BOLD}Backends:''${NC} $backends"
fi
fi
fi
'';
# Helper script for proxy status
homelab-proxy-status = pkgs.writeShellScriptBin "homelab-proxy-status" ''
#! /usr/bin/env bash
# Colors
RED="\e[31m"
GREEN="\e[32m"
YELLOW="\e[33m"
BLUE='\e[0;34m'
CYAN='\e[0;36m'
WHITE='\e[1;37m'
NC='\e[0m'
BOLD='\e[1m'
CONFIG_FILE="/etc/homelab/config.json"
if [[ ! -f "$CONFIG_FILE" ]]; then
echo -e "''${RED} Global homelab configuration not found''${NC}"
exit 1
fi
echo -e "''${BOLD}''${BLUE}🔗 Reverse Proxy Status''${NC}"
echo -e "''${BLUE}======================''${NC}"
proxy_count=$(${pkgs.jq}/bin/jq '.reverseProxy.entries | length' "$CONFIG_FILE" 2>/dev/null || echo 0)
if [[ $proxy_count -gt 0 ]]; then
${pkgs.jq}/bin/jq -r '.reverseProxy.entries[]? |
" ''${GREEN}''${NC} ''${BOLD}\(.subdomain)''${NC}: \(.externalHost) \(.internalHost)\(if .enableAuth then " ''${YELLOW}🔐''${NC}" else "" end)\(if .enableSSL then " ''${GREEN}🔒''${NC}" else "" end)"' "$CONFIG_FILE" 2>/dev/null
echo -e "\n''${BOLD}Legend:''${NC} ''${YELLOW}🔐''${NC} Auth enabled, ''${GREEN}🔒''${NC} SSL enabled"
echo -e "''${BOLD}Total: ''${proxy_count} proxy entries''${NC}"
else
echo -e " ''${YELLOW}No proxy entries configured''${NC}"
fi
'';
in {
options.homelab.motd = {
enable = mkEnableOption "Simple homelab MOTD";
clearScreen = mkOption {
type = types.bool;
default = true;
description = "Clear screen before showing MOTD";
};
showServices = mkOption {
type = types.bool;
default = true;
description = "Show local homelab services status";
};
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 = "Local homelab services to show in MOTD";
example = literalExpression ''
{
"nginx" = {
systemdService = "nginx";
description = "Web server";
};
"grafana" = {
systemdService = "grafana";
description = "Monitoring dashboard";
};
}
'';
};
};
config = mkIf cfg.enable {
# Create helper commands
environment.systemPackages = with pkgs; [
jq
homelab-motd
homelab-monitor-status
homelab-backup-status
homelab-proxy-status
];
# Set up MOTD to run on login
programs.bash.interactiveShellInit = ''
# Run homelab MOTD on interactive login (only once per session)
if [[ $- == *i* ]] && [[ -z "$MOTD_SHOWN" ]] && [[ -n "$SSH_CONNECTION" || "$TERM" == "linux" ]]; then
export MOTD_SHOWN=1
${homelab-motd}/bin/homelab-motd
fi
'';
# Disable default MOTD
users.motd = mkDefault "";
security.pam.services.login.showMotd = mkDefault false;
};
}