mirror of
https://github.com/aljazceru/enclava.git
synced 2025-12-17 07:24:34 +01:00
auth consolidation
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
"use client"
|
||||
|
||||
import { useAuth } from "@/contexts/AuthContext"
|
||||
import { useAuth } from "@/components/providers/auth-provider"
|
||||
import { useState, useEffect } from "react"
|
||||
import { ProtectedRoute } from "@/components/auth/ProtectedRoute"
|
||||
import { useToast } from "@/hooks/use-toast"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { useState } from "react"
|
||||
import { useRouter } from "next/navigation"
|
||||
import { useAuth } from "@/contexts/AuthContext"
|
||||
import { useAuth } from "@/components/providers/auth-provider"
|
||||
|
||||
// Force dynamic rendering for authentication
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
@@ -31,7 +31,7 @@ import { Edit3, RotateCcw, Loader2, Save, AlertTriangle, Plus, Sparkles } from '
|
||||
import toast from 'react-hot-toast'
|
||||
import { apiClient } from '@/lib/api-client'
|
||||
import { config } from '@/lib/config'
|
||||
import { useAuth } from '@/contexts/AuthContext'
|
||||
import { useAuth } from '@/components/providers/auth-provider'
|
||||
|
||||
interface PromptTemplate {
|
||||
id: string
|
||||
|
||||
@@ -11,7 +11,7 @@ import { Plus, Database, Upload, Search, Trash2, FileText, AlertCircle } from "l
|
||||
import { CollectionManager } from "@/components/rag/collection-manager"
|
||||
import { DocumentUpload } from "@/components/rag/document-upload"
|
||||
import { DocumentBrowser } from "@/components/rag/document-browser"
|
||||
import { useAuth } from "@/contexts/AuthContext"
|
||||
import { useAuth } from "@/components/providers/auth-provider"
|
||||
import { ProtectedRoute } from '@/components/auth/ProtectedRoute'
|
||||
import { apiClient } from '@/lib/api-client'
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client"
|
||||
|
||||
import { useAuth } from "@/contexts/AuthContext"
|
||||
import { useAuth } from "@/components/providers/auth-provider"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
import { useState } from "react"
|
||||
|
||||
@@ -28,7 +28,7 @@ import {
|
||||
AlertCircle
|
||||
} from 'lucide-react';
|
||||
import { usePlugin, type PluginInfo, type AvailablePlugin } from '../../contexts/PluginContext';
|
||||
import { useAuth } from '../../contexts/AuthContext';
|
||||
import { useAuth } from '@/components/providers/auth-provider';
|
||||
import { PluginConfigurationDialog } from './PluginConfigurationDialog';
|
||||
|
||||
interface PluginCardProps {
|
||||
|
||||
@@ -8,7 +8,8 @@ import { Alert, AlertDescription } from '@/components/ui/alert';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { Skeleton } from '@/components/ui/skeleton';
|
||||
import { AlertCircle, Loader2 } from 'lucide-react';
|
||||
import { useAuth } from '../../contexts/AuthContext';
|
||||
import { useAuth } from '@/components/providers/auth-provider';
|
||||
import { tokenManager } from '@/lib/token-manager';
|
||||
import { usePlugin, type PluginInfo } from '../../contexts/PluginContext';
|
||||
import { config } from '../../lib/config';
|
||||
|
||||
@@ -48,8 +49,8 @@ const PluginIframe: React.FC<PluginIframeProps> = ({
|
||||
// Validate origin - should be from our backend
|
||||
const allowedOrigins = [
|
||||
window.location.origin,
|
||||
config.getBackendUrl(),
|
||||
config.getApiUrl()
|
||||
config.API_BASE_URL,
|
||||
config.API_BASE_URL
|
||||
].filter(Boolean);
|
||||
|
||||
if (!allowedOrigins.some(origin => event.origin.startsWith(origin))) {
|
||||
@@ -161,7 +162,8 @@ export const PluginPageRenderer: React.FC<PluginPageRendererProps> = ({
|
||||
pagePath,
|
||||
componentName
|
||||
}) => {
|
||||
const { user, token } = useAuth();
|
||||
const { user } = useAuth();
|
||||
const token = tokenManager.getAccessToken();
|
||||
const {
|
||||
installedPlugins,
|
||||
getPluginPages,
|
||||
|
||||
@@ -9,6 +9,7 @@ interface User {
|
||||
id: string
|
||||
username: string
|
||||
email: string
|
||||
name?: string
|
||||
role: string
|
||||
permissions: string[]
|
||||
created_at: string
|
||||
@@ -18,6 +19,7 @@ interface User {
|
||||
interface AuthContextType {
|
||||
user: User | null
|
||||
isLoading: boolean
|
||||
isAuthenticated: boolean
|
||||
login: (username: string, password: string) => Promise<void>
|
||||
logout: () => void
|
||||
register: (username: string, email: string, password: string) => Promise<void>
|
||||
@@ -114,6 +116,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
||||
const value: AuthContextType = {
|
||||
user,
|
||||
isLoading,
|
||||
isAuthenticated: !!user,
|
||||
login,
|
||||
logout,
|
||||
register,
|
||||
|
||||
@@ -1,194 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import { createContext, useContext, useState, useEffect, ReactNode } from "react"
|
||||
import { useRouter } from "next/navigation"
|
||||
import { tokenManager } from "@/lib/token-manager"
|
||||
|
||||
// Helper function to get API URL with proper protocol
|
||||
const getApiUrl = () => {
|
||||
if (typeof window !== 'undefined') {
|
||||
const protocol = window.location.protocol.slice(0, -1) // Remove ':' from 'https:'
|
||||
const host = window.location.hostname
|
||||
return `${protocol}://${host}`
|
||||
}
|
||||
return `http://${process.env.NEXT_PUBLIC_BASE_URL || 'localhost'}`
|
||||
}
|
||||
|
||||
interface User {
|
||||
id: string
|
||||
email: string
|
||||
name: string
|
||||
role: string
|
||||
}
|
||||
|
||||
interface AuthContextType {
|
||||
user: User | null
|
||||
isAuthenticated: boolean
|
||||
login: (email: string, password: string) => Promise<void>
|
||||
logout: () => void
|
||||
isLoading: boolean
|
||||
}
|
||||
|
||||
const AuthContext = createContext<AuthContextType | undefined>(undefined)
|
||||
|
||||
export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
const [user, setUser] = useState<User | null>(null)
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
const router = useRouter()
|
||||
|
||||
// Initialize auth state and listen to token manager events
|
||||
useEffect(() => {
|
||||
const initAuth = async () => {
|
||||
// Check if we have valid tokens
|
||||
if (tokenManager.isAuthenticated()) {
|
||||
// Try to get user info
|
||||
await fetchUserInfo()
|
||||
}
|
||||
setIsLoading(false)
|
||||
}
|
||||
|
||||
// Set up event listeners
|
||||
const handleTokensUpdated = () => {
|
||||
// Tokens were updated (refreshed), update user if needed
|
||||
if (!user) {
|
||||
fetchUserInfo()
|
||||
}
|
||||
}
|
||||
|
||||
const handleTokensCleared = () => {
|
||||
// Tokens were cleared, clear user
|
||||
setUser(null)
|
||||
}
|
||||
|
||||
const handleSessionExpired = (reason: string) => {
|
||||
console.log('Session expired:', reason)
|
||||
setUser(null)
|
||||
// TokenManager and API client will handle redirect
|
||||
}
|
||||
|
||||
const handleLogout = () => {
|
||||
setUser(null)
|
||||
router.push('/login')
|
||||
}
|
||||
|
||||
// Register event listeners
|
||||
tokenManager.on('tokensUpdated', handleTokensUpdated)
|
||||
tokenManager.on('tokensCleared', handleTokensCleared)
|
||||
tokenManager.on('sessionExpired', handleSessionExpired)
|
||||
tokenManager.on('logout', handleLogout)
|
||||
|
||||
// Initialize
|
||||
initAuth()
|
||||
|
||||
// Cleanup
|
||||
return () => {
|
||||
tokenManager.off('tokensUpdated', handleTokensUpdated)
|
||||
tokenManager.off('tokensCleared', handleTokensCleared)
|
||||
tokenManager.off('sessionExpired', handleSessionExpired)
|
||||
tokenManager.off('logout', handleLogout)
|
||||
}
|
||||
}, [])
|
||||
|
||||
const fetchUserInfo = async () => {
|
||||
try {
|
||||
const token = await tokenManager.getAccessToken()
|
||||
if (!token) return
|
||||
|
||||
const response = await fetch(`${getApiUrl()}/api-internal/v1/auth/me`, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
},
|
||||
})
|
||||
|
||||
if (response.ok) {
|
||||
const userData = await response.json()
|
||||
const user = {
|
||||
id: userData.id || userData.sub,
|
||||
email: userData.email,
|
||||
name: userData.name || userData.email,
|
||||
role: userData.role || 'user',
|
||||
}
|
||||
setUser(user)
|
||||
|
||||
// Store user info for offline access
|
||||
if (typeof window !== 'undefined') {
|
||||
localStorage.setItem('user', JSON.stringify(user))
|
||||
}
|
||||
} else if (response.status === 401) {
|
||||
// Token is invalid or expired, clear it
|
||||
console.log('Token invalid, clearing tokens')
|
||||
tokenManager.clearTokens()
|
||||
setUser(null)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch user info:', error)
|
||||
// If there's an error, clear tokens to be safe
|
||||
tokenManager.clearTokens()
|
||||
setUser(null)
|
||||
}
|
||||
}
|
||||
|
||||
const login = async (email: string, password: string) => {
|
||||
setIsLoading(true)
|
||||
|
||||
try {
|
||||
const response = await fetch(`${getApiUrl()}/api-internal/v1/auth/login`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ email, password }),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json()
|
||||
throw new Error(error.detail || 'Invalid credentials')
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
// Store tokens in TokenManager
|
||||
tokenManager.setTokens(data.access_token, data.refresh_token)
|
||||
|
||||
// Fetch user info
|
||||
await fetchUserInfo()
|
||||
|
||||
// Navigate to dashboard
|
||||
router.push('/dashboard')
|
||||
|
||||
} catch (error) {
|
||||
console.error('Login error:', error)
|
||||
throw error
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const logout = () => {
|
||||
tokenManager.clearTokens()
|
||||
setUser(null)
|
||||
router.push('/login')
|
||||
}
|
||||
|
||||
return (
|
||||
<AuthContext.Provider
|
||||
value={{
|
||||
user,
|
||||
isAuthenticated: tokenManager.isAuthenticated(),
|
||||
login,
|
||||
logout,
|
||||
isLoading
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</AuthContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export function useAuth() {
|
||||
const context = useContext(AuthContext)
|
||||
if (!context) {
|
||||
throw new Error("useAuth must be used within an AuthProvider")
|
||||
}
|
||||
return context
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
* Plugin Context - Manages plugin state and UI integration
|
||||
*/
|
||||
import React, { createContext, useContext, useState, useEffect, useCallback, ReactNode } from 'react';
|
||||
import { useAuth } from './AuthContext';
|
||||
import { useAuth } from '@/components/providers/auth-provider';
|
||||
import { apiClient } from '@/lib/api-client';
|
||||
|
||||
export interface PluginInfo {
|
||||
|
||||
Reference in New Issue
Block a user