Backend (server/main.py): - GET /api/calendars: lista las colecciones de calendario bajo el calendar-home con nombre y color (compone dav_list_calendars del registry). - GET /api/calendar?cal=&from=&to=: eventos de una colección concreta (caché por colección validada por ctag). dtstart/dtend ahora en ISO con offset + tz original + all_day; parseo robusto de TZID/UTC/todo-el-día con zoneinfo. - POST/PUT/DELETE /api/event[/<uid>]: CRUD de VEVENT contra Xandikos (fuente de verdad). Construye el VCALENDAR (con VTIMEZONE para zonas con DST), reutiliza el UID al editar (idempotente), trata 404 del DELETE como idempotente, invalida la caché de la colección tras escribir. Frontend: - CalendarView reescrita: conmutador Mes/Semana/Día con rejilla horaria propia (Mantine + dayjs, sin react-big-calendar para evitar fricción con React 19), mini-calendario de navegación, selector de calendario (con color), selector de zona horaria que recoloca los eventos, colores por evento (del VEVENT o del calendario). - EventModal: alta/edición/borrado con summary, inicio/fin, todo-el-día, TZ, calendario, color, ubicación y descripción. Fechas en formato local 24h. - calendar.ts: helpers de TZ (dayjs utc+timezone), posicionado por hora, semana empezando en lunes, locale es. api.ts: tipos y funciones de eventos/calendarios. Verificado: ciclo real crear→editar→borrar contra Xandikos (cero residuo), render del calendario en navegador (React 19 + Mantine v9 montan), pnpm build verde, 40 tests verdes (+ smoke gateado). MKCALENDAR queda fuera (documentado). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Frontend de osint_web
Frontend web local del explorador OSINT. Lee el backend FastAPI (../server/main.py,
escucha solo en 127.0.0.1:8470) y ofrece cinco vistas de lectura sobre el vault
de Obsidian osint + la agenda/calendario del servidor Xandikos.
Stack
- React 19 + Vite 6 + TypeScript + Mantine v9. Mantine v9 exige React 19 (con
React 18 compila pero no monta — error "s is not a function"); por eso el
package.jsonpina React 19. Iconos de@tabler/icons-react. Theming concreateTheme()(src/theme.ts), sin Tailwind ni CSS variables custom (reglafrontend_theming.md). - Grafo:
sigma(v3) +graphology+graphology-layout-forceatlas2(las únicas deps fuera de Mantine; KISS). Layout force-directed en un web worker (no bloquea la UI con 1199 nodos), pausable. - Markdown de las fichas con
react-markdown. Calendario con@mantine/dates(que usadayjs). - Vite proxya
/api→http://127.0.0.1:8470en dev (sobrescribible conVITE_API_BASE).
Vistas
| Vista | Archivo | Endpoint(s) | Qué muestra |
|---|---|---|---|
| Grafo | src/views/GraphView.tsx |
GET /api/graph, GET /api/search |
sigma.js force-directed; color por tipo, tamaño por grado; panel lateral con toggles de tipos + dangling + buscador (centra el nodo). Click en nodo → ficha. Layout pausable + reset de cámara. |
| Tablas | src/views/TablesView.tsx |
GET /api/graph, GET /api/nodes?tipo= |
una pestaña por tipo real; Table Mantine con columnas deducidas del frontmatter, ordenable y filtrable. Click en fila → ficha. |
| Ficha | src/views/NodeCard.tsx |
GET /api/node/<slug>, GET /api/attachment |
modal: frontmatter clave-valor (fechas europeas DD/MM/AAAA), cuerpo Markdown, galería de imágenes con lightbox, documentos/PDFs como enlace, wikilinks navegables. |
| Contactos | src/views/ContactsView.tsx |
GET /api/contacts |
agenda: lista + buscador (nombre/alias/tel/email); detalle con teléfonos, correos, bloque osint (dni/país/sexo…) y nota. |
| Calendario | src/views/CalendarView.tsx |
GET /api/calendar |
mini-calendario @mantine/dates con punto en días con eventos + lista de eventos del mes/día agrupados por fecha (hora local, lugar, descripción). |
Botón global Refrescar (header) → POST /api/refresh + recarga de la vista activa.
Arrancar (dev)
Necesitas backend + frontend a la vez:
# Terminal 1 — backend (escucha solo en 127.0.0.1)
cd projects/osint/apps/osint_web
.venv/bin/python server/main.py --vault /home/enmanuel/Obsidian/osint --port 8470
# Terminal 2 — frontend
cd projects/osint/apps/osint_web/frontend
pnpm install # primera vez
pnpm dev # http://127.0.0.1:5173
Abrir http://127.0.0.1:5173. El proxy de Vite reenvía /api al backend, así
que no hay que tocar CORS. Solo localhost (datos sensibles del vault: DNIs, fotos).
Build
cd projects/osint/apps/osint_web/frontend
pnpm install
pnpm build # tsc -b && vite build → dist/
Gotcha pnpm 10/11 (esbuild)
pnpm bloquea por seguridad los scripts de build de dependencias. esbuild (el
bundler nativo de Vite) necesita su postinstall. El pnpm-workspace.yaml lo
permite con allowBuilds: { esbuild: true }. Si pnpm build falla con
"esbuild ... was not found", ejecuta pnpm rebuild esbuild.
Notas
- Grafo sin WebGL: si el navegador no expone WebGL (headless sin GPU), la vista Grafo muestra un aviso en vez de crashear; el resto de la app sigue funcionando.
- Contactos/Calendario dependen del servidor Xandikos: si no responde, esas dos vistas muestran un aviso naranja y el grafo/tablas siguen operativos (offline).
- Las fechas se presentan en europeo (
src/format.ts): ISO de Obsidian2026-06-07→07/06/2026; iCal20220829T133000Z→ hora local15:30.