Skip to content

Commit 4cb6402

Browse files
committed
Fix widget API 500 error and login 405 error
- Add public-proxy utility for URL normalization and response handling - Fix widget API proxy to handle URLs without protocol and non-JSON responses - Fix login form 405 error by adding action='#' and stopPropagation - Improve error handling in public API routes
1 parent 75398cc commit 4cb6402

3 files changed

Lines changed: 76 additions & 8 deletions

File tree

frontend/app/api/public/agents/[id]/route.ts

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import { NextRequest, NextResponse } from 'next/server'
77
import { logger } from '@/lib/utils/logger'
8+
import { normalizeServiceUrl, handleResponse } from '@/lib/api/public-proxy'
89

910
const AGENT_SERVICE_URL = process.env.AGENT_SERVICE_URL || 'http://localhost:4002'
1011

@@ -28,23 +29,41 @@ export async function GET(request: NextRequest,
2829
id = resolvedParams.id
2930
const authHeader = request.headers.get('authorization')
3031

31-
// Proxy to agent service
32-
const response = await fetch(`${AGENT_SERVICE_URL}/api/public/agents/${id}`, {
32+
// Normalize service URL (ensure protocol)
33+
const serviceUrl = normalizeServiceUrl(AGENT_SERVICE_URL)
34+
const targetUrl = `${serviceUrl}/api/public/agents/${id}`
35+
36+
logger.debug('Proxying public agent request', { targetUrl, agentId: id, hasAuth: !!authHeader })
37+
38+
const response = await fetch(targetUrl, {
3339
method: 'GET',
3440
headers: {
3541
...(authHeader ? { 'Authorization': authHeader } : {}),
3642
'Content-Type': 'application/json',
3743
},
3844
})
3945

40-
const data = await response.json()
46+
// Handle response (JSON or error)
47+
let data: unknown
48+
try {
49+
data = await handleResponse(response, { agentId: id, endpoint: 'getAgent' })
50+
} catch (error) {
51+
return NextResponse.json(
52+
{ error: 'Invalid response from service', details: error instanceof Error ? error.message : String(error) },
53+
{ status: response.status || 500, headers: corsHeaders }
54+
)
55+
}
4156

4257
return NextResponse.json(data, {
4358
status: response.status,
4459
headers: corsHeaders,
4560
})
4661
} catch (error) {
47-
logger.error('Public API error proxying agent', { error, agentId: id })
62+
logger.error('Public API error proxying agent', {
63+
error: error instanceof Error ? error.message : String(error),
64+
stack: error instanceof Error ? error.stack : undefined,
65+
agentId: id
66+
})
4867
return NextResponse.json(
4968
{ error: 'Internal server error', details: error instanceof Error ? error.message : String(error) },
5069
{ status: 500, headers: corsHeaders }

frontend/components/auth/sign-in-form.tsx

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client'
22

3-
import { useState, useEffect } from 'react'
3+
import { useState, useEffect, useTransition } from 'react'
44
import { useRouter, useSearchParams } from 'next/navigation'
55
import { useForm } from 'react-hook-form'
66
import { zodResolver } from '@hookform/resolvers/zod'
@@ -22,6 +22,7 @@ import { toast } from 'sonner'
2222
export function SignInForm() {
2323
const router = useRouter()
2424
const searchParams = useSearchParams()
25+
const [isPending, startTransition] = useTransition()
2526
const [isLoading, setIsLoading] = useState(false)
2627
const [isOAuthLoading, setIsOAuthLoading] = useState<string | null>(null)
2728
const redirectTo = searchParams?.get('redirect') || '/dashboard'
@@ -77,7 +78,15 @@ export function SignInForm() {
7778
return (
7879
<div className="space-y-6">
7980
<Form {...form}>
80-
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
81+
<form
82+
action="#"
83+
onSubmit={(e) => {
84+
e.preventDefault()
85+
e.stopPropagation()
86+
form.handleSubmit(onSubmit)(e)
87+
}}
88+
className="space-y-4"
89+
>
8190
<FormField
8291
control={form.control}
8392
name="email"
@@ -116,8 +125,8 @@ export function SignInForm() {
116125
)}
117126
/>
118127

119-
<Button type="submit" className="w-full" disabled={isLoading}>
120-
{isLoading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
128+
<Button type="submit" className="w-full" disabled={isLoading || isPending}>
129+
{(isLoading || isPending) && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
121130
Sign in
122131
</Button>
123132
</form>

frontend/lib/api/public-proxy.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/**
2+
* Public API Proxy Utility
3+
*
4+
* Helper functions for proxying public API requests to backend services.
5+
* Handles URL protocol normalization and error handling.
6+
*/
7+
8+
import { logger } from '@/lib/utils/logger'
9+
10+
/**
11+
* Normalize service URL by ensuring it has a protocol
12+
*/
13+
export function normalizeServiceUrl(url: string): string {
14+
let normalized = url.replace(/\/$/, '') // Remove trailing slash
15+
if (!normalized.startsWith('http://') && !normalized.startsWith('https://')) {
16+
normalized = `https://${normalized}`
17+
}
18+
return normalized
19+
}
20+
21+
/**
22+
* Handle non-JSON responses from backend services
23+
*/
24+
export async function handleResponse(response: Response, context: { agentId?: string; endpoint?: string }) {
25+
const contentType = response.headers.get('content-type')
26+
27+
if (contentType?.includes('application/json')) {
28+
return await response.json()
29+
} else {
30+
const text = await response.text()
31+
logger.error('Non-JSON response from backend service', {
32+
status: response.status,
33+
contentType,
34+
text: text.substring(0, 200),
35+
...context,
36+
})
37+
throw new Error(`Invalid response from service: ${text.substring(0, 200)}`)
38+
}
39+
}
40+

0 commit comments

Comments
 (0)