diff --git a/README.md b/README.md
index 538738876..2442ffdd3 100644
--- a/README.md
+++ b/README.md
@@ -78,6 +78,47 @@
| 11 | [Cloudinary](https://cloudinary.com/) | 配置 `Cloud Name`、`API Key`、`API Secret` 参数 | [如何使用 Cloudinary?](https://cloudinary.com/documentation/upload_images) |
| 12 | 自定义上传 | 是 | [如何自定义上传?](/docs/custom-upload.md) |
+## 🔑 配置 AI API Key(环境变量)
+
+支持通过环境变量预设各服务类型的 API Key,有以下几种方式:
+
+**方式 1:本地部署**
+
+在 `apps/web/.env.local` 文件中配置(该文件会被 git 忽略,不会提交到仓库):
+
+```bash
+# .env.local
+VITE_OPENAI_KEY_openai=sk-your-openai-key
+VITE_OPENAI_KEY_deepseek=sk-your-deepseek-key
+VITE_OPENAI_KEY_qwen=sk-your-qwen-key
+```
+
+**方式 2:Docker 部署**
+
+通过构建参数传递环境变量:
+
+```sh
+# 配置单个服务的 API Key
+docker build --build-arg VITE_OPENAI_KEY_openai=sk-your-openai-key -t md:latest .
+
+# 同时配置多个服务的 API Key
+docker build \
+ --build-arg VITE_OPENAI_KEY_openai=sk-openai-key \
+ --build-arg VITE_OPENAI_KEY_deepseek=sk-deepseek-key \
+ --build-arg VITE_OPENAI_KEY_qwen=sk-qwen-key \
+ -t md:latest .
+```
+
+**支持的服务类型:** `openai`, `deepseek`, `qwen`, `moonshot`, `ernie`, `hunyuan`, `doubao`, `baichuan`, `bigmodel`, `siliconflow`, `302ai`, `lingyiwanwu`, `custom`
+
+**环境变量格式:** `VITE_OPENAI_KEY_<服务类型>`,例如 `VITE_OPENAI_KEY_openai`、`VITE_OPENAI_KEY_deepseek`
+
+**注意:**
+
+- 环境变量中的 API Key 会作为默认值使用
+- 如果用户在界面中手动配置了 API Key,会优先使用用户配置的值(保存到浏览器 localStorage)
+- `.env.local` 文件需要放在 `apps/web/` 目录下,且变量名必须以 `VITE_` 开头
+
## 🎬 产品演示
diff --git a/apps/web/.env.local.example b/apps/web/.env.local.example
new file mode 100644
index 000000000..c05c3689f
--- /dev/null
+++ b/apps/web/.env.local.example
@@ -0,0 +1,18 @@
+# 开发配置,启动编辑器
+# https://github.com/yyx990803/launch-editor?tab=readme-ov-file#supported-editors
+# VITE_LAUNCH_EDITOR=code
+
+# api key
+# VITE_OPENAI_KEY_openai=""
+# VITE_OPENAI_KEY_deepseek=""
+# VITE_OPENAI_KEY_qwen=""
+# VITE_OPENAI_KEY_moonshot=""
+# VITE_OPENAI_KEY_ernie=""
+# VITE_OPENAI_KEY_hunyuan=""
+# VITE_OPENAI_KEY_doubao=""
+# VITE_OPENAI_KEY_baichuan=""
+# VITE_OPENAI_KEY_bigmodel=""
+# VITE_OPENAI_KEY_siliconflow=""
+# VITE_OPENAI_KEY_302ai=""
+# VITE_OPENAI_KEY_lingyiwanwu=""
+# VITE_OPENAI_KEY_custom=""
diff --git a/apps/web/src/stores/aiConfig.ts b/apps/web/src/stores/aiConfig.ts
index 37ee608d5..222565424 100644
--- a/apps/web/src/stores/aiConfig.ts
+++ b/apps/web/src/stores/aiConfig.ts
@@ -33,14 +33,35 @@ export const useAIConfigStore = defineStore(`AIConfig`, () => {
// ==================== API Key 管理 ====================
+ // 从环境变量获取默认 API Key
+ // 支持多种方式:
+ // 1. 本地开发:在 .env.local 或 .env 文件中配置 VITE_OPENAI_KEY_<服务类型>
+ // 2. Docker 构建:通过 --build-arg 传递环境变量
+ // 3. 运行时环境变量:在运行环境中设置
+ const getDefaultApiKey = (serviceType: string): string => {
+ // 使用服务特定的环境变量,例如 VITE_OPENAI_KEY_openai
+ const serviceSpecificKey = import.meta.env[`VITE_OPENAI_KEY_${serviceType}`]
+ if (serviceSpecificKey)
+ return serviceSpecificKey
+
+ // 如果没有设置环境变量,使用常量默认值
+ return DEFAULT_SERVICE_KEY
+ }
+
// API Key(按服务类型分别持久化)
const apiKey = customRef((track, trigger) => {
let cachedKey = ``
+ // 加载 API Key 的函数
+ const loadApiKey = async (serviceType: string) => {
+ const storedValue = await store.get(`openai_key_${serviceType}`)
+ // 如果 localStorage 中有值,使用它;否则使用环境变量中的默认值
+ cachedKey = storedValue || getDefaultApiKey(serviceType)
+ trigger()
+ }
+
// 异步加载初始值
- store.get(`openai_key_${type.value}`).then((value) => {
- cachedKey = value || DEFAULT_SERVICE_KEY
- })
+ loadApiKey(type.value)
return {
get() {
@@ -51,6 +72,8 @@ export const useAIConfigStore = defineStore(`AIConfig`, () => {
cachedKey = val
trigger()
+ // 保存到 localStorage(用户手动设置或环境变量初始化都会保存)
+ // 这样用户可以在界面中看到环境变量提供的默认值,也可以手动修改
if (type.value !== DEFAULT_SERVICE_TYPE) {
store.set(`openai_key_${type.value}`, val)
}
@@ -75,6 +98,23 @@ export const useAIConfigStore = defineStore(`AIConfig`, () => {
// 保存当前模型
await store.set(`openai_model_${newType}`, model.value)
+
+ // 重新加载当前服务类型的 API Key
+ const storedKey = await store.get(`openai_key_${newType}`)
+ if (storedKey) {
+ // localStorage 中有值,使用它
+ apiKey.value = storedKey
+ }
+ else {
+ // localStorage 中没有值,使用环境变量中的默认值
+ const envKey = getDefaultApiKey(newType)
+ if (envKey) {
+ apiKey.value = envKey
+ }
+ else {
+ apiKey.value = DEFAULT_SERVICE_KEY
+ }
+ }
},
{ immediate: true }, // 首次加载时也执行
)
diff --git a/docker/latest/Dockerfile.base b/docker/latest/Dockerfile.base
index 9c08bd088..82a43d9c9 100644
--- a/docker/latest/Dockerfile.base
+++ b/docker/latest/Dockerfile.base
@@ -7,6 +7,40 @@ RUN curl -L "https://github.com/doocs/md/archive/refs/heads/main.zip" -o "main.z
WORKDIR /app
RUN npm install -g pnpm
ENV NODE_OPTIONS="--openssl-legacy-provider"
+
+# 支持构建时传递各服务类型的 API Key 环境变量
+# 格式:--build-arg VITE_OPENAI_KEY_<服务类型>=your-key
+# 支持的服务类型:openai, deepseek, qwen, moonshot, ernie, hunyuan, doubao, baichuan, bigmodel, siliconflow, 302ai, lingyiwanwu, custom
+# 示例:docker build --build-arg VITE_OPENAI_KEY_openai=sk-xxx -t md:latest .
+ARG VITE_OPENAI_KEY_openai=""
+ARG VITE_OPENAI_KEY_deepseek=""
+ARG VITE_OPENAI_KEY_qwen=""
+ARG VITE_OPENAI_KEY_moonshot=""
+ARG VITE_OPENAI_KEY_ernie=""
+ARG VITE_OPENAI_KEY_hunyuan=""
+ARG VITE_OPENAI_KEY_doubao=""
+ARG VITE_OPENAI_KEY_baichuan=""
+ARG VITE_OPENAI_KEY_bigmodel=""
+ARG VITE_OPENAI_KEY_siliconflow=""
+ARG VITE_OPENAI_KEY_302ai=""
+ARG VITE_OPENAI_KEY_lingyiwanwu=""
+ARG VITE_OPENAI_KEY_custom=""
+
+# 设置环境变量供构建时使用
+ENV VITE_OPENAI_KEY_openai=${VITE_OPENAI_KEY_openai}
+ENV VITE_OPENAI_KEY_deepseek=${VITE_OPENAI_KEY_deepseek}
+ENV VITE_OPENAI_KEY_qwen=${VITE_OPENAI_KEY_qwen}
+ENV VITE_OPENAI_KEY_moonshot=${VITE_OPENAI_KEY_moonshot}
+ENV VITE_OPENAI_KEY_ernie=${VITE_OPENAI_KEY_ernie}
+ENV VITE_OPENAI_KEY_hunyuan=${VITE_OPENAI_KEY_hunyuan}
+ENV VITE_OPENAI_KEY_doubao=${VITE_OPENAI_KEY_doubao}
+ENV VITE_OPENAI_KEY_baichuan=${VITE_OPENAI_KEY_baichuan}
+ENV VITE_OPENAI_KEY_bigmodel=${VITE_OPENAI_KEY_bigmodel}
+ENV VITE_OPENAI_KEY_siliconflow=${VITE_OPENAI_KEY_siliconflow}
+ENV VITE_OPENAI_KEY_302ai=${VITE_OPENAI_KEY_302ai}
+ENV VITE_OPENAI_KEY_lingyiwanwu=${VITE_OPENAI_KEY_lingyiwanwu}
+ENV VITE_OPENAI_KEY_custom=${VITE_OPENAI_KEY_custom}
+
RUN pnpm install && pnpm web build:h5-netlify
FROM scratch