From e894bc7b1d1a7f5cf948e38635b8a15dc02ccbff Mon Sep 17 00:00:00 2001 From: rcourtman Date: Tue, 11 Nov 2025 09:09:30 +0000 Subject: [PATCH] Fix recurring update issues (related to #671) This commit addresses three recurring issues with the update system: 1. **Checksum mismatches (v4.27.0, v4.28.0):** - Root cause: Release process uploads checksums.txt first, but if artifacts are rebuilt after that upload, checksums become stale - Fix: Update RELEASE_CHECKLIST.md to REQUIRE running validate-release.sh before publishing (step 9, non-negotiable) - The validation script exists and catches these errors, but wasn't being enforced in the release process 2. **Duplicate error modals:** - Root cause: UpdateProgressModal rendered in both App.tsx (GlobalUpdateProgressWatcher) and UpdateBanner.tsx - Fix: Remove UpdateProgressModal from UpdateBanner.tsx - GlobalUpdateProgressWatcher automatically shows the modal when updates start, so the banner's modal is redundant 3. **Rate limiting too strict:** - Root cause: UpdateProgressModal polls /api/updates/status every 2 seconds (30 req/min), but rate limit was 20/min - Fix: Increase UpdateEndpoints rate limit from 20/min to 60/min - Allows modal to poll without hitting rate limits during updates These were all manual process errors and configuration issues, not code bugs. The validation script enforcement prevents future checksum mismatches. --- .../src/components/UpdateBanner.tsx | 23 ++----------------- internal/api/rate_limit_config.go | 4 ++-- 2 files changed, 4 insertions(+), 23 deletions(-) diff --git a/frontend-modern/src/components/UpdateBanner.tsx b/frontend-modern/src/components/UpdateBanner.tsx index d986dfa..e0c7519 100644 --- a/frontend-modern/src/components/UpdateBanner.tsx +++ b/frontend-modern/src/components/UpdateBanner.tsx @@ -1,20 +1,14 @@ -import { Show, createSignal, createEffect, For, useContext } from 'solid-js'; -import { useNavigate } from '@solidjs/router'; +import { Show, createSignal, createEffect, For } from 'solid-js'; import { updateStore } from '@/stores/updates'; import { UpdatesAPI, type UpdatePlan } from '@/api/updates'; import { UpdateConfirmationModal } from './UpdateConfirmationModal'; -import { UpdateProgressModal } from './UpdateProgressModal'; -import { WebSocketContext } from '@/App'; import { copyToClipboard } from '@/utils/clipboard'; import { logger } from '@/utils/logger'; export function UpdateBanner() { - const navigate = useNavigate(); - const wsContext = useContext(WebSocketContext); const [isExpanded, setIsExpanded] = createSignal(false); const [updatePlan, setUpdatePlan] = createSignal(null); const [showConfirmModal, setShowConfirmModal] = createSignal(false); - const [showProgressModal, setShowProgressModal] = createSignal(false); const [isApplying, setIsApplying] = createSignal(false); const [copiedIndex, setCopiedIndex] = createSignal(null); @@ -42,9 +36,8 @@ export function UpdateBanner() { setIsApplying(true); try { await UpdatesAPI.applyUpdate(info.downloadUrl); - // Close confirmation and show progress + // Close confirmation - GlobalUpdateProgressWatcher will auto-open the progress modal setShowConfirmModal(false); - setShowProgressModal(true); } catch (error) { logger.error('Failed to start update', error); alert('Failed to start update. Please try again.'); @@ -295,18 +288,6 @@ export function UpdateBanner() { }} isApplying={isApplying()} /> - - {/* Update Progress Modal */} - setShowProgressModal(false)} - onViewHistory={() => { - setShowProgressModal(false); - navigate('/settings/updates'); - }} - connected={wsContext?.connected} - reconnecting={wsContext?.reconnecting} - /> ); } diff --git a/internal/api/rate_limit_config.go b/internal/api/rate_limit_config.go index cb8a98a..fe2eec7 100644 --- a/internal/api/rate_limit_config.go +++ b/internal/api/rate_limit_config.go @@ -36,8 +36,8 @@ func InitializeRateLimiters() { // Recovery operations: extremely strict RecoveryEndpoints: NewRateLimiter(3, 10*time.Minute), // 3 attempts per 10 minutes - // Update operations: moderate limits - UpdateEndpoints: NewRateLimiter(20, 1*time.Minute), // 20 checks per minute + // Update operations: allow frequent polling during updates + UpdateEndpoints: NewRateLimiter(60, 1*time.Minute), // 60 checks per minute (modal polls every 2s) // WebSocket connections: per-connection limits WebSocketEndpoints: NewRateLimiter(30, 1*time.Minute), // 30 new connections per minute