"use client"; import { useState, useEffect } from "react"; interface EnvVars { OPENAI_API_KEY: string; OPENAI_BASE_URL: string; OPENAI_TTS_API_KEY: string; OPENAI_TTS_BASE_URL: string; OPENAI_TTS_MODEL: string; FISH_AUDIO_API_KEY: string; MINIMAX_API_KEY: string; MINIMAX_GROUP_ID: string; ELEVENLABS_API_KEY: string; OSS_REGION: string; OSS_BUCKET: string; OSS_ACCESS_KEY_ID: string; OSS_ACCESS_KEY_SECRET: string; OSS_PUBLIC_DOMAIN: string; FEISHU_WEBHOOK_URL: string; FEISHU_WEBHOOK_SECRET: string; } const DEFAULT_ENV: EnvVars = { OPENAI_API_KEY: "", OPENAI_BASE_URL: "https://api.openai.com/v1", OPENAI_TTS_API_KEY: "", OPENAI_TTS_BASE_URL: "https://new-api.corp.shuidi.tech/v1", OPENAI_TTS_MODEL: "seed-tts-1.1", FISH_AUDIO_API_KEY: "", MINIMAX_API_KEY: "", MINIMAX_GROUP_ID: "", ELEVENLABS_API_KEY: "", OSS_REGION: "oss-cn-hangzhou", OSS_BUCKET: "", OSS_ACCESS_KEY_ID: "", OSS_ACCESS_KEY_SECRET: "", OSS_PUBLIC_DOMAIN: "", FEISHU_WEBHOOK_URL: "", FEISHU_WEBHOOK_SECRET: "", }; export default function SettingsPage() { const [keys, setKeys] = useState(DEFAULT_ENV); const [loaded, setLoaded] = useState(false); const [saving, setSaving] = useState(false); const [saved, setSaved] = useState(false); const [sources, setSources] = useState<{ name: string; url: string }[]>([]); useEffect(() => { fetch("/api/settings") .then((r) => r.json()) .then((data) => { setKeys((prev) => ({ ...prev, ...data })); setLoaded(true); }) .catch(() => setLoaded(true)); fetch("/api/collect/sources") .then((r) => r.json()) .then((data) => setSources(data.sources ?? [])) .catch(() => {}); }, []); async function handleSave() { setSaving(true); try { await fetch("/api/settings", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(keys), }); setSaved(true); setTimeout(() => setSaved(false), 2000); } finally { setSaving(false); } } if (!loaded) { return (

Settings

Loading...
); } return (

Settings

Configure API keys and service endpoints (saved to .env)

LLM Configuration

setKeys({ ...keys, OPENAI_API_KEY: e.target.value })} placeholder="sk-..." />
setKeys({ ...keys, OPENAI_BASE_URL: e.target.value })} placeholder="https://api.openai.com/v1" />
OpenAI-compatible endpoints (DeepSeek, Qwen, etc.)

TTS Providers

OpenAI TTS (Seed TTS) — Default
setKeys({ ...keys, OPENAI_TTS_API_KEY: e.target.value })} />
setKeys({ ...keys, OPENAI_TTS_BASE_URL: e.target.value })} />
setKeys({ ...keys, OPENAI_TTS_MODEL: e.target.value })} placeholder="seed-tts-1.1" />
Fish Audio
setKeys({ ...keys, FISH_AUDIO_API_KEY: e.target.value })} />
MiniMax
setKeys({ ...keys, MINIMAX_API_KEY: e.target.value })} />
setKeys({ ...keys, MINIMAX_GROUP_ID: e.target.value })} />
ElevenLabs
setKeys({ ...keys, ELEVENLABS_API_KEY: e.target.value })} />

Publish — OSS & Feishu

Rendered videos are uploaded to OSS and results are pushed to a Feishu webhook. Also configurable in config/default.yaml.
Alibaba Cloud OSS
setKeys({ ...keys, OSS_REGION: e.target.value })} placeholder="oss-cn-hangzhou" />
setKeys({ ...keys, OSS_BUCKET: e.target.value })} />
setKeys({ ...keys, OSS_ACCESS_KEY_ID: e.target.value })} />
setKeys({ ...keys, OSS_ACCESS_KEY_SECRET: e.target.value })} />
setKeys({ ...keys, OSS_PUBLIC_DOMAIN: e.target.value })} placeholder="https://cdn.example.com" />
Base URL used to build resource links (e.g. a CDN). If empty, the bucket default domain is used.
Feishu Webhook
setKeys({ ...keys, FEISHU_WEBHOOK_URL: e.target.value })} placeholder="https://open.feishu.cn/open-apis/bot/v2/hook/..." />
setKeys({ ...keys, FEISHU_WEBHOOK_SECRET: e.target.value })} />

Data Sources

Configured in config/default.yaml
{sources.length === 0 ? (
No data sources configured.
) : (
{sources.map((s) => (
{s.name}
{s.url}
active
))}
)}
{saved && Saved! Restart services to apply.}
); }