From 68b9beaf1d8bc6938cc2d500c5b916b2becd2ad1 Mon Sep 17 00:00:00 2001 From: gnacho Date: Sun, 18 Oct 2026 09:11:22 +0200 Subject: [PATCH] docs: sanitize deployment references and refresh the project status --- PLAN.md | 48 +++++++++++++++++++----------------- README.es.md | 5 +++- README.md | 11 ++++++--- deploy/README.md | 30 ++++++++++++---------- internal/store/store_test.go | 3 ++- 5 files changed, 56 insertions(+), 41 deletions(-) diff --git a/PLAN.md b/PLAN.md index 282136a..86e7676 100644 --- a/PLAN.md +++ b/PLAN.md @@ -1,12 +1,14 @@ # PLAN - davcloud: de esqueleto a reemplazo de Radicale -Objetivo: sustituir Radicale (binario + systemd en el CT) por davcloud sin -que DAVx⁵, Thunderbird, iOS ni las extensiones web (opencloud-agenda, +Objetivo: sustituir Radicale (binario + systemd en un contenedor) por davcloud +sin que DAVx⁵, Thunderbird, iOS ni las extensiones web (opencloud-agenda, opencloud-contacts) noten la diferencia. -Estado al 17-Oct-2026: **fases 0-3 completadas** (issues #1-#9 cerrados) y -**nada de Docker**: el despliegue es un binario estático + systemd (ver -`deploy/README.md`). +Estado al 18-Oct-2026: **fases 0-4 completadas** (issues #1-#9 y #21 cerrados; +davcloud es el servidor DAV de producción desde el 17-Oct) y **fase 5 en +curso** (soak + drill de rollback, issue #22). **Nada de Docker**: el +despliegue es un binario estático + systemd (ver `deploy/README.md`). Repo +público desde el 18-Oct-2026, licencia AGPL-3.0. ## Hecho @@ -27,31 +29,33 @@ Estado al 17-Oct-2026: **fases 0-3 completadas** (issues #1-#9 cerrados) y - **Recurrencias** (PR #13): caracterización: go-webdav YA expande RRULE/RDATE/EXDATE en los time-ranges; tolerancia a RRULE rotas (fallback a evento simple) y TZID sin VTIMEZONE verificado. -- **Contrato real de producción** (PR #15): verificado read-only contra el CT: - el proxy elimina el prefijo y manda `X-Script-Name` (→ modo `-dav-root`); - el `.Radicale.props` real guarda el component-set como string (→ formato - compatible en ambos sentidos). +- **Contrato real de producción** (PR #15): verificado read-only contra el + despliegue real: el proxy elimina el prefijo y manda `X-Script-Name` + (→ modo `-dav-root`); el `.Radicale.props` real guarda el component-set + como string (→ formato compatible en ambos sentidos). - **Extras**: CI de 4 jobs (PR #25), binario versionado (PR #26), README bilingüe (PR #17), licencia AGPL-3.0 (PR #14), sin Docker (PR #27). -- **Ensayo de migración en seco**: copia real del volumen del CT montada en - davcloud: lectura, escritura byte a byte y sync verificados con dos - usuarios reales. El CT quedó intacto. +- **Fase 4 - migración de producción** (issue #21, 17-Oct-2026): swap de + systemd en el contenedor según `deploy/README.md`, con backup previo, + verificado con la UI y con DAV por curl (PROPFIND con hrefs prefijados, + sync-collection, ciclo PUT/GET/DELETE). Radicale queda parado y deshabilitado. +- **Web UI**: vCard 3.0 al escribir y tests de los helpers (PR #30), vista de + tarjetas y arreglos de la libreta (PRs #21, #33). +- **Ensayo de migración en seco**: copia real del volumen de producción + montada en davcloud: lectura, escritura byte a byte y sync verificados con + dos usuarios reales. El original quedó intacto. ## Pendiente (issues) -- **#21 - Fase 4: migración de producción** (swap systemd en el CT 4125 - según `deploy/README.md`; backup primero; verificación con la UI y DAVx⁵). -- **#22 - Fase 5: soak + drill de rollback** (1-2 semanas con davcloud como - único servidor DAV; después arrancar Radicale sobre el mismo volumen y - verificar que lee todo lo escrito por davcloud; solo entonces retirar - Radicale). -- **#23, #24 - Web UI menores**: vCard 3.0 en vez de 4.0 y tests de los - helpers (la edición de recurrencias en la UI sigue siendo un TODO - documentado en `web/src/lib/ics.ts`). +- **#22 - Fase 5: soak + drill de rollback** (en curso desde el 17-Oct: + davcloud es el único servidor DAV; 1-2 semanas de uso diario, después + arrancar Radicale sobre el mismo volumen y verificar que lee todo lo escrito + por davcloud; solo entonces retirar Radicale). +- **Edición de recurrencias en la web UI**: sigue siendo un TODO documentado + en `web/src/lib/ics.ts`. - **Fase 6 - paridad extendida (opcional, por necesidad)**: iMIP (invitaciones por email) y compartición de calendarios (requiere ACLs que el proxy no proporciona). Radicale tampoco cubre bien ninguno de los dos. -- Publicar o no el repo en GitHub: decisión aparte (hoy privado). ## Regla de oro diff --git a/README.es.md b/README.es.md index 7864a78..235f416 100644 --- a/README.es.md +++ b/README.es.md @@ -153,7 +153,10 @@ el mismo volumen (layout y metadatos son byte-compatibles en ambos sentidos). ## Estado -Sirviendo producción. Cubierto por 37 tests de Go y 10 de la web, CI en cada +En uso diario como servidor DAV detrás del despliegue de OpenCloud del autor; +el periodo de soak está en marcha y el drill de rollback sigue pendiente, así +que es software en desarrollo activo. La lista de fases y lo que queda está en +[PLAN.md](PLAN.md). Cubierto por 40 tests de Go y 10 de la web, CI en cada push (test/lint/security/web) y un ensayo de migración contra una copia de un volumen real de producción. Las limitaciones conocidas están seguidas como issues: iMIP, compartición de calendarios, edición de recurrencias en la web. diff --git a/README.md b/README.md index e1dd5b3..3f4e7ce 100644 --- a/README.md +++ b/README.md @@ -147,10 +147,13 @@ volume (the layout and metadata are byte-compatible both ways). ## Status -Serving production. Covered by 37 Go tests and 10 web tests, CI on every -push (test/lint/security/web), and a migration dry run against a copy of a -real production volume. Known limitations are tracked as issues: iMIP, -calendar sharing, recurrence editing in the web UI. +In daily use as the DAV server behind the author's OpenCloud deployment; the +soak period is running and the rollback drill is still pending, so treat it as +software in active development. The phase list and what is left are in +[PLAN.md](PLAN.md). Covered by 40 Go tests and 10 web tests, CI on every push +(test/lint/security/web), and a migration dry run against a copy of a real +production volume. Known limitations are tracked as issues: iMIP, calendar +sharing, recurrence editing in the web UI. ## License diff --git a/deploy/README.md b/deploy/README.md index 6eafe1c..48ee3f1 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -1,9 +1,9 @@ # Despliegue de davcloud en sustitución de Radicale davcloud es un binario estático de Go: no hay contenedores por medio. El -despliegue real (CT 4125, nodo4) sustituye el servicio systemd de Radicale por -uno propio, sobre el MISMO puerto y el MISMO volumen, así que el proxy de -OpenCloud no se toca. +despliegue de referencia (un contenedor LXC sobre Proxmox, en nuestro caso) +sustituye el servicio systemd de Radicale por uno propio, sobre el MISMO +puerto y el MISMO volumen, así que el proxy de OpenCloud no se toca. Contrato que el proxy ya cumple y que NO hay que cambiar (verificado en producción, `proxy.yaml`): reenvía `/caldav/` y `/carddav/` a @@ -13,10 +13,11 @@ usuario. Por eso davcloud arranca con `-dav-root`. ## 0. Backup (obligatorio) -El CT está fuera de PBS: copia fría del volumen antes de nada. +Haz copia fría del volumen antes de nada (si el contenedor no está cubierto +por backups automáticos, es la única red de seguridad): ``` -pct exec 4125 -- sh -c 'systemctl stop radicale && cp -a /var/lib/radicale /var/lib/radicale.bak-$(date +%Y%m%d) && systemctl start radicale' +pct exec -- sh -c 'systemctl stop radicale && cp -a /var/lib/radicale /var/lib/radicale.bak-$(date +%Y%m%d) && systemctl start radicale' ``` ## 1. Binario estático @@ -35,8 +36,8 @@ Subida al CT (el `scp` del binario en marcha da "Text file busy": parar el servicio antes del `mv`, como en ocapps): ``` -scp -P 2222 davcloud root@nodo4.domatix.com:/root/davcloud-new -pct push 4125 /root/davcloud-new /usr/local/bin/davcloud --perms 755 +scp davcloud root@:/root/davcloud-new # -P si tu SSH no es el 22 +pct push /root/davcloud-new /usr/local/bin/davcloud --perms 755 ``` ## 2. Web UI compilada (opcional) @@ -46,9 +47,11 @@ este paso es solo si quieres conservar la UI para uso directo: ``` cd web && npm ci && npm run build -pct push 4125 web/dist root@nodo4.domatix.com... # o copiala fuera si no la vas a usar +pct push web/dist /srv/davcloud-web --recursive ``` +Si la sirves, apunta el binario a ese directorio con `-ui-dir /srv/davcloud-web`. + ## 3. Unit systemd (mismo puerto, mismo volumen, mismo usuario) `/etc/systemd/system/davcloud.service`. Usa el usuario `radicale` porque es el @@ -86,7 +89,7 @@ Sin `-dev-user` en producción: la identidad SIEMPRE la inyecta el proxy. ## 4. Swap (transparente para el proxy) ``` -pct exec 4125 -- sh -c 'systemctl stop radicale && systemctl disable radicale && systemctl daemon-reload && systemctl enable --now davcloud' +pct exec -- sh -c 'systemctl stop radicale && systemctl disable radicale && systemctl daemon-reload && systemctl enable --now davcloud' ``` ## 5. Verificación (dentro del CT, sin tocar el proxy) @@ -108,9 +111,10 @@ curl -s -X REPORT -H 'X-Script-Name: /caldav' -H 'X-Remote-User: ' \ ``` Y desde fuera (navegador): abrir la app agenda de OpenCloud y comprobar que -los calendarios y eventos reales aparecen. **No hacer `curl` sin auth contra -`drive.domatix.cloud`**: el fail2ban del NPM banea la IP de casa por 401 -repetidos (lección del 13-Sep-2026). +los calendarios y eventos reales aparecen. **No hagas `curl` sin autenticar +contra el hostname público para "ver si responde"**: si delante hay un +fail2ban, una racha de 401 te banea la IP y te deja fuera del servicio (nos +pasó: el corte alcanza a toda la red de la que salgas). ## 6. Rollback drill (fase 5 del PLAN) @@ -118,7 +122,7 @@ El volumen es byte-compatible en ambos sentidos (davcloud guarda los items tal cual y las props de colección en el mismo formato de Radicale): ``` -pct exec 4125 -- sh -c 'systemctl stop davcloud && systemctl start radicale' +pct exec -- sh -c 'systemctl stop davcloud && systemctl start radicale' ``` Después de un periodo con davcloud, arrancar Radicale y comprobar que ve los diff --git a/internal/store/store_test.go b/internal/store/store_test.go index 88db0da..fbc205c 100644 --- a/internal/store/store_test.go +++ b/internal/store/store_test.go @@ -51,7 +51,8 @@ func TestEnsureUserCreatesDefaultsAndIsIdempotent(t *testing.T) { } // TestReadsRealRadicaleProps is a compatibility regression against the exact -// .Radicale.props read from a live Radicale deployment (CT 4125): the +// .Radicale.props read from a live Radicale deployment (in a container, +// behind an OpenCloud proxy): the // supported-calendar-component-set is a comma separated STRING, not a JSON // array, and it must be written back in that same shape so the volume stays // interchangeable in both directions.