@saleandwin/config
Version:
Configuration center with KV capabilities for framework-ts projects
1,010 lines (1,004 loc) • 31.3 kB
JavaScript
// src/config-service.ts
import {
createBatchValidator,
createTransaction,
notExists,
exists
} from "@saleandwin/common";
// src/config-table.ts
var ConfigTable = class _ConfigTable {
db;
/**
* 创建ConfigTable实例的静态工厂方法
* @param env Cloudflare Workers 环境对象
*/
static createInstance(env) {
const db = this.getDatabase(env);
return new _ConfigTable(db);
}
constructor(db) {
this.db = db;
}
/**
* 获取Cloudflare环境中的数据库连接
* @param env Cloudflare Workers 环境对象
*/
static getDatabase(env) {
if (!env?.DB) {
throw new Error("\u6570\u636E\u5E93\u8FDE\u63A5\u4E0D\u53EF\u7528\uFF0C\u8BF7\u786E\u4FDD\u5728 Cloudflare Workers \u73AF\u5883\u4E2D\u6B63\u786E\u914D\u7F6E\u4E86 D1 \u6570\u636E\u5E93\u7ED1\u5B9A");
}
return env.DB;
}
/**
* 根据ID查询配置
*/
prepareGetById(id) {
return this.db.prepare(`
SELECT id, key, value, type, namespace, description, is_active, is_encrypted,
created_at, updated_at, created_by, updated_by
FROM configs
WHERE id = ? AND is_active = 1
`).bind(id);
}
/**
* 根据key查询配置
*/
prepareGetByKey(key) {
return this.db.prepare(`
SELECT id, key, value, type, namespace, description, is_active, is_encrypted,
created_at, updated_at, created_by, updated_by
FROM configs
WHERE key = ? AND is_active = 1
`).bind(key);
}
/**
* 检查配置key是否存在
*/
prepareCheckKeyExists(key, excludeId) {
if (excludeId) {
return this.db.prepare(`
SELECT id FROM configs WHERE key = ? AND id != ?
`).bind(key, excludeId);
}
return this.db.prepare(`
SELECT id FROM configs WHERE key = ?
`).bind(key);
}
/**
* 查询配置列表
*/
prepareGetList(params, limit, offset) {
let sql = `
SELECT id, key, value, type, category, description, is_active, is_encrypted,
created_at, updated_at, created_by, updated_by
FROM configs
WHERE 1=1
`;
const bindings = [];
if (params.namespace) {
sql += ` AND namespace = ?`;
bindings.push(params.namespace);
}
if (params.key) {
sql += ` AND key LIKE ?`;
bindings.push(`%${params.key}%`);
}
if (params.is_active !== void 0) {
sql += ` AND is_active = ?`;
bindings.push(params.is_active ? 1 : 0);
}
if (params.search) {
sql += ` AND (key LIKE ? OR description LIKE ? OR namespace LIKE ?)`;
const searchTerm = `%${params.search}%`;
bindings.push(searchTerm, searchTerm, searchTerm);
}
sql += ` ORDER BY namespace, key LIMIT ? OFFSET ?`;
bindings.push(limit, offset);
return this.db.prepare(sql).bind(...bindings);
}
/**
* 统计配置总数
*/
prepareGetCount(params) {
let sql = `SELECT COUNT(*) as count FROM configs WHERE 1=1`;
const bindings = [];
if (params.namespace) {
sql += ` AND namespace = ?`;
bindings.push(params.namespace);
}
if (params.key) {
sql += ` AND key LIKE ?`;
bindings.push(`%${params.key}%`);
}
if (params.is_active !== void 0) {
sql += ` AND is_active = ?`;
bindings.push(params.is_active ? 1 : 0);
}
if (params.search) {
sql += ` AND (key LIKE ? OR description LIKE ? OR namespace LIKE ?)`;
const searchTerm = `%${params.search}%`;
bindings.push(searchTerm, searchTerm, searchTerm);
}
return this.db.prepare(sql).bind(...bindings);
}
/**
* 创建配置
*/
prepareCreate(data) {
const id = crypto.randomUUID();
const now = (/* @__PURE__ */ new Date()).toISOString();
return this.db.prepare(`
INSERT INTO configs (
id, key, value, type, namespace, description, is_active, is_encrypted,
created_at, updated_at, created_by, updated_by
) VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?)
`).bind(
id,
data.key,
data.value,
data.type,
data.namespace,
data.description || null,
data.is_encrypted ? 1 : 0,
now,
now,
data.created_by || null,
data.created_by || null
);
}
/**
* 更新配置
*/
prepareUpdate(id, data) {
const now = (/* @__PURE__ */ new Date()).toISOString();
const updates = [];
const bindings = [];
if (data.value !== void 0) {
updates.push("value = ?");
bindings.push(data.value);
}
if (data.type !== void 0) {
updates.push("type = ?");
bindings.push(data.type);
}
if (data.namespace !== void 0) {
updates.push("namespace = ?");
bindings.push(data.namespace);
}
if (data.description !== void 0) {
updates.push("description = ?");
bindings.push(data.description);
}
if (data.is_active !== void 0) {
updates.push("is_active = ?");
bindings.push(data.is_active ? 1 : 0);
}
if (data.is_encrypted !== void 0) {
updates.push("is_encrypted = ?");
bindings.push(data.is_encrypted ? 1 : 0);
}
updates.push("updated_at = ?", "updated_by = ?");
bindings.push(now, data.updated_by || null);
bindings.push(id);
return this.db.prepare(`
UPDATE configs SET ${updates.join(", ")} WHERE id = ?
`).bind(...bindings);
}
/**
* 删除配置(软删除)
*/
prepareDelete(id, deletedBy) {
const now = (/* @__PURE__ */ new Date()).toISOString();
return this.db.prepare(`
UPDATE configs
SET is_active = 0, updated_at = ?, updated_by = ?
WHERE id = ?
`).bind(now, deletedBy || null, id);
}
/**
* 获取所有命名空间
*/
prepareGetNamespaces() {
return this.db.prepare(`
SELECT namespace, COUNT(*) as count
FROM configs
WHERE is_active = 1
GROUP BY namespace
ORDER BY namespace
`);
}
/**
* 根据命名空间获取配置
*/
prepareGetByNamespace(namespace) {
return this.db.prepare(`
SELECT id, key, value, type, namespace, description, is_active, is_encrypted,
created_at, updated_at, created_by, updated_by
FROM configs
WHERE namespace = ? AND is_active = 1
ORDER BY key
`).bind(namespace);
}
/**
* 批量获取配置
*/
prepareGetByKeys(keys) {
const placeholders = keys.map(() => "?").join(",");
return this.db.prepare(`
SELECT id, key, value, type, namespace, description, is_active, is_encrypted,
created_at, updated_at, created_by, updated_by
FROM configs
WHERE key IN (${placeholders}) AND is_active = 1
`).bind(...keys);
}
/**
* 获取配置历史记录
*/
prepareGetHistory(configKey, limit = 50) {
return this.db.prepare(`
SELECT id, config_id, config_key, old_value, new_value, action, changed_at, changed_by
FROM config_history
WHERE config_key = ?
ORDER BY changed_at DESC
LIMIT ?
`).bind(configKey, limit);
}
};
// src/config-service.ts
var ConfigService = class _ConfigService {
configTable;
env;
// 移除缓存功能 - 直接从数据库获取数据
validationRules = /* @__PURE__ */ new Map();
constructor(configTable, env) {
this.configTable = configTable;
this.env = env;
}
/**
* 创建ConfigService实例的静态工厂方法
* @param env Cloudflare Workers 环境对象
*/
static createInstance(env) {
const configTable = ConfigTable.createInstance(env);
return new _ConfigService(configTable, env);
}
/**
* 添加验证规则
*/
addValidationRule(rule) {
this.validationRules.set(rule.key, rule);
}
/**
* 验证配置值
*/
validateConfigValue(key, value, type) {
const rule = this.validationRules.get(key);
if (!rule) return null;
if (rule.type && rule.type !== type) {
return `\u914D\u7F6E\u7C7B\u578B\u5FC5\u987B\u662F ${rule.type}`;
}
if (type === "string") {
if (rule.minLength && value.length < rule.minLength) {
return `\u914D\u7F6E\u503C\u957F\u5EA6\u4E0D\u80FD\u5C11\u4E8E ${rule.minLength} \u4E2A\u5B57\u7B26`;
}
if (rule.maxLength && value.length > rule.maxLength) {
return `\u914D\u7F6E\u503C\u957F\u5EA6\u4E0D\u80FD\u8D85\u8FC7 ${rule.maxLength} \u4E2A\u5B57\u7B26`;
}
if (rule.pattern && !new RegExp(rule.pattern).test(value)) {
return `\u914D\u7F6E\u503C\u683C\u5F0F\u4E0D\u6B63\u786E`;
}
if (rule.enum && !rule.enum.includes(value)) {
return `\u914D\u7F6E\u503C\u5FC5\u987B\u662F\u4EE5\u4E0B\u503C\u4E4B\u4E00: ${rule.enum.join(", ")}`;
}
}
if (type === "number") {
const numValue = parseFloat(value);
if (isNaN(numValue)) {
return "\u914D\u7F6E\u503C\u5FC5\u987B\u662F\u6709\u6548\u6570\u5B57";
}
if (rule.min !== void 0 && numValue < rule.min) {
return `\u914D\u7F6E\u503C\u4E0D\u80FD\u5C0F\u4E8E ${rule.min}`;
}
if (rule.max !== void 0 && numValue > rule.max) {
return `\u914D\u7F6E\u503C\u4E0D\u80FD\u5927\u4E8E ${rule.max}`;
}
}
if (type === "json") {
try {
JSON.parse(value);
} catch {
return "\u914D\u7F6E\u503C\u5FC5\u987B\u662F\u6709\u6548\u7684JSON\u683C\u5F0F";
}
}
if (rule.validator) {
const result = rule.validator(this.parseConfigValue(value, type));
if (typeof result === "string") {
return result;
}
if (!result) {
return "\u914D\u7F6E\u503C\u9A8C\u8BC1\u5931\u8D25";
}
}
return null;
}
/**
* 解析配置值
*/
parseConfigValue(value, type) {
switch (type) {
case "number":
return parseFloat(value);
case "boolean":
return value.toLowerCase() === "true";
case "json":
try {
return JSON.parse(value);
} catch {
return value;
}
case "array":
try {
const parsed = JSON.parse(value);
return Array.isArray(parsed) ? parsed : [value];
} catch {
return [value];
}
default:
return value;
}
}
/**
* 创建配置
*/
async createConfig(data) {
const validationError = this.validateConfigValue(data.key, data.value, data.type);
if (validationError) {
throw new Error(validationError);
}
const validator = createBatchValidator(this.env);
const validationResult = await validator.validate([
{
name: "checkKeyNotExists",
statement: this.configTable.prepareCheckKeyExists(data.key),
validator: notExists("\u914D\u7F6Ekey\u5DF2\u5B58\u5728")
}
]);
if (!validationResult.valid) {
throw new Error(validationResult.error);
}
const transaction = createTransaction(this.env);
const createStatement = this.configTable.prepareCreate(data);
transaction.add(createStatement);
await transaction.execute();
const getStatement = this.configTable.prepareGetByKey(data.key);
const result = await getStatement.first();
return result;
}
/**
* 更新配置
*/
async updateConfig(id, data) {
const validator = createBatchValidator(this.env);
const validationResult = await validator.validate([
{
name: "checkConfigExists",
statement: this.configTable.prepareGetById(id),
validator: exists("\u914D\u7F6E\u4E0D\u5B58\u5728", "config")
}
]);
if (!validationResult.valid) {
throw new Error(validationResult.error);
}
const existingConfig = validationResult.data.config;
if (data.value !== void 0 && data.type !== void 0) {
const validationError = this.validateConfigValue(existingConfig.key, data.value, data.type);
if (validationError) {
throw new Error(validationError);
}
}
const transaction = createTransaction(this.env);
const updateStatement = this.configTable.prepareUpdate(id, data);
transaction.add(updateStatement);
await transaction.execute();
const getStatement = this.configTable.prepareGetById(id);
const result = await getStatement.first();
return result;
}
/**
* 删除配置
*/
async deleteConfig(id, deletedBy) {
const validator = createBatchValidator(this.env);
const validationResult = await validator.validate([
{
name: "checkConfigExists",
statement: this.configTable.prepareGetById(id),
validator: exists("\u914D\u7F6E\u4E0D\u5B58\u5728", "config")
}
]);
if (!validationResult.valid) {
throw new Error(validationResult.error);
}
const existingConfig = validationResult.data.config;
const transaction = createTransaction(this.env);
const deleteStatement = this.configTable.prepareDelete(id, deletedBy);
transaction.add(deleteStatement);
await transaction.execute();
}
/**
* 获取配置(移除缓存功能)
*/
async getConfig(key) {
const statement = this.configTable.prepareGetByKey(key);
const result = await statement.first();
if (!result) {
return null;
}
return this.parseConfigValue(result.value, result.type);
}
/**
* 批量获取配置
*/
async getConfigs(keys) {
const statement = this.configTable.prepareGetByKeys(keys);
const results = await statement.all();
return results.results?.map((item) => ({
key: item.key,
value: this.parseConfigValue(item.value, item.type)
})) || [];
}
/**
* 获取配置列表
*/
async getConfigList(params, page, limit) {
const offset = (page - 1) * limit;
const [listStatement, countStatement] = [
this.configTable.prepareGetList(params, limit, offset),
this.configTable.prepareGetCount(params)
];
const transaction = createTransaction(this.env);
transaction.addAll([listStatement, countStatement]);
const results = await transaction.execute();
const configs = results[0].results || [];
const totalCount = results[1].results?.[0]?.count || 0;
const pagination = {
page,
limit,
total: totalCount,
totalPages: Math.ceil(totalCount / limit)
};
return { configs, pagination };
}
/**
* 获取命名空间列表
*/
async getNamespaces() {
const statement = this.configTable.prepareGetNamespaces();
const results = await statement.all();
return results.results?.map((item) => ({
namespace: item.namespace,
count: item.count
})) || [];
}
/**
* 根据命名空间获取配置
*/
async getConfigsByNamespace(namespace) {
const statement = this.configTable.prepareGetByNamespace(namespace);
const results = await statement.all();
return results.results?.map((item) => ({
key: item.key,
value: this.parseConfigValue(item.value, item.type)
})) || [];
}
/**
* 获取配置历史记录
*/
async getConfigHistory(configKey, limit = 50) {
const statement = this.configTable.prepareGetHistory(configKey, limit);
const results = await statement.all();
return results.results || [];
}
// 缓存功能已移除
};
// src/schemas.ts
import { z } from "zod";
var ConfigTypeSchema = z.enum(["string", "number", "boolean", "json", "array"]);
var ConfigKeySchema = z.string().describe("\u914D\u7F6E\u952E").min(1, "\u914D\u7F6Ekey\u4E0D\u80FD\u4E3A\u7A7A").max(100, "\u914D\u7F6Ekey\u957F\u5EA6\u4E0D\u80FD\u8D85\u8FC7100\u4E2A\u5B57\u7B26").regex(/^[a-zA-Z][a-zA-Z0-9._-]*$/, "\u914D\u7F6Ekey\u5FC5\u987B\u4EE5\u5B57\u6BCD\u5F00\u5934\uFF0C\u53EA\u80FD\u5305\u542B\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u70B9\u3001\u4E0B\u5212\u7EBF\u548C\u8FDE\u5B57\u7B26");
var ConfigValueSchema = z.string().min(1, "\u914D\u7F6E\u503C\u4E0D\u80FD\u4E3A\u7A7A").max(1e4, "\u914D\u7F6E\u503C\u957F\u5EA6\u4E0D\u80FD\u8D85\u8FC710000\u4E2A\u5B57\u7B26");
var ConfigNamespaceSchema = z.string().min(1, "\u547D\u540D\u7A7A\u95F4\u4E0D\u80FD\u4E3A\u7A7A").max(50, "\u547D\u540D\u7A7A\u95F4\u957F\u5EA6\u4E0D\u80FD\u8D85\u8FC750\u4E2A\u5B57\u7B26").regex(/^[a-zA-Z][a-zA-Z0-9_-]*$/, "\u547D\u540D\u7A7A\u95F4\u5FC5\u987B\u4EE5\u5B57\u6BCD\u5F00\u5934\uFF0C\u53EA\u80FD\u5305\u542B\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u4E0B\u5212\u7EBF\u548C\u8FDE\u5B57\u7B26");
var ConfigDescriptionSchema = z.string().max(500, "\u63CF\u8FF0\u957F\u5EA6\u4E0D\u80FD\u8D85\u8FC7500\u4E2A\u5B57\u7B26").optional();
var CreateConfigRequestSchema = z.object({
/**
* 配置键
*/
key: ConfigKeySchema,
value: ConfigValueSchema,
type: ConfigTypeSchema,
namespace: ConfigNamespaceSchema,
description: ConfigDescriptionSchema,
is_encrypted: z.boolean().optional().default(false),
created_by: z.string().optional()
});
var UpdateConfigRequestSchema = z.object({
value: ConfigValueSchema.optional(),
type: ConfigTypeSchema.optional(),
namespace: ConfigNamespaceSchema.optional(),
description: ConfigDescriptionSchema,
is_active: z.boolean().optional(),
is_encrypted: z.boolean().optional(),
updated_by: z.string().optional()
});
var ConfigQueryParamsSchema = z.object({
namespace: z.string().optional(),
key: z.string().optional(),
is_active: z.boolean().optional(),
search: z.string().max(100, "\u641C\u7D22\u5173\u952E\u8BCD\u957F\u5EA6\u4E0D\u80FD\u8D85\u8FC7100\u4E2A\u5B57\u7B26").optional()
});
var PaginationParamsSchema = z.object({
page: z.number().int().min(1, "\u9875\u7801\u5FC5\u987B\u5927\u4E8E0").max(1e4, "\u9875\u7801\u4E0D\u80FD\u8D85\u8FC710000"),
limit: z.number().int().min(1, "\u6BCF\u9875\u6570\u91CF\u5FC5\u987B\u5927\u4E8E0").max(100, "\u6BCF\u9875\u6570\u91CF\u4E0D\u80FD\u8D85\u8FC7100")
});
var BatchGetConfigsSchema = z.object({
keys: z.array(ConfigKeySchema).min(1, "\u81F3\u5C11\u9700\u8981\u4E00\u4E2A\u914D\u7F6Ekey").max(50, "\u4E00\u6B21\u6700\u591A\u83B7\u53D650\u4E2A\u914D\u7F6E")
});
var ConfigIdSchema = z.string().uuid("\u914D\u7F6EID\u683C\u5F0F\u4E0D\u6B63\u786E");
var ConfigHistoryParamsSchema = z.object({
configKey: ConfigKeySchema,
limit: z.number().int().min(1).max(100).optional().default(50)
});
var validateConfigValue = (value, type) => {
switch (type) {
case "number":
return !isNaN(parseFloat(value));
case "boolean":
return ["true", "false"].includes(value.toLowerCase());
case "json":
try {
JSON.parse(value);
return true;
} catch {
return false;
}
case "array":
try {
const parsed = JSON.parse(value);
return Array.isArray(parsed);
} catch {
return false;
}
case "string":
default:
return true;
}
};
var CreateConfigRequestWithValueValidationSchema = CreateConfigRequestSchema.refine(
(data) => validateConfigValue(data.value, data.type),
{
message: "\u914D\u7F6E\u503C\u4E0E\u7C7B\u578B\u4E0D\u5339\u914D",
path: ["value"]
}
);
var UpdateConfigRequestWithValueValidationSchema = UpdateConfigRequestSchema.refine(
(data) => {
if (data.value && data.type) {
return validateConfigValue(data.value, data.type);
}
return true;
},
{
message: "\u914D\u7F6E\u503C\u4E0E\u7C7B\u578B\u4E0D\u5339\u914D",
path: ["value"]
}
);
var ConfigValidator = class {
/**
* 验证创建配置请求
*/
static validateCreateRequest(data) {
try {
const validated = CreateConfigRequestWithValueValidationSchema.parse(data);
return { success: true, data: validated };
} catch (error) {
if (error instanceof z.ZodError) {
const errorMessage = error.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join("; ");
return { success: false, error: errorMessage };
}
return { success: false, error: "\u9A8C\u8BC1\u5931\u8D25" };
}
}
/**
* 验证更新配置请求
*/
static validateUpdateRequest(data) {
try {
const validated = UpdateConfigRequestWithValueValidationSchema.parse(data);
return { success: true, data: validated };
} catch (error) {
if (error instanceof z.ZodError) {
const errorMessage = error.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join("; ");
return { success: false, error: errorMessage };
}
return { success: false, error: "\u9A8C\u8BC1\u5931\u8D25" };
}
}
/**
* 验证查询参数
*/
static validateQueryParams(data) {
try {
const validated = ConfigQueryParamsSchema.parse(data);
return { success: true, data: validated };
} catch (error) {
if (error instanceof z.ZodError) {
const errorMessage = error.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join("; ");
return { success: false, error: errorMessage };
}
return { success: false, error: "\u9A8C\u8BC1\u5931\u8D25" };
}
}
/**
* 验证分页参数
*/
static validatePaginationParams(data) {
try {
const validated = PaginationParamsSchema.parse(data);
return { success: true, data: validated };
} catch (error) {
if (error instanceof z.ZodError) {
const errorMessage = error.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join("; ");
return { success: false, error: errorMessage };
}
return { success: false, error: "\u9A8C\u8BC1\u5931\u8D25" };
}
}
/**
* 验证批量获取请求
*/
static validateBatchGetRequest(data) {
try {
const validated = BatchGetConfigsSchema.parse(data);
return { success: true, data: validated };
} catch (error) {
if (error instanceof z.ZodError) {
const errorMessage = error.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join("; ");
return { success: false, error: errorMessage };
}
return { success: false, error: "\u9A8C\u8BC1\u5931\u8D25" };
}
}
/**
* 验证配置ID
*/
static validateConfigId(id) {
try {
const validated = ConfigIdSchema.parse(id);
return { success: true, data: validated };
} catch (error) {
if (error instanceof z.ZodError) {
return { success: false, error: "\u914D\u7F6EID\u683C\u5F0F\u4E0D\u6B63\u786E" };
}
return { success: false, error: "\u9A8C\u8BC1\u5931\u8D25" };
}
}
};
// src/config-handler.ts
var ConfigHandler = class _ConfigHandler {
configService;
constructor(configService) {
this.configService = configService;
}
/**
* 创建ConfigHandler实例的静态工厂方法
* @param env Cloudflare Workers 环境对象
*/
static createInstance(env) {
const configService = ConfigService.createInstance(env);
return new _ConfigHandler(configService);
}
/**
* 创建配置
*/
async handleCreate(data) {
const validation = ConfigValidator.validateCreateRequest(data);
if (!validation.success) {
throw new Error(`\u9A8C\u8BC1\u5931\u8D25: ${validation.error}`);
}
return await this.configService.createConfig(validation.data);
}
/**
* 更新配置
*/
async handleUpdate(id, data) {
const idValidation = ConfigValidator.validateConfigId(id);
if (!idValidation.success) {
throw new Error(`ID\u9A8C\u8BC1\u5931\u8D25: ${idValidation.error}`);
}
const dataValidation = ConfigValidator.validateUpdateRequest(data);
if (!dataValidation.success) {
throw new Error(`\u6570\u636E\u9A8C\u8BC1\u5931\u8D25: ${dataValidation.error}`);
}
return await this.configService.updateConfig(idValidation.data, dataValidation.data);
}
/**
* 删除配置
*/
async handleDelete(id, deletedBy) {
const idValidation = ConfigValidator.validateConfigId(id);
if (!idValidation.success) {
throw new Error(`ID\u9A8C\u8BC1\u5931\u8D25: ${idValidation.error}`);
}
await this.configService.deleteConfig(idValidation.data, deletedBy);
}
/**
* 获取配置值(移除缓存参数)
*/
async handleGet(key) {
const value = await this.configService.getConfig(key);
if (value === null) {
throw new Error(`\u914D\u7F6E\u4E0D\u5B58\u5728: ${key}`);
}
return { key, value };
}
/**
* 批量获取配置
*/
async handleBatchGet(data) {
const validation = ConfigValidator.validateBatchGetRequest(data);
if (!validation.success) {
throw new Error(`\u9A8C\u8BC1\u5931\u8D25: ${validation.error}`);
}
return await this.configService.getConfigs(validation.data.keys);
}
/**
* 获取配置列表
*/
async handleList(queryParams, paginationParams) {
const queryValidation = ConfigValidator.validateQueryParams(queryParams);
if (!queryValidation.success) {
throw new Error(`\u67E5\u8BE2\u53C2\u6570\u9A8C\u8BC1\u5931\u8D25: ${queryValidation.error}`);
}
const paginationValidation = ConfigValidator.validatePaginationParams(paginationParams);
if (!paginationValidation.success) {
throw new Error(`\u5206\u9875\u53C2\u6570\u9A8C\u8BC1\u5931\u8D25: ${paginationValidation.error}`);
}
const result = await this.configService.getConfigList(
queryValidation.data,
paginationValidation.data.page,
paginationValidation.data.limit
);
return {
configs: result.configs,
pagination: result.pagination
};
}
/**
* 获取命名空间列表
*/
async handleGetNamespaces() {
return await this.configService.getNamespaces();
}
/**
* 根据命名空间获取配置
*/
async handleGetByNamespace(namespace) {
return await this.configService.getConfigsByNamespace(namespace);
}
/**
* 获取配置历史记录
*/
async handleGetHistory(configKey, limit = 50) {
return await this.configService.getConfigHistory(configKey, limit);
}
/**
* 健康检查
*/
async handleHealthCheck() {
return {
status: "healthy",
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
service: "config-service"
};
}
};
// src/index.ts
var ConfigUtils = class {
/**
* 验证配置key格式
*/
static isValidKey(key) {
return /^[a-zA-Z][a-zA-Z0-9._-]*$/.test(key);
}
/**
* 验证配置类型
*/
static isValidType(type) {
return ["string", "number", "boolean", "json", "array"].includes(type);
}
/**
* 解析配置值
*/
static parseValue(value, type) {
switch (type) {
case "number":
const num = parseFloat(value);
return isNaN(num) ? 0 : num;
case "boolean":
return value.toLowerCase() === "true";
case "json":
try {
return JSON.parse(value);
} catch {
return {};
}
case "array":
try {
const parsed = JSON.parse(value);
return Array.isArray(parsed) ? parsed : [value];
} catch {
return [value];
}
default:
return value;
}
}
/**
* 序列化配置值
*/
static serializeValue(value, type) {
switch (type) {
case "number":
return String(value);
case "boolean":
return String(Boolean(value));
case "json":
case "array":
return JSON.stringify(value);
default:
return String(value);
}
}
/**
* 生成配置key的建议
*/
static suggestKey(category, name) {
const cleanCategory = category.toLowerCase().replace(/[^a-z0-9]/g, "_");
const cleanName = name.toLowerCase().replace(/[^a-z0-9]/g, "_");
return `${cleanCategory}.${cleanName}`;
}
/**
* 验证JSON格式
*/
static isValidJson(value) {
try {
JSON.parse(value);
return true;
} catch {
return false;
}
}
/**
* 从环境变量创建配置
* @param env Cloudflare Workers 环境对象
* @param envKey 环境变量键名
* @param configKey 配置键名
* @param type 配置类型
* @param defaultValue 默认值
*/
static fromEnv(env, envKey, configKey, type = "string", defaultValue) {
const value = env[envKey] || defaultValue || "";
return {
key: configKey,
value: this.serializeValue(value, type),
type,
category: "environment",
description: `\u4ECE\u73AF\u5883\u53D8\u91CF ${envKey} \u5BFC\u5165`,
created_by: "system"
};
}
/**
* 批量从环境变量创建配置
* @param env Cloudflare Workers 环境对象
* @param mappings 环境变量映射配置
*/
static batchFromEnv(env, mappings) {
return mappings.map((mapping) => ({
key: mapping.configKey,
value: this.serializeValue(
env[mapping.envKey] || mapping.defaultValue || "",
mapping.type || "string"
),
type: mapping.type || "string",
category: mapping.category || "environment",
description: `\u4ECE\u73AF\u5883\u53D8\u91CF ${mapping.envKey} \u5BFC\u5165`,
created_by: "system"
}));
}
};
var CONFIG_CONSTANTS = {
// 默认分类
DEFAULT_CATEGORIES: [
"system",
"database",
"cache",
"api",
"security",
"feature",
"environment"
],
// 配置类型
CONFIG_TYPES: [
"string",
"number",
"boolean",
"json",
"array"
],
// 缓存TTL(秒)
CACHE_TTL: {
MEMORY: 300,
// 5分钟
KV: 3600,
// 1小时
DATABASE: 0
// 不缓存
},
// 验证规则
VALIDATION: {
KEY_MAX_LENGTH: 100,
VALUE_MAX_LENGTH: 1e4,
DESCRIPTION_MAX_LENGTH: 500,
CATEGORY_MAX_LENGTH: 50
},
// 系统配置key前缀
SYSTEM_PREFIX: "system.",
// 保留的配置key
RESERVED_KEYS: [
"system.version",
"system.maintenance",
"system.debug",
"system.environment"
]
};
var CONFIG_TEMPLATES = {
// 数据库配置模板
DATABASE: {
name: "\u6570\u636E\u5E93\u914D\u7F6E",
category: "database",
configs: [
{ key: "database.host", defaultValue: "localhost", type: "string", description: "\u6570\u636E\u5E93\u4E3B\u673A\u5730\u5740" },
{ key: "database.port", defaultValue: "5432", type: "number", description: "\u6570\u636E\u5E93\u7AEF\u53E3" },
{ key: "database.name", defaultValue: "app", type: "string", description: "\u6570\u636E\u5E93\u540D\u79F0" },
{ key: "database.ssl", defaultValue: "false", type: "boolean", description: "\u662F\u5426\u542F\u7528SSL" }
]
},
// 缓存配置模板
CACHE: {
name: "\u7F13\u5B58\u914D\u7F6E",
category: "cache",
configs: [
{ key: "cache.ttl", defaultValue: "3600", type: "number", description: "\u9ED8\u8BA4\u7F13\u5B58\u65F6\u95F4\uFF08\u79D2\uFF09" },
{ key: "cache.max_size", defaultValue: "1000", type: "number", description: "\u6700\u5927\u7F13\u5B58\u6761\u76EE\u6570" },
{ key: "cache.enabled", defaultValue: "true", type: "boolean", description: "\u662F\u5426\u542F\u7528\u7F13\u5B58" }
]
},
// API配置模板
API: {
name: "API\u914D\u7F6E",
category: "api",
configs: [
{ key: "api.rate_limit", defaultValue: "100", type: "number", description: "API\u901F\u7387\u9650\u5236\uFF08\u6BCF\u5206\u949F\uFF09" },
{ key: "api.timeout", defaultValue: "30000", type: "number", description: "API\u8D85\u65F6\u65F6\u95F4\uFF08\u6BEB\u79D2\uFF09" },
{ key: "api.cors_origins", defaultValue: '["*"]', type: "array", description: "\u5141\u8BB8\u7684CORS\u6E90" }
]
}
};
export {
CONFIG_CONSTANTS,
CONFIG_TEMPLATES,
ConfigHandler,
ConfigService,
ConfigTable,
ConfigUtils,
ConfigValidator
};