A NixOS-based system for managing personal data across three tiers: - Tier 1: Configuration (shareable via git) - Tier 2: Syncable data (nb + Syncthing) - Tier 3: Large data (self-hosted services + backup) Includes: - NixOS modules for nb, Syncthing, backup (restic) - Server modules for Forgejo, Immich, Jellyfin - Helper scripts (usync, ustatus) - Comprehensive documentation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
228 lines
7.3 KiB
Bash
Executable File
228 lines
7.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# ustatus - Status dashboard for the Ultimate Notetaking System
|
|
#
|
|
# Shows status of all sync and backup systems.
|
|
#
|
|
# Usage:
|
|
# ustatus - Show full status
|
|
# ustatus brief - Show brief status
|
|
|
|
set -euo pipefail
|
|
|
|
# Colors
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
BOLD='\033[1m'
|
|
NC='\033[0m'
|
|
|
|
status_ok() {
|
|
echo -e "${GREEN}●${NC} $1"
|
|
}
|
|
|
|
status_warn() {
|
|
echo -e "${YELLOW}●${NC} $1"
|
|
}
|
|
|
|
status_error() {
|
|
echo -e "${RED}●${NC} $1"
|
|
}
|
|
|
|
header() {
|
|
echo -e "\n${BOLD}═══ $1 ═══${NC}"
|
|
}
|
|
|
|
# Check if a command exists
|
|
has_cmd() {
|
|
command -v "$1" &> /dev/null
|
|
}
|
|
|
|
brief_status() {
|
|
echo -e "${BOLD}Ultimate Notetaking System Status${NC}"
|
|
echo ""
|
|
|
|
# nb
|
|
if has_cmd nb; then
|
|
notebook_count=$(nb notebooks --names 2>/dev/null | wc -l || echo 0)
|
|
status_ok "nb: $notebook_count notebooks"
|
|
else
|
|
status_error "nb: not installed"
|
|
fi
|
|
|
|
# Syncthing
|
|
if has_cmd syncthing && syncthing cli show system &> /dev/null 2>&1; then
|
|
status_ok "Syncthing: running"
|
|
elif has_cmd syncthing; then
|
|
status_warn "Syncthing: not running"
|
|
else
|
|
status_error "Syncthing: not installed"
|
|
fi
|
|
|
|
# Backup
|
|
if systemctl is-active restic-backup.timer &> /dev/null; then
|
|
status_ok "Backup: timer active"
|
|
elif has_cmd restic; then
|
|
status_warn "Backup: timer not active"
|
|
else
|
|
status_error "Backup: restic not installed"
|
|
fi
|
|
}
|
|
|
|
full_status() {
|
|
echo -e "${BOLD}╔════════════════════════════════════════════════════════════╗${NC}"
|
|
echo -e "${BOLD}║ Ultimate Notetaking, Sync & Backup System ║${NC}"
|
|
echo -e "${BOLD}╚════════════════════════════════════════════════════════════╝${NC}"
|
|
|
|
# ==================== TIER 1: Configuration ====================
|
|
header "TIER 1: Configuration"
|
|
|
|
# Nix/NixOS
|
|
if has_cmd nixos-rebuild; then
|
|
generation=$(nixos-rebuild list-generations 2>/dev/null | head -1 | awk '{print $1}' || echo "?")
|
|
status_ok "NixOS generation: $generation"
|
|
fi
|
|
|
|
if has_cmd nix; then
|
|
nix_version=$(nix --version | head -1)
|
|
status_ok "Nix: $nix_version"
|
|
fi
|
|
|
|
# Git
|
|
if has_cmd git; then
|
|
status_ok "Git: $(git --version | cut -d' ' -f3)"
|
|
fi
|
|
|
|
# ==================== TIER 2: Notes ====================
|
|
header "TIER 2: Notes (nb)"
|
|
|
|
if has_cmd nb; then
|
|
nb_version=$(nb version 2>/dev/null || echo "unknown")
|
|
status_ok "nb version: $nb_version"
|
|
|
|
echo ""
|
|
echo "Notebooks:"
|
|
nb_dir="${NB_DIR:-$HOME/.nb}"
|
|
for notebook in $(nb notebooks --names 2>/dev/null || true); do
|
|
if [ -d "$nb_dir/$notebook" ]; then
|
|
note_count=$(find "$nb_dir/$notebook" -name "*.md" 2>/dev/null | wc -l)
|
|
|
|
# Check if has remote
|
|
if [ -d "$nb_dir/$notebook/.git" ]; then
|
|
remote=$(git -C "$nb_dir/$notebook" remote get-url origin 2>/dev/null || echo "")
|
|
if [ -n "$remote" ]; then
|
|
last_sync=$(git -C "$nb_dir/$notebook" log -1 --format="%ar" 2>/dev/null || echo "never")
|
|
echo -e " ${GREEN}●${NC} $notebook: $note_count notes, last sync: $last_sync"
|
|
else
|
|
echo -e " ${YELLOW}●${NC} $notebook: $note_count notes (no remote)"
|
|
fi
|
|
else
|
|
echo -e " ${YELLOW}●${NC} $notebook: $note_count notes (not git repo)"
|
|
fi
|
|
fi
|
|
done
|
|
else
|
|
status_error "nb not installed"
|
|
fi
|
|
|
|
# ==================== TIER 2: Syncthing ====================
|
|
header "TIER 2: File Sync (Syncthing)"
|
|
|
|
if has_cmd syncthing; then
|
|
if syncthing cli show system &> /dev/null 2>&1; then
|
|
uptime=$(syncthing cli show system 2>/dev/null | jq -r '.uptime // 0' | awk '{printf "%dd %dh %dm", $1/86400, ($1%86400)/3600, ($1%3600)/60}')
|
|
status_ok "Syncthing running (uptime: $uptime)"
|
|
|
|
echo ""
|
|
echo "Folders:"
|
|
syncthing cli show config 2>/dev/null | jq -r '.folders[] | "\(.label // .id)|\(.path)"' 2>/dev/null | while IFS='|' read -r label path; do
|
|
if [ -d "$path" ]; then
|
|
file_count=$(find "$path" -type f 2>/dev/null | wc -l)
|
|
echo -e " ${GREEN}●${NC} $label: $path ($file_count files)"
|
|
else
|
|
echo -e " ${YELLOW}●${NC} $label: $path (not found)"
|
|
fi
|
|
done || echo " Unable to list folders"
|
|
|
|
echo ""
|
|
echo "Devices:"
|
|
syncthing cli show config 2>/dev/null | jq -r '.devices[] | "\(.name // .deviceID[:8])"' 2>/dev/null | while read -r device; do
|
|
echo " ● $device"
|
|
done || echo " Unable to list devices"
|
|
else
|
|
status_warn "Syncthing installed but not running"
|
|
fi
|
|
else
|
|
status_error "Syncthing not installed"
|
|
fi
|
|
|
|
# ==================== TIER 3: Backup ====================
|
|
header "TIER 3: Backup (restic)"
|
|
|
|
if has_cmd restic; then
|
|
status_ok "restic: $(restic version | head -1)"
|
|
|
|
# Check systemd timer
|
|
if systemctl is-active restic-backup.timer &> /dev/null; then
|
|
next_run=$(systemctl show restic-backup.timer --property=NextElapseUSecRealtime 2>/dev/null | cut -d= -f2)
|
|
status_ok "Backup timer active (next: $next_run)"
|
|
else
|
|
status_warn "Backup timer not active"
|
|
fi
|
|
|
|
# Try to show recent snapshots (requires RESTIC_REPOSITORY to be set)
|
|
if [ -n "${RESTIC_REPOSITORY:-}" ]; then
|
|
echo ""
|
|
echo "Recent snapshots:"
|
|
restic snapshots --last 3 2>/dev/null | tail -n +3 | head -5 || echo " Unable to list snapshots"
|
|
fi
|
|
else
|
|
status_error "restic not installed"
|
|
fi
|
|
|
|
# ==================== TIER 3: Services ====================
|
|
header "TIER 3: Services"
|
|
|
|
# Check common services
|
|
for service in forgejo immich jellyfin; do
|
|
if systemctl is-active "$service" &> /dev/null 2>&1; then
|
|
status_ok "$service: running"
|
|
elif systemctl is-enabled "$service" &> /dev/null 2>&1; then
|
|
status_warn "$service: enabled but not running"
|
|
else
|
|
echo -e " ${BLUE}○${NC} $service: not configured"
|
|
fi
|
|
done
|
|
|
|
# ==================== Summary ====================
|
|
header "Quick Actions"
|
|
echo " usync - Sync all (nb + Syncthing)"
|
|
echo " ubackup now - Run backup immediately"
|
|
echo " nb add - Create new note"
|
|
echo ""
|
|
}
|
|
|
|
# Main
|
|
case "${1:-full}" in
|
|
brief|short|b)
|
|
brief_status
|
|
;;
|
|
full|"")
|
|
full_status
|
|
;;
|
|
-h|--help|help)
|
|
echo "ustatus - System status dashboard"
|
|
echo ""
|
|
echo "Usage:"
|
|
echo " ustatus Show full status"
|
|
echo " ustatus brief Show brief status"
|
|
echo " ustatus help Show this help"
|
|
;;
|
|
*)
|
|
echo "Unknown option: $1"
|
|
echo "Run 'ustatus help' for usage"
|
|
exit 1
|
|
;;
|
|
esac
|