Features: - Image generation (OpenAI, Gemini, Leonardo, Bria, Stability, Flux) - Nano Banana iterative editing - Video generation and upscaling - Audio TTS, STT, sound effects (ElevenLabs) - Text prompt studio and alt text - User authentication with JWT/cookies - Admin panel with voice management - Job queue with Celery - PostgreSQL + Redis backend - Next.js 15 + FastAPI architecture 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
77 lines
2.3 KiB
TypeScript
77 lines
2.3 KiB
TypeScript
'use client';
|
|
|
|
import { useEffect, useState } from 'react';
|
|
import { useRouter, usePathname } from 'next/navigation';
|
|
import { useStore } from '@/lib/store';
|
|
import { authApi } from '@/lib/api';
|
|
import { Loader2 } from 'lucide-react';
|
|
|
|
// Pages that don't require authentication
|
|
const PUBLIC_PAGES = ['/login', '/signup'];
|
|
|
|
export default function AuthProvider({ children }: { children: React.ReactNode }) {
|
|
const router = useRouter();
|
|
const pathname = usePathname();
|
|
const { user, token, setUser, setToken, logout } = useStore();
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
useEffect(() => {
|
|
const initAuth = async () => {
|
|
// If on a public page, no need to check auth
|
|
if (PUBLIC_PAGES.includes(pathname)) {
|
|
setLoading(false);
|
|
return;
|
|
}
|
|
|
|
// Try to verify auth with the backend (uses cookie automatically)
|
|
try {
|
|
const response = await authApi.me();
|
|
if (response.data) {
|
|
const userData = {
|
|
id: response.data.id,
|
|
email: response.data.email,
|
|
name: response.data.display_name || response.data.email,
|
|
role: response.data.role,
|
|
avatar_url: response.data.avatar_url,
|
|
};
|
|
setUser(userData);
|
|
// Also set a dummy token for compatibility (actual auth is via cookie)
|
|
setToken('cookie-auth');
|
|
}
|
|
} catch (error) {
|
|
// Not authenticated, clear state and redirect
|
|
console.log('Not authenticated, redirecting to login');
|
|
logout();
|
|
router.push('/login');
|
|
}
|
|
|
|
setLoading(false);
|
|
};
|
|
|
|
initAuth();
|
|
}, [pathname]);
|
|
|
|
// Show loading spinner while checking auth
|
|
if (loading && !PUBLIC_PAGES.includes(pathname)) {
|
|
return (
|
|
<div className="min-h-screen bg-forge-gray flex items-center justify-center">
|
|
<div className="text-center">
|
|
<Loader2 className="w-8 h-8 text-forge-yellow animate-spin mx-auto mb-4" />
|
|
<p className="text-gray-500">Loading...</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// On public pages, just render children
|
|
if (PUBLIC_PAGES.includes(pathname)) {
|
|
return <>{children}</>;
|
|
}
|
|
|
|
// On protected pages, only render if we have a user
|
|
if (!user && !loading) {
|
|
return null;
|
|
}
|
|
|
|
return <>{children}</>;
|
|
}
|