feat: Introduce a Python health checker, reorganize static assets and data, and remove Zsh helper scripts.

This commit is contained in:
Marco Gallegos
2025-12-17 19:38:01 -06:00
parent 0d4601385c
commit e8d8a58c46
12 changed files with 422 additions and 728 deletions

View File

@@ -1,37 +1,22 @@
# Soul:23 coming soon page
Una landing page responsive Built con Bootstrap 4 que muestra una cuenta regresiva y un formulario de notificaciones. Se accede a una versión viva en https://solu23.cloud.
A responsive landing page built with Bootstrap 4 that displays a countdown and a notification form.
**Author:** Marco Gallegos
## Instalación local
## Local Installation
```bash
npm install
npm start
```
El servidor Express sirve todos los assets desde la raíz y expone `/healthchecker` con el script de salud como `text/plain`, listo para que operadores lo descarguen con `curl`.
The Express server serves all assets from the root and exposes a `/healthchecker` endpoint with the health script as `text/plain`, ready for operators to download with `curl`.
## Deploy con Coolify / Traefik
## Countdown and Form
1. Importa el repositorio como app Docker en Coolify.
2. Elige el `Dockerfile` del proyecto, expone el puerto `3001` (ya definido en el contenedor).
3. Coolify/Traefik se encargan del TLS; usa el dominio que asignas en la app.
4. Si necesitas alertas, define la variable de entorno `WEBHOOK_URLS` antes de levantar la app.
Una vez desplegado podrás invocar el verificador con:
```bash
curl https://soul23.cloud/healthchecker
```
## Contador y formulario
El componente de tiempo lee el atributo `data-date` en `#countdown-timer`. Cámbialo por cualquier fecha válida:
The time component reads the `data-date` attribute in `#countdown-timer`. Change it to any valid date:
```html
<div id="countdown-timer" data-date="January 17, 2025 03:24:00">
```
Si prefieres programarlo con JavaScript, reasigna la variable `countDownDate` dentro de `js/countdown.js` antes de que empiece el intervalo.
If you prefer to program it with JavaScript, reassign the `countDownDate` variable inside `js/countdown.js` before the interval starts.

15
data/README.md Normal file
View File

@@ -0,0 +1,15 @@
# Data Files
This directory contains various data files used by the application.
## Organization
* `quotes.json`: Stores a collection of phrases for the `/day-quote` endpoint.
* Other `.json` files: Should contain structured data for specific application features.
* Avoid storing large binary files directly in this directory; consider external storage solutions or dedicated asset folders for those.
## Best Practices
* Ensure all data files are in a structured, easily parsable format (e.g., JSON, YAML).
* Provide a clear description for each data file, either in comments within the file (if the format supports it) or in this README.
* Do not store sensitive information in plaintext here. Use environment variables or a secure configuration management system for secrets.

121
data/quotes.json Normal file
View File

@@ -0,0 +1,121 @@
{
"phrases": [
"Cada decisión consciente alinea tu camino.",
"Cada día alineado refuerza tu destino.",
"Cada día consciente fortalece tu señal.",
"Cada día consciente refuerza tu camino.",
"Cada elección consciente te alinea más.",
"Cada experiencia trae un mensaje oculto.",
"Cada intención sostenida genera respuesta.",
"Cada paso alineado atrae claridad.",
"Cada paso consciente es escuchado.",
"Cada paso honesto genera respuesta.",
"Confía: estás siendo sostenido.",
"Cuando confías, avanzas más ligero.",
"Cuando confías, todo se acomoda.",
"Cuando confías, todo fluye con menos esfuerzo.",
"Cuando eliges bien, el camino se suaviza.",
"Cuando eliges claridad, todo coopera.",
"Cuando eliges paz, eliges bien.",
"Cuando eliges paz, todo se ordena.",
"Cuando sueltas el control, el camino se acomoda.",
"Cuando te alineas, todo coopera.",
"El camino se aclara cuando sigues caminando.",
"El equilibrio es parte del plan.",
"El equilibrio llega cuando dejas de forzar.",
"El equilibrio llega cuando sueltas el miedo.",
"El universo acomoda lo que tu mente no puede.",
"El universo acomoda piezas invisibles.",
"El universo acompaña decisiones conscientes.",
"El universo acompaña tu enfoque.",
"El universo ajusta lo que tú ya soltaste.",
"El universo ajusta lo que tú sostienes.",
"El universo amplifica lo que sostienes.",
"El universo avanza contigo.",
"El universo avanza contigo, incluso cuando dudas.",
"El universo abre caminos cuando confías.",
"El universo cuida lo que cuidas.",
"El universo cuida los procesos honestos.",
"El universo escucha lo que practicas.",
"El universo escucha lo que repites.",
"El universo escucha lo que sostienes a diario.",
"El universo guía, no empuja.",
"El universo honra la intención sostenida.",
"El universo no se equivoca con los tiempos.",
"El universo premia la coherencia.",
"El universo recompensa la constancia silenciosa.",
"El universo responde a la constancia.",
"El universo responde a la constancia real.",
"El universo responde a tu coherencia.",
"El universo responde mejor a la calma que a la prisa.",
"El universo respalda la claridad.",
"El universo respalda las decisiones honestas.",
"El universo respeta tus decisiones.",
"El universo se mueve a favor del equilibrio.",
"El universo se ordena cuando tú lo haces.",
"El universo también necesita tu paciencia.",
"El universo también responde a la gratitud.",
"El universo trabaja en silencio.",
"El universo trabaja en tu favor.",
"El universo trabaja mientras tú sigues avanzando.",
"El universo trabaja contigo, no contra ti.",
"El universo ya escuchó tu intención; ahora confía en el proceso.",
"Estás aprendiendo a recibir.",
"Estás más alineado de lo que crees.",
"Estás más cerca de lo que parece.",
"Estás siendo guiado aunque no lo notes.",
"Estás siendo llevado hacia algo mejor.",
"Estás siendo preparado.",
"Estás exactamente donde necesitas estar ahora.",
"La calma abre puertas invisibles.",
"La calma amplifica tu claridad.",
"La calma es una señal poderosa.",
"La calma también es acción.",
"La claridad llega después de la acción.",
"La claridad se construye caminando.",
"La coherencia atrae oportunidades.",
"La coherencia es una forma de magnetismo.",
"La intención clara atrae resultados claros.",
"La paciencia también es una señal de confianza.",
"La paciencia también atrae.",
"Lo correcto no genera caos.",
"Lo correcto se siente estable.",
"Lo correcto se siente ligero.",
"Lo que es para ti encuentra la forma de alcanzarte.",
"Lo que es para ti se siente en paz.",
"Lo que es para ti se sentirá claro.",
"Lo que es real no necesita prisa.",
"Lo que estás viviendo hoy te está preparando para algo mejor.",
"Lo que fluye contigo es real.",
"Lo que fluye no se fuerza.",
"Lo que hoy confías se fortalece.",
"Lo que hoy confías, mañana florece.",
"Lo que hoy eliges construye mañana.",
"Lo que hoy eliges define tu energía.",
"Lo que hoy parece confuso se ordenará pronto.",
"Lo que hoy parece lento es profundo.",
"Lo que hoy pesa, mañana tendrá sentido.",
"Lo que hoy siembras se manifestará mañana.",
"Lo que necesitas llegará en la forma correcta.",
"Lo que respetas, crece.",
"Lo que vibra alto atrae estabilidad.",
"Lo que vibra contigo permanece.",
"Nada auténtico necesita forzarse.",
"Nada llega antes de que estés listo.",
"Nada llega antes de tiempo.",
"Nada llega para confundirte sin razón.",
"Nada llega para romperte, sino para formarte.",
"Nada que sea para ti se pierde.",
"Nada real se desvanece.",
"Nada se acomoda sin intención.",
"Nada tiene que entenderse para funcionar.",
"Nada verdadero se pierde.",
"Nada verdadero se pierde en el camino.",
"No estás solo en este proceso.",
"No estás tarde; estás justo a tiempo para tu proceso.",
"Todo llega cuando dejas espacio para recibirlo.",
"Todo movimiento consciente tiene respuesta.",
"Todo se ordena cuando confías en tu proceso.",
"Todo tiene un ritmo perfecto."
]
}

36
data/sites.json Normal file
View File

@@ -0,0 +1,36 @@
{
"internos": {
"vps_soul23": "https://soul23.cloud/health",
"coolify": "https://aperture.soul23.cloud",
"formbricks": "https://feedback.soul23.cloud/health",
"vikunja": "https://tasks.soul23.cloud",
"gokapi": "https://wetrans.soul23.cloud",
"n8n_flows": "https://flows.soul23.cloud",
"ap_pos": "https://apos.soul23.cloud",
"appsmith": "https://appsmith.soul23.cloud"
},
"sitios_empresa": {
"vanity_web": "https://vanityexperience.mx/",
"vanity_cursos": "https://cursos.vanityexperience.mx/",
"vanity_academy": "https://academy.vanityexperience.mx/",
"vanity_unirse": "https://unirse.vanityexperience.mx/",
"soul23": "https://soul23.cloud/",
"socias_vanity": "https://socias.vanityexperience.mx/"
},
"externos": {
"fresha": "https://www.freshastatus.com/",
"google_gemini": "https://aistudio.google.com/status?hl=es-419",
"openai": "https://status.openai.com/",
"canva": "https://www.canvastatus.com/",
"tiktok": "https://www.tiktok.com",
"facebook": "https://www.facebook.com",
"instagram": "https://www.instagram.com",
"telegram": "https://core.telegram.org",
"whatsapp": "https://www.whatsapp.com",
"x": "https://www.x.com",
"youtube": "https://www.youtube.com",
"cloudflare": "https://www.cloudflarestatus.com",
"aws": "https://status.aws.amazon.com/rss/all.rss",
"azure": "https://status.azure.com/en-us/status/feed"
}
}

14
htmls/README.md Normal file
View File

@@ -0,0 +1,14 @@
# HTML Templates
This directory contains HTML files that serve as templates or specific pages for the application, not meant to be served as static assets from the root.
## Organization
* `telegram.html`: A specific HTML page used for Telegram redirection logic.
* Other `.html` files: Should be specific templates or special pages.
## Best Practices
* HTML files in this directory are typically served dynamically by the Express server for specific routes.
* Keep the HTML semantic and clean.
* Avoid placing general static HTML assets here; those should reside in the project root if they are meant to be served as part of the primary static content.

191
scripts/health_checker.py Normal file
View File

@@ -0,0 +1,191 @@
#!/usr/bin/env python3
import requests
import json
from datetime import datetime, timezone
import os
import time
# --- CONFIGURACIÓN DE WEBHOOKS (Desde GitHub Secrets) ---
webhook_urls_str = os.environ.get('WEBHOOK_URLS', '')
WEBHOOK_URLS = [url.strip() for url in webhook_urls_str.split(',') if url.strip()]
# --- Cargar sitios desde JSON ---
sites_file_path = os.path.join(os.path.dirname(__file__), '..', 'data', 'sites.json')
try:
with open(sites_file_path, 'r') as f:
sites_data = json.load(f)
INTERNOS = sites_data.get("internos", {})
SITIOS_EMPRESA = sites_data.get("sitios_empresa", {})
EXTERNOS = sites_data.get("externos", {})
except (FileNotFoundError, json.JSONDecodeError) as e:
print(json.dumps({"error": f"Could not load or parse sites.json: {e}"}))
exit(1)
# Servicios que usan Atlassian StatusPage
STATUSPAGE_SERVICES = ["openai", "canva", "cloudflare"]
# --- Funciones de Verificación de Estado ---
def check_url(url):
"""Verificación simple: ¿La web carga (Código 200)?"""
try:
headers = {'User-Agent': 'HealthCheckMonitor/1.0'}
r = requests.get(url, headers=headers, timeout=10)
return r.status_code
except requests.exceptions.RequestException:
return 0
def check_vps_health_endpoint(url):
"""
Verifica el estado del VPS consultando el endpoint JSON personalizado.
Espera: {"checks": {"vps_ping": {"alive": true, ...}}}
"""
try:
r = requests.get(url, timeout=10)
if r.status_code == 200:
try:
data = r.json()
vps_alive = data.get('checks', {}).get('vps_ping', {}).get('alive', False)
if vps_alive:
return "🟢 OK (VPS Reachable)"
else:
return "🔴 Caído (VPS reporta 'alive': false)"
except json.JSONDecodeError:
return "🟡 Advertencia (Respuesta no es JSON válido)"
else:
return f"🔴 Caído (Endpoint status: {r.status_code})"
except requests.RequestException as e:
return f"🔴 Error Conexión ({str(e)})"
def check_formbricks_health(url):
"""
Verifica el endpoint de salud de Formbricks.
NOTA: Usa la URL tal cual viene del diccionario (sin agregar /health extra).
"""
try:
response = requests.get(url, timeout=8)
if response.status_code == 200:
try:
data = response.json()
if data.get("status") == "ok":
return f"🟢 OK (API Health: ok)"
else:
return f"🟡 Advertencia ({data.get('status', 'unknown')})"
except json.JSONDecodeError:
return "🟡 Advertencia (No JSON)"
else:
return f"🔴 Caído (Código: {response.status_code})"
except requests.RequestException:
return f"🔴 Caído (Error red)"
def get_statuspage_status(base_url):
"""Consulta la API de statuspage.io (OpenAI, Canva)"""
api_url = f"{base_url.rstrip('/')}/api/v2/summary.json"
try:
response = requests.get(api_url, timeout=8)
if response.status_code == 200:
data = response.json()
description = data.get('status', {}).get('description')
indicator = data.get('status', {}).get('indicator')
if indicator == 'none':
return f"🟢 OK ({description})"
else:
return f"🟡 Advertencia ({description})"
return f"🔴 Caído ({response.status_code})"
except requests.exceptions.RequestException:
return f"🔴 Error verificación"
def get_gemini_status(display_url):
"""Verifica incidentes en Google Cloud (Vertex AI/Gemini)"""
feed_url = "https://status.cloud.google.com/incidents.json"
try:
response = requests.get(feed_url, timeout=8)
if response.status_code == 200:
incidents = response.json()
active_problems = []
for i in incidents:
if not i.get('end'): # Si no ha terminado
service_name = i.get('service_name', '').lower()
if 'gemini' in service_name or 'vertex' in service_name or 'generative' in service_name:
active_problems.append(i.get('external_desc', 'Fallo desconocido'))
if not active_problems:
return "🟢 OK (Sin incidentes en Google AI)"
else:
return f"🟡 Advertencia ({len(active_problems)} incidentes activos)"
else:
code = check_url(display_url)
return human_state(code)
except requests.exceptions.RequestException:
return f"🔴 Error de conexión"
def human_state(code):
if code == 200:
return f"🟢 OK ({code})"
if code in (301, 302, 307, 308):
return f"🟢 OK (Redirección {code})"
if code in (401, 403, 404):
return f"🟡 Advertencia ({code})"
return f"🔴 Caído ({code})"
# --- Lógica Principal ---
def build_section(diccionario):
salida = {}
for nombre, url_or_ip in diccionario.items():
# 1. Check VPS
if nombre == 'vps_soul23':
status_message = check_vps_health_endpoint(url_or_ip)
# 2. StatusPage
elif nombre in STATUSPAGE_SERVICES:
status_message = get_statuspage_status(url_or_ip)
# 3. Google Gemini
elif nombre == 'google_gemini':
status_message = get_gemini_status(url_or_ip)
# 4. Formbricks (URL completa)
elif nombre == 'formbricks':
status_message = check_formbricks_health(url_or_ip)
# 5. Resto
else:
status = check_url(url_or_ip)
salida[f"{nombre}_url"] = url_or_ip
salida[f"{nombre}_status"] = status
salida[f"{nombre}_state"] = human_state(status)
continue
salida[f"{nombre}_url"] = url_or_ip
salida[f"{nombre}_status"] = status_message
salida[f"{nombre}_state"] = status_message
return salida
def main():
start_time = time.time()
resultado = {
"timestamp": datetime.now(timezone.utc).isoformat(),
"internos": build_section(INTERNOS),
"empresa": build_section(SITIOS_EMPRESA),
"externos": build_section(EXTERNOS)
}
end_time = time.time()
execution_time = round(end_time - start_time, 2)
resultado["execution_time_seconds"] = execution_time
for url in WEBHOOK_URLS:
try:
resp = requests.post(url, json=resultado, timeout=10)
except requests.RequestException:
pass
print(json.dumps(resultado, indent=4))
if __name__ == "__main__":
main()

View File

@@ -1,5 +1,6 @@
const express = require("express");
const path = require("path");
const fs = require("fs");
const { exec } = require("child_process");
@@ -12,13 +13,26 @@ app.use(express.static(rootDir, { index: "index.html" }));
// Health checker should always return the raw script with text/plain
app.get("/healthchecker", (req, res) => {
res.type("text/plain");
res.sendFile(path.join(rootDir, "scripts", "health_checker"));
const pythonScriptPath = path.join(rootDir, "scripts", "health_checker.py");
exec(`python3 ${pythonScriptPath}`, (error, stdout, stderr) => {
if (error) {
console.error(`Error executing health_checker.py: ${stderr}`);
return res.status(500).json({ error: "Failed to execute health checker" });
}
try {
const healthData = JSON.parse(stdout);
res.status(200).json(healthData);
} catch (parseErr) {
console.error(`Error parsing health checker output: ${parseErr}`);
res.status(500).json({ error: "Failed to parse health checker output" });
}
});
});
// Magic link para redirigir a la app de Telegram según plataforma
app.get("/telegram", (req, res) => {
res.sendFile(path.join(rootDir, "telegram.html"));
res.sendFile(path.join(rootDir, "htmls", "telegram.html"));
});
// Standard health check endpoint for monitoring with VPS ping
@@ -41,6 +55,28 @@ app.get("/health", (req, res) => {
});
});
// Endpoint to get a random phrase
app.get("/day-quote", (req, res) => {
fs.readFile(path.join(rootDir, "data", "quotes.json"), "utf8", (err, data) => {
if (err) {
console.error("Error reading quotes file:", err);
return res.status(500).json({ error: "Could not read quotes file" });
}
try {
const quotes = JSON.parse(data);
const phrases = quotes.phrases;
if (!phrases || phrases.length === 0) {
return res.status(500).json({ error: "No phrases found" });
}
const randomPhrase = phrases[Math.floor(Math.random() * phrases.length)];
res.status(200).json({ phrase: randomPhrase });
} catch (parseErr) {
console.error("Error parsing quotes file:", parseErr);
return res.status(500).json({ error: "Could not parse quotes file" });
}
});
});
// Endpoint to get the current time in multiple formats
app.get("/time-server", (req, res) => {
const now = new Date();

View File

@@ -1,449 +0,0 @@
# =============================================================================
# CONFIGURACIÓN ZSH - Marco Gallegos v3.1
# =============================================================================
#
# Este archivo configura el entorno de la terminal Zsh. Incluye la
# configuración del PATH, la carga de Oh My Zsh, la inicialización de
# Oh My Posh, y una colección de alias y funciones para mejorar la
# productividad.
#
# =============================================================================
# --- PATH --------------------------------------------------------------------
# Define las rutas donde el sistema buscará los programas ejecutables.
# `typeset -U` se asegura de que no haya rutas duplicadas.
typeset -U PATH path
path=(
$HOME/.local/bin # Scripts y binarios instalados por el usuario.
$HOME/bin # Directorio personal de binarios.
$HOME/.npm-global/bin # Paquetes de Node.js instalados globalmente.
$HOME/AppImages # Aplicaciones en formato AppImage.
$HOME/go/bin # Binarios de Go.
$path # Rutas del sistema existentes.
)
# --- Oh My Zsh ---------------------------------------------------------------
# Configuración y carga del framework Oh My Zsh.
export ZSH="$HOME/.oh-my-zsh"
# El tema se deja vacío porque Oh My Posh se encargará de gestionar el prompt.
ZSH_THEME=""
# Lista de plugins de Oh My Zsh a cargar.
plugins=(
git sudo history colorize
docker docker-compose
npm node python pip golang
copypath copyfile
)
# Desactiva la comprobación de seguridad de Oh My Zsh para directorios
# con permisos de escritura para otros usuarios, lo que puede ser molesto.
export ZSH_DISABLE_COMPFIX=true
# Configuración de la caché de autocompletado para mejorar el rendimiento.
zstyle ':completion::complete:*' use-cache on
zstyle ':completion::complete:*' cache-path "$HOME/.zcompcache"
# Hace que el autocompletado no distinga entre mayúsculas y minúsculas.
zstyle ':completion:*' matcher-list 'm:{a-z}={A-Za-z}'
# Habilita colores en el menú de autocompletado.
zstyle ':completion:*' list-colors "${(s.:.)LS_COLORS}"
zstyle ':completion:*' menu select
# Carga Oh My Zsh.
[ -r "$ZSH/oh-my-zsh.sh" ] && source "$ZSH/oh-my-zsh.sh"
# Carga los plugins de resaltado de sintaxis y autosugerencias.
# Intenta cargar la versión instalada con Oh My Zsh y, si no la encuentra,
# busca la versión instalada en el sistema.
[ -r "${ZSH_CUSTOM:-$ZSH/custom}/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh" ] && \
source "${ZSH_CUSTOM:-$ZSH/custom}/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh"
if [ ! -r "${ZSH_CUSTOM:-$ZSH/custom}/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh" ] && \
[ -r "/usr/share/zsh/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh" ]; then
source "/usr/share/zsh/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh"
fi
[ -r "${ZSH_CUSTOM:-$ZSH/custom}/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh" ] && \
source "${ZSH_CUSTOM:-$ZSH/custom}/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh"
if [ ! -r "${ZSH_CUSTOM:-$ZSH/custom}/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh" ] && \
[ -r "/usr/share/zsh/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh" ]; then
source "/usr/share/zsh/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh"
fi
# --- Oh My Posh --------------------------------------------------------------
# Inicializa Oh My Posh para personalizar el prompt.
if command -v oh-my-posh >/dev/null 2>&1; then
# Carga el tema Catppuccin Frappe si existe.
if [ -f ~/.poshthemes/catppuccin_frappe.omp.json ]; then
eval "$(oh-my-posh init zsh --config ~/.poshthemes/catppuccin_frappe.omp.json)"
else
# Si no, carga el tema por defecto.
eval "$(oh-my-posh init zsh)"
fi
fi
# --- Go ----------------------------------------------------------------------
# Configura las variables de entorno para el lenguaje de programación Go.
export GOPATH="$HOME/go"
export GOBIN="$GOPATH/bin"
# --- NVM (Node Version Manager) ----------------------------------------------
# COMENTADO: NVM deshabilitado porque estamos usando mise para gestionar Node.js
# Si deseas usar NVM en lugar de mise, descomenta estas líneas y comenta la
# sección de mise más abajo.
#export NVM_DIR="$HOME/.nvm"
#[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
#[ -s "$NVM_DIR/bash_completion" ] && . "$NVM_DIR/bash_completion"
# --- Python ------------------------------------------------------------------
# Alias para usar las versiones 3 de python y pip por defecto.
alias pip='pip3'
alias python='python3'
# Función para gestionar entornos virtuales de Python.
venv() {
case "$1" in
create) python -m venv .venv && echo "✅ Entorno virtual creado en ./.venv" ;;
on|activate)
if [ -f ".venv/bin/activate" ]; then
. .venv/bin/activate
echo "🟢 Entorno virtual activado"
else
echo "❌ Entorno virtual no encontrado en ./.venv"
fi
;;
off|deactivate)
if command -v deactivate &>/dev/null; then
deactivate 2>/dev/null
echo "🔴 Entorno virtual desactivado"
else
echo "🤷 No hay un entorno virtual activo para desactivar"
fi
;;
*) echo "Uso: venv [create|on|off|activate|deactivate]" ;;
esac
}
# --- Aliases -----------------------------------------------------------------
# Colección de atajos para comandos comunes.
# Generales
alias cls='clear'
alias ll='ls -alF'
alias la='ls -A'
alias l='ls -CF'
alias ..='cd ..'
alias ...='cd ../..'
alias ....='cd ../../..'
# Información del sistema
alias ff='fastfetch'
alias nf='fastfetch'
# Gestión de paquetes en Arch Linux
alias pacu='sudo pacman -Syu'
alias paci='sudo pacman -S'
alias pacr='sudo pacman -Rns'
alias pacs='pacman -Ss'
alias yayu='yay -Syu' # Requiere yay
alias yayi='yay -S' # Requiere yay
# Git
alias gs='git status'
alias ga='git add'
alias gc='git commit'
alias gcm='git commit -m'
alias gfa='git fetch --all'
alias gfr='git fetch origin'
alias gp='git push'
alias gl='git pull'
alias gd='git diff'
alias gb='git branch'
alias gco='git checkout'
alias gcb='git checkout -b'
alias glog='git log --oneline --graph --decorate'
gac(){ git add . && git commit -m "$1"; }
# Docker
# Detecta si se usa `docker compose` (nuevo) o `docker-compose` (antiguo).
docker compose version >/dev/null 2>&1 && alias dc='docker compose' || alias dc='docker-compose'
alias d='docker'
alias dps='docker ps -a'
alias di='docker images'
alias dex='docker exec -it'
alias dlog='docker logs -f'
# NPM
alias nrs='npm run start'
alias nrd='npm run dev'
alias nrb='npm run build'
alias nrt='npm run test'
alias ni='npm install'
alias nid='npm install --save-dev'
alias nig='npm install -g'
# Python
alias py='python'
alias pir='pip install -r requirements.txt'
alias pipi='pip install'
alias pipf='pip freeze > requirements.txt'
# ZeroTier
alias zt='sudo zerotier-cli'
alias ztstatus='sudo zerotier-cli listnetworks'
alias ztinfo='sudo zerotier-cli info'
# Utilidades
alias clima='curl wttr.in/Saltillo'
# --- IA y ChatGPT ------------------------------------------------------------
# Alias para un cliente de ChatGPT en la terminal (ej. 'chatgpt-cli').
# Reemplaza 'chatgpt-cli' por el nombre del programa que uses.
#
# alias chat='chatgpt-cli'
# alias chat-q='chatgpt-cli -q' # Para una pregunta rápida sin guardar en el historial.
# alias chat-c='chatgpt-cli --continue' # Para continuar la conversación anterior.
# alias chat-code='chatgpt-cli --code' # Para preguntas de código.
# --- Funciones ---------------------------------------------------------------
# Funciones personalizadas para tareas comunes.
# Crea un directorio y se mueve a él.
mkcd(){ mkdir -p "$1" && cd "$1"; }
# Extrae cualquier tipo de archivo comprimido.
extract(){
[ ! -f "$1" ] && echo "No es un archivo" && return 1
case "$1" in
*.tar.bz2) tar xjf "$1" ;;
*.tar.gz) tar xzf "$1" ;;
*.bz2) bunzip2 "$1" ;;
*.rar) unrar e "$1" ;;
*.gz) gunzip "$1" ;;
*.tar) tar xf "$1" ;;
*.tbz2) tar xjf "$1" ;;
*.tgz) tar xzf "$1" ;;
*.zip) unzip "$1" ;;
*.Z) uncompress "$1" ;;
*.7z) 7z x "$1" ;;
*) echo "No se puede extraer '$1': formato no reconocido." ;;
esac
}
# Mata el proceso que esté usando un puerto específico.
killport(){
[ $# -eq 0 ] && echo "Uso: killport <puerto>" && return 1
local pid=$(lsof -ti:"$1" 2>/dev/null)
[ -n "$pid" ] && kill -9 "$pid" && echo "✅ Proceso en puerto $1 eliminado (PID: $pid)" || echo "🤷 No se encontró ningún proceso en el puerto $1"
}
# Inicia un servidor HTTP simple en el directorio actual.
serve(){ python -m http.server "${1:-8000}"; }
# Carga el archivo de ayuda si existe
[ -f ~/.zshrc.help ] && source ~/.zshrc.help
# --- yt-dlp (Descargador de vídeos) ------------------------------------------
# Funciones mejoradas para descargar audio y video desde YouTube.
export YTDLP_DIR="$HOME/Videos/YouTube"
mkdir -p "$YTDLP_DIR"/{Music,Videos} 2>/dev/null
# Descarga audio en formato MP3.
ytm() {
case "$1" in
-h|--help|'')
echo "🎵 ytm <URL|búsqueda> - Descarga audio (MP3 320kbps) a $YTDLP_DIR/Music/"
echo "Ejemplos:"
echo " ytm https://youtu.be/dQw4w9WgXcQ"
echo " ytm 'Never Gonna Give You Up'"
return 0
;;
esac
if ! command -v yt-dlp &>/dev/null; then
echo "❌ yt-dlp no está instalado. Por favor, instálalo para usar esta función."
return 1
fi
local out="$YTDLP_DIR/Music/%(title).180s.%(ext)s"
local opts=(
--extract-audio --audio-format mp3 --audio-quality 320K
--embed-metadata --embed-thumbnail --convert-thumbnails jpg
--no-playlist --retries 10 --fragment-retries 10
--user-agent "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36"
--extractor-args "youtube:player_client=android,web"
--progress --newline -o "$out"
)
if [[ "$1" == http* ]]; then
echo "📥 Descargando audio..."
yt-dlp "${opts[@]}" "$@"
else
echo "🔍 Buscando: $*"
yt-dlp "${opts[@]}" "ytsearch1:$*"
fi
[ $? -eq 0 ] && echo "✅ Audio descargado en: $YTDLP_DIR/Music/" || echo "❌ Falló la descarga de audio."
}
# Descarga vídeo en formato MP4.
ytv() {
case "$1" in
-h|--help|'')
echo "🎬 ytv <URL|búsqueda> [calidad] - Descarga video a $YTDLP_DIR/Videos/"
echo "Calidades disponibles: 1080, 720, 480 (por defecto: mejor disponible MP4)"
echo "Ejemplos:"
echo " ytv https://youtu.be/dQw4w9WgXcQ 1080"
echo " ytv 'Rick Astley - Never Gonna Give You Up' 720"
return 0
;;
esac
if ! command -v yt-dlp &>/dev/null; then
echo "❌ yt-dlp no está instalado. Por favor, instálalo para usar esta función."
return 1
fi
local quality="${2:-best}"
local out="$YTDLP_DIR/Videos/%(title).180s.%(ext)s"
local fmt
case "$quality" in
1080) fmt='bv*[height<=1080][ext=mp4]+ba/b[height<=1080]' ;;
720) fmt='bv*[height<=720][ext=mp4]+ba/b[height<=720]' ;;
480) fmt='bv*[height<=480][ext=mp4]+ba/b[height<=480]' ;;
*) fmt='bv*[ext=mp4]+ba/b[ext=mp4]/b' ;; # Mejor calidad MP4
esac
local opts=(
-f "$fmt" --embed-metadata --embed-thumbnail
--embed-subs --sub-langs "es.*,en.*" --convert-thumbnails jpg
--no-playlist --retries 10 --fragment-retries 10
--user-agent "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36"
--extractor-args "youtube:player_client=android,web"
--progress --newline -o "$out"
)
if [[ "$1" == http* ]]; then
echo "📥 Descargando video..."
yt-dlp "${opts[@]}" "$1"
else
echo "🔍 Buscando: $1"
yt-dlp "${opts[@]}" "ytsearch1:$1"
fi
[ $? -eq 0 ] && echo "✅ Video descargado en: $YTDLP_DIR/Videos/" || echo "❌ Falló la descarga de video."
}
# Lista los últimos archivos descargados.
ytls() {
echo "🎵 Últimos 5 audios descargados en Music:"
ls -1t "$YTDLP_DIR/Music" 2>/dev/null | head -5 | sed 's/^/ /' || echo " (vacío)"
echo ""
echo "🎬 Últimos 5 videos descargados en Videos:"
ls -1t "$YTDLP_DIR/Videos" 2>/dev/null | head -5 | sed 's/^/ /' || echo " (vacío)"
}
# --- GNOME Keyring y Agente SSH ----------------------------------------------
# Configuración para que GNOME Keyring gestione las claves SSH.
if [ -n "$DESKTOP_SESSION" ] && command -v gnome-keyring-daemon >/dev/null 2>&1; then
if ! pgrep -u "$USER" gnome-keyring-daemon > /dev/null 2>&1; then
eval "$(gnome-keyring-daemon --start --components=pkcs11,secrets,ssh 2>/dev/null)" || true
fi
export SSH_AUTH_SOCK GPG_AGENT_INFO GNOME_KEYRING_CONTROL GNOME_KEYRING_PID
fi
# Fallback a un agente SSH estándar si GNOME Keyring no está disponible.
if [ -z "$SSH_AUTH_SOCK" ]; then
export SSH_AGENT_DIR="$HOME/.ssh/agent"
mkdir -p "$SSH_AGENT_DIR"
SSH_ENV="$SSH_AGENT_DIR/env"
start_agent() {
echo "🔑 Iniciando ssh-agent..."
ssh-agent > "$SSH_ENV"
chmod 600 "$SSH_ENV"
. "$SSH_ENV" > /dev/null
}
if [ -f "$SSH_ENV" ]; then
. "$SSH_ENV" > /dev/null
ps -p $SSH_AGENT_PID > /dev/null 2>&1 || start_agent
else
start_agent
fi
if [ -d "$HOME/.ssh" ]; then
for key in "$HOME/.ssh"/*; do
if [ -f "$key" ] && [[ ! "$key" =~ \.pub$ ]] && \
[[ ! "$key" =~ known_hosts ]] && [[ ! "$key" =~ authorized_keys ]] && \
[[ ! "$key" =~ config ]] && [[ ! "$key" =~ agent ]]; then
if ssh-keygen -l -f "$key" &>/dev/null; then
local key_fingerprint=$(ssh-keygen -lf "$key" 2>/dev/null | awk '{print $2}')
if ! ssh-add -l 2>/dev/null | grep -q "$key_fingerprint"; then
if ssh-add "$key" 2>/dev/null; then
echo "✅ Llave SSH agregada: $(basename $key)"
fi
fi
fi
fi
done
fi
fi
# Alias para gestionar el agente SSH.
alias ssh-list='ssh-add -l'
alias ssh-clear='ssh-add -D'
alias ssh-reload='
ssh-add -D 2>/dev/null
for key in ~/.ssh/*; do
if [ -f "$key" ] && [[ ! "$key" =~ \.pub$ ]] && ssh-keygen -l -f "$key" &>/dev/null; then
ssh-add "$key" 2>/dev/null && echo "✅ $(basename $key)"
fi
done
'
alias ssh-github='ssh -T git@github.com'
# --- zoxide ------------------------------------------------------------------
# Reemplazo inteligente de `cd` que recuerda los directorios que visitas.
if command -v zoxide >/dev/null 2>&1; then
eval "$(zoxide init zsh)"
alias zz='z -' # Ir al directorio anterior
alias zi='zi' # Modo interactivo
fi
# --- Historial de Zsh --------------------------------------------------------
# Configuración para un historial de comandos más útil y persistente.
HISTSIZE=100000
SAVEHIST=100000
HISTFILE=~/.zsh_history
setopt APPEND_HISTORY SHARE_HISTORY HIST_IGNORE_DUPS HIST_IGNORE_ALL_DUPS HIST_IGNORE_SPACE AUTO_CD EXTENDED_GLOB
# Deshabilita el bloqueo de la terminal con CTRL+S.
stty -ixon 2>/dev/null
# Habilita colores en `man` y `less`.
export LESS='-R'
# --- Funciones y Configuraciones Locales -------------------------------------
# Carga archivos de funciones personalizadas desde ~/.zsh_functions/
[ -d "$HOME/.zsh_functions" ] || mkdir -p "$HOME/.zsh_functions"
for func_file in "$HOME/.zsh_functions"/*.zsh(N); do
source "$func_file"
done
# Carga un archivo de configuración local (~/.zshrc.local) si existe.
# Ideal para añadir variables de entorno y configuraciones privadas.
[ -f ~/.zshrc.local ] && source ~/.zshrc.local
# --- mise (Gestor de versiones de herramientas) ------------------------------
# IMPORTANTE: Esta sección debe ir AL FINAL del archivo para que mise pueda
# sobrescribir correctamente el PATH y usar las versiones configuradas.
# mise gestiona versiones de Node.js, Python, y otras herramientas de desarrollo.
if command -v mise >/dev/null 2>&1; then
eval "$(mise activate zsh)"
# Alias útiles para mise
alias mise-list='mise list'
alias mise-current='mise current'
alias mise-install='mise install'
alias mise-use='mise use'
fi

View File

@@ -1,127 +0,0 @@
# --- Funciones de Ayuda ------------------------------------------------------
# Muestra una lista detallada de todos los alias y funciones personalizadas.
# Soporta modo interactivo con fzf si está instalado.
zsh_help() {
# --- Colores ---
local C_DEFAULT="\e[0m"
local C_BOLD="\e[1m"
local C_TITLE="\e[1;35m" # Bold Magenta
local C_SUBTITLE="\e[1;36m" # Bold Cyan
local C_SECTION="\e[1;34m" # Bold Blue
local C_CMD="\e[0;32m" # Green
local C_DESC="\e[0;37m" # White
local C_NOTE="\e[0;90m" # Gray
# --- Componentes de la Ayuda ---
_help_header() {
clear
echo -e "${C_TITLE}╔════════════════════════════════════════════════════════════════════╗"
echo -e "${C_TITLE}║ GUÍA DEL SISTEMA Y CONFIGURACIÓN DE ZSH ║"
echo -e "${C_TITLE}╚════════════════════════════════════════════════════════════════════╝${C_DEFAULT}"
}
_help_print_section() { echo -e "\n${C_SECTION}--- $1 ---${C_DEFAULT}"; }
_help_print_cmd() { printf " ${C_CMD}%-18s ${C_DESC}%s\n${C_DEFAULT}" "$1" "$2"; }
_help_nav() {
_help_print_section "Navegación y Archivos"
_help_print_cmd ".." "Subir un nivel (cd ..)"
_help_print_cmd "..." "Subir dos niveles (cd ../..)"
_help_print_cmd "ll" "Listar con detalles (ls -alF)"
_help_print_cmd "mkcd <dir>" "Crear directorio y entrar en él"
_help_print_cmd "z <dir>" "Saltar a directorio (zoxide)"
_help_print_cmd "zi" "Seleccionar directorio interactivo"
_help_print_cmd "zz" "Volver al directorio anterior (z -)"
_help_print_cmd "extract <file>" "Descomprimir (zip, tar, rar...)"
}
_help_git() {
_help_print_section "Git - Flujo de Trabajo"
_help_print_cmd "gs" "Ver estado (git status)"
_help_print_cmd "ga" "Añadir cambios (git add)"
_help_print_cmd "gc" "Guardar cambios (git commit)"
_help_print_cmd "gcm <msg>" "Guardar con mensaje"
_help_print_cmd "gac <msg>" "Añadir Y Guardar todo"
_help_print_cmd "gp" "Subir cambios (git push)"
_help_print_cmd "gl" "Bajar cambios (git pull)"
_help_print_cmd "gfa" "Traer todo (git fetch --all)"
_help_print_cmd "gfr" "Traer origen (git fetch origin)"
}
_help_docker() {
_help_print_section "Docker - Contenedores"
_help_print_cmd "dps" "Ver contenedores"
_help_print_cmd "dc" "Docker Compose"
_help_print_cmd "dex <id>" "Entrar a terminal de contenedor"
_help_print_cmd "dlog <id>" "Ver logs en tiempo real"
}
_help_dev() {
_help_print_section "Desarrollo"
_help_print_cmd "py / python" "Ejecuta python3"
_help_print_cmd "pip" "Ejecuta pip3"
_help_print_cmd "venv create" "Crea entorno virtual (.venv)"
_help_print_cmd "venv on" "Activa entorno virtual"
_help_print_cmd "serve" "Servidor web en puerto 8000"
_help_print_cmd "nrd" "npm run dev"
}
_help_utils() {
_help_print_section "Utilidades"
_help_print_cmd "ff / nf" "Info del sistema (Fastfetch)"
_help_print_cmd "killport <N>" "Matar proceso en puerto N"
_help_print_cmd "clima" "Ver pronóstico del tiempo"
_help_print_cmd "ytm <url>" "Descargar música (MP3)"
_help_print_cmd "ytv <url>" "Descargar video (MP4)"
}
_help_all() {
_help_header
echo -e "\n${C_SUBTITLE}Resumen del Sistema:${C_DEFAULT}"
echo -e "${C_BOLD}Framework:${C_DEFAULT} Oh My Zsh"
echo -e "${C_BOLD}Plugins:${C_DEFAULT} git, docker, npm, python..."
_help_nav
_help_git
_help_docker
_help_dev
_help_utils
echo -e "\n${C_NOTE}Tip: Usa 'help' sin argumentos para el menú interactivo.${C_DEFAULT}\n"
}
_wait_for_key() {
echo ""
read -k 1 "key?Presiona cualquier tecla para volver..."
}
# --- Lógica Principal ---
if [[ "$1" == "--all" ]]; then
_help_all
return 0
fi
# Si fzf está instalado, mostrar menú interactivo en bucle
if command -v fzf >/dev/null 2>&1; then
while true; do
local options="Todo\nNavegación\nGit\nDocker\nDesarrollo\nUtilidades\nSalir (q)"
local selected=$(echo -e "$options" | fzf --ansi --height=40% --layout=reverse --border --prompt="Ayuda > " --header="Selecciona un tema")
case "$selected" in
"Todo") _help_all; _wait_for_key ;;
"Navegación") _help_header; _help_nav; _wait_for_key ;;
"Git") _help_header; _help_git; _wait_for_key ;;
"Docker") _help_header; _help_docker; _wait_for_key ;;
"Desarrollo") _help_header; _help_dev; _wait_for_key ;;
"Utilidades") _help_header; _help_utils; _wait_for_key ;;
"Salir (q)"|"") break ;;
esac
done
else
# Fallback si no hay fzf
_help_all
fi
}
# Alias para acceder a la función de ayuda.
alias zsh-help='zsh_help'
alias help='zsh_help'

View File

@@ -1,109 +0,0 @@
# --- Funciones de Ayuda ------------------------------------------------------
# Muestra una lista de todos los alias y funciones personalizadas.
zsh_help() {
# --- Colores ---
local C_DEFAULT="\e[0m"
local C_BOLD="\e[1m"
local C_TITLE="\e[1;35m" # Bold Magenta
local C_SECTION="\e[1;34m" # Bold Blue
local C_CMD="\e[0;32m" # Green
local C_DESC="\e[0;37m" # White
local C_HL="\e[0;33m" # Yellow
# --- Encabezado ---
echo -e "${C_TITLE}╔═════════════════════════════════════════════════════════╗"
echo -e "${C_TITLE}║ AYUDA DE LA CONFIGURACIÓN DE ZSH - Comandos ║"
echo -e "${C_TITLE}╚═════════════════════════════════════════════════════════╝${C_DEFAULT}"
# --- Función para imprimir secciones ---
print_section() {
echo -e "\n${C_SECTION}--- $1 ---${C_DEFAULT}"
}
# --- Función para imprimir comandos ---
print_command() {
printf " ${C_CMD}%-15s ${C_DESC}%s\n${C_DEFAULT}" "$1" "$2"
}
# --- Alias Generales ---
print_section "Alias Generales"
print_command "cls" "Limpia la pantalla."
print_command "ll" "Lista archivos en formato largo."
print_command "la" "Lista todos los archivos (incluyendo ocultos)."
print_command "l" "Lista archivos en columnas."
print_command ".." "Sube un nivel en el árbol de directorios."
print_command "..." "Sube dos niveles."
print_command "...." "Sube tres niveles."
print_command "ff / nf" "Muestra información del sistema (fastfetch)."
# --- Gestión de Paquetes (Arch) ---
print_section "Gestión de Paquetes (Arch Linux)"
print_command "pacu" "Actualiza el sistema (pacman)."
print_command "paci" "Instala un paquete (pacman)."
print_command "pacr" "Elimina un paquete (pacman)."
print_command "pacs" "Busca un paquete (pacman)."
print_command "yayu" "Actualiza el sistema (yay)."
print_command "yayi" "Instala un paquete (yay)."
# --- Git ---
print_section "Git"
print_command "gs" "git status"
print_command "ga" "git add"
print_command "gc" "git commit"
print_command "gcm" "git commit -m '...'"
print_command "gac <msg>" "git add . && git commit -m '<msg>'"
print_command "gp" "git push"
print_command "gl" "git pull"
print_command "gd" "git diff"
print_command "gb" "git branch"
print_command "gco" "git checkout"
print_command "gcb" "git checkout -b"
print_command "glog" "Muestra un log de commits formateado."
# --- Docker ---
print_section "Docker"
print_command "d" "docker"
print_command "dc" "docker compose"
print_command "dps" "Muestra todos los contenedores."
print_command "di" "Muestra todas las imágenes."
print_command "dex <id>" "Ejecuta una terminal en un contenedor."
print_command "dlog <id>" "Muestra los logs de un contenedor."
# --- Python ---
print_section "Python"
print_command "py" "python3"
print_command "pip" "pip3"
print_command "pipi" "pip install ..."
print_command "pir" "pip install -r requirements.txt"
print_command "pipf" "pip freeze > requirements.txt"
print_command "venv create" "Crea un entorno virtual."
print_command "venv on" "Activa el entorno virtual."
print_command "venv off" "Desactiva el entorno virtual."
print_command "serve [port]" "Inicia un servidor HTTP (puerto 8000 por defecto)."
# --- Funciones de Utilidad ---
print_section "Funciones de Utilidad"
print_command "mkcd <dir>" "Crea un directorio y entra en él."
print_command "extract <file>" "Extrae cualquier archivo comprimido."
print_command "killport <port>" "Mata el proceso que usa un puerto."
print_command "clima" "Muestra el clima de Saltillo."
print_command "zsh-help" "Muestra esta ayuda."
# --- Descargas (yt-dlp) ---
print_section "Descargas (yt-dlp)"
print_command "ytm <url|bús>" "Descarga audio de YouTube como MP3."
print_command "ytv <url|bús>" "Descarga video de YouTube como MP4."
print_command "ytls" "Lista los últimos 5 archivos descargados."
# --- Agente SSH ---
print_section "Agente SSH"
print_command "ssh-list" "Lista las llaves SSH cargadas."
print_command "ssh-clear" "Elimina todas las llaves del agente."
print_command "ssh-reload" "Recarga todas las llaves SSH."
print_command "ssh-github" "Prueba la conexión SSH con GitHub."
echo ""
}
# Alias para acceder a la función de ayuda.
alias zsh-help='zsh_help'
alias help='zsh_help'

View File

@@ -1,19 +0,0 @@
# =============================================================================
# CONFIGURACIÓN LOCAL ZSH - .zshrc.local
# =============================================================================
#
# Este archivo se carga automáticamente desde .zshrc si existe.
# Úsalo para configuraciones específicas de esta máquina, claves privadas,
# tokens de API, o alias que no quieres sincronizar en tu dotfiles público.
#
# =============================================================================
# --- Variables de Entorno Privadas ---
# export GITHUB_TOKEN="tu_token_aqui"
# export OPENAI_API_KEY="sk-..."
# --- Alias Locales ---
# alias work="cd /path/to/work"
# --- Configuraciones Específicas ---
# git config --global user.email "marco@trabajo.com"