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"
|
"use client"
|
||||||
|
|
||||||
import { useAuth } from "@/contexts/AuthContext"
|
import { useAuth } from "@/components/providers/auth-provider"
|
||||||
import { useState, useEffect } from "react"
|
import { useState, useEffect } from "react"
|
||||||
import { ProtectedRoute } from "@/components/auth/ProtectedRoute"
|
import { ProtectedRoute } from "@/components/auth/ProtectedRoute"
|
||||||
import { useToast } from "@/hooks/use-toast"
|
import { useToast } from "@/hooks/use-toast"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { useState } from "react"
|
import { useState } from "react"
|
||||||
import { useRouter } from "next/navigation"
|
import { useRouter } from "next/navigation"
|
||||||
import { useAuth } from "@/contexts/AuthContext"
|
import { useAuth } from "@/components/providers/auth-provider"
|
||||||
|
|
||||||
// Force dynamic rendering for authentication
|
// Force dynamic rendering for authentication
|
||||||
export const dynamic = 'force-dynamic'
|
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 toast from 'react-hot-toast'
|
||||||
import { apiClient } from '@/lib/api-client'
|
import { apiClient } from '@/lib/api-client'
|
||||||
import { config } from '@/lib/config'
|
import { config } from '@/lib/config'
|
||||||
import { useAuth } from '@/contexts/AuthContext'
|
import { useAuth } from '@/components/providers/auth-provider'
|
||||||
|
|
||||||
interface PromptTemplate {
|
interface PromptTemplate {
|
||||||
id: string
|
id: string
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { Plus, Database, Upload, Search, Trash2, FileText, AlertCircle } from "l
|
|||||||
import { CollectionManager } from "@/components/rag/collection-manager"
|
import { CollectionManager } from "@/components/rag/collection-manager"
|
||||||
import { DocumentUpload } from "@/components/rag/document-upload"
|
import { DocumentUpload } from "@/components/rag/document-upload"
|
||||||
import { DocumentBrowser } from "@/components/rag/document-browser"
|
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 { ProtectedRoute } from '@/components/auth/ProtectedRoute'
|
||||||
import { apiClient } from '@/lib/api-client'
|
import { apiClient } from '@/lib/api-client'
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { useAuth } from "@/contexts/AuthContext"
|
import { useAuth } from "@/components/providers/auth-provider"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
||||||
import { useState } from "react"
|
import { useState } from "react"
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import {
|
|||||||
AlertCircle
|
AlertCircle
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { usePlugin, type PluginInfo, type AvailablePlugin } from '../../contexts/PluginContext';
|
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';
|
import { PluginConfigurationDialog } from './PluginConfigurationDialog';
|
||||||
|
|
||||||
interface PluginCardProps {
|
interface PluginCardProps {
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ import { Alert, AlertDescription } from '@/components/ui/alert';
|
|||||||
import { Card, CardContent } from '@/components/ui/card';
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
import { Skeleton } from '@/components/ui/skeleton';
|
import { Skeleton } from '@/components/ui/skeleton';
|
||||||
import { AlertCircle, Loader2 } from 'lucide-react';
|
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 { usePlugin, type PluginInfo } from '../../contexts/PluginContext';
|
||||||
import { config } from '../../lib/config';
|
import { config } from '../../lib/config';
|
||||||
|
|
||||||
@@ -48,8 +49,8 @@ const PluginIframe: React.FC<PluginIframeProps> = ({
|
|||||||
// Validate origin - should be from our backend
|
// Validate origin - should be from our backend
|
||||||
const allowedOrigins = [
|
const allowedOrigins = [
|
||||||
window.location.origin,
|
window.location.origin,
|
||||||
config.getBackendUrl(),
|
config.API_BASE_URL,
|
||||||
config.getApiUrl()
|
config.API_BASE_URL
|
||||||
].filter(Boolean);
|
].filter(Boolean);
|
||||||
|
|
||||||
if (!allowedOrigins.some(origin => event.origin.startsWith(origin))) {
|
if (!allowedOrigins.some(origin => event.origin.startsWith(origin))) {
|
||||||
@@ -161,7 +162,8 @@ export const PluginPageRenderer: React.FC<PluginPageRendererProps> = ({
|
|||||||
pagePath,
|
pagePath,
|
||||||
componentName
|
componentName
|
||||||
}) => {
|
}) => {
|
||||||
const { user, token } = useAuth();
|
const { user } = useAuth();
|
||||||
|
const token = tokenManager.getAccessToken();
|
||||||
const {
|
const {
|
||||||
installedPlugins,
|
installedPlugins,
|
||||||
getPluginPages,
|
getPluginPages,
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ interface User {
|
|||||||
id: string
|
id: string
|
||||||
username: string
|
username: string
|
||||||
email: string
|
email: string
|
||||||
|
name?: string
|
||||||
role: string
|
role: string
|
||||||
permissions: string[]
|
permissions: string[]
|
||||||
created_at: string
|
created_at: string
|
||||||
@@ -18,6 +19,7 @@ interface User {
|
|||||||
interface AuthContextType {
|
interface AuthContextType {
|
||||||
user: User | null
|
user: User | null
|
||||||
isLoading: boolean
|
isLoading: boolean
|
||||||
|
isAuthenticated: boolean
|
||||||
login: (username: string, password: string) => Promise<void>
|
login: (username: string, password: string) => Promise<void>
|
||||||
logout: () => void
|
logout: () => void
|
||||||
register: (username: string, email: string, password: string) => Promise<void>
|
register: (username: string, email: string, password: string) => Promise<void>
|
||||||
@@ -114,6 +116,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
|||||||
const value: AuthContextType = {
|
const value: AuthContextType = {
|
||||||
user,
|
user,
|
||||||
isLoading,
|
isLoading,
|
||||||
|
isAuthenticated: !!user,
|
||||||
login,
|
login,
|
||||||
logout,
|
logout,
|
||||||
register,
|
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
|
* Plugin Context - Manages plugin state and UI integration
|
||||||
*/
|
*/
|
||||||
import React, { createContext, useContext, useState, useEffect, useCallback, ReactNode } from 'react';
|
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';
|
import { apiClient } from '@/lib/api-client';
|
||||||
|
|
||||||
export interface PluginInfo {
|
export interface PluginInfo {
|
||||||
|
|||||||
Reference in New Issue
Block a user