use-config/src/env.ts
2025-05-05 15:38:28 +08:00

230 lines
6.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 = <T>(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<ConfigOpts, 'envConfigFile'>) => {
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<ConfigOpts, 'envConfigFile'>) => {
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);
}
};