diff --git a/app/Http/Controllers/Settings/ApiKeyController.php b/app/Http/Controllers/Settings/ApiKeyController.php index bff15e0..cf66494 100644 --- a/app/Http/Controllers/Settings/ApiKeyController.php +++ b/app/Http/Controllers/Settings/ApiKeyController.php @@ -66,4 +66,32 @@ public function destroy(Request $request): RedirectResponse return redirect()->route('api-keys.edit')->with('success', 'API key deleted successfully.'); } + + /** + * Store the OpenAI API key (used by onboarding) + */ + public function store(Request $request) + { + $request->validate([ + 'api_key' => ['required', 'string', 'min:20'], + ]); + + $apiKey = $request->input('api_key'); + + // Validate the API key + if (!$this->apiKeyService->validateApiKey($apiKey)) { + return response()->json([ + 'success' => false, + 'message' => 'The provided API key is invalid. Please check and try again.', + ], 422); + } + + // Store the API key + $this->apiKeyService->setApiKey($apiKey); + + return response()->json([ + 'success' => true, + 'message' => 'API key saved successfully.', + ]); + } } diff --git a/app/Http/Middleware/CheckOnboarding.php b/app/Http/Middleware/CheckOnboarding.php new file mode 100644 index 0000000..1952f69 --- /dev/null +++ b/app/Http/Middleware/CheckOnboarding.php @@ -0,0 +1,57 @@ +apiKeyService = $apiKeyService; + } + + /** + * Handle an incoming request. + * + * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next + */ + public function handle(Request $request, Closure $next): Response + { + // Routes that should be accessible without API key + $excludedRoutes = [ + 'onboarding', + 'api-keys.edit', + 'api-keys.update', + 'api-keys.destroy', + 'api.openai.status', + 'api.openai.api-key.store', + 'appearance', + ]; + + // Skip check for excluded routes + if ($request->route() && in_array($request->route()->getName(), $excludedRoutes)) { + return $next($request); + } + + // Skip if API request (they handle their own errors) + if ($request->is('api/*')) { + return $next($request); + } + + // Check if API key exists + if (!$this->apiKeyService->hasApiKey()) { + // If not on onboarding page and no API key, redirect to onboarding + if ($request->route() && $request->route()->getName() !== 'onboarding') { + return redirect()->route('onboarding'); + } + } + + return $next($request); + } +} \ No newline at end of file diff --git a/bootstrap/app.php b/bootstrap/app.php index 57cea11..d07f444 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -1,5 +1,6 @@ encryptCookies(except: ['appearance', 'sidebar_state']); $middleware->web(append: [ + CheckOnboarding::class, HandleAppearance::class, HandleInertiaRequests::class, AddLinkHeadersForPreloadedAssets::class, diff --git a/resources/js/pages/Onboarding.vue b/resources/js/pages/Onboarding.vue new file mode 100644 index 0000000..6287612 --- /dev/null +++ b/resources/js/pages/Onboarding.vue @@ -0,0 +1,292 @@ + + + diff --git a/resources/js/pages/RealtimeAgent/Main.vue b/resources/js/pages/RealtimeAgent/Main.vue index e958c03..d7f3c0f 100644 --- a/resources/js/pages/RealtimeAgent/Main.vue +++ b/resources/js/pages/RealtimeAgent/Main.vue @@ -2657,7 +2657,7 @@ onMounted(async () => { try { const response = await axios.get('/api/openai/status'); hasApiKey.value = response.data.hasApiKey; - + if (!hasApiKey.value) { // Redirect to settings page if no API key window.location.href = '/settings/api-keys'; diff --git a/resources/js/pages/Welcome.vue b/resources/js/pages/Welcome.vue index 69cc5ce..f3e0d5d 100644 --- a/resources/js/pages/Welcome.vue +++ b/resources/js/pages/Welcome.vue @@ -1,5 +1,5 @@