feat: add save feedback prompts, featured agent fields, dynamic Dify API config, and chat improvements

- Add success/error feedback messages near submit buttons in all admin forms
- Display success prompt for 1s before redirect after save
- Show API error details on save failure
- Add isFeatured/featuredOrder fields to Agent model and admin UI
- Add difyApiUrl/difyApiKey fields for per-agent Dify API configuration
- Show featured badge column in agent admin list
- Display featured agents on homepage sorted by order
- Refactor chat page streaming with AbortController and stable userId
- Improve Dify API proxy to use per-agent credentials
This commit is contained in:
root
2026-05-08 20:15:54 +08:00
parent 362c37fb42
commit f2d7037ca2
16 changed files with 298 additions and 102 deletions
+4
View File
@@ -17,10 +17,14 @@ export async function PUT(
description: data.description,
icon: data.icon || null,
categoryId: data.categoryId ? parseInt(data.categoryId) : null,
difyApiUrl: data.difyApiUrl || null,
difyApiKey: data.difyApiKey || null,
features: data.features || "",
hotQuestions: data.hotQuestions || "[]",
quickQuestions: data.quickQuestions || "[]",
status: data.status || "active",
isFeatured: data.isFeatured ?? false,
featuredOrder: data.featuredOrder ?? 0,
},
})
return NextResponse.json(agent)
+4
View File
@@ -12,10 +12,14 @@ export async function POST(request: NextRequest) {
description: data.description,
icon: data.icon || null,
categoryId: data.categoryId ? parseInt(data.categoryId) : null,
difyApiUrl: data.difyApiUrl || null,
difyApiKey: data.difyApiKey || null,
features: data.features || "",
hotQuestions: data.hotQuestions || "[]",
quickQuestions: data.quickQuestions || "[]",
status: data.status || "active",
isFeatured: data.isFeatured ?? false,
featuredOrder: data.featuredOrder ?? 0,
},
})
return NextResponse.json(agent)
+37 -19
View File
@@ -1,33 +1,51 @@
import { NextResponse } from "next/server"
import { prisma } from "@/app/lib/prisma"
const API_KEY = 'app-lbe2lglt7taGtZk0dG7pAhbx'
const API_URL = 'http://df.clkeji.com/v1/chat-messages'
const FALLBACK_API_KEY = 'app-lbe2lglt7taGtZk0dG7pAhbx'
const FALLBACK_API_URL = 'http://df.clkeji.com/v1/chat-messages'
export async function POST(request: Request) {
try {
const body = await request.json()
const { agentId, ...difyBody } = body
const response = await fetch(API_URL, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
})
let apiKey = FALLBACK_API_KEY
let apiUrl = FALLBACK_API_URL
if (body.response_mode === 'streaming') {
return new NextResponse(response.body, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
},
if (agentId) {
const agent = await prisma.agent.findUnique({
where: { id: parseInt(agentId) },
})
if (agent?.difyApiUrl && agent?.difyApiKey) {
apiUrl = agent.difyApiUrl.replace(/\/+$/, '') + '/chat-messages'
apiKey = agent.difyApiKey
}
}
const data = await response.json()
return NextResponse.json(data)
const response = await fetch(apiUrl, {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ ...difyBody, response_mode: 'streaming' }),
})
if (!response.ok) {
const text = await response.text()
console.error('Dify API error:', response.status, text.slice(0, 500))
return NextResponse.json(
{ error: 'Dify request failed' },
{ status: 502 }
)
}
const headers = new Headers()
headers.set('Content-Type', 'text/event-stream')
headers.set('Cache-Control', 'no-cache')
headers.set('Connection', 'keep-alive')
return new Response(response.body, { headers })
} catch (error) {
console.error('Chat API error:', error)
return NextResponse.json(