# Dashboard de Seguimiento - Guía de Implementación
> 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
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*