fix: version 3.1.1 -- Actualizador no queda colgado al reiniciar el backend

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Milton Gonzalez
2026-10-23 14:25:49 -05:00
parent 4056c70d90
commit 72890e37d9
6 changed files with 24 additions and 9 deletions
+8
View File
@@ -1,5 +1,13 @@
# Changelog - project.linkedcorp.net # Changelog - project.linkedcorp.net
## [3.1.1] - 2026-10-23
### Fixed
- El popup de actualización quedaba en "Reiniciando servicios" hasta agotar los 10 minutos: el
script corre en el cgroup del backend y al reiniciarlo systemd lo mataba antes de marcar el fin.
Ahora reinicia el frontend primero, marca el fin y reinicia el backend al final; el popup da por
terminado cuando el backend ya reiniciado reporta la versión nueva y el frontend sirve la misma.
- El porcentaje del popup avanza según la fase real del script, no por tiempo transcurrido.
## [3.1.0] - 2026-10-23 ## [3.1.0] - 2026-10-23
### Added ### Added
- Acceso por módulo y por usuario: el superadmin, desde Organización → Usuarios → menú de la - Acceso por módulo y por usuario: el superadmin, desde Organización → Usuarios → menú de la
+1 -1
View File
@@ -1 +1 @@
3.1.0 3.1.1
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "project-linkedcorp-backend", "name": "project-linkedcorp-backend",
"version": "3.1.0", "version": "3.1.1",
"private": true, "private": true,
"type": "module", "type": "module",
"main": "src/server.js", "main": "src/server.js",
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "project-linkedcorp-frontend", "name": "project-linkedcorp-frontend",
"private": true, "private": true,
"version": "3.1.0", "version": "3.1.1",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
@@ -18,6 +18,9 @@ const PHASE_KEYS = {
complete: 'updater_phase_verify', complete: 'updater_phase_verify',
}; };
// Avance por fase real del script (no por tiempo). El 100% solo llega al confirmar las versiones.
const PHASE_PCT = { backup: 10, source: 20, backend: 40, frontend: 65, publish: 85, restart: 90, complete: 95 };
export default function UpdaterPanel() { export default function UpdaterPanel() {
const { t, openDialog } = useUi(); const { t, openDialog } = useUi();
const { data: status, loading, error, reload } = useAsync(getUpdateStatus, []); const { data: status, loading, error, reload } = useAsync(getUpdateStatus, []);
@@ -36,7 +39,9 @@ export default function UpdaterPanel() {
return () => { mountedRef.current = false; }; return () => { mountedRef.current = false; };
}, []); }, []);
// El estado del script confirma el final; ambas versiones comprueban lo que realmente se sirve. // Termina cuando el backend YA reiniciado reporta la version nueva (runningVersion se lee al
// arrancar el proceso) y el frontend sirve la misma. No depende de la marca "done" del script:
// al reiniciar el backend, systemd puede matar al script (corre en el cgroup del backend).
async function pollUntilReady(targetVersion, startedAt) { async function pollUntilReady(targetVersion, startedAt) {
if (pollingRef.current) return; if (pollingRef.current) return;
pollingRef.current = true; pollingRef.current = true;
@@ -46,16 +51,16 @@ export default function UpdaterPanel() {
while (mountedRef.current && Date.now() - startedAt < POLL_TIMEOUT_MS) { while (mountedRef.current && Date.now() - startedAt < POLL_TIMEOUT_MS) {
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS)); await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
if (!mountedRef.current) return; if (!mountedRef.current) return;
setProgressPct(Math.min(95, Math.round(((Date.now() - startedAt) / POLL_TIMEOUT_MS) * 100)));
try { try {
const progress = await getApplyProgress(); const progress = await getApplyProgress();
if (progress.targetVersion !== targetVersion) continue; if (progress.targetVersion !== targetVersion) continue;
setPhase(progress.phase); setPhase(progress.phase);
setProgressPct((pct) => Math.max(pct, PHASE_PCT[progress.phase] ?? pct));
if (progress.state === 'failed') { if (progress.state === 'failed') {
setApplyError(t('updater_apply_failed')); setApplyError(t('updater_apply_failed'));
return; return;
} }
if (progress.state === 'done' && progress.runningVersion === targetVersion) { if (progress.runningVersion === targetVersion) {
const frontendVersion = await getFrontendVersion(); const frontendVersion = await getFrontendVersion();
if (frontendVersion !== targetVersion) continue; if (frontendVersion !== targetVersion) continue;
setProgressPct(100); setProgressPct(100);
+5 -3
View File
@@ -82,9 +82,11 @@ write_status running
# matchea ninguna de las dos reglas, sudo pide contraseña, falla en este contexto sin terminal, y # matchea ninguna de las dos reglas, sudo pide contraseña, falla en este contexto sin terminal, y
# el restart nunca pasa (el resto del update ya corrio bien, pero los servicios quedan con el # el restart nunca pasa (el resto del update ya corrio bien, pero los servicios quedan con el
# codigo viejo sin que nada avise del error). Bug real encontrado el 2026-09-30. # codigo viejo sin que nada avise del error). Bug real encontrado el 2026-09-30.
sudo systemctl restart "$BACKEND_SERVICE" # Frontend primero y backend AL FINAL: este script corre dentro del cgroup del backend, asi que
# reiniciarlo lo mata. Por eso se marca "done" antes; el popup confirma igual que el backend nuevo
# este arriba (runningVersion) antes de recargar.
sudo systemctl restart "$FRONTEND_SERVICE" sudo systemctl restart "$FRONTEND_SERVICE"
PHASE=complete PHASE=complete
write_status done write_status done
echo "=== update done $(date -Iseconds) -- v$NEW_VERSION ===" echo "=== update done $(date -Iseconds) -- v$NEW_VERSION (reiniciando backend) ==="
sudo systemctl restart "$BACKEND_SERVICE"