Merge pull request #200 from builderz-labs/fix/198-auth-ux-redirect
fix(auth): redirect unauthenticated panel requests to login
This commit is contained in:
commit
32f1e4b48a
|
|
@ -18,9 +18,18 @@ export default defineConfig({
|
|||
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }
|
||||
],
|
||||
webServer: {
|
||||
command: 'pnpm start',
|
||||
command: 'node .next/standalone/server.js',
|
||||
url: 'http://127.0.0.1:3005',
|
||||
reuseExistingServer: true,
|
||||
timeout: 30_000,
|
||||
timeout: 120_000,
|
||||
env: {
|
||||
...process.env,
|
||||
HOSTNAME: process.env.HOSTNAME || '127.0.0.1',
|
||||
PORT: process.env.PORT || '3005',
|
||||
MC_DISABLE_RATE_LIMIT: process.env.MC_DISABLE_RATE_LIMIT || '1',
|
||||
API_KEY: process.env.API_KEY || 'test-api-key-e2e-12345',
|
||||
AUTH_USER: process.env.AUTH_USER || 'testadmin',
|
||||
AUTH_PASS: process.env.AUTH_PASS || 'testpass1234!',
|
||||
},
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
'use client'
|
||||
|
||||
import { useEffect, useState } from 'react'
|
||||
import { usePathname } from 'next/navigation'
|
||||
import { usePathname, useRouter } from 'next/navigation'
|
||||
import { NavRail } from '@/components/layout/nav-rail'
|
||||
import { HeaderBar } from '@/components/layout/header-bar'
|
||||
import { LiveFeed } from '@/components/layout/live-feed'
|
||||
|
|
@ -42,6 +42,7 @@ import { useServerEvents } from '@/lib/use-server-events'
|
|||
import { useMissionControl } from '@/store'
|
||||
|
||||
export default function Home() {
|
||||
const router = useRouter()
|
||||
const { connect } = useWebSocket()
|
||||
const { activeTab, setActiveTab, setCurrentUser, setDashboardMode, setGatewayAvailable, setSubscription, setUpdateAvailable, liveFeedOpen, toggleLiveFeed } = useMissionControl()
|
||||
|
||||
|
|
@ -62,7 +63,13 @@ export default function Home() {
|
|||
|
||||
// Fetch current user
|
||||
fetch('/api/auth/me')
|
||||
.then(res => res.ok ? res.json() : null)
|
||||
.then(async (res) => {
|
||||
if (res.ok) return res.json()
|
||||
if (res.status === 401) {
|
||||
router.replace(`/login?next=${encodeURIComponent(pathname)}`)
|
||||
}
|
||||
return null
|
||||
})
|
||||
.then(data => { if (data?.user) setCurrentUser(data.user) })
|
||||
.catch(() => {})
|
||||
|
||||
|
|
@ -120,7 +127,7 @@ export default function Home() {
|
|||
const wsUrl = explicitWsUrl || `${gatewayProto}://${gatewayHost}:${gatewayPort}`
|
||||
connect(wsUrl, wsToken)
|
||||
})
|
||||
}, [connect, setCurrentUser, setDashboardMode, setGatewayAvailable, setSubscription, setUpdateAvailable])
|
||||
}, [connect, pathname, router, setCurrentUser, setDashboardMode, setGatewayAvailable, setSubscription, setUpdateAvailable])
|
||||
|
||||
if (!isClient) {
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -96,7 +96,14 @@ export function AgentSquadPanelPhase3() {
|
|||
setSyncToast(null)
|
||||
try {
|
||||
const response = await fetch('/api/agents/sync', { method: 'POST' })
|
||||
if (response.status === 401) {
|
||||
window.location.assign('/login?next=%2Fagents')
|
||||
return
|
||||
}
|
||||
const data = await response.json()
|
||||
if (response.status === 403) {
|
||||
throw new Error('Admin access required for agent sync')
|
||||
}
|
||||
if (!response.ok) throw new Error(data.error || 'Sync failed')
|
||||
setSyncToast(`Synced ${data.synced} agents (${data.created} new, ${data.updated} updated)`)
|
||||
fetchAgents()
|
||||
|
|
@ -116,7 +123,17 @@ export function AgentSquadPanelPhase3() {
|
|||
if (agents.length === 0) setLoading(true)
|
||||
|
||||
const response = await fetch('/api/agents')
|
||||
if (!response.ok) throw new Error('Failed to fetch agents')
|
||||
if (response.status === 401) {
|
||||
window.location.assign('/login?next=%2Fagents')
|
||||
return
|
||||
}
|
||||
if (response.status === 403) {
|
||||
throw new Error('Access denied')
|
||||
}
|
||||
if (!response.ok) {
|
||||
const data = await response.json().catch(() => ({}))
|
||||
throw new Error(data.error || 'Failed to fetch agents')
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
setAgents(data.agents || [])
|
||||
|
|
|
|||
|
|
@ -45,12 +45,17 @@ export function SettingsPanel() {
|
|||
const fetchSettings = useCallback(async () => {
|
||||
try {
|
||||
const res = await fetch('/api/settings')
|
||||
if (res.status === 401) {
|
||||
window.location.assign('/login?next=%2Fsettings')
|
||||
return
|
||||
}
|
||||
if (res.status === 403) {
|
||||
setError('Admin access required')
|
||||
return
|
||||
}
|
||||
if (!res.ok) {
|
||||
setError('Failed to load settings')
|
||||
const data = await res.json().catch(() => ({}))
|
||||
setError(data.error || 'Failed to load settings')
|
||||
return
|
||||
}
|
||||
const data = await res.json()
|
||||
|
|
|
|||
|
|
@ -59,6 +59,8 @@ export function useWebSocket() {
|
|||
const pingCounterRef = useRef<number>(0)
|
||||
const pingSentTimestamps = useRef<Map<string, number>>(new Map())
|
||||
const missedPongsRef = useRef<number>(0)
|
||||
// Compat flag for gateway versions that may not implement ping RPC.
|
||||
const gatewaySupportsPingRef = useRef<boolean>(true)
|
||||
|
||||
const {
|
||||
connection,
|
||||
|
|
@ -116,6 +118,7 @@ export function useWebSocket() {
|
|||
|
||||
pingIntervalRef.current = setInterval(() => {
|
||||
if (!wsRef.current || wsRef.current.readyState !== WebSocket.OPEN || !handshakeCompleteRef.current) return
|
||||
if (!gatewaySupportsPingRef.current) return
|
||||
|
||||
// Check missed pongs
|
||||
if (missedPongsRef.current >= MAX_MISSED_PONGS) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue