#!/usr/bin/bash set -euo pipefail IFS=$'\n\t' SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" if [[ -d "${SCRIPT_DIR}/../../.git" ]] || [[ -f "${SCRIPT_DIR}/../../nodo/config/etc/mirrors.env" ]]; then REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" else REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" fi if [[ -f "${REPO_ROOT}/nodo/config/etc/mirrors.env" ]]; then # shellcheck source=/dev/null source "${REPO_ROOT}/nodo/config/etc/mirrors.env" elif [[ -f "${REPO_ROOT}/config/etc/mirrors.env" ]]; then # shellcheck source=/dev/null source "${REPO_ROOT}/config/etc/mirrors.env" elif [[ -f "${REPO_ROOT}/etc/mirrors.env" ]]; then # shellcheck source=/dev/null source "${REPO_ROOT}/etc/mirrors.env" fi MIRRORS_STAGING_DIR="${MIRRORS_STAGING_DIR:-$HOME/.local/share/mxos-mirrors/staging}" MIRRORS_DOMAIN="${MIRRORS_DOMAIN:-mirrors.mx-os.mx}" usage() { cat << USAGE Uso: $0 [OPCIONES] Herramienta de orquestación de la Red de Espejos Comunitarios de MxOS. Comandos: sync Sincroniza paquetes y metadatos upstream hacia el directorio de staging html Compila README.rst a README.html e index.html con pandoc ingest Ingesta el directorio de staging en Kubo generando el CID raíz publish Actualiza el registro DNSLink en Cloudflare con un CID específico ipns Publica el CID raíz en IPNS usando la llave Ed25519 (mxos-mirrors) key Administra, exporta y respalda la llave criptográfica Ed25519 run-all Ejecuta el flujo completo: sincronizar, ingerir, publicar y replicar sizes Calcula y muestra los tamaños medidos y estimados de los módulos en staging tmux-sync Administra la sincronización en segundo plano con tmux (start, status, attach, stop) lock-status Muestra el estado del candado de sincronización (activo, huérfano o libre) lock-clean Limpia el candado si el proceso registrado se encuentra inactivo status Muestra el estado de resolución, llave IPNS, demonio IPFS y registro DNSLink replicate Replica el CID raíz actual hacia el nodo público web0.mx-os.mx help Muestra este mensaje de ayuda USAGE exit 1 } cmd_status() { echo "==========================================================" echo " Estado de la Red de Espejos MxOS (${MIRRORS_DOMAIN})" echo "==========================================================" echo -n "1. Demonio IPFS local: " if command -v ipfs >/dev/null 2>&1 && ipfs id >/dev/null 2>&1; then local peer_id peer_id="$(ipfs id -f='\n')" echo "Activo (PeerID: ${peer_id})" else echo "Inactivo o no instalado" fi echo -n "2. Llave IPNS (${MIRRORS_IPNS_KEY:-mxos-mirrors}): " if ipfs key list 2>/dev/null | grep -Fxq "${MIRRORS_IPNS_KEY:-mxos-mirrors}"; then local key_id key_id="$(ipfs key list -l | awk -v k="${MIRRORS_IPNS_KEY:-mxos-mirrors}" '$2 == k {print $1}')" echo "Presente (${key_id})" else echo "No encontrada en el almacén de Kubo" fi echo -n "3. Registro DNSLink en Cloudflare (_dnslink.${MIRRORS_DOMAIN}): " local dns_val dns_val="$(dig +short TXT "_dnslink.${MIRRORS_DOMAIN}" @1.1.1.1 2>/dev/null || echo "No resoluble")" echo "${dns_val:-Sin registro}" echo -n "4. Resolución canónica DNSLink (/ipns/${MIRRORS_DOMAIN}): " if command -v ipfs >/dev/null 2>&1; then local ipns_val ipns_val="$(ipfs resolve "/ipns/${MIRRORS_DOMAIN}" 2>/dev/null || echo "Error de resolución")" echo "${ipns_val}" else echo "No disponible (sin cliente ipfs)" fi echo -n "5. Punto final HTTPS público (https://${MIRRORS_DOMAIN}/): " local http_code http_code="$(curl -s -o /dev/null -w "%{http_code}" "https://${MIRRORS_DOMAIN}/" 2>/dev/null || echo "Fallo de conexión")" echo "Código HTTP ${http_code}" echo -n "6. Candado de sincronización: " local lock_file="${MIRRORS_LOCK_FILE:-/tmp/mxos-repo-sync.lock}" if [[ -f "${lock_file}" ]]; then local lpid lpid="$(head -n 1 "${lock_file}" 2>/dev/null | tr -dc '0-9' || true)" if [[ -n "${lpid}" ]] && kill -0 "${lpid}" 2>/dev/null; then echo "Activo (PID: ${lpid})" else echo "Huérfano (PID: ${lpid:-desconocido} inactivo; se limpiará en la próxima ejecución)" fi else echo "Libre" fi echo "==========================================================" } cmd_lock_status() { local lock_file="${MIRRORS_LOCK_FILE:-/tmp/mxos-repo-sync.lock}" echo "==> Estado del Candado de Sincronización (${lock_file})" if [[ -f "${lock_file}" ]]; then local lpid lpid="$(head -n 1 "${lock_file}" 2>/dev/null | tr -dc '0-9' || true)" if [[ -n "${lpid}" ]] && kill -0 "${lpid}" 2>/dev/null; then echo "Estado: ACTIVO" echo "PID registrado: ${lpid}" cat "${lock_file}" else echo "Estado: HUÉRFANO" echo "PID registrado (${lpid:-desconocido}) inactivo. El candado será limpiado automáticamente en la próxima ejecución." fi else echo "Estado: LIBRE (Sin sincronización activa)" fi } cmd_lock_clean() { local lock_file="${MIRRORS_LOCK_FILE:-/tmp/mxos-repo-sync.lock}" if [[ -f "${lock_file}" ]]; then local lpid lpid="$(head -n 1 "${lock_file}" 2>/dev/null | tr -dc '0-9' || true)" if [[ -n "${lpid}" ]] && kill -0 "${lpid}" 2>/dev/null; then echo "Error: El proceso con PID ${lpid} sigue activo en el sistema. No se puede limpiar un candado activo." >&2 return 1 else echo "==> Limpiando candado huérfano (${lock_file})..." rm -f "${lock_file}" echo " Candado eliminado exitosamente." fi else echo "Aviso: No existe ningún archivo de candado activo." fi } cmd_replicate() { local target_cid="${1:-}" if [[ -z "${target_cid}" ]]; then target_cid="$(ipfs resolve "/ipns/${MIRRORS_DOMAIN}" 2>/dev/null || true)" target_cid="${target_cid#/ipfs/}" fi if [[ -z "${target_cid}" ]]; then echo "Error: No se pudo determinar el CID raíz a replicar." >&2 exit 1 fi echo "==> Replicando activos web selectivos de ${target_cid} hacia web0.mx-os.mx..." # shellcheck disable=SC2029 ssh root@web0.mx-os.mx \ "ipfs --api=/ip4/127.0.0.1/tcp/5001 pin add ${target_cid}/index.html ${target_cid}/README.html ${target_cid}/cover.jpg ${target_cid}/docs ${target_cid}/bitacora-cids.json ${target_cid}/vigia ${target_cid}/modules.json ${target_cid}/guardianes.json 2>/dev/null || true; systemctl restart ipfs" echo "==> Enclavamiento selectivo y purga de caché completados con éxito en web0." } cmd_run_all() { echo "==> Iniciando ciclo completo de sincronización, ingesta, publicación y replicación..." "${SCRIPT_DIR}/mxos-repo-sync.bash" echo "==> Ciclo completado." cmd_status } main() { local cmd="${1:-}" shift || true case "${cmd}" in sync) "${SCRIPT_DIR}/mxos-mirror-sync.bash" "$@" ;; html|build-html) "${SCRIPT_DIR}/mxos-build-html.bash" "$@" ;; ingest) "${SCRIPT_DIR}/mxos-mirror-ingest.bash" "$@" ;; publish) "${SCRIPT_DIR}/mxos-dnslink-update.bash" "$@" ;; ipns|ipns-publish) "${SCRIPT_DIR}/mxos-ipns-publish.bash" "$@" ;; key|key-backup) "${SCRIPT_DIR}/mxos-key-backup.bash" "$@" ;; run-all) cmd_run_all ;; sizes) "${SCRIPT_DIR}/mxos-sizes.bash" "$@" ;; tmux-sync|sync-tmux) "${SCRIPT_DIR}/mxos-tmux-sync.bash" "$@" ;; lock-status) cmd_lock_status ;; lock-clean) cmd_lock_clean ;; status) cmd_status ;; replicate) cmd_replicate "$@" ;; help|--help|-h|"") usage ;; *) echo "Comando desconocido: ${cmd}" >&2 usage ;; esac } main "$@"