fix api config auth and image model compatibility
Some checks failed
Build & Push Docker Image / build-and-push (push) Has been cancelled

This commit is contained in:
2026-04-17 18:41:47 +08:00
parent 531de26d48
commit 022d581c60
9 changed files with 244 additions and 8 deletions

View File

@@ -3,6 +3,7 @@ import { prisma } from '@/lib/prisma'
import { requireUserAuth, isErrorResponse } from '@/lib/api-auth'
import { ApiError, apiHandler } from '@/lib/api-errors'
import { isArtStyleValue } from '@/lib/constants'
import { assertPersistedUser } from '@/lib/persisted-user'
function validateArtStyleField(value: unknown): string {
if (typeof value !== 'string') {
@@ -29,6 +30,7 @@ export const GET = apiHandler(async () => {
const authResult = await requireUserAuth()
if (isErrorResponse(authResult)) return authResult
const { session } = authResult
await assertPersistedUser(session.user.id)
// 获取或创建用户偏好
const preference = await prisma.userPreference.upsert({
@@ -46,6 +48,7 @@ export const PATCH = apiHandler(async (request: NextRequest) => {
const authResult = await requireUserAuth()
if (isErrorResponse(authResult)) return authResult
const { session } = authResult
await assertPersistedUser(session.user.id)
const body = await request.json()

View File

@@ -10,6 +10,7 @@ import { prisma } from '@/lib/prisma'
import { encryptApiKey, decryptApiKey } from '@/lib/crypto-utils'
import { requireUserAuth, isErrorResponse } from '@/lib/api-auth'
import { apiHandler, ApiError } from '@/lib/api-errors'
import { assertPersistedUser } from '@/lib/persisted-user'
import {
composeModelKey,
parseModelKeyStrict,
@@ -1654,6 +1655,7 @@ export const GET = apiHandler(async () => {
if (isErrorResponse(authResult)) return authResult
const { session } = authResult
const userId = session.user.id
await assertPersistedUser(userId)
const pref = await prisma.userPreference.findUnique({
where: { userId },
@@ -1766,6 +1768,7 @@ export const PUT = apiHandler(async (request: NextRequest) => {
if (isErrorResponse(authResult)) return authResult
const { session } = authResult
const userId = session.user.id
await assertPersistedUser(userId)
let body: ApiConfigPutBody
try {

View File

@@ -23,6 +23,18 @@ import { generateSiliconFlowAudio, generateSiliconFlowImage, generateSiliconFlow
const OFFICIAL_ONLY_PROVIDER_KEYS = new Set(['bailian', 'siliconflow'])
const OPENAI_COMPAT_IMAGE_MODEL_ID_ALIASES: Record<string, string> = {
'gemini-3-pro-image': 'gemini-3-pro-image-preview',
'gemini-3.1-flash-image': 'gemini-3.1-flash-image-preview',
'gemini-2.5-flash-image': 'gemini-2.5-flash-image-preview',
}
function normalizeOpenAICompatImageModelId(modelId: string): string {
const normalized = modelId.trim()
if (!normalized) return normalized
return OPENAI_COMPAT_IMAGE_MODEL_ID_ALIASES[normalized] || normalized
}
/**
* 将 aspectRatio 映射为 OpenAI 兼容的 size
*/
@@ -105,6 +117,7 @@ export async function generateImage(
const { referenceImages, ...generatorOptions } = options || {}
if (gatewayRoute === 'openai-compat') {
const compatTemplate = selection.compatMediaTemplate
const normalizedCompatModelId = normalizeOpenAICompatImageModelId(selection.modelId)
if (providerKey === 'openai-compatible' && !compatTemplate) {
throw new Error(`MODEL_COMPAT_MEDIA_TEMPLATE_REQUIRED: ${selection.modelKey}`)
}
@@ -112,7 +125,7 @@ export async function generateImage(
return await generateImageViaOpenAICompatTemplate({
userId,
providerId: selection.provider,
modelId: selection.modelId,
modelId: normalizedCompatModelId,
modelKey: selection.modelKey,
prompt,
referenceImages,
@@ -141,7 +154,7 @@ export async function generateImage(
return await generateImageViaOpenAICompat({
userId,
providerId: selection.provider,
modelId: selection.modelId,
modelId: normalizedCompatModelId,
prompt,
referenceImages,
options: {

31
src/lib/persisted-user.ts Normal file
View File

@@ -0,0 +1,31 @@
import { ApiError } from '@/lib/api-errors'
import { prisma } from '@/lib/prisma'
import { withPrismaRetry } from '@/lib/prisma-retry'
const SESSION_USER_MISSING_MESSAGE = '登录状态已失效,请重新登录'
export async function assertPersistedUser(userId: string): Promise<void> {
const normalizedUserId = typeof userId === 'string' ? userId.trim() : ''
if (!normalizedUserId) {
throw new ApiError('UNAUTHORIZED', {
code: 'SESSION_USER_MISSING',
field: 'userId',
message: SESSION_USER_MISSING_MESSAGE,
})
}
const user = await withPrismaRetry(() =>
prisma.user.findUnique({
where: { id: normalizedUserId },
select: { id: true },
}),
)
if (user) return
throw new ApiError('UNAUTHORIZED', {
code: 'SESSION_USER_MISSING',
field: 'userId',
message: SESSION_USER_MISSING_MESSAGE,
})
}