diff --git a/PLAN.md b/PLAN.md new file mode 100644 index 0000000..4d9af12 --- /dev/null +++ b/PLAN.md @@ -0,0 +1,148 @@ +# Plan: Emacs Integration for napi — Remote File-Notify via TRAMP + +## Context + +Fénix wants **real-time awareness in Emacs** of student deliveries on zzz (qu3v3d0.tech). The GNU Emacs manual confirms that `file-notify-add-watch` works on **remote machines** via TRAMP — it runs `inotifywait` on the remote host through SSH and streams events back in real-time. No polling. + +This means: **no modifications to watcher scripts on zzz**, no reverse SSH tunnels, no Porthole/JSON-RPC. Pure Emacs, using built-in capabilities + a dedicated daemon. + +## Architecture + +``` +emacs --daemon=napi (local) + │ + ├── TRAMP SSH → zzz:/home/*/python/ (ASIR1 — 21 dirs) + │ └── inotifywait running on zzz (auto-started by TRAMP) + │ + ├── TRAMP SSH → zzz:/home/*/html/ (DDAW2 — 19 dirs) + │ └── inotifywait running on zzz (auto-started by TRAMP) + │ + └── On file event → forward notification to `water` daemon + ├── Desktop notification (D-Bus) + ├── Sound alert (paplay) + ├── *napi-log* buffer (persistent log) + └── Minibuffer message + +emacsclient --socket-name=napi --eval '(napi-dashboard)' ← interact +emacsclient --socket-name=water --eval '(napi-log)' ← view in main UI +``` + +## Prerequisites + +- `inotify-tools` installed on zzz (provides `inotifywait`) — **already present** (watchers use it) +- SSH key access from local → zzz — **already working** (`ssh fenix@qu3v3d0.tech`) +- Emacs 30.1 on local — **confirmed** + +## Implementation Steps + +### Step 1: Create `~/napi/emacs/napi.el` + +The core elisp package. ~200 lines. Key components: + +| Component | Purpose | +|:----------|:--------| +| `napi-watch-start` | Sets up TRAMP file-notify watches on all student dirs | +| `napi-watch-stop` | Removes all watches cleanly | +| `napi-notify` | Handles a file event: log + desktop notification + sound | +| `napi-log` | Interactive: open `*napi-log*` buffer | +| `napi-open-student` | Interactive: completing-read → open student dir/notas.md | +| `napi-dashboard` | Interactive: overview of all students + last delivery | +| `C-c n` prefix | Keybindings for all interactive commands | + +**Student name maps** (username → full name) embedded in elisp, mirroring watcher scripts. + +**TRAMP watch setup** — the core innovation: +```elisp +;; For each ASIR1 student: +(file-notify-add-watch + "/ssh:fenix@qu3v3d0.tech:/home/jara/python/" + '(change) + #'napi--handle-event) + +;; 40 watches total (21 ASIR1 + 19 DDAW2) +;; Each spawns a persistent inotifywait on zzz via SSH +``` + +**Event handler** filters noise (Thumbs.db, __pycache__, .tmp, etc.) and forwards clean notifications to `water` daemon via: +```elisp +(shell-command + "emacsclient --socket-name=water --eval '(napi--show-notification ...)' &") +``` + +**Reconnection**: TRAMP sends a `stopped` event when SSH drops. The handler auto-re-establishes the watch after a delay. + +### Step 2: Create systemd user unit for `napi` daemon + +File: `~/.config/systemd/user/emacs-napi.service` + +```ini +[Unit] +Description=Emacs daemon for napi student monitoring +After=network-online.target +Wants=network-online.target + +[Service] +Type=forking +ExecStart=/usr/bin/emacs --daemon=napi -l ~/napi/emacs/napi-init.el +ExecStop=/usr/bin/emacsclient --socket-name=napi --eval "(kill-emacs)" +Restart=on-failure +RestartSec=30 + +[Install] +WantedBy=default.target +``` + +### Step 3: Create `~/napi/emacs/napi-init.el` + +Minimal init file for the `napi` daemon (not the full `~/.emacs.d/init.el`): + +```elisp +;; Loaded ONLY by emacs --daemon=napi +(load "~/napi/emacs/napi.el") +(napi-watch-start) +(message "napi: watching %d student directories on zzz" napi--watch-count) +``` + +### Step 4: Also load `napi.el` in `water` daemon + +Add to `~/.emacs.d/init.el` (near line 1478, following `funciones-art.el` pattern): + +```elisp +(load "~/napi/emacs/napi.el") +``` + +This gives the `water` daemon the `napi-log`, `napi-dashboard`, `napi-open-student` commands and the `C-c n` keybindings — but **no watches** (those run in the `napi` daemon only). + +### Step 5: Verify `inotifywait` on zzz + +```bash +ssh fenix@qu3v3d0.tech "which inotifywait && inotifywait --help | head -1" +``` + +## Files to Create/Modify + +| File | Action | Location | +|:-----|:-------|:---------| +| `emacs/napi.el` | **CREATE** | `~/napi/emacs/napi.el` | +| `emacs/napi-init.el` | **CREATE** | `~/napi/emacs/napi-init.el` | +| `emacs-napi.service` | **CREATE** | `~/.config/systemd/user/emacs-napi.service` | +| `init.el` | **MODIFY** (1 line) | `~/.emacs.d/init.el` | + +**No modifications to zzz** — no watcher changes, no new SSH keys, no reverse tunnels. + +## Testing + +1. **Unit test** — load `napi.el` in `water`, call `(napi-notify ...)` manually +2. **TRAMP watch test** — single watch on one student dir, touch a file on zzz +3. **Full integration** — start `napi` daemon, simulate SFTP upload as student +4. **Stress test** — confirm 40 concurrent TRAMP watches are stable +5. **Reconnection** — kill SSH, verify watch auto-restores +6. **UI test** — `C-c n l` (log), `C-c n s` (student), `C-c n d` (dashboard) + +## Risk: 40 Concurrent SSH/inotifywait Processes + +Each `file-notify-add-watch` via TRAMP spawns a separate `inotifywait` process on zzz. 40 watches = 40 persistent SSH channels + 40 inotifywait processes. + +**Mitigation**: TRAMP reuses SSH connections (ControlMaster). So it's 1 SSH connection + 40 remote processes. The zzz server's `inotify` limit (`fs.inotify.max_user_watches`) is typically 65536 — 40 is nothing. + +**Fallback**: If 40 watches proves unstable, consolidate to 2 watches on parent dirs (`/home/` with recursive) + filter events by path. Or switch to the emacsclient-via-SSH approach (see FINDINGS.md). diff --git a/Screenshots/napi-sketch.jpg b/Screenshots/napi-sketch.jpg new file mode 100644 index 0000000..7f61d4a Binary files /dev/null and b/Screenshots/napi-sketch.jpg differ diff --git a/TASKS.org b/TASKS.org index ee3c26f..1020086 100644 --- a/TASKS.org +++ b/TASKS.org @@ -18,4 +18,37 @@ es decir, mencionar el/la autor/a de la entrega por ambos, nombre y apellido -* esto +* TODO Emacs packages using JSON-RPC o other kinda-API thing i could be using in a complementary way to streamline my interaction with the students via 'zzz' server where 'napi' is hosted / serving + + + --- + Emacs Packages Using JSON-RPC / Structured Protocols + + For napi/ Specifically (file-sync + notification) + + ┌──────────────────────────────────────────────────────────────────────────────────────────────┬──────────────────────────────────────────┬────────────────────────────────────────────────────────────────────┐ + │ Package │ What It Does │ napi Complement │ + ├──────────────────────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────────┼────────────────────────────────────────────────────────────────────┤ + │ https://github.com/legoscia/emacs-jabber │ XMPP client for Emacs │ Direct integration with your slixmpp notification system │ + ├──────────────────────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────────┼────────────────────────────────────────────────────────────────────┤ + │ https://www.gnu.org/software/emacs/manual/html_node/elisp/File-Notifications.html (built-in) │ inotify/kqueue file watching │ Watch ~/napi-data/ for student uploads natively in Emacs │ + ├──────────────────────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────────┼────────────────────────────────────────────────────────────────────┤ + │ https://github.com/jcaw/json-rpc-server.el + https://github.com/jcaw/porthole │ Expose Emacs functions via HTTP JSON-RPC │ Let external scripts (your inotify watchers) trigger Emacs actions │ + └──────────────────────────────────────────────────────────────────────────────────────────────┴──────────────────────────────────────────┴────────────────────────────────────────────────────────────────────┘ + +** Explore the idea (as referred on above's gnu.org manual - link) "Notifications on File Changes", which states : + +"Several operating systems support watching of filesystems for changes to files or their attributes. If configured +properly, Emacs links a respective library like inotify and others. These libraries enable watching of filesystems on the local machine. + +**It is also possible to watch filesystems on remote machines**, see Remote Files in The GNU Emacs Manual. This does not +depend on one of the libraries linked to Emacs. + +Since all these libraries emit different events upon notified file changes, Emacs provides a special library filenotify +which presents a unified interface to applications. Lisp programs that want to receive file notifications should always +use this library in preference to the native ones. [..] + + +the **bold** is mine ! ... + +*** Am i crazy saying that ~/napi could be running remotely, as long as a 'emacs -daemon=napi' which i could hook into an 'emacsclient' ? diff --git a/api.php b/api.php new file mode 100644 index 0000000..033508e --- /dev/null +++ b/api.php @@ -0,0 +1,56 @@ + Sistema KISS de seguimiento académico: Markdown + Basic Auth + PHP + Vanilla JS + +**Servidor:** qu3v3d0.tech | **Filosofía:** Unix + KISS + +--- + +## Qué hace este sistema + +``` +Profesor sube tabla-de-seguimiento.md + ↓ +Alumnos acceden con user/pass SFTP + ↓ +Cada alumno ve SOLO su progreso +``` + +--- + +## Paso 1: Crear htpasswd para Basic Auth + +```bash +# Generar archivo de passwords (password = username por defecto) +sudo touch /etc/nginx/.htpasswd_sftp + +for user in alumno{01..20}; do + echo "$user:$(openssl passwd -apr1 $user)" | \ + sudo tee -a /etc/nginx/.htpasswd_sftp > /dev/null +done + +# Permisos restrictivos +sudo chmod 640 /etc/nginx/.htpasswd_sftp +sudo chown root:www-data /etc/nginx/.htpasswd_sftp + +# Verificar +sudo cat /etc/nginx/.htpasswd_sftp +``` + +**✅ Verificación:** +``` +alumno01:$apr1$xyz$... +alumno02:$apr1$abc$... +... +``` + +--- + +## Paso 2: Crear estructura de directorios + +```bash +# Crear directorios +sudo mkdir -p /home/admin/html/dashboard/data + +# Permisos +sudo chown -R admin:www-data /home/admin/html/dashboard +sudo chmod 755 /home/admin/html/dashboard +sudo chmod 775 /home/admin/html/dashboard/data +``` + +**Estructura final:** +``` +/home/admin/html/dashboard/ +├── index.html # Cliente (vanilla JS + marked.js) +├── api.php # filtro por usuario +├── style.css # estilo minimalista +└── data/ + └── tabla-de-seguimiento.md +``` + +--- + +## Paso 3: Crear api.php + +```bash +sudo tee /home/admin/html/dashboard/api.php > /dev/null << 'EOF' + /dev/null << 'EOF' + + + + + + Dashboard de Seguimiento + + + + +
+

🎯 Dashboard de Seguimiento

+

qu3v3d0.tech

+
+ +
+
+

Cargando tu progreso...

+
+
+
+ + + + + + +EOF + +sudo chown admin:www-data /home/admin/html/dashboard/index.html +sudo chmod 644 /home/admin/html/dashboard/index.html +``` + +--- + +## Paso 5: Crear style.css + +```bash +sudo tee /home/admin/html/dashboard/style.css > /dev/null << 'EOF' +:root { + --bg: #f5f5f5; + --fg: #333; + --accent: #0066cc; + --success: #28a745; + --error: #dc3545; +} + +* { margin: 0; padding: 0; box-sizing: border-box; } + +body { + font-family: system-ui, sans-serif; + line-height: 1.6; + color: var(--fg); + background: var(--bg); + min-height: 100vh; + display: flex; + flex-direction: column; +} + +header { + background: var(--accent); + color: white; + padding: 1.5rem; + text-align: center; +} + +header h1 { font-size: 1.8rem; margin-bottom: 0.5rem; } +.subtitle { font-size: 0.9rem; opacity: 0.9; } + +main { + flex: 1; + max-width: 900px; + width: 100%; + margin: 2rem auto; + background: white; + padding: 2rem; + border-radius: 8px; + box-shadow: 0 2px 8px rgba(0,0,0,0.1); +} + +.loader { text-align: center; padding: 2rem; } +.spinner { + width: 40px; + height: 40px; + margin: 1rem auto; + border: 4px solid #ddd; + border-top-color: var(--accent); + border-radius: 50%; + animation: spin 1s linear infinite; +} +@keyframes spin { to { transform: rotate(360deg); } } + +h1 { + color: var(--accent); + border-bottom: 2px solid var(--accent); + padding-bottom: 0.5rem; + margin-bottom: 1.5rem; +} + +h2 { margin-top: 1.5rem; margin-bottom: 1rem; color: #555; } + +table { + width: 100%; + border-collapse: collapse; + margin: 1rem 0; +} + +th, td { + padding: 0.75rem; + text-align: left; + border-bottom: 1px solid #ddd; +} + +th { + background: var(--bg); + font-weight: 600; + color: var(--accent); +} + +tbody tr:hover { background: #f9f9f9; } + +ul { margin-left: 1.5rem; margin-bottom: 1rem; } +li { margin-bottom: 0.5rem; } + +input[type="checkbox"] { margin-right: 0.5rem; } + +.error { + background: #fff3cd; + border: 2px solid #ffc107; + border-radius: 8px; + padding: 1.5rem; + text-align: center; +} + +.error h2 { color: var(--error); margin-bottom: 1rem; } + +footer { + background: #2c3e50; + color: white; + text-align: center; + padding: 1rem; + font-size: 0.85rem; +} + +@media (max-width: 768px) { + main { margin: 1rem; padding: 1rem; } + header h1 { font-size: 1.4rem; } +} +EOF + +sudo chown admin:www-data /home/admin/html/dashboard/style.css +sudo chmod 644 /home/admin/html/dashboard/style.css +``` + +--- + +## Paso 6: Configurar nginx + +```bash +sudo tee /etc/nginx/sites-available/dashboard > /dev/null << 'EOF' +server { + listen 80; + server_name dashboard.qu3v3d0.tech; + + root /home/admin/html/dashboard; + index index.html; + + # Basic Auth + auth_basic "Dashboard Alumnos"; + auth_basic_user_file /etc/nginx/.htpasswd_sftp; + + # PHP-FPM + location ~ \.php$ { + include snippets/fastcgi-php.conf; + fastcgi_pass unix:/var/run/php/php-fpm.sock; + fastcgi_param REMOTE_USER $remote_user; + } + + # Caché de estáticos + location ~* \.(css|js)$ { + expires 1h; + add_header Cache-Control "public"; + } + + # Proteger data/ + location /data/ { deny all; } + + # Logs + access_log /var/log/nginx/dashboard-access.log; + error_log /var/log/nginx/dashboard-error.log; + + autoindex off; +} +EOF + +# Activar +sudo ln -s /etc/nginx/sites-available/dashboard /etc/nginx/sites-enabled/ + +# Validar y recargar +sudo nginx -t +sudo systemctl reload nginx +``` + +--- + +## Paso 7: Crear tabla-de-seguimiento.md + +```bash +sudo tee /home/admin/html/dashboard/data/tabla-de-seguimiento.md > /dev/null << 'EOF' +# Seguimiento - Grupo 2026 + +## alumno01 +| Tarea | Estado | Fecha | +|:------|:------:|------:| +| Config nginx básica | ❌ | - | +| JSON con CORS | ❌ | - | +| Error pages | ❌ | - | +| Caché estático | ❌ | - | + +## alumno02 +| Tarea | Estado | Fecha | +|:------|:------:|------:| +| Config nginx básica | ❌ | - | +| JSON con CORS | ❌ | - | +| Error pages | ❌ | - | +| Caché estático | ❌ | - | + +## alumno03 +| Tarea | Estado | Fecha | +|:------|:------:|------:| +| Config nginx básica | ❌ | - | +| JSON con CORS | ❌ | - | +| Error pages | ❌ | - | +| Caché estático | ❌ | - | +EOF + +sudo chown admin:www-data /home/admin/html/dashboard/data/tabla-de-seguimiento.md +sudo chmod 664 /home/admin/html/dashboard/data/tabla-de-seguimiento.md +``` + +**Formato alternativo con checkboxes:** +```markdown +## alumno01 +- [ ] Config nginx básica +- [ ] JSON con CORS +- [ ] Error pages +- [ ] Caché estático +``` + +--- + +## Paso 8: Verificación + +```bash +# 1. Permisos +ls -la /home/admin/html/dashboard/ +ls -la /home/admin/html/dashboard/data/ + +# 2. Nginx +sudo nginx -t +sudo systemctl status nginx + +# 3. PHP-FPM +sudo systemctl status php*-fpm + +# 4. Test htpasswd +htpasswd -v /etc/nginx/.htpasswd_sftp alumno01 + +# 5. Test curl +curl -u alumno01:alumno01 http://localhost/dashboard/api.php +``` + +**✅ Debe retornar:** +```markdown +# Seguimiento - Grupo 2026 + +## alumno01 +| Tarea | Estado | Fecha | +... +``` + +--- + +## Paso 9: Test en navegador + +1. Abrir: `http://dashboard.qu3v3d0.tech` +2. Login: `alumno01` / `alumno01` +3. Debe mostrar solo la sección de alumno01 +4. Probar con alumno02, alumno03... + +--- + +## Uso para el Profesor + +### Actualizar tabla via SFTP + +``` +Host: qu3v3d0.tech +User: admin +Password: **** +Path: /html/dashboard/data/ +File: tabla-de-seguimiento.md +``` + +### Actualizar desde servidor + +```bash +ssh admin@qu3v3d0.tech +nano ~/html/dashboard/data/tabla-de-seguimiento.md +# Editar y guardar +``` + +### Formato de la tabla + +**Emojis de estado:** +- ✅ Completado +- ❌ Pendiente +- ⏳ En progreso + +**Ejemplo:** +```markdown +## alumno01 +| Tarea | Estado | Fecha | +|:------|:------:|------:| +| Config nginx básica | ✅ | 2026-01-20 | +| JSON con CORS | ⏳ | - | +| Error pages | ❌ | - | + +**Observaciones:** Buen progreso, revisar CORS. +``` + +--- + +## Troubleshooting + +### Error 401 Unauthorized + +```bash +# Verificar htpasswd +ls -la /etc/nginx/.htpasswd_sftp +grep alumno01 /etc/nginx/.htpasswd_sftp +htpasswd -v /etc/nginx/.htpasswd_sftp alumno01 +``` + +### Error 403 Forbidden + +```bash +# Verificar permisos +ls -la /home/admin/html/dashboard/ +ps aux | grep nginx # Usuario debe ser www-data +``` + +### PHP no procesa + +```bash +# Verificar PHP-FPM +sudo systemctl status php*-fpm +ls -la /var/run/php/php*-fpm.sock +sudo tail -f /var/log/nginx/dashboard-error.log +``` + +### Página en blanco + +```bash +# Test API directamente +curl -u alumno01:alumno01 http://dashboard.qu3v3d0.tech/api.php + +# Ver consola navegador (F12) +# Verificar que marked.js carga correctamente +``` + +### Usuario ve datos de otros + +```bash +# En api.php, agregar debug temporal: +echo "DEBUG: User = $user\n"; + +# Verificar que ## username coincide exactamente +# (sin espacios extras, case-sensitive) +``` + +--- + +## Extensión: Notificación XMPP + +Notificar cuando profesor actualiza la tabla: + +```bash +# Script watcher +sudo tee /usr/local/bin/dashboard-notify.sh > /dev/null << 'EOF' +#!/bin/bash +WATCH_FILE="/home/admin/html/dashboard/data/tabla-de-seguimiento.md" +inotifywait -m -e close_write "$WATCH_FILE" | while read event; do + /usr/local/bin/xmpp-notify.py "📊 Tabla actualizada por el profesor" +done +EOF + +sudo chmod +x /usr/local/bin/dashboard-notify.sh + +# Servicio systemd +sudo tee /etc/systemd/system/dashboard-notify.service > /dev/null << 'EOF' +[Unit] +Description=Dashboard Update Notifier +After=network.target + +[Service] +Type=simple +ExecStart=/usr/local/bin/dashboard-notify.sh +Restart=always + +[Install] +WantedBy=multi-user.target +EOF + +sudo systemctl daemon-reload +sudo systemctl enable --now dashboard-notify.service +``` + +--- + +## Checklist de Implementación + +- [ ] htpasswd creado (`/etc/nginx/.htpasswd_sftp`) +- [ ] Directorios creados (`/home/admin/html/dashboard/`) +- [ ] api.php desplegado (644) +- [ ] index.html desplegado (644) +- [ ] style.css desplegado (644) +- [ ] nginx config creado (`/etc/nginx/sites-available/dashboard`) +- [ ] nginx config activado (symlink a sites-enabled) +- [ ] nginx reload sin errores (`nginx -t`) +- [ ] tabla-de-seguimiento.md inicial (664) +- [ ] PHP-FPM corriendo +- [ ] Test curl exitoso +- [ ] Test navegador alumno01 OK +- [ ] Test navegador alumno02 OK +- [ ] Cada alumno ve solo sus datos +- [ ] Markdown renderiza correctamente +- [ ] Responsive en móvil OK + +--- + +## Resumen + +**Componentes:** +- nginx: Auth + routing +- PHP: Filtrado por usuario +- Markdown: Storage +- marked.js: Parsing +- Vanilla JS: Cliente + +**Filosofía KISS:** +- 3 archivos principales +- Sin DB +- Sin frameworks +- Sin compilación + +**Resultado:** Sistema robusto, mantenible, educativo. + +--- + +*Implementado: 2026-02-13 | qu3v3d0.tech | fenix + Claude*