|
@@ -58,15 +58,23 @@ function serializeEnv(vars: Record<string, string>, original: string): string {
|
|
|
return updated.join("\n");
|
|
return updated.join("\n");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// Keys shown on the Settings page. GET reads these from the LIVE environment
|
|
|
|
|
+// (container-injected vars; or .env via next.config in dev) — NOT the .env
|
|
|
|
|
+// file, which does not exist in the container. Reading process.env is what
|
|
|
|
|
+// makes Settings reflect the actual runtime config instead of appearing empty.
|
|
|
|
|
+const SETTINGS_KEYS = [
|
|
|
|
|
+ "OPENAI_API_KEY", "OPENAI_BASE_URL",
|
|
|
|
|
+ "OPENAI_TTS_API_KEY", "OPENAI_TTS_BASE_URL", "OPENAI_TTS_MODEL",
|
|
|
|
|
+ "FISH_AUDIO_API_KEY", "MINIMAX_API_KEY", "MINIMAX_GROUP_ID", "ELEVENLABS_API_KEY",
|
|
|
|
|
+ "OSS_REGION", "OSS_BUCKET", "OSS_ACCESS_KEY_ID", "OSS_ACCESS_KEY_SECRET", "OSS_PUBLIC_DOMAIN",
|
|
|
|
|
+ "FEISHU_WEBHOOK_URL", "FEISHU_WEBHOOK_SECRET",
|
|
|
|
|
+] as const;
|
|
|
|
|
+
|
|
|
export async function GET() {
|
|
export async function GET() {
|
|
|
- if (!existsSync(ENV_PATH)) {
|
|
|
|
|
- return NextResponse.json({});
|
|
|
|
|
- }
|
|
|
|
|
- const content = readFileSync(ENV_PATH, "utf-8");
|
|
|
|
|
- const parsed = parseEnv(content);
|
|
|
|
|
const masked: Record<string, string> = {};
|
|
const masked: Record<string, string> = {};
|
|
|
- for (const [key, val] of Object.entries(parsed)) {
|
|
|
|
|
- masked[key] = maskValue(key, val);
|
|
|
|
|
|
|
+ for (const key of SETTINGS_KEYS) {
|
|
|
|
|
+ const raw = process.env[key];
|
|
|
|
|
+ masked[key] = raw === undefined ? "" : maskValue(key, raw);
|
|
|
}
|
|
}
|
|
|
return NextResponse.json(masked);
|
|
return NextResponse.json(masked);
|
|
|
}
|
|
}
|
|
@@ -89,7 +97,16 @@ export async function POST(request: Request) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const serialized = serializeEnv(filtered, original);
|
|
const serialized = serializeEnv(filtered, original);
|
|
|
- writeFileSync(ENV_PATH, serialized, "utf-8");
|
|
|
|
|
|
|
+ try {
|
|
|
|
|
+ writeFileSync(ENV_PATH, serialized, "utf-8");
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ // In a container the env is injected externally and .env is usually not
|
|
|
|
|
+ // writable (k8s runs as non-root) — surface that clearly instead of a 500.
|
|
|
|
|
+ return NextResponse.json(
|
|
|
|
|
+ { ok: false, error: `无法写入 .env(容器内环境变量通常由外部注入,不可在此修改): ${(e as Error).message}` },
|
|
|
|
|
+ { status: 403 },
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
// Re-read to return masked values
|
|
// Re-read to return masked values
|
|
|
const reparsed = parseEnv(serialized);
|
|
const reparsed = parseEnv(serialized);
|