All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m6s
变更: - scheduler 加判断: MOCK_REVENUE_API=false 且 URL 仍指 /mock 时跳过 revenue cron, 避免对未挂载的端点 404 写错误日志 - config.ts 新增 envBool() preprocess: 替代 z.coerce.boolean(), 正确把 "false"/"0"/"no" 识别为 false (zod 默认所有非空串都是 true) - 影响:AI_ENABLED 和 MOCK_REVENUE_API 两个 boolean env 现在按字面值生效 副作用: - 数据库 1997 条 mock revenue + 5 条 unmapped 已清空 (mysql DELETE 手动执行) - 项目级 ROI 现在显示真实状态: 成本来自 commit 估算, 产出 ¥0 (待业务方接入) - 真实营收 API 就绪后只需改 REVENUE_API_BASE_URL 即可恢复 cron Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
60 lines
2.1 KiB
TypeScript
60 lines
2.1 KiB
TypeScript
import { z } from 'zod';
|
|
|
|
// z.coerce.boolean() 把任何非空字符串当 true(包括"false"),用 preprocess 修正
|
|
const envBool = (defaultValue: boolean) =>
|
|
z.preprocess((v) => {
|
|
if (typeof v !== 'string') return v;
|
|
return ['true', '1', 'yes', 'on'].includes(v.toLowerCase());
|
|
}, z.boolean()).default(defaultValue);
|
|
|
|
const envSchema = z.object({
|
|
JWT_SECRET: z.string().min(16, 'JWT_SECRET must be at least 16 characters'),
|
|
PORT: z.coerce.number().default(3200),
|
|
|
|
MYSQL_HOST: z.string().default('localhost'),
|
|
MYSQL_PORT: z.coerce.number().default(3306),
|
|
MYSQL_USER: z.string().default('root'),
|
|
MYSQL_PASSWORD: z.string().default(''),
|
|
MYSQL_DATABASE: z.string().default('devperf'),
|
|
|
|
PLANE_BASE_URL: z.string().url().default('http://plane-api:8000'),
|
|
PLANE_API_TOKEN: z.string().default(''),
|
|
PLANE_WORKSPACE_SLUG: z.string().default('jasonqiyuan'),
|
|
|
|
GITEA_BASE_URL: z.string().url().default('http://gitea:3000'),
|
|
GITEA_API_TOKEN: z.string().default(''),
|
|
GITEA_ORG: z.string().default('jasonqiyuan'),
|
|
|
|
SYNC_PLANE_INTERVAL: z.coerce.number().default(15),
|
|
SYNC_GITEA_INTERVAL: z.coerce.number().default(30),
|
|
|
|
ADMIN_EMAIL: z.string().email().default('admin@jasonqiyuan.com'),
|
|
ADMIN_PASSWORD: z.string().min(6).default('Admin123!'),
|
|
|
|
// AI (豆包 Doubao / 火山引擎 Ark)
|
|
AI_ENABLED: envBool(false),
|
|
AI_API_KEY: z.string().default(''),
|
|
AI_MODEL: z.string().default('doubao-seed-2-0-pro-260215'),
|
|
AI_BASE_URL: z.string().default('https://ark.cn-beijing.volces.com/api/v3'),
|
|
|
|
// ROI 外部营收 API
|
|
MOCK_REVENUE_API: envBool(false),
|
|
REVENUE_API_BASE_URL: z.string().default('http://localhost:3200/mock'),
|
|
REVENUE_API_KEY: z.string().default('mock-dev-key-12345'),
|
|
});
|
|
|
|
function loadConfig() {
|
|
const result = envSchema.safeParse(process.env);
|
|
if (!result.success) {
|
|
const missing = result.error.issues.map(
|
|
(i) => ` ${i.path.join('.')}: ${i.message}`
|
|
);
|
|
console.error('Environment validation failed:\n' + missing.join('\n'));
|
|
process.exit(1);
|
|
}
|
|
return result.data;
|
|
}
|
|
|
|
export const config = loadConfig();
|
|
export type Config = z.infer<typeof envSchema>;
|