feat: 添加 .env.example 配置示例 & 全环境变量支持

- 新增 .env.example 配置模板,含使用教程和所有供应商参考
- 所有 API 路由支持环境变量 fallback (API_KEY/GEMINI_API_KEY/CLAUDE_API_KEY/OPENAI_API_KEY 等)
- 支持自定义端口 (PORT 环境变量)
- Electron 启动时自动加载 .env.local
- .gitignore 排除 .env.example 以确保模板被追踪
This commit is contained in:
YuanShiJiLoong committed 2026-03-03 15:42:19 +09:00
1 parent 5dde77474b
commit aa6b74d450
7 files changed
+147 -12

No files matched your search

+110
View File
@@ -0,0 +1,110 @@
# ============================================
# Author App — 环境变量配置
# ============================================
#
# 📖 使用教程:
#
# 1. 复制本文件并重命名:
# 将 .env.example 复制一份,命名为 .env.local
# (Windows: 复制粘贴后重命名; 命令行: copy .env.example .env.local)
#
# 2. 编辑 .env.local:
# 取消你需要的配置项的注释(删掉行首的 #),填入你自己的值
# 例如想用智谱AI,只需修改 API_KEY 那一行
#
# 3. 启动应用:
# 配置会自动生效,无需其他操作
#
# ⚠️ 注意事项:
# - 不要直接编辑 .env.example,它只是模板
# - .env.local 已被 .gitignore 忽略,不会泄露你的 Key
# - 如果同时在应用内 ⚙️ 设置了配置,应用内设置优先
# - 只需配置你使用的那一个供应商,不需要全部填写
#
# ============================================
# ==================== 服务端口 ====================
# 自定义服务端口(默认 3000,被占用时自动递增查找)
# PORT=3000
# ==================== OpenAI 兼容 API ====================
# 适用于: 智谱AI / DeepSeek / SiliconFlow / 火山引擎 / Moonshot 等
# 所有兼容 OpenAI /v1/chat/completions 格式的供应商均可使用
# API_KEY=你的Key
# API_BASE_URL=https://open.bigmodel.cn/api/paas/v4
# API_MODEL=glm-4-flash
# 各供应商 Base URL 参考:
# 智谱AI: https://open.bigmodel.cn/api/paas/v4
# DeepSeek: https://api.deepseek.com/v1
# OpenAI: https://api.openai.com/v1
# SiliconFlow: https://api.siliconflow.cn/v1
# 火山引擎: https://ark.cn-beijing.volces.com/api/v3
# Moonshot: https://api.moonshot.cn/v1
# 各供应商推荐模型:
# 智谱AI: glm-4-flash (免费) / glm-4-plus / glm-4-long / glm-4
# DeepSeek: deepseek-chat / deepseek-reasoner
# SiliconFlow: deepseek-ai/DeepSeek-V3 / Qwen/Qwen2.5-72B-Instruct
# Moonshot: moonshot-v1-8k / moonshot-v1-32k / moonshot-v1-128k
# 兼容旧版变量名(优先使用 API_KEY)
# ZHIPU_API_KEY=
# ==================== Google Gemini ====================
# 官网: https://aistudio.google.com
# GEMINI_API_KEY=你的Key
# GEMINI_BASE_URL=https://generativelanguage.googleapis.com/v1beta
# GEMINI_MODEL=gemini-2.0-flash
# 可选模型: gemini-2.0-flash-lite / gemini-1.5-flash / gemini-1.5-pro
# OpenAI 兼容模式也可用,将 Base URL 改为:
# https://generativelanguage.googleapis.com/v1beta/openai
# 然后使用上方 API_KEY/API_BASE_URL/API_MODEL 配置即可
# ==================== Claude (Anthropic) ====================
# 官网: https://console.anthropic.com
# CLAUDE_API_KEY=你的Key
# CLAUDE_BASE_URL=https://api.anthropic.com
# CLAUDE_MODEL=claude-sonnet-4-20250514
# 可选模型: claude-3-7-sonnet-20250219 / claude-3-5-haiku-20241022
# ==================== OpenAI Responses API ====================
# 适用于 OpenAI /v1/responses 格式
# OPENAI_API_KEY=你的Key
# OPENAI_BASE_URL=https://api.openai.com/v1
# OPENAI_MODEL=gpt-4o-mini
# 可选模型: gpt-4o / gpt-4-turbo / gpt-3.5-turbo
# ==================== 快速配置示例 ====================
#
# 🔹 示例1: 用智谱AI免费模型(最简配置,只需一行)
# API_KEY=你的智谱Key
#
# 🔹 示例2: 用DeepSeek
# API_KEY=你的DeepSeek_Key
# API_BASE_URL=https://api.deepseek.com/v1
# API_MODEL=deepseek-chat
#
# 🔹 示例3: 用Gemini
# GEMINI_API_KEY=你的Gemini_Key
#
# 🔹 示例4: 用Claude
# CLAUDE_API_KEY=你的Claude_Key
#
# 🔹 示例5: 自定义端口 + 智谱AI
# PORT=8080
# API_KEY=你的智谱Key
BIN
View File
Binary file not shown.
+3 -3
View File
@@ -7,9 +7,9 @@ export async function POST(request) {
try {
const { systemPrompt, userPrompt, apiConfig, maxTokens, temperature, topP, reasoningEffort } = await request.json();
const apiKey = apiConfig?.apiKey;
const baseUrl = (apiConfig?.baseUrl || 'https://api.anthropic.com').replace(/\/$/, '');
const model = apiConfig?.model || 'claude-sonnet-4-20250514';
const apiKey = apiConfig?.apiKey || process.env.CLAUDE_API_KEY;
const baseUrl = (apiConfig?.baseUrl || process.env.CLAUDE_BASE_URL || 'https://api.anthropic.com').replace(/\/$/, '');
const model = apiConfig?.model || process.env.CLAUDE_MODEL || 'claude-sonnet-4-20250514';
if (!apiKey) {
return new Response(
+2 -2
View File
@@ -10,10 +10,10 @@ export async function POST(request) {
const apiKey = apiConfig?.apiKey || process.env.GEMINI_API_KEY;
let rawBaseUrl = apiConfig?.baseUrl;
if (!rawBaseUrl || rawBaseUrl.includes('open.bigmodel.cn')) {
rawBaseUrl = 'https://generativelanguage.googleapis.com/v1beta';
rawBaseUrl = process.env.GEMINI_BASE_URL || 'https://generativelanguage.googleapis.com/v1beta';
}
const baseUrl = rawBaseUrl.replace(/\/$/, '');
const model = apiConfig?.model || 'gemini-2.0-flash';
const model = apiConfig?.model || process.env.GEMINI_MODEL || 'gemini-2.0-flash';
if (!apiKey) {
return new Response(
+3 -3
View File
@@ -7,9 +7,9 @@ export async function POST(request) {
try {
const { systemPrompt, userPrompt, apiConfig, maxTokens, temperature, topP } = await request.json();
const apiKey = apiConfig?.apiKey;
const baseUrl = (apiConfig?.baseUrl || 'https://api.openai.com/v1').replace(/\/$/, '');
const model = apiConfig?.model || 'gpt-4o-mini';
const apiKey = apiConfig?.apiKey || process.env.OPENAI_API_KEY;
const baseUrl = (apiConfig?.baseUrl || process.env.OPENAI_BASE_URL || 'https://api.openai.com/v1').replace(/\/$/, '');
const model = apiConfig?.model || process.env.OPENAI_MODEL || 'gpt-4o-mini';
if (!apiKey) {
return new Response(
+3 -3
View File
@@ -7,9 +7,9 @@ export async function POST(request) {
try {
const { systemPrompt, userPrompt, apiConfig, maxTokens, temperature, topP, reasoningEffort } = await request.json();
const apiKey = apiConfig?.apiKey || process.env.ZHIPU_API_KEY;
const baseUrl = apiConfig?.baseUrl || 'https://open.bigmodel.cn/api/paas/v4';
const model = apiConfig?.model || 'glm-4-flash';
const apiKey = apiConfig?.apiKey || process.env.API_KEY || process.env.ZHIPU_API_KEY;
const baseUrl = apiConfig?.baseUrl || process.env.API_BASE_URL || 'https://open.bigmodel.cn/api/paas/v4';
const model = apiConfig?.model || process.env.API_MODEL || 'glm-4-flash';
if (!apiKey) {
return new Response(
+26 -1
View File
@@ -7,6 +7,31 @@ const net = require('net');
const fs = require('fs');
const os = require('os');
// 加载 .env.local(轻量实现,无需 dotenv 依赖)
(function loadEnvFile() {
const envPaths = [
path.join(__dirname, '..', '.env.local'),
path.join(__dirname, '..', '.env'),
];
for (const envPath of envPaths) {
if (fs.existsSync(envPath)) {
const lines = fs.readFileSync(envPath, 'utf8').split('\n');
for (const line of lines) {
const trimmed = line.trim();
if (!trimmed || trimmed.startsWith('#')) continue;
const eqIdx = trimmed.indexOf('=');
if (eqIdx === -1) continue;
const key = trimmed.slice(0, eqIdx).trim();
const value = trimmed.slice(eqIdx + 1).trim();
if (key && !process.env[key]) {
process.env[key] = value;
}
}
break; // 只加载第一个找到的文件
}
}
})();
// 日志文件 - 写到用户桌面方便查看
const logFile = path.join(app.getPath('desktop'), 'author-debug.log');
function log(msg) {
@@ -27,7 +52,7 @@ let mainWindow;
let serverProcess;
const isDev = process.argv.includes('--dev');
const BASE_PORT = 3000;
const BASE_PORT = parseInt(process.env.PORT, 10) || 3000;
let actualPort = BASE_PORT;
let loadRetries = 0;
const MAX_LOAD_RETRIES = 10;