-
Notifications
You must be signed in to change notification settings - Fork 0
Guardrails UI: include in config editor #61
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
464887c
da559ec
067ea27
99a4610
9cc6b14
2bbcfbd
855281a
2455ed6
9690443
b0b3572
6a8493c
4871f2a
9ee79ac
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| NEXT_PUBLIC_BACKEND_URL= | ||
|
|
||
| GUARDRAIL_TOKEN= | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,44 @@ | ||||||||||||||||||||||||||||
| import { NextRequest, NextResponse } from 'next/server'; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| const backendUrl = process.env.NEXT_PUBLIC_BACKEND_URL || 'http://localhost:8000'; | ||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Default backend port is inconsistent with sibling guardrails routes. Line 3 falls back to 🔧 Proposed fix-const backendUrl = process.env.NEXT_PUBLIC_BACKEND_URL || 'http://localhost:8000';
+const backendUrl = process.env.NEXT_PUBLIC_BACKEND_URL || 'http://localhost:8001';📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| export async function GET(request: NextRequest) { | ||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||
| // Get the API key from request headers | ||||||||||||||||||||||||||||
| const apiKey = request.headers.get('X-API-KEY'); | ||||||||||||||||||||||||||||
| if (!apiKey) { | ||||||||||||||||||||||||||||
| return NextResponse.json( | ||||||||||||||||||||||||||||
| { error: 'Missing X-API-KEY header' }, | ||||||||||||||||||||||||||||
| { status: 401 } | ||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // Forward the request to the actual backend | ||||||||||||||||||||||||||||
| const url = `${backendUrl}/api/v1/apikeys/verify`; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| const response = await fetch(url, { | ||||||||||||||||||||||||||||
| method: 'GET', | ||||||||||||||||||||||||||||
| headers: { | ||||||||||||||||||||||||||||
| 'X-API-KEY': apiKey, | ||||||||||||||||||||||||||||
| 'Content-Type': 'application/json', | ||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // Handle empty responses (204 No Content, etc.) | ||||||||||||||||||||||||||||
| const text = await response.text(); | ||||||||||||||||||||||||||||
| const data = text ? JSON.parse(text) : {}; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
|
Comment on lines
+27
to
+30
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Guard JSON parsing to avoid false 500s on non-JSON backend bodies. At Line 29, 🔧 Proposed fix- const text = await response.text();
- const data = text ? JSON.parse(text) : {};
+ const text = await response.text();
+ let data: unknown = {};
+ if (text) {
+ try {
+ data = JSON.parse(text);
+ } catch {
+ data = { message: text };
+ }
+ }📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||
| // Return the response with the same status code | ||||||||||||||||||||||||||||
| if (!response.ok) { | ||||||||||||||||||||||||||||
| return NextResponse.json(data, { status: response.status }); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| return NextResponse.json(data, { status: response.status }); | ||||||||||||||||||||||||||||
| } catch (error: any) { | ||||||||||||||||||||||||||||
| console.error('Proxy error:', error); | ||||||||||||||||||||||||||||
| return NextResponse.json( | ||||||||||||||||||||||||||||
| { error: 'Failed to forward request to backend', details: error.message }, | ||||||||||||||||||||||||||||
| { status: 500 } | ||||||||||||||||||||||||||||
|
Comment on lines
+39
to
+41
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do not expose raw internal error details to clients. Line 40 returns 🔧 Proposed fix return NextResponse.json(
- { error: 'Failed to forward request to backend', details: error.message },
+ { error: 'Failed to forward request to backend' },
{ status: 500 }
);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,157 @@ | ||
| import { NextResponse } from 'next/server'; | ||
|
|
||
| const backendUrl = process.env.NEXT_PUBLIC_BACKEND_URL || 'http://localhost:8001'; | ||
|
|
||
| export async function GET( | ||
| request: Request, | ||
| { params }: { params: Promise<{ ban_list_id: string }> } | ||
| ) { | ||
| const { ban_list_id } = await params; | ||
| const apiKey = request.headers.get('X-API-KEY'); | ||
|
|
||
| if (!apiKey) { | ||
| return NextResponse.json( | ||
| { error: 'Missing X-API-KEY header' }, | ||
| { status: 401 } | ||
| ); | ||
| } | ||
|
|
||
| try { | ||
| const url = `${backendUrl}/api/v1/guardrails/ban_lists/${ban_list_id}`; | ||
|
|
||
| console.log('[GET /api/guardrails/ban_lists/[ban_list_id]] Forwarding to:', url); | ||
|
|
||
| const response = await fetch(url, { | ||
| headers: { | ||
| 'X-API-KEY': apiKey, | ||
| 'Content-Type': 'application/json', | ||
| }, | ||
| }); | ||
|
|
||
| console.log('[GET /api/guardrails/ban_list/[ban_list_id]] Backend response status:', response.status, response.statusText); | ||
|
|
||
| // Handle empty responses (204 No Content, etc.) | ||
| const text = await response.text(); | ||
| const data = text ? JSON.parse(text) : {}; | ||
|
|
||
| console.log('[GET /api/guardrails/ban_list/[ban_list_id]] Backend response data:', JSON.stringify(data, null, 2)); | ||
|
|
||
| if (!response.ok) { | ||
| console.error('[GET /api/guardrails/ban_list/[ban_list_id]] Backend error:', response.status, data); | ||
| return NextResponse.json(data, { status: response.status }); | ||
| } | ||
|
|
||
| return NextResponse.json(data, { status: response.status }); | ||
| } catch (error: any) { | ||
| console.error('Proxy error:', error); | ||
| return NextResponse.json( | ||
| { error: 'Failed to forward request to backend', details: error.message }, | ||
| { status: 500 } | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| export async function PUT( | ||
| request: Request, | ||
| { params }: { params: Promise<{ ban_list_id: string }> } | ||
| ) { | ||
| const { ban_list_id } = await params; | ||
| const apiKey = request.headers.get('X-API-KEY'); | ||
|
|
||
| if (!apiKey) { | ||
| return NextResponse.json( | ||
| { error: 'Missing X-API-KEY header' }, | ||
| { status: 401 } | ||
| ); | ||
| } | ||
|
|
||
| try { | ||
| // Get the JSON body from the request | ||
| const body = await request.json(); | ||
|
|
||
| const url = `${backendUrl}/api/v1/guardrails/ban_lists/${ban_list_id}`; | ||
|
|
||
| console.log('[PUT /api/guardrails/ban_list/[ban_list_id]] Forwarding to:', url); | ||
| console.log('[PUT /api/guardrails/ban_list/[ban_list_id]] Body:', JSON.stringify(body, null, 2)); | ||
|
|
||
| const response = await fetch(url, { | ||
| method: 'PUT', | ||
| body: JSON.stringify(body), | ||
| headers: { | ||
| 'X-API-KEY': apiKey, | ||
| 'Content-Type': 'application/json', | ||
| }, | ||
| }); | ||
|
|
||
| console.log('[PUT /api/guardrails/ban_list/[ban_list_id]] Backend response status:', response.status, response.statusText); | ||
|
|
||
| // Handle empty responses (204 No Content, etc.) | ||
| const text = await response.text(); | ||
| const data = text ? JSON.parse(text) : { success: true }; | ||
|
|
||
| console.log('[PUT /api/guardrails/ban_list/[ban_list_id]] Backend response data:', JSON.stringify(data, null, 2)); | ||
|
|
||
| if (!response.ok) { | ||
| console.error('[PUT /api/guardrails/ban_list/[ban_list_id]] Backend error:', response.status, data); | ||
| return NextResponse.json(data, { status: response.status }); | ||
| } | ||
|
|
||
| return NextResponse.json(data, { status: response.status }); | ||
| } catch (error: any) { | ||
| console.error('Proxy error:', error); | ||
| return NextResponse.json( | ||
| { error: 'Failed to forward request to backend', details: error.message }, | ||
| { status: 500 } | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| export async function DELETE( | ||
| request: Request, | ||
| { params }: { params: Promise<{ ban_list_id: string }> } | ||
| ) { | ||
| const { ban_list_id } = await params; | ||
| const apiKey = request.headers.get('X-API-KEY'); | ||
|
|
||
| if (!apiKey) { | ||
| return NextResponse.json( | ||
| { error: 'Missing X-API-KEY header' }, | ||
| { status: 401 } | ||
| ); | ||
| } | ||
|
|
||
| try { | ||
| const url = `${backendUrl}/api/v1/guardrails/ban_lists/${ban_list_id}`; | ||
|
|
||
| console.log('[DELETE /api/guardrails/ban_lists/[ban_list_id]] Forwarding to:', url); | ||
|
|
||
| const response = await fetch(url, { | ||
| method: 'DELETE', | ||
| headers: { | ||
| 'X-API-KEY': apiKey, | ||
| 'Content-Type': 'application/json', | ||
| }, | ||
| }); | ||
|
|
||
| console.log('[DELETE /api/guardrails/ban_list/[ban_list_id]] Backend response status:', response.status, response.statusText); | ||
|
|
||
| // Handle empty responses (204 No Content, etc.) | ||
| const text = await response.text(); | ||
| const data = text ? JSON.parse(text) : { success: true }; | ||
|
|
||
| console.log('[DELETE /api/guardrails/ban_list/[ban_list_id]] Backend response data:', JSON.stringify(data, null, 2)); | ||
|
|
||
| if (!response.ok) { | ||
| console.error('[DELETE /api/guardrails/ban_list/[ban_list_id]] Backend error:', response.status, data); | ||
| return NextResponse.json(data, { status: response.status }); | ||
| } | ||
|
|
||
| return NextResponse.json(data, { status: response.status }); | ||
| } catch (error: any) { | ||
| console.error('Proxy error:', error); | ||
| return NextResponse.json( | ||
| { error: 'Failed to forward request to backend', details: error.message }, | ||
| { status: 500 } | ||
| ); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,104 @@ | ||
| import { NextRequest, NextResponse } from 'next/server'; | ||
|
|
||
| const backendUrl = process.env.NEXT_PUBLIC_BACKEND_URL || 'http://localhost:8001'; | ||
|
|
||
| export async function GET(request: NextRequest) { | ||
| try { | ||
| // Get the Kaapi API key from request headers | ||
| const apiKey = request.headers.get('X-API-KEY'); | ||
| if (!apiKey) { | ||
| return NextResponse.json( | ||
| { error: 'Missing X-API-KEY header' }, | ||
| { status: 401 } | ||
| ); | ||
| } | ||
|
|
||
| const url = `${backendUrl}/api/v1/guardrails/ban_lists`; | ||
|
|
||
| console.log('[GET /api/guardrails/ban_list] Forwarding to:', url); | ||
|
|
||
| // Forward the request to the actual backend | ||
| const response = await fetch(url, { | ||
| method: 'GET', | ||
| headers: { | ||
| 'X-API-KEY': apiKey, | ||
| 'Content-Type': 'application/json', | ||
| }, | ||
| }); | ||
|
|
||
| console.log('[GET /api/guardrails/ban_list] Backend response status:', response.status, response.statusText); | ||
|
|
||
| // Handle empty responses (204 No Content, etc.) | ||
| const text = await response.text(); | ||
| const data = text ? JSON.parse(text) : { data: [] }; | ||
|
|
||
| console.log('[GET /api/guardrails/ban_list] Backend response data:', JSON.stringify(data, null, 2)); | ||
|
|
||
| // Return the response with the same status code | ||
| if (!response.ok) { | ||
| console.error('[GET /api/guardrails/ban_list] Backend error:', response.status, data); | ||
| return NextResponse.json(data, { status: response.status }); | ||
| } | ||
|
|
||
| return NextResponse.json(data, { status: response.status }); | ||
| } catch (error: any) { | ||
| console.error('Proxy error:', error); | ||
| return NextResponse.json( | ||
| { error: 'Failed to forward request to backend', details: error.message }, | ||
| { status: 500 } | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| export async function POST(request: NextRequest) { | ||
| try { | ||
| // Get the Kaapi API key from request headers | ||
| const apiKey = request.headers.get('X-API-KEY'); | ||
| if (!apiKey) { | ||
| return NextResponse.json( | ||
| { error: 'Missing X-API-KEY header' }, | ||
| { status: 401 } | ||
| ); | ||
| } | ||
|
|
||
| // Get the JSON body from the request | ||
| const body = await request.json(); | ||
|
|
||
| const url = `${backendUrl}/api/v1/guardrails/ban_lists`; | ||
|
|
||
| console.log('[POST /api/guardrails/ban_list] Forwarding to:', url); | ||
| console.log('[POST /api/guardrails/ban_list] Body:', JSON.stringify(body, null, 2)); | ||
|
|
||
| // Forward the request to the actual backend | ||
| const response = await fetch(url, { | ||
| method: 'POST', | ||
| body: JSON.stringify(body), | ||
| headers: { | ||
| 'X-API-KEY': apiKey, | ||
| 'Content-Type': 'application/json', | ||
| }, | ||
| }); | ||
|
|
||
| console.log('[POST /api/guardrails/ban_list] Backend response status:', response.status, response.statusText); | ||
|
|
||
| // Handle empty responses (204 No Content, etc.) | ||
| const text = await response.text(); | ||
| const data = text ? JSON.parse(text) : { success: true }; | ||
|
|
||
| console.log('[POST /api/guardrails/ban_list] Backend response data:', JSON.stringify(data, null, 2)); | ||
|
|
||
| // Return the response with the same status code | ||
| if (!response.ok) { | ||
| console.error('[POST /api/guardrails/ban_list] Backend error:', response.status, data); | ||
| return NextResponse.json(data, { status: response.status }); | ||
| } | ||
|
|
||
| return NextResponse.json(data, { status: response.status }); | ||
| } catch (error: any) { | ||
| console.error('Proxy error:', error); | ||
| return NextResponse.json( | ||
| { error: 'Failed to forward request to backend', details: error.message }, | ||
| { status: 500 } | ||
| ); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add blank line at the end of the file.
As per dotenv-linter, environment files should end with a blank line.
📝 Proposed fix
GUARDRAILS_TOKEN= +🧰 Tools
🪛 dotenv-linter (4.0.0)
[warning] 3-3: [EndingBlankLine] No blank line at the end of the file
(EndingBlankLine)
🤖 Prompt for AI Agents
🧩 Analysis chain
🏁 Script executed:
Repository: ProjectTech4DevAI/kaapi-frontend
Length of output: 2381
🏁 Script executed:
cat -n .env.example | head -10Repository: ProjectTech4DevAI/kaapi-frontend
Length of output: 139
Environment variable name mismatch will cause runtime errors.
The
.env.examplefile definesGUARDRAIL_TOKEN(singular), but the code readsGUARDRAILS_TOKEN(plural). This mismatch will cause guardrails validator/config routes to fail at runtime with "Missing GUARDRAILS_TOKEN environment variable" errors.🔧 Proposed fix
📝 Committable suggestion
🧰 Tools
🪛 dotenv-linter (4.0.0)
[warning] 3-3: [EndingBlankLine] No blank line at the end of the file
(EndingBlankLine)
🤖 Prompt for AI Agents