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>
6.3 KiB
name, lang, domain, version, description, tags, uses_functions, uses_types, framework, entry_point, dir_path, repo_url, e2e_checks
| name | lang | domain | version | description | tags | uses_functions | uses_types | framework | entry_point | dir_path | repo_url | e2e_checks | |||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| osint_web | py | osint | 0.1.0 | App web local OSINT: explora el vault de Obsidian osint (grafo sigma.js, tablas por tipo, fichas con galería de attachments) y la agenda/calendario del servidor Xandikos (CardDAV/CalDAV). Backend FastAPI que orquesta los grupos obsidian y dav del registry; escucha solo en 127.0.0.1 (datos sensibles). |
|
|
react-vite-mantine | server/main.py | projects/osint/apps/osint_web | https://gitea-dgg044oo04woo4ggcsws4gk0.organic-machine.com/dataforge/osint_web |
|
Qué es
App del issue 0172 (project osint). Combina dos fuentes de datos en un frontend
web local:
- El vault de Obsidian
~/Obsidian/osint(sin BD intermedia — decisión KISS): grafo explorable (sigma.js), tablas filtradas por tipo y fichas con la galería de attachments de cada nodo. - El servidor Xandikos (CardDAV/CalDAV): agenda de contactos y calendario de eventos.
Edición de contactos (CRUD): la app permite crear, editar y borrar contactos
(personas y organizaciones). La fuente de verdad es la ficha .md del vault
(esquema canónico de CONVENTIONS.md §3b/§6); Xandikos es el retransmisor al
móvil. Cada alta/edición/borrado escribe primero la ficha del vault (acción
primaria con create_obsidian_note / update_obsidian_note /
delete_obsidian_note) y a continuación refleja el cambio en Xandikos de
inmediato (carddav_put_vcard / dav_delete_resource) para que se vea ya en la
app y en el móvil sin esperar al sync periódico del dag_engine.
Registry-first: el backend NO parsea el vault ni habla DAV a mano — orquesta las
funciones del grupo obsidian (build_obsidian_graph, read_obsidian_note,
create_obsidian_note, update_obsidian_note, delete_obsidian_note,
resolve_obsidian_embed, ...) y del grupo dav (dav_get_collection,
dav_collection_ctag, carddav_put_vcard, dav_delete_resource,
split_vcards) más pass_get_secret para la credencial, todas declaradas en
uses_functions.
Stack
- Backend: FastAPI + uvicorn (venv propio en
.venv/, deps enpyproject.toml). Escucha solo en127.0.0.1. - Frontend (pendiente — otro agente): React + Vite + Mantine v9 +
@fn_library+ sigma.js / graphology. Verfrontend/README.md.
Arrancar el backend
cd projects/osint/apps/osint_web
.venv/bin/python server/main.py --vault ~/Obsidian/osint --port 8470
El servidor cachea el grafo en memoria al arrancar; POST /api/refresh
re-escanea el vault bajo demanda (botón "refrescar" del frontend). Los datos DAV
no se cachean (se piden a Xandikos en cada llamada).
Endpoints
| Método | Ruta | Devuelve |
|---|---|---|
| GET | /api/health |
estado + nº de nodos/aristas cacheados |
| GET | /api/graph |
grafo completo {nodes, edges, counts} para sigma.js |
| GET | /api/nodes?tipo=persona |
filas de la tabla de ese tipo (id, label, tipo, frontmatter) |
| GET | /api/node/<slug> |
ficha: frontmatter + body Markdown + attachments + wikilinks |
| GET | /api/attachment?path=<rel> |
binario del attachment (path relativo al vault, allowlist) |
| GET | /api/search?q=... |
nodos cuyo contenido matchea la query |
| GET | /api/contacts |
contactos del addressbook Xandikos (CardDAV) a JSON |
| GET | /api/contact/<uid> |
un vCard concreto a JSON |
| GET | /api/calendar?from=&to= |
eventos del calendario Xandikos (CalDAV) en el rango |
| POST | /api/refresh |
re-escanea el vault y reconstruye la caché |
Configuración Xandikos
- Base URL:
https://dav-eedeb681c4ab89ab8e444ac9.organic-machine.com - Usuario:
enmanuel; password enpass dav/xandikos-enmanuel(víapass_get_secret, nunca hardcodeada). - Colecciones:
/enmanuel/contacts/addressbook/(CardDAV),/enmanuel/calendars/calendar/(CalDAV).
Seguridad
- El vault contiene datos personales sensibles (DNIs, fotos): el server escucha
solo en
127.0.0.1—--hostdistinto avisa en stderr y NO es un service desplegable a VPS (sin tagservice). /api/attachmentbloquea path traversal:realpathdel candidato debe quedar estrictamente bajo elrealpathdel vault; cualquier otro caso → 403 (404 si el path es legítimo dentro del vault pero el archivo no existe).- Vault inexistente al arrancar → error claro en stderr + exit 2 (nunca 500 silencioso).
- Sin red / Xandikos caído → los endpoints DAV devuelven
{"status":"error"}con código 502/503, nunca un crash.
Tests
cd projects/osint/apps/osint_web
.venv/bin/python -m pytest tests -q
Cubren el DoD backend del issue 0172 + las extensiones DAV: grafo golden, tabla
por tipo, ficha con attachments (embed por path), wikilink dangling (nodo
fantasma), slug con acentos (María del Mar → maria-del-mar), path traversal
bloqueado, attachment legítimo servido, vault inexistente con error claro,
parseo vCard/iCalendar a JSON y degradación de los endpoints DAV sin red.
Estado / pendiente
- Hecho: scaffold del sub-repo + backend FastAPI completo (vault + DAV) con 13 tests verdes.
- Pendiente (siguiente agente):
frontend/React + Vite + Mantine v9 +@fn_librarycon sigma.js + graphology (GraphView, TablesView, NodeCard, ContactsView, CalendarView). Onboarding previsto: backend en 8470 +pnpm devenfrontend/→ abrirhttp://127.0.0.1:5173. Verfrontend/README.md. - Cuando exista el manifest de sub-repos del project (issue 0171), añadir esta
app a
projects/osint/subrepos.yaml.