fix: recover gracefully from stale Server Action hashes after deployment
Wrap all Server Action calls (getSessionAction, exchangeAuthCode, logoutAction) in try-catch so a cached browser bundle with old action IDs triggers a hard reload instead of an infinite loading spinner.
This commit is contained in:
@@ -12,6 +12,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Login no longer gets stuck after updates.** If you happened to have Pulse open when a new version was deployed, logging back in could get stuck on a loading screen. The app now automatically refreshes itself to pick up the latest version.
|
||||
- **City and region data is now accurate.** Location data was incorrectly showing the CDN server's location (e.g. Paris, Villeurbanne) instead of the visitor's actual city. Fixed by reading the correct visitor IP header from Bunny CDN.
|
||||
- **"Reset Data" now clears everything.** Previously, resetting a site's data in Settings only removed pageviews and daily stats. Uptime check history, uptime daily stats, and cached dashboard data were left behind. All collected data is now properly cleared when you reset, while your site configuration, goals, funnels, and uptime monitors are kept.
|
||||
|
||||
|
||||
@@ -22,7 +22,14 @@ function AuthCallbackContent() {
|
||||
const codeVerifier = localStorage.getItem('oauth_code_verifier')
|
||||
const redirectUri = typeof window !== 'undefined' ? window.location.origin + '/auth/callback' : ''
|
||||
if (!code) return
|
||||
const result = await exchangeAuthCode(code, codeVerifier, redirectUri)
|
||||
let result: Awaited<ReturnType<typeof exchangeAuthCode>>
|
||||
try {
|
||||
result = await exchangeAuthCode(code, codeVerifier, redirectUri)
|
||||
} catch {
|
||||
// * Stale build — cached JS has old Server Action hashes. Hard reload to fix.
|
||||
window.location.reload()
|
||||
return
|
||||
}
|
||||
if (result.success && result.user) {
|
||||
// * Fetch full profile (including display_name) before navigating so header shows correct name on first paint
|
||||
try {
|
||||
|
||||
@@ -90,7 +90,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
||||
|
||||
const logout = useCallback(async () => {
|
||||
setIsLoggingOut(true)
|
||||
await logoutAction()
|
||||
try { await logoutAction() } catch { /* stale build — continue with client-side cleanup */ }
|
||||
localStorage.removeItem('user')
|
||||
localStorage.removeItem('ciphera_token_refreshed_at')
|
||||
localStorage.removeItem('ciphera_last_activity')
|
||||
@@ -132,7 +132,15 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
||||
useEffect(() => {
|
||||
const init = async () => {
|
||||
// * 1. Check server-side session (cookies)
|
||||
let session = await getSessionAction()
|
||||
let session: Awaited<ReturnType<typeof getSessionAction>> = null
|
||||
try {
|
||||
session = await getSessionAction()
|
||||
} catch {
|
||||
// * Stale build — browser has cached JS with old Server Action hashes.
|
||||
// * Force a hard reload to fetch fresh bundles from the server.
|
||||
window.location.reload()
|
||||
return
|
||||
}
|
||||
|
||||
// * 2. If no access_token but refresh_token may exist, try refresh (fixes 15-min inactivity logout)
|
||||
if (!session && typeof window !== 'undefined') {
|
||||
@@ -142,7 +150,12 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
||||
credentials: 'include',
|
||||
})
|
||||
if (refreshRes.ok) {
|
||||
session = await getSessionAction()
|
||||
try {
|
||||
session = await getSessionAction()
|
||||
} catch {
|
||||
window.location.reload()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user