import fs from 'fs'; import path from 'path'; import dotenv from 'dotenv'; import { fileURLToPath } from 'url'; // 配置类型 export type Config = { PORT: number; // redis REDIS_HOST?: string; REDIS_PORT?: number; REDIS_PASSWORD?: string; REDIS_VERSION?: string; REDIS_DB?: number; REDIS_USER?: string; // postgres POSTGRES_HOST?: string; POSTGRES_PORT?: number; POSTGRES_PASSWORD?: string; POSTGRES_USER?: string; POSTGRES_DB?: string; // minio MINIO_ENDPOINT?: string; MINIO_BUCKET_NAME?: string; MINIO_ACCESS_KEY?: string; MINIO_SECRET_KEY?: string; MINIO_USE_SSL?: boolean; // mongo MONGO_URI?: string; // [key: string]: any; }; export const initConfig: Config = { PORT: 3000, }; export const fileIsExist = (path: string) => { try { fs.accessSync(path, fs.constants.F_OK); return true; } catch (e) { return false; } }; export const getDirname = () => { return process.cwd(); }; /** * 配置选项 */ export type ConfigOpts = { /** * 环境配置文件路径, default: 自定设置,优先级最高, process.cwd() + envConfigFile */ envConfigFile?: string; /** * 当前工作目录, default: process.cwd() */ cwd?: string; /** * 配置文件名, default: .env */ fileName?: string; }; /** * 查找文件,3级查找。往上查询 * @param fileName 文件名 * @returns */ export const getConfigFile = (opts?: ConfigOpts) => { if (opts?.envConfigFile) { const filePath = path.join(opts.cwd || getDirname(), opts.envConfigFile); if (fileIsExist(filePath)) { return filePath; } } const fileName = opts?.fileName || '.env'; const dirname = opts?.cwd || getDirname(); // 本级 const benPath = dirname + '/' + fileName; const ben = fileIsExist(benPath); if (ben) return benPath; // 上级 const lastPath = path.join(dirname, '../' + fileName); const last = fileIsExist(lastPath); if (last) return lastPath; // 上上级 const lastLastPath = path.join(dirname, '../../' + fileName); const lastLast = fileIsExist(lastLastPath); if (lastLast) return lastLastPath; return ''; }; type GetConfigOpts = ConfigOpts & { dotenvOpts?: dotenv.DotenvConfigOptions }; /** * 初始化配置, 不会把配置内容挂载到全局 * @param opts 配置选项 * @param opts.envConfigFile 环境配置文件路径, default: 为空,process.cwd() + envConfigFile * @param opts.cwd 当前工作目录, default: process.cwd() * @param opts.fileName 配置文件名, default: .env * @param opts.dotenvOpts dotenv配置选项 * @returns 配置 */ export const getConfig = (opts?: GetConfigOpts): Config => { if (opts?.dotenvOpts) { const prased = dotenv.config(opts.dotenvOpts).parsed as Config; if (prased) { return prased; } else { throw new Error('未找到配置文件'); } } // 配置读取路径,3级判断 const filePath = getConfigFile(opts); console.log('config pathname:', filePath); if (!filePath) { throw new Error('未找到配置文件'); } const value = dotenv.config({ path: filePath }).parsed as Config; return value; }; /** * 从全局获取 * @param initConfig 在全局未找到配置时,初始化配置的内容 * @param force 是否强制重新获取配置 * @returns Config */ export const useConfig = (onlyInitConfig?: GetConfigOpts, force?: boolean): Config & T => { const config = (global as any).config; let _config = config || getConfig(onlyInitConfig); !config && ((global as any)['config'] = _config); if (force && onlyInitConfig) { const _newConfig = getConfig(onlyInitConfig); _config = mergeConfig(_newConfig); } return _config; }; /** * 更新配置 * @param configOpts 配置 * @param opts 配置选项 * @param opts.cwd 当前工作目录, default: process.cwd() * @param opts.fileName 配置文件名, default: config.json */ export const updateConfig = (configOpts: GetConfigOpts) => { const newConfig = getConfig(configOpts); return mergeConfig(newConfig); }; export const mergeConfig = (config: { [key: string]: any }) => { const _config = (global as any).config || {}; Object.assign(_config, config); (global as any).config = _config; return _config; }; export const useKey = (key: string, opts?: { defaultValue?: string; isNumber?: boolean; isBoolean?: boolean }): string | number | boolean => { let v = useConfig()[key]; if (!v) { v = process.env[key]; } if (!v) { return opts?.defaultValue || null; } if (opts?.isNumber && typeof v === 'string') { return Number(v); } if (opts?.isBoolean && typeof v === 'string') { return v === 'true'; } return v; }; /** * 写入json配置文件 * @param config 配置 * @param opts 配置选项 * @param opts.cwd 当前工作目录, default: process.cwd() * @param opts.fileName 配置文件名, default: config.json */ export const writeJsonConfig = (config = {}, opts?: Omit) => { const filePath = getConfigFile({ ...opts, fileName: opts?.fileName || 'config.json' }); if (!filePath) { throw new Error(`未找到配置文件: ${opts?.fileName || 'config.json'}`); } fs.writeFileSync(filePath, JSON.stringify(config, null, 2)); }; /** * 读取json配置文件 * @param opts 配置选项 * @param opts.cwd 当前工作目录, default: process.cwd() * @param opts.fileName 配置文件名, default: config.json * @returns 配置 */ export const readJsonConfig = (opts?: Omit) => { const filePath = getConfigFile({ ...opts, fileName: opts?.fileName || 'config.json' }); if (!filePath) { throw new Error(`未找到配置文件: ${opts?.fileName || 'config.json'}`); } try { const value = JSON.parse(fs.readFileSync(filePath, 'utf-8')); return value; } catch (e) { return {}; } }; type ResolvePathOptions = { meta?: { url: string; }; cwd?: string; }; /** * 获取相对当前路径的path * @param releactivePath * @returns */ export const resolvePath = (releactivePath: string = '', opts?: ResolvePathOptions) => { if (opts?.meta) { const __filename = fileURLToPath(opts.meta.url); const __dirname = path.dirname(__filename); return path.resolve(__dirname, releactivePath); } else if (opts?.cwd) { return path.resolve(opts.cwd, releactivePath); } else { return path.resolve(getDirname(), releactivePath); } };