anti_api / src /config /config.js
ZhaoShanGeng
fix: 优化初次生成 .env 的格式,参考 .env.example
165bd05
import dotenv from 'dotenv';
import fs from 'fs';
import crypto from 'crypto';
import log from '../utils/logger.js';
import { deepMerge } from '../utils/deepMerge.js';
import { getConfigPaths } from '../utils/paths.js';
import {
DEFAULT_SERVER_PORT,
DEFAULT_SERVER_HOST,
DEFAULT_HEARTBEAT_INTERVAL,
DEFAULT_TIMEOUT,
DEFAULT_RETRY_TIMES,
DEFAULT_MAX_REQUEST_SIZE,
DEFAULT_MAX_IMAGES,
MODEL_LIST_CACHE_TTL,
DEFAULT_GENERATION_PARAMS
} from '../constants/index.js';
// 生成随机凭据的缓存
let generatedCredentials = null;
/**
* 生成或获取管理员凭据
* 如果用户未配置,自动生成随机凭据
*/
function getAdminCredentials() {
const username = process.env.ADMIN_USERNAME;
const password = process.env.ADMIN_PASSWORD;
const jwtSecret = process.env.JWT_SECRET;
// 如果全部配置了,直接返回
if (username && password && jwtSecret) {
return { username, password, jwtSecret };
}
// 生成随机凭据(只生成一次)
if (!generatedCredentials) {
generatedCredentials = {
username: username || crypto.randomBytes(8).toString('hex'),
password: password || crypto.randomBytes(16).toString('base64').replace(/[+/=]/g, ''),
jwtSecret: jwtSecret || crypto.randomBytes(32).toString('hex')
};
// 显示生成的凭据
if (!username || !password) {
log.warn('═══════════════════════════════════════════════════════════');
log.warn('⚠️ 未配置管理员账号密码,已自动生成随机凭据:');
log.warn(` 用户名: ${generatedCredentials.username}`);
log.warn(` 密码: ${generatedCredentials.password}`);
log.warn('═══════════════════════════════════════════════════════════');
log.warn('⚠️ 重启后凭据将重新生成!建议在 .env 文件中配置:');
log.warn(' ADMIN_USERNAME=你的用户名');
log.warn(' ADMIN_PASSWORD=你的密码');
log.warn(' JWT_SECRET=你的密钥');
log.warn('═══════════════════════════════════════════════════════════');
} else if (!jwtSecret) {
log.warn('⚠️ 未配置 JWT_SECRET,已生成随机密钥(重启后登录会话将失效)');
}
}
return generatedCredentials;
}
const { envPath, configJsonPath } = getConfigPaths();
// 默认系统提示词
const DEFAULT_SYSTEM_INSTRUCTION = '你是聊天机器人,名字叫萌萌,如同名字这般,你的性格是软软糯糯萌萌哒的,专门为用户提供聊天和情绪价值,协助进行小说创作或者角色扮演';
// 确保 .env 存在(如果缺失则创建带默认配置的文件)
if (!fs.existsSync(envPath)) {
const defaultEnvContent = `# 敏感配置(只在 .env 中配置)
# 如果不配置以下三项,系统会自动生成随机凭据并在启动时显示
# API_KEY=your-api-key
# ADMIN_USERNAME=your-username
# ADMIN_PASSWORD=your-password
# JWT_SECRET=your-jwt-secret
# 可选配置
# PROXY=http://127.0.0.1:7890
SYSTEM_INSTRUCTION=${DEFAULT_SYSTEM_INSTRUCTION}
# IMAGE_BASE_URL=http://your-domain.com
`;
fs.writeFileSync(envPath, defaultEnvContent, 'utf8');
log.info('✓ 已创建 .env 文件,包含默认萌萌系统提示词');
}
// 加载 config.json
let jsonConfig = {};
if (fs.existsSync(configJsonPath)) {
jsonConfig = JSON.parse(fs.readFileSync(configJsonPath, 'utf8'));
}
// 加载 .env(指定路径)
dotenv.config({ path: envPath });
// 获取代理配置:优先使用 PROXY,其次使用系统代理环境变量
export function getProxyConfig() {
// 优先使用显式配置的 PROXY
if (process.env.PROXY) {
return process.env.PROXY;
}
// 检查系统代理环境变量(按优先级)
const systemProxy = process.env.HTTPS_PROXY ||
process.env.https_proxy ||
process.env.HTTP_PROXY ||
process.env.http_proxy ||
process.env.ALL_PROXY ||
process.env.all_proxy;
if (systemProxy) {
log.info(`使用系统代理: ${systemProxy}`);
}
return systemProxy || null;
}
/**
* 从 JSON 和环境变量构建配置对象
* @param {Object} jsonConfig - JSON 配置对象
* @returns {Object} 完整配置对象
*/
export function buildConfig(jsonConfig) {
return {
server: {
port: jsonConfig.server?.port || DEFAULT_SERVER_PORT,
host: jsonConfig.server?.host || DEFAULT_SERVER_HOST,
heartbeatInterval: jsonConfig.server?.heartbeatInterval || DEFAULT_HEARTBEAT_INTERVAL,
memoryThreshold: jsonConfig.server?.memoryThreshold || 100
},
cache: {
modelListTTL: jsonConfig.cache?.modelListTTL || MODEL_LIST_CACHE_TTL
},
rotation: {
strategy: jsonConfig.rotation?.strategy || 'round_robin',
requestCount: jsonConfig.rotation?.requestCount || 10
},
imageBaseUrl: process.env.IMAGE_BASE_URL || null,
maxImages: jsonConfig.other?.maxImages || DEFAULT_MAX_IMAGES,
api: {
url: jsonConfig.api?.url || 'https://daily-cloudcode-pa.sandbox.googleapis.com/v1internal:streamGenerateContent?alt=sse',
modelsUrl: jsonConfig.api?.modelsUrl || 'https://daily-cloudcode-pa.sandbox.googleapis.com/v1internal:fetchAvailableModels',
noStreamUrl: jsonConfig.api?.noStreamUrl || 'https://daily-cloudcode-pa.sandbox.googleapis.com/v1internal:generateContent',
host: jsonConfig.api?.host || 'daily-cloudcode-pa.sandbox.googleapis.com',
userAgent: jsonConfig.api?.userAgent || 'antigravity/1.11.3 windows/amd64'
},
defaults: {
temperature: jsonConfig.defaults?.temperature ?? DEFAULT_GENERATION_PARAMS.temperature,
top_p: jsonConfig.defaults?.topP ?? DEFAULT_GENERATION_PARAMS.top_p,
top_k: jsonConfig.defaults?.topK ?? DEFAULT_GENERATION_PARAMS.top_k,
max_tokens: jsonConfig.defaults?.maxTokens ?? DEFAULT_GENERATION_PARAMS.max_tokens,
thinking_budget: jsonConfig.defaults?.thinkingBudget ?? DEFAULT_GENERATION_PARAMS.thinking_budget
},
security: {
maxRequestSize: jsonConfig.server?.maxRequestSize || DEFAULT_MAX_REQUEST_SIZE,
apiKey: process.env.API_KEY || null
},
admin: getAdminCredentials(),
useNativeAxios: jsonConfig.other?.useNativeAxios !== false,
timeout: jsonConfig.other?.timeout || DEFAULT_TIMEOUT,
retryTimes: Number.isFinite(jsonConfig.other?.retryTimes) ? jsonConfig.other.retryTimes : DEFAULT_RETRY_TIMES,
proxy: getProxyConfig(),
systemInstruction: process.env.SYSTEM_INSTRUCTION || '',
skipProjectIdFetch: jsonConfig.other?.skipProjectIdFetch === true,
useContextSystemPrompt: jsonConfig.other?.useContextSystemPrompt === true,
passSignatureToClient: jsonConfig.other?.passSignatureToClient === true
};
}
const config = buildConfig(jsonConfig);
log.info('✓ 配置加载成功');
export default config;
export function getConfigJson() {
if (fs.existsSync(configJsonPath)) {
return JSON.parse(fs.readFileSync(configJsonPath, 'utf8'));
}
return {};
}
export function saveConfigJson(data) {
const existing = getConfigJson();
const merged = deepMerge(existing, data);
fs.writeFileSync(configJsonPath, JSON.stringify(merged, null, 2), 'utf8');
}