feat: finalize local OpenAI key flow and MCP docs
- add server-side .env.local OpenAI key management for the open-source app - route AI features through the preferred local key path and lazy-load embed recovery - rewrite README and docs for current MCP setup, schema, and fully-local guidance Generated with Claude Code
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
|
||||
import { NextResponse } from 'next/server';
|
||||
import { checkDatabaseHealth } from '@/services/database';
|
||||
import { hasValidOpenAiKey } from '@/services/storage/apiKeys';
|
||||
import { hasPreferredOpenAiKey } from '@/services/storage/openaiKeyServer';
|
||||
|
||||
export const runtime = 'nodejs';
|
||||
|
||||
@@ -17,7 +17,7 @@ export async function GET() {
|
||||
status: dbHealth.connected ? 'ok' : 'degraded',
|
||||
database: dbHealth.connected ? 'connected' : 'disconnected',
|
||||
vectorSearch: dbHealth.vectorExtension ? 'enabled' : 'disabled',
|
||||
aiFeatures: hasValidOpenAiKey() ? 'enabled' : 'disabled (no API key)',
|
||||
aiFeatures: hasPreferredOpenAiKey() ? 'enabled' : 'disabled (no API key)',
|
||||
timestamp: new Date().toISOString(),
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import {
|
||||
getEnvLocalPath,
|
||||
isValidOpenAiKey,
|
||||
maskOpenAiKey,
|
||||
readStoredOpenAiKey,
|
||||
writeOpenAiKeyToEnvLocal,
|
||||
} from '@/services/storage/envLocalServer';
|
||||
|
||||
export const runtime = 'nodejs';
|
||||
|
||||
function buildResponse(key: string | null) {
|
||||
const configured = isValidOpenAiKey(key);
|
||||
return {
|
||||
configured,
|
||||
maskedKey: configured ? maskOpenAiKey(key) : null,
|
||||
envPath: getEnvLocalPath(),
|
||||
};
|
||||
}
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
const storedKey = await readStoredOpenAiKey();
|
||||
return NextResponse.json(buildResponse(storedKey));
|
||||
} catch (error) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
configured: false,
|
||||
error: error instanceof Error ? error.message : 'Failed to read .env.local',
|
||||
},
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const body = await request.json();
|
||||
const key = typeof body?.key === 'string' ? body.key.trim() : '';
|
||||
|
||||
if (!isValidOpenAiKey(key)) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Invalid OpenAI API key format.' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
await writeOpenAiKeyToEnvLocal(key);
|
||||
process.env.OPENAI_API_KEY = key;
|
||||
|
||||
return NextResponse.json(buildResponse(key));
|
||||
} catch (error) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: error instanceof Error ? error.message : 'Failed to save OpenAI API key',
|
||||
},
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export async function DELETE() {
|
||||
try {
|
||||
await writeOpenAiKeyToEnvLocal(null);
|
||||
delete process.env.OPENAI_API_KEY;
|
||||
return NextResponse.json(buildResponse(null));
|
||||
} catch (error) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: error instanceof Error ? error.message : 'Failed to remove OpenAI API key',
|
||||
},
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user