fix: enhance error logging by replacing console.error with a centralized logger across the application to improve security and maintainability
This commit is contained in:
@@ -31,7 +31,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|||||||
|
|
||||||
- **No more loading flicker.** Fast-loading pages no longer flash a loading state for a split second before showing content.
|
- **No more loading flicker.** Fast-loading pages no longer flash a loading state for a split second before showing content.
|
||||||
- **Organization context switch.** Switching away from a deleted organization now stores the session correctly instead of using an insecure fallback.
|
- **Organization context switch.** Switching away from a deleted organization now stores the session correctly instead of using an insecure fallback.
|
||||||
- **Removed debug logs.** Auth and organization-switching details no longer leak into the browser console in production.
|
- **Removed debug logs.** Auth and organization-switching details no longer leak into the browser console in production. Error logs are now also suppressed in production and only appear during development.
|
||||||
- **Dark mode uptime chart.** The response time chart on the uptime page now correctly follows your dark mode preference instead of always showing a white tooltip background.
|
- **Dark mode uptime chart.** The response time chart on the uptime page now correctly follows your dark mode preference instead of always showing a white tooltip background.
|
||||||
|
|
||||||
## [0.10.0-alpha] - 2026-02-21
|
## [0.10.0-alpha] - 2026-02-21
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
'use server'
|
'use server'
|
||||||
|
|
||||||
import { cookies } from 'next/headers'
|
import { cookies } from 'next/headers'
|
||||||
|
import { logger } from '@/lib/utils/logger'
|
||||||
|
|
||||||
const AUTH_API_URL = process.env.NEXT_PUBLIC_AUTH_API_URL || process.env.NEXT_PUBLIC_AUTH_URL || 'http://localhost:8081'
|
const AUTH_API_URL = process.env.NEXT_PUBLIC_AUTH_API_URL || process.env.NEXT_PUBLIC_AUTH_URL || 'http://localhost:8081'
|
||||||
|
|
||||||
@@ -102,7 +103,7 @@ export async function exchangeAuthCode(code: string, codeVerifier: string, redir
|
|||||||
}
|
}
|
||||||
|
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
console.error('Auth Exchange Error:', error)
|
logger.error('Auth Exchange Error:', error)
|
||||||
const isNetwork =
|
const isNetwork =
|
||||||
error instanceof TypeError ||
|
error instanceof TypeError ||
|
||||||
(error instanceof Error && (error.name === 'AbortError' || /fetch|network|ECONNREFUSED|ETIMEDOUT/i.test(error.message)))
|
(error instanceof Error && (error.name === 'AbortError' || /fetch|network|ECONNREFUSED|ETIMEDOUT/i.test(error.message)))
|
||||||
@@ -152,7 +153,7 @@ export async function setSessionAction(accessToken: string, refreshToken?: strin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('[setSessionAction] Error:', e)
|
logger.error('[setSessionAction] Error:', e)
|
||||||
return { success: false as const, error: 'invalid' }
|
return { success: false as const, error: 'invalid' }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useEffect, useState, Suspense, useRef, useCallback } from 'react'
|
import { useEffect, useState, Suspense, useRef, useCallback } from 'react'
|
||||||
|
import { logger } from '@/lib/utils/logger'
|
||||||
import { useRouter, useSearchParams } from 'next/navigation'
|
import { useRouter, useSearchParams } from 'next/navigation'
|
||||||
import { useAuth } from '@/lib/auth/context'
|
import { useAuth } from '@/lib/auth/context'
|
||||||
import { AUTH_URL, default as apiRequest } from '@/lib/api/client'
|
import { AUTH_URL, default as apiRequest } from '@/lib/api/client'
|
||||||
@@ -96,7 +97,7 @@ function AuthCallbackContent() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (state !== storedState) {
|
if (state !== storedState) {
|
||||||
console.error('State mismatch', { received: state, stored: storedState })
|
logger.error('State mismatch', { received: state, stored: storedState })
|
||||||
setError('Invalid state')
|
setError('Invalid state')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { useAuth } from '@/lib/auth/context'
|
|||||||
import { useOnlineStatus } from '@/lib/hooks/useOnlineStatus'
|
import { useOnlineStatus } from '@/lib/hooks/useOnlineStatus'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
|
import { logger } from '@/lib/utils/logger'
|
||||||
import { getUserOrganizations, switchContext } from '@/lib/api/organization'
|
import { getUserOrganizations, switchContext } from '@/lib/api/organization'
|
||||||
import { setSessionAction } from '@/app/actions/auth'
|
import { setSessionAction } from '@/app/actions/auth'
|
||||||
import { LoadingOverlay } from '@ciphera-net/ui'
|
import { LoadingOverlay } from '@ciphera-net/ui'
|
||||||
@@ -39,7 +40,7 @@ export default function LayoutContent({ children }: { children: React.ReactNode
|
|||||||
if (auth.user) {
|
if (auth.user) {
|
||||||
getUserOrganizations()
|
getUserOrganizations()
|
||||||
.then((organizations) => setOrgs(Array.isArray(organizations) ? organizations : []))
|
.then((organizations) => setOrgs(Array.isArray(organizations) ? organizations : []))
|
||||||
.catch(err => console.error('Failed to fetch orgs for header', err))
|
.catch(err => logger.error('Failed to fetch orgs for header', err))
|
||||||
}
|
}
|
||||||
}, [auth.user])
|
}, [auth.user])
|
||||||
|
|
||||||
@@ -51,7 +52,7 @@ export default function LayoutContent({ children }: { children: React.ReactNode
|
|||||||
sessionStorage.setItem(ORG_SWITCH_KEY, 'true')
|
sessionStorage.setItem(ORG_SWITCH_KEY, 'true')
|
||||||
window.location.reload()
|
window.location.reload()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Failed to switch organization', err)
|
logger.error('Failed to switch organization', err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useAuth } from '@/lib/auth/context'
|
import { useAuth } from '@/lib/auth/context'
|
||||||
|
import { logger } from '@/lib/utils/logger'
|
||||||
import { useCallback, useEffect, useState } from 'react'
|
import { useCallback, useEffect, useState } from 'react'
|
||||||
import { useParams, useRouter } from 'next/navigation'
|
import { useParams, useRouter } from 'next/navigation'
|
||||||
import { motion } from 'framer-motion'
|
import { motion } from 'framer-motion'
|
||||||
@@ -85,7 +86,7 @@ export default function SiteDashboardPage() {
|
|||||||
if (settings.multiDayInterval) setMultiDayInterval(settings.multiDayInterval)
|
if (settings.multiDayInterval) setMultiDayInterval(settings.multiDayInterval)
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Failed to load dashboard settings', e)
|
logger.error('Failed to load dashboard settings', e)
|
||||||
} finally {
|
} finally {
|
||||||
setIsSettingsLoaded(true)
|
setIsSettingsLoaded(true)
|
||||||
}
|
}
|
||||||
@@ -103,7 +104,7 @@ export default function SiteDashboardPage() {
|
|||||||
}
|
}
|
||||||
localStorage.setItem('pulse_dashboard_settings', JSON.stringify(settings))
|
localStorage.setItem('pulse_dashboard_settings', JSON.stringify(settings))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Failed to save dashboard settings', e)
|
logger.error('Failed to save dashboard settings', e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
|
import { logger } from '@/lib/utils/logger'
|
||||||
import { useRouter } from 'next/navigation'
|
import { useRouter } from 'next/navigation'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { createSite, listSites, getSite, type Site } from '@/lib/api/sites'
|
import { createSite, listSites, getSite, type Site } from '@/lib/api/sites'
|
||||||
@@ -65,7 +66,7 @@ export default function NewSitePage() {
|
|||||||
router.replace('/')
|
router.replace('/')
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to check limits', error)
|
logger.error('Failed to check limits', error)
|
||||||
} finally {
|
} finally {
|
||||||
setLimitsChecked(true)
|
setLimitsChecked(true)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
|
import { logger } from '@/lib/utils/logger'
|
||||||
import { useSearchParams } from 'next/navigation'
|
import { useSearchParams } from 'next/navigation'
|
||||||
import { motion } from 'framer-motion'
|
import { motion } from 'framer-motion'
|
||||||
import { Button, CheckCircleIcon } from '@ciphera-net/ui'
|
import { Button, CheckCircleIcon } from '@ciphera-net/ui'
|
||||||
@@ -140,7 +141,7 @@ export default function PricingSection() {
|
|||||||
// Clear intent
|
// Clear intent
|
||||||
localStorage.removeItem('pulse_pending_checkout')
|
localStorage.removeItem('pulse_pending_checkout')
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Failed to parse pending checkout', e)
|
logger.error('Failed to parse pending checkout', e)
|
||||||
localStorage.removeItem('pulse_pending_checkout')
|
localStorage.removeItem('pulse_pending_checkout')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -203,7 +204,7 @@ export default function PricingSection() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
console.error('Checkout error:', error)
|
logger.error('Checkout error:', error)
|
||||||
toast.error('Failed to start checkout — please try again')
|
toast.error('Failed to start checkout — please try again')
|
||||||
} finally {
|
} finally {
|
||||||
setLoadingPlan(null)
|
setLoadingPlan(null)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { useRouter } from 'next/navigation'
|
|||||||
import { PlusIcon, PersonIcon, CubeIcon, CheckIcon } from '@radix-ui/react-icons'
|
import { PlusIcon, PersonIcon, CubeIcon, CheckIcon } from '@radix-ui/react-icons'
|
||||||
import { switchContext, OrganizationMember } from '@/lib/api/organization'
|
import { switchContext, OrganizationMember } from '@/lib/api/organization'
|
||||||
import { setSessionAction } from '@/app/actions/auth'
|
import { setSessionAction } from '@/app/actions/auth'
|
||||||
|
import { logger } from '@/lib/utils/logger'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
|
|
||||||
export default function OrganizationSwitcher({ orgs, activeOrgId }: { orgs: OrganizationMember[], activeOrgId: string | null }) {
|
export default function OrganizationSwitcher({ orgs, activeOrgId }: { orgs: OrganizationMember[], activeOrgId: string | null }) {
|
||||||
@@ -37,7 +38,7 @@ export default function OrganizationSwitcher({ orgs, activeOrgId }: { orgs: Orga
|
|||||||
window.location.reload()
|
window.location.reload()
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Failed to switch organization', err)
|
logger.error('Failed to switch organization', err)
|
||||||
setSwitching(null)
|
setSwitching(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useState, useEffect, useMemo } from 'react'
|
import { useState, useEffect, useMemo } from 'react'
|
||||||
|
import { logger } from '@/lib/utils/logger'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
import { formatNumber } from '@ciphera-net/ui'
|
import { formatNumber } from '@ciphera-net/ui'
|
||||||
@@ -58,7 +59,7 @@ export default function Campaigns({ siteId, dateRange }: CampaignsProps) {
|
|||||||
const result = await getCampaigns(siteId, dateRange.start, dateRange.end, 10)
|
const result = await getCampaigns(siteId, dateRange.start, dateRange.end, 10)
|
||||||
setData(result)
|
setData(result)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
logger.error(e)
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
}
|
}
|
||||||
@@ -74,7 +75,7 @@ export default function Campaigns({ siteId, dateRange }: CampaignsProps) {
|
|||||||
const result = await getCampaigns(siteId, dateRange.start, dateRange.end, 100)
|
const result = await getCampaigns(siteId, dateRange.start, dateRange.end, 100)
|
||||||
setFullData(result)
|
setFullData(result)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
logger.error(e)
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoadingFull(false)
|
setIsLoadingFull(false)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
|
import { logger } from '@/lib/utils/logger'
|
||||||
import { formatNumber } from '@ciphera-net/ui'
|
import { formatNumber } from '@ciphera-net/ui'
|
||||||
import { useTabListKeyboard } from '@/lib/hooks/useTabListKeyboard'
|
import { useTabListKeyboard } from '@/lib/hooks/useTabListKeyboard'
|
||||||
import { TopPage, getTopPages, getEntryPages, getExitPages } from '@/lib/api/stats'
|
import { TopPage, getTopPages, getEntryPages, getExitPages } from '@/lib/api/stats'
|
||||||
@@ -50,7 +51,7 @@ export default function ContentStats({ topPages, entryPages, exitPages, domain,
|
|||||||
}
|
}
|
||||||
setFullData(filterGenericPaths(data))
|
setFullData(filterGenericPaths(data))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
logger.error(e)
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoadingFull(false)
|
setIsLoadingFull(false)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
|
import { logger } from '@/lib/utils/logger'
|
||||||
import { formatNumber } from '@ciphera-net/ui'
|
import { formatNumber } from '@ciphera-net/ui'
|
||||||
import { useTabListKeyboard } from '@/lib/hooks/useTabListKeyboard'
|
import { useTabListKeyboard } from '@/lib/hooks/useTabListKeyboard'
|
||||||
import * as Flags from 'country-flag-icons/react/3x2'
|
import * as Flags from 'country-flag-icons/react/3x2'
|
||||||
@@ -48,7 +49,7 @@ export default function Locations({ countries, cities, regions, geoDataLevel = '
|
|||||||
}
|
}
|
||||||
setFullData(data)
|
setFullData(data)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
logger.error(e)
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoadingFull(false)
|
setIsLoadingFull(false)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
|
import { logger } from '@/lib/utils/logger'
|
||||||
import { formatNumber } from '@ciphera-net/ui'
|
import { formatNumber } from '@ciphera-net/ui'
|
||||||
import { useTabListKeyboard } from '@/lib/hooks/useTabListKeyboard'
|
import { useTabListKeyboard } from '@/lib/hooks/useTabListKeyboard'
|
||||||
import { getBrowserIcon, getOSIcon, getDeviceIcon } from '@/lib/utils/icons'
|
import { getBrowserIcon, getOSIcon, getDeviceIcon } from '@/lib/utils/icons'
|
||||||
@@ -58,7 +59,7 @@ export default function TechSpecs({ browsers, os, devices, screenResolutions, co
|
|||||||
}
|
}
|
||||||
setFullData(filterUnknown(data))
|
setFullData(filterUnknown(data))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
logger.error(e)
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoadingFull(false)
|
setIsLoadingFull(false)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
|
import { logger } from '@/lib/utils/logger'
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
import { formatNumber } from '@ciphera-net/ui'
|
import { formatNumber } from '@ciphera-net/ui'
|
||||||
import { getReferrerDisplayName, getReferrerFavicon, getReferrerIcon, mergeReferrersByDisplayName } from '@/lib/utils/icons'
|
import { getReferrerDisplayName, getReferrerFavicon, getReferrerIcon, mergeReferrersByDisplayName } from '@/lib/utils/icons'
|
||||||
@@ -66,7 +67,7 @@ export default function TopReferrers({ referrers, collectReferrers = true, siteI
|
|||||||
)
|
)
|
||||||
setFullData(filtered)
|
setFullData(filtered)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
logger.error(e)
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoadingFull(false)
|
setIsLoadingFull(false)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
import { useState, useEffect, useCallback, useRef } from 'react'
|
import { useState, useEffect, useCallback, useRef } from 'react'
|
||||||
import { useRouter, useSearchParams } from 'next/navigation'
|
import { useRouter, useSearchParams } from 'next/navigation'
|
||||||
import { setSessionAction } from '@/app/actions/auth'
|
import { setSessionAction } from '@/app/actions/auth'
|
||||||
|
import { logger } from '@/lib/utils/logger'
|
||||||
import { useAuth } from '@/lib/auth/context'
|
import { useAuth } from '@/lib/auth/context'
|
||||||
import {
|
import {
|
||||||
deleteOrganization,
|
deleteOrganization,
|
||||||
@@ -170,7 +171,7 @@ export default function OrganizationSettings() {
|
|||||||
setOrgName(orgData.name)
|
setOrgName(orgData.name)
|
||||||
setOrgSlug(orgData.slug)
|
setOrgSlug(orgData.slug)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load data:', error)
|
logger.error('Failed to load data:', error)
|
||||||
// toast.error('Failed to load members')
|
// toast.error('Failed to load members')
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoadingMembers(false)
|
setIsLoadingMembers(false)
|
||||||
@@ -184,7 +185,7 @@ export default function OrganizationSettings() {
|
|||||||
const sub = await getSubscription()
|
const sub = await getSubscription()
|
||||||
setSubscription(sub)
|
setSubscription(sub)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load subscription:', error)
|
logger.error('Failed to load subscription:', error)
|
||||||
// toast.error('Failed to load subscription details')
|
// toast.error('Failed to load subscription details')
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoadingSubscription(false)
|
setIsLoadingSubscription(false)
|
||||||
@@ -198,7 +199,7 @@ export default function OrganizationSettings() {
|
|||||||
const invs = await getInvoices()
|
const invs = await getInvoices()
|
||||||
setInvoices(invs)
|
setInvoices(invs)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load invoices:', error)
|
logger.error('Failed to load invoices:', error)
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoadingInvoices(false)
|
setIsLoadingInvoices(false)
|
||||||
}
|
}
|
||||||
@@ -247,7 +248,7 @@ export default function OrganizationSettings() {
|
|||||||
setAuditEntries(Array.isArray(entries) ? entries : [])
|
setAuditEntries(Array.isArray(entries) ? entries : [])
|
||||||
setAuditTotal(typeof total === 'number' ? total : 0)
|
setAuditTotal(typeof total === 'number' ? total : 0)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load audit log', error)
|
logger.error('Failed to load audit log', error)
|
||||||
toast.error(getAuthErrorMessage(error as Error) || 'Failed to load audit log entries')
|
toast.error(getAuthErrorMessage(error as Error) || 'Failed to load audit log entries')
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoadingAudit(false)
|
setIsLoadingAudit(false)
|
||||||
@@ -279,7 +280,7 @@ export default function OrganizationSettings() {
|
|||||||
setNotificationSettings(res.settings || {})
|
setNotificationSettings(res.settings || {})
|
||||||
setNotificationCategories(res.categories || [])
|
setNotificationCategories(res.categories || [])
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load notification settings', error)
|
logger.error('Failed to load notification settings', error)
|
||||||
toast.error(getAuthErrorMessage(error as Error) || 'Failed to load notification settings')
|
toast.error(getAuthErrorMessage(error as Error) || 'Failed to load notification settings')
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoadingNotificationSettings(false)
|
setIsLoadingNotificationSettings(false)
|
||||||
@@ -422,13 +423,13 @@ export default function OrganizationSettings() {
|
|||||||
sessionStorage.setItem('pulse_switching_org', 'true')
|
sessionStorage.setItem('pulse_switching_org', 'true')
|
||||||
window.location.href = '/'
|
window.location.href = '/'
|
||||||
} catch (switchErr) {
|
} catch (switchErr) {
|
||||||
console.error('Failed to switch to personal context after delete:', switchErr)
|
logger.error('Failed to switch to personal context after delete:', switchErr)
|
||||||
sessionStorage.setItem('pulse_switching_org', 'true')
|
sessionStorage.setItem('pulse_switching_org', 'true')
|
||||||
window.location.href = '/'
|
window.location.href = '/'
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
console.error(err)
|
logger.error(err)
|
||||||
toast.error(getAuthErrorMessage(err) || (err instanceof Error ? err.message : '') || 'Failed to delete organization')
|
toast.error(getAuthErrorMessage(err) || (err instanceof Error ? err.message : '') || 'Failed to delete organization')
|
||||||
setIsDeleting(false)
|
setIsDeleting(false)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
|
import { logger } from '@/lib/utils/logger'
|
||||||
import { CopyIcon, CheckIcon } from '@radix-ui/react-icons'
|
import { CopyIcon, CheckIcon } from '@radix-ui/react-icons'
|
||||||
import { listSites, Site } from '@/lib/api/sites'
|
import { listSites, Site } from '@/lib/api/sites'
|
||||||
import { Select, Input, Button } from '@ciphera-net/ui'
|
import { Select, Input, Button } from '@ciphera-net/ui'
|
||||||
@@ -30,7 +31,7 @@ export default function UtmBuilder({ initialSiteId }: UtmBuilderProps) {
|
|||||||
const data = await listSites()
|
const data = await listSites()
|
||||||
setSites(data)
|
setSites(data)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Failed to load sites for UTM builder', e)
|
logger.error('Failed to load sites for UTM builder', e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fetchSites()
|
fetchSites()
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import apiRequest from '@/lib/api/client'
|
|||||||
import { LoadingOverlay } from '@ciphera-net/ui'
|
import { LoadingOverlay } from '@ciphera-net/ui'
|
||||||
import { logoutAction, getSessionAction, setSessionAction } from '@/app/actions/auth'
|
import { logoutAction, getSessionAction, setSessionAction } from '@/app/actions/auth'
|
||||||
import { getUserOrganizations, switchContext } from '@/lib/api/organization'
|
import { getUserOrganizations, switchContext } from '@/lib/api/organization'
|
||||||
|
import { logger } from '@/lib/utils/logger'
|
||||||
|
|
||||||
interface User {
|
interface User {
|
||||||
id: string
|
id: string
|
||||||
@@ -66,7 +67,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
|||||||
return merged
|
return merged
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.catch((e) => console.error('Failed to fetch full profile after login', e))
|
.catch((e) => logger.error('Failed to fetch full profile after login', e))
|
||||||
}
|
}
|
||||||
|
|
||||||
const logout = useCallback(async () => {
|
const logout = useCallback(async () => {
|
||||||
@@ -96,7 +97,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
|||||||
return merged
|
return merged
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Failed to refresh user data', e)
|
logger.error('Failed to refresh user data', e)
|
||||||
}
|
}
|
||||||
router.refresh()
|
router.refresh()
|
||||||
}, [router])
|
}, [router])
|
||||||
@@ -121,7 +122,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
|||||||
setUser(merged)
|
setUser(merged)
|
||||||
localStorage.setItem('user', JSON.stringify(merged))
|
localStorage.setItem('user', JSON.stringify(merged))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Failed to fetch full profile', e)
|
logger.error('Failed to fetch full profile', e)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// * Session invalid/expired
|
// * Session invalid/expired
|
||||||
@@ -178,11 +179,11 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
|||||||
router.refresh()
|
router.refresh()
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Failed to auto-switch context', e)
|
logger.error('Failed to auto-switch context', e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Failed to fetch organizations", e)
|
logger.error("Failed to fetch organizations", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
16
lib/utils/logger.ts
Normal file
16
lib/utils/logger.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Dev-only logger that suppresses client-side output in production.
|
||||||
|
* Server-side logs always pass through (they go to server logs, not the browser).
|
||||||
|
*/
|
||||||
|
|
||||||
|
const isServer = typeof window === 'undefined'
|
||||||
|
const isDev = process.env.NODE_ENV === 'development'
|
||||||
|
|
||||||
|
export const logger = {
|
||||||
|
error(...args: unknown[]) {
|
||||||
|
if (isServer || isDev) console.error(...args)
|
||||||
|
},
|
||||||
|
warn(...args: unknown[]) {
|
||||||
|
if (isServer || isDev) console.warn(...args)
|
||||||
|
},
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user