import fs from 'node:fs' import path from 'node:path' import { z } from 'zod' const ConfigSchema = z.object({ rpcHttp: z.string().url(), rpcWs: z.string().url(), positionCopy: z.object({ enabled: z.boolean().default(false), config: z.record(z.string().min(1), z.object({ minimumDeposit: z.number().min(0), copyMount: z.record(z.string().min(1), z.number().min(0)) })), url: z.string().url() }), openPosition: z.object({ enabled: z.boolean().default(false), programId: z.string().min(32), logIncludes: z.array(z.string().min(1)).default([]) }), closePosition: z.object({ userAddress: z.string().min(32), discordWebhookUrl: z.string().url(), pageSize: z.number().min(1).max(100).default(50), lpCloseApiUrl: z.string().min(1).default('http://91.108.80.73/api/lp-index/lp-close'), lpCloseAuth: z.string().min(1).default('Basic YWRtaW46YzU4ODk5Njc=') }).optional(), blacklist: z.array(z.string().min(32)).default([]), discordWebhookUrl: z.string().url() }) export type AppConfig = z.infer; export function loadConfig(): AppConfig { const configPath = process.env.CONFIG_PATH ?? path.resolve(process.cwd(), 'config.json') if (!fs.existsSync(configPath)) { throw new Error( `找不到配置文件:${configPath}\n` + '请复制 config.example.json 为 config.json 并填写你的 rpc/ws 与 openPosition 配置' ) } const raw = fs.readFileSync(configPath, 'utf-8') const parsed = JSON.parse(raw) as unknown return ConfigSchema.parse(parsed) }