RuntimeConfig.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. const db = require('../plugin/DataBase/db')
  2. const Redis = require('../plugin/DataBase/Redis')
  3. const CONFIG_KEYS = ['pay', 'email', 'unilogin', 'proxyForwardServer']
  4. const CACHE_TTL_SECONDS = 300
  5. class RuntimeConfigNotFoundError extends Error {
  6. constructor(key) {
  7. super(`未配置或未启用 ${key}`)
  8. this.name = 'RuntimeConfigNotFoundError'
  9. this.code = 'RUNTIME_CONFIG_NOT_FOUND'
  10. this.key = key
  11. }
  12. }
  13. function cacheKey(key) {
  14. return `runtimeConfig:${key}`
  15. }
  16. async function getRuntimeConfig(key, options = {}) {
  17. const { required = true, defaultValue = undefined } = options
  18. if (!CONFIG_KEYS.includes(key)) throw new Error('不支持的运行时配置')
  19. const cached = await Redis.get(cacheKey(key))
  20. if (cached) return JSON.parse(cached)
  21. const rows = await db.query(
  22. `SELECT config_value FROM runtime_configs
  23. WHERE config_key = ? AND enabled = 1
  24. ORDER BY is_default DESC, updated_at DESC LIMIT 1`,
  25. [key]
  26. )
  27. if (!rows?.[0]) {
  28. if (!required) return defaultValue
  29. throw new RuntimeConfigNotFoundError(key)
  30. }
  31. const value = typeof rows[0].config_value === 'object'
  32. ? rows[0].config_value
  33. : JSON.parse(rows[0].config_value)
  34. await Redis.set(cacheKey(key), JSON.stringify(value), { EX: CACHE_TTL_SECONDS })
  35. return value
  36. }
  37. async function getAllRuntimeConfigs() {
  38. const rows = await db.query(
  39. `SELECT config_key, config_name, config_value, enabled, is_default, updated_at, updated_by
  40. FROM runtime_configs ORDER BY config_key, is_default DESC, updated_at DESC`
  41. ) || []
  42. return rows.map(row => ({
  43. ...row,
  44. config_value: typeof row.config_value === 'object' ? row.config_value : JSON.parse(row.config_value)
  45. }))
  46. }
  47. function validateRuntimeConfig(key, value) {
  48. if (key === 'email') {
  49. if (!Array.isArray(value)) throw new Error('邮件配置必须为数组')
  50. return value.map((item, index) => ({ ...item, priority: Number(item.priority ?? index) }))
  51. .sort((a, b) => a.priority - b.priority)
  52. }
  53. if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error(`${key} 配置必须为对象`)
  54. if (key === 'pay' && (!Array.isArray(value.methods) || value.methods.some(item => !item?.type))) {
  55. throw new Error('付款方式配置无效')
  56. }
  57. return value
  58. }
  59. async function saveRuntimeConfig({ key, name = 'default', value, enabled = true, isDefault = false, updatedBy }) {
  60. if (!CONFIG_KEYS.includes(key)) throw new Error('不支持的运行时配置')
  61. const normalized = validateRuntimeConfig(key, value)
  62. const result = await db.query(
  63. `INSERT INTO runtime_configs (config_key, config_name, config_value, enabled, is_default, updated_at, updated_by)
  64. VALUES (?, ?, ?, ?, ?, ?, ?)
  65. ON DUPLICATE KEY UPDATE config_value = VALUES(config_value), enabled = VALUES(enabled),
  66. is_default = VALUES(is_default), updated_at = VALUES(updated_at), updated_by = VALUES(updated_by)`,
  67. [key, String(name).trim(), JSON.stringify(normalized), enabled ? 1 : 0, isDefault ? 1 : 0, Date.now(), updatedBy || null]
  68. )
  69. if (result === undefined) throw new Error('保存运行时配置失败')
  70. if (isDefault) {
  71. await db.query('UPDATE runtime_configs SET is_default = IF(config_name = ?, 1, 0) WHERE config_key = ?', [String(name).trim(), key])
  72. }
  73. await Redis.del(cacheKey(key))
  74. return normalized
  75. }
  76. async function selectRuntimeConfig({ key, name, updatedBy }) {
  77. if (!CONFIG_KEYS.includes(key) || !name) throw new Error('配置类型或名称无效')
  78. const result = await db.query(
  79. 'UPDATE runtime_configs SET is_default = IF(config_name = ?, 1, 0), updated_at = ?, updated_by = ? WHERE config_key = ?',
  80. [name, Date.now(), updatedBy || null, key]
  81. )
  82. if (result === undefined) throw new Error('切换运行时配置失败')
  83. await Redis.del(cacheKey(key))
  84. }
  85. module.exports = {
  86. CONFIG_KEYS,
  87. RuntimeConfigNotFoundError,
  88. getRuntimeConfig,
  89. getAllRuntimeConfigs,
  90. saveRuntimeConfig,
  91. selectRuntimeConfig
  92. }