- CalendarView vista Lista: rango ampliado a todos los eventos (pasados y futuros),
agrupados por dia con cabecera DD/MM/AAAA.
- TablesView: pestana Eventos nueva (fecha, hora, titulo, calendario, ubicacion,
indicador de recurrencia), mismo patron de tabla ordenable/filtrable que el resto.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- ContactIn + frontmatter + vCard multi-valor: emite N TEL, N EMAIL, N ADR; _vcard_to_json
parsea ADR -> direcciones[] (y sigue leyendo X-OSINT-DIRECCION legacy). Los singulares
telefono/email/direccion se mantienen por compat (= primer elemento de cada lista).
- Libretas de contactos (addressbooks): endpoints GET/POST /api/addressbooks; en
ContactsView un selector de libreta + boton 'Nueva libreta' (replica del patron de crear
calendario) + filtro por libreta en la lista.
- Frontend ContactsView: TagsInput para telefonos/emails/direcciones, cargando TODOS los
valores al editar (antes solo el primero).
- Feature flag OSINT_DB_BACKEND (dev/feature_flags.json, default off): con ON, osint_web
lee/escribe contra el service osint_db (DuckDB = fuente de verdad) via
server/osintdb_client.py; con OFF, el comportamiento historico (vault .md + vCard
Xandikos) queda intacto byte a byte.
Verificado: 52 tests backend (40 + 12 nuevos), tsc --noEmit limpio.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Backend (server/main.py):
- EventIn.rrule + emision/parseo de RRULE en el VCALENDAR.
- calendar() expande las series recurrentes a sus ocurrencias dentro de [from,to]
(compone expand_rrule del registry); helpers _expand_event_occurrences /
_occurrence_clone preservan hora local, offset y duracion por ocurrencia.
- POST /api/calendars: crea una coleccion de calendario nueva (compone
dav_make_calendar); invalida la cache de colecciones.
Frontend:
- EventModal: controles de repeticion (frecuencia, intervalo, BYDAY para semanal,
fin por N veces / hasta fecha); parseRrule/buildRrule; aviso 'afecta a la serie'.
- CalendarView: vista Lista/Agenda (eventos por dia, click para editar, nuevo
evento), linea roja de hora actual (refresco cada 60s, solo columna de hoy),
boton Nuevo calendario (modal nombre/color), indicador de recurrencia (IconRepeat).
- api.ts/calendar.ts: rrule/recurring/occurrence en los tipos, createCalendar,
helpers nowLinePct/slugifyCalendar.
Verificado: tsc -b + vite build limpios; smoke backend (FREQ=WEEKLY;COUNT=3 -> 3
ocurrencias con hora/offset/duracion correctas); render en navegador (vista Lista,
Nuevo calendario, Nuevo evento, selectores presentes).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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>
Añade alta, edición y borrado de contactos (personas y organizaciones) a la
app osint_web. La fuente de verdad es la ficha .md del vault Obsidian
(CONVENTIONS.md §3b/§6); Xandikos es el retransmisor al móvil.
Backend (server/main.py):
- POST /api/contact: genera slug, escribe la ficha .md con el frontmatter
canónico + PUT del vCard a Xandikos. 409 si el slug ya existe.
- PUT /api/contact/{slug}: merge del frontmatter (preserva campos heredados)
+ re-PUT del vCard. 404 si no existe.
- DELETE /api/contact/{slug}: borra la ficha .md + DELETE del vCard. 404 si
no existe.
Cada escritura invalida la caché DAV para que el cambio se vea ya en la app.
Registry-first: orquesta create/update/delete_obsidian_note del grupo obsidian
y carddav_put_vcard/dav_delete_resource del grupo dav (sin reimplementar
parseo ni HTTP). Mapea los campos OSINT a propiedades X-OSINT-* del vCard.
Frontend (ContactsView.tsx + api.ts + format.ts):
- Botón "Nuevo contacto" → modal con formulario Mantine (TextInput,
TagsInput aliases, Select contexto, Textarea notas).
- Detalle: botones "Editar" (formulario precargado) y "Borrar" (con
confirmación). Tras guardar refresca la lista.
- Helper slugify (replica slugify_obsidian_name) para resolver la ficha.
Tests: 6 nuevos casos (ciclo crear→editar→borrar con .md real + reflejo vCard
mockeado, organización, 404s, tipo inválido, preserva campos heredados). Suite
27 passed. Ciclo e2e real verificado contra Xandikos + vault (vCard creado,
editado y borrado; slug zz-test-crud limpiado). pnpm build verde (React 19 +
Mantine v9).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Frontend web de lectura del vault osint + agenda/calendario Xandikos.
- Stack: React 19 + Vite 6 + TypeScript + Mantine v9 (React 19 obligatorio para
que Mantine v9 monte). Grafo con sigma v3 + graphology + forceatlas2 en web
worker. Markdown con react-markdown, calendario con @mantine/dates.
- AppShell con navbar de 4 secciones + botón global de refresco (POST /api/refresh).
- GraphView: force-directed, color por tipo, tamaño por grado, panel lateral con
toggles de tipo + dangling + buscador (centra el nodo). Guard de WebGL: si el
navegador no lo expone, avisa en vez de crashear.
- TablesView: una pestaña por tipo, tabla ordenable/filtrable con columnas del
frontmatter. Click en fila -> ficha.
- NodeCard (modal): frontmatter clave-valor (fechas europeas), cuerpo Markdown,
galería de imágenes con lightbox, PDFs/docs como enlace, wikilinks navegables.
- ContactsView: agenda con buscador + detalle (teléfonos, correos, bloque osint,
nota). CalendarView: mini-calendario con días marcados + eventos agrupados por
día (hora local).
- Vite proxya /api -> 127.0.0.1:8470. Verificado end-to-end contra el backend
real: 1199 nodos / 618 aristas, 539 personas en tabla, 1064 contactos, 98
eventos; grafo renderiza con WebGL y NodeCard abre con frontmatter+body.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reescribe el backend a FastAPI + uvicorn y añade los endpoints DAV
(CardDAV/CalDAV) sobre el servidor Xandikos, además de las vistas del vault
osint de Obsidian.
Vault (grupo obsidian del registry):
- /api/graph, /api/nodes, /api/node/{slug}, /api/attachment, /api/search,
/api/refresh. Allowlist estricta de path traversal en /api/attachment.
- Resolución de embeds por path relativo al vault y por basename (registry).
Xandikos (grupo dav del registry + pass_get_secret):
- /api/contacts, /api/contact/{uid} (CardDAV, parseo vCard a JSON).
- /api/calendar?from=&to= (CalDAV, parseo VEVENT a JSON, filtro por rango).
- Credencial vía pass dav/xandikos-enmanuel; degradación clara sin red (502/503).
Solo escucha en 127.0.0.1 (datos sensibles). 13 tests verdes (pytest).
frontend/README.md describe el montaje React+Vite+Mantine+sigma.js posterior.