214 lines
5.9 KiB
TypeScript
214 lines
5.9 KiB
TypeScript
import fs from 'fs';
|
||
import path from 'path';
|
||
import dotenv from 'dotenv';
|
||
// 配置类型
|
||
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 {};
|
||
}
|
||
};
|
||
/**
|
||
* 获取相对当前路径的path
|
||
* @param releactivePath
|
||
* @returns
|
||
*/
|
||
export const resolvePath = (releactivePath: string = '') => {
|
||
const __dirname = import.meta.url ? path.dirname(import.meta.url) : '';
|
||
return path.resolve(__dirname, releactivePath);
|
||
};
|