UNPKG

@saleandwin/config

Version:

Configuration center with KV capabilities for framework-ts projects

1,010 lines (1,004 loc) 31.2 kB
// 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.checkConfigExists; 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(1e5, "\u914D\u7F6E\u503C\u957F\u5EA6\u4E0D\u80FD\u8D85\u8FC7100000\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(); 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 };