Move allauth + auth UI to legacy/

allauth/ (44 files) is a django-allauth React UI — a separate concern
from the Mizan protocol. Moved to legacy/ pending extraction into a
standalone mizan-django-allauth package.

Also moved to legacy/:
- client/AuthContext.tsx — generic auth state from /me endpoint
- client/RouterContext.tsx — framework-agnostic router adapter
- client/routing.tsx — UserRoute/StaffRoute/AnonymousRoute guards
- client/nextjs.tsx — Next.js router adapter for auth

These are auth UI infrastructure, not Mizan protocol. The Mizan core
only needs JWT for auth header selection (jwt/ stays — MizanProvider
depends on useJWT() to decide between Bearer and session auth).

Cleaned up re-exports in client/react.ts and vitest aliases.

33 React tests pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-07 03:41:22 -04:00
parent 24ff0ae66d
commit 27c30d7e50
50 changed files with 0 additions and 8 deletions

View File

@@ -0,0 +1,88 @@
'use client'
import { useState, useEffect } from 'react'
import { useAllauthAPI } from '../../contexts/APIContext'
import { SettingsSection, SettingsItem, SettingsList, Badge, Button } from './SettingsComponents'
import type { Session } from '../../types'
function parseUserAgent(ua: string): string {
if (ua.includes('Chrome')) return 'Chrome'
if (ua.includes('Firefox')) return 'Firefox'
if (ua.includes('Safari')) return 'Safari'
if (ua.includes('Edge')) return 'Edge'
return 'Unknown Browser'
}
export function SessionsSection() {
const api = useAllauthAPI()
const [sessions, setSessions] = useState<Session[]>([])
const [loading, setLoading] = useState(true)
const [available, setAvailable] = useState(true)
const fetchSessions = async () => {
try {
const res = await api.session.list()
if (res.status === 200 && res.data) {
setSessions(res.data as Session[])
} else {
// Non-200 status means sessions feature not available
setAvailable(false)
}
} catch {
setAvailable(false)
}
setLoading(false)
}
useEffect(() => { fetchSessions() }, [])
const handleEnd = async (id: number) => {
if (!confirm('End this session?')) return
await api.session.remove([id])
fetchSessions()
}
const handleEndAllOthers = async () => {
const otherIds = sessions.filter(s => !s.is_current).map(s => s.id)
if (otherIds.length === 0) return
if (!confirm(`End ${otherIds.length} other session(s)?`)) return
await api.session.remove(otherIds)
fetchSessions()
}
if (loading || !available) return null
const otherSessions = sessions.filter(s => !s.is_current)
return (
<SettingsSection title="Active Sessions">
<SettingsList>
{sessions.map(session => (
<SettingsItem
key={session.id}
label={
<>
{parseUserAgent(session.user_agent)}
{session.is_current && <Badge variant="success">Current</Badge>}
</>
}
meta={`${session.ip} · ${session.last_seen_at ? new Date(session.last_seen_at * 1000).toLocaleString() : 'Unknown'}`}
actions={
!session.is_current && (
<Button variant="danger" onClick={() => handleEnd(session.id)}>
End
</Button>
)
}
/>
))}
</SettingsList>
{otherSessions.length > 0 && (
<Button variant="danger" onClick={handleEndAllOthers}>
End All Other Sessions
</Button>
)}
</SettingsSection>
)
}