@svton/dynamic-config
v0.3.0
Published
Dynamic configuration management system with multi-layer caching
Maintainers
Readme
@svton/dynamic-config
动态配置管理系统,支持多层缓存、热更新、NestJS 集成和 React 组件。
特性
- 🚀 动态配置 - 运行时修改配置,无需重启服务
- 💾 多层缓存 - Redis 优先,内存兜底的分层缓存策略
- 🔧 类型安全 - 完整的 TypeScript 类型支持
- 📦 模块化 - 核心逻辑与框架集成分离
- 🎨 React 组件 - 开箱即用的配置管理 UI 组件
- 🔌 可扩展 - 支持自定义 Repository 和缓存策略
安装
pnpm add @svton/dynamic-config子模块
| 模块 | 说明 | 导入路径 |
|------|------|----------|
| core | 核心逻辑(框架无关) | @svton/dynamic-config/core |
| nestjs | NestJS 集成 | @svton/dynamic-config/nestjs |
| prisma | Prisma 适配器 | @svton/dynamic-config/prisma |
| react | React 组件和 Hooks | @svton/dynamic-config/react |
快速开始
1. 添加 Prisma Schema
将 src/prisma/schema.prisma.template 中的模型添加到你的 schema.prisma 文件。
2. NestJS 集成
// app.module.ts
import { Module } from '@nestjs/common';
import { DynamicConfigModule } from '@svton/dynamic-config/nestjs';
import { PrismaConfigRepository, PrismaDictionaryRepository } from '@svton/dynamic-config/prisma';
import { TieredCache, RedisCache, MemoryCache } from '@svton/dynamic-config/core';
@Module({
imports: [
DynamicConfigModule.forRootAsync({
imports: [PrismaModule, RedisModule],
useFactory: (prisma, redis) => ({
configRepository: new PrismaConfigRepository(prisma),
dictionaryRepository: new PrismaDictionaryRepository(prisma),
cache: new TieredCache(
new RedisCache(redis, { prefix: 'config:' }),
new MemoryCache({ prefix: 'config:' }),
),
}),
inject: [PrismaService, RedisService],
}),
],
})
export class AppModule {}3. 使用配置服务
import { Injectable } from '@nestjs/common';
import { DynamicConfigService } from '@svton/dynamic-config/nestjs';
@Injectable()
export class UploadService {
constructor(private configService: DynamicConfigService) {}
async upload(file: File) {
const storageType = await this.configService.getString('storage.type', 'local');
const maxSize = await this.configService.getNumber('upload.maxSize', 10485760);
// ...
}
}4. 自定义 Controller(添加权限控制)
import { Controller, UseGuards, Delete, Param } from '@nestjs/common';
import { BaseConfigController, DynamicConfigService } from '@svton/dynamic-config/nestjs';
import { JwtAuthGuard } from './auth/guards/jwt-auth.guard';
import { RolesGuard } from './auth/guards/roles.guard';
import { Roles } from './auth/decorators/roles.decorator';
import { Public } from './auth/decorators/public.decorator';
@Controller('config')
@UseGuards(JwtAuthGuard, RolesGuard)
export class ConfigController extends BaseConfigController {
constructor(configService: DynamicConfigService) {
super(configService);
}
@Public()
@Get('public')
async getPublicConfigs() {
return super.getPublicConfigs();
}
@Roles('admin')
@Delete(':key')
async delete(@Param('key') key: string) {
return super.delete(key);
}
}5. React 组件
import {
DynamicConfigProvider,
useConfigCategory,
useConfigMutation,
ConfigForm,
initConfigFormValues,
} from '@svton/dynamic-config/react';
// 1. 实现 API 客户端
const configApi = {
getPublicConfigs: () => fetch('/api/config/public').then(r => r.json()),
getByCategory: (category) => fetch(`/api/config/category/${category}`).then(r => r.json()),
batchUpdate: (configs) => fetch('/api/config/batch', {
method: 'PUT',
body: JSON.stringify({ configs }),
}),
// ... 其他方法
};
// 2. 包装 Provider
function App() {
return (
<DynamicConfigProvider configApi={configApi} dictionaryApi={dictionaryApi}>
<ConfigPage />
</DynamicConfigProvider>
);
}
// 3. 使用 Hooks 和组件
function ConfigPage() {
const { configs, loading, refetch } = useConfigCategory('storage');
const { batchUpdate, loading: saving } = useConfigMutation();
const [values, setValues] = useState({});
useEffect(() => {
if (configs.length > 0) {
setValues(initConfigFormValues(configs));
}
}, [configs]);
const handleSave = async () => {
const updates = Object.entries(values).map(([key, value]) => ({ key, value }));
await batchUpdate(updates);
refetch();
};
return (
<ConfigForm
configs={configs}
values={values}
onChange={(key, value) => setValues(prev => ({ ...prev, [key]: value }))}
onSave={handleSave}
saving={saving}
components={uiComponents} // 传入 shadcn/ui 组件
/>
);
}API 文档
Core
ConfigManager
const manager = new ConfigManager({
repository: configRepository,
cache: cacheStrategy,
preload: true,
logger: customLogger,
});
// 获取配置
await manager.get<string>('storage.type', 'local');
await manager.getString('storage.type', 'local');
await manager.getNumber('upload.maxSize', 10485760);
await manager.getBoolean('features.enableComment', true);
await manager.getJson<string[]>('upload.allowedTypes', []);
// 设置配置
await manager.set('storage.type', 'cos');
// 批量更新
await manager.batchUpdate([
{ key: 'storage.type', value: 'cos' },
{ key: 'upload.maxSize', value: 20971520 },
]);
// 获取分类配置
await manager.getByCategory('storage');
// 获取公开配置
await manager.getPublicConfigs();
// 获取系统配置(嵌套结构)
await manager.getSystemConfig();
// 重新加载
await manager.reload();缓存策略
import { MemoryCache, RedisCache, TieredCache } from '@svton/dynamic-config/core';
// 内存缓存
const memoryCache = new MemoryCache({ prefix: 'config:', defaultTtl: 3600 });
// Redis 缓存
const redisCache = new RedisCache(redisClient, { prefix: 'config:', defaultTtl: 3600 });
// 分层缓存(Redis 优先,内存兜底)
const tieredCache = new TieredCache(redisCache, memoryCache);NestJS
DynamicConfigModule
// 同步配置
DynamicConfigModule.forRoot({
configRepository: new PrismaConfigRepository(prisma),
dictionaryRepository: new PrismaDictionaryRepository(prisma),
cache: new MemoryCache(),
registerController: true, // 是否注册默认 Controller
isGlobal: false, // 是否全局模块
preload: true, // 是否预加载配置
});
// 异步配置
DynamicConfigModule.forRootAsync({
imports: [PrismaModule],
useFactory: (prisma) => ({
configRepository: new PrismaConfigRepository(prisma),
// ...
}),
inject: [PrismaService],
});React
Hooks
// 获取单个配置
const { value, loading, error, update } = useConfig('storage.type', 'local');
// 获取分类配置
const { configs, loading, error, refetch } = useConfigCategory('storage');
// 获取公开配置
const { configs, loading, error } = usePublicConfigs();
// 获取系统配置
const { config, loading, error } = useSystemConfig();
// 配置修改
const { set, batchUpdate, remove, reload, loading } = useConfigMutation();
// 字典相关
const { items, loading, error } = useDictionaries();
const { items, loading, error } = useDictionaryByCode('storage_type');
const { tree, loading, error } = useDictionaryTree('category');
const { create, update, remove, loading } = useDictionaryMutation();数据库 Schema
Config 表
| 字段 | 类型 | 说明 | |------|------|------| | id | int | 主键 | | key | varchar(100) | 配置键(唯一) | | value | text | 配置值(JSON 字符串) | | type | varchar(20) | 值类型 | | category | varchar(50) | 分类 | | label | varchar(100) | 显示名称 | | description | varchar(500) | 说明 | | isPublic | boolean | 是否公开 | | isRequired | boolean | 是否必填 | | defaultValue | text | 默认值 | | options | text | 可选项(enum 类型) | | sort | int | 排序 |
Dictionary 表
| 字段 | 类型 | 说明 | |------|------|------| | id | int | 主键 | | code | varchar(50) | 字典编码 | | parentId | int | 父级 ID | | label | varchar(100) | 显示名称 | | value | varchar(200) | 字典值 | | type | varchar(20) | 类型(enum/tree/list) | | sort | int | 排序 | | isEnabled | boolean | 是否启用 | | description | varchar(500) | 说明 | | extra | text | 扩展字段 |
License
MIT
