You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
513 lines
17 KiB
513 lines
17 KiB
import SiteConfigModel from "../model/site-config"
|
|
import { logger } from "@/logger.js"
|
|
|
|
/**
|
|
* 站点配置服务类
|
|
* 提供站点配置相关的业务逻辑
|
|
*/
|
|
class SiteConfigService {
|
|
/**
|
|
* 获取配置值
|
|
* @param {string} key - 配置键
|
|
* @param {*} defaultValue - 默认值
|
|
* @returns {Promise<*>} 配置值
|
|
*/
|
|
static async get(key, defaultValue = null) {
|
|
try {
|
|
const value = await SiteConfigModel.get(key)
|
|
return value !== null ? value : defaultValue
|
|
} catch (error) {
|
|
logger.error(`获取配置失败 (${key}):`, error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 设置配置值
|
|
* @param {string} key - 配置键
|
|
* @param {*} value - 配置值
|
|
* @returns {Promise<Object>} 配置对象
|
|
*/
|
|
static async set(key, value) {
|
|
try {
|
|
// 验证配置键
|
|
this.validateConfigKey(key)
|
|
|
|
// 序列化值
|
|
const serializedValue = this.serializeValue(value)
|
|
|
|
const config = await SiteConfigModel.set(key, serializedValue)
|
|
|
|
logger.info(`配置设置成功: ${key}`)
|
|
return this.formatConfigResponse(config)
|
|
} catch (error) {
|
|
logger.error(`设置配置失败 (${key}):`, error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 批量获取配置
|
|
* @param {Array} keys - 配置键数组
|
|
* @returns {Promise<Object>} 配置对象
|
|
*/
|
|
static async getMany(keys) {
|
|
try {
|
|
if (!Array.isArray(keys) || keys.length === 0) {
|
|
return {}
|
|
}
|
|
|
|
const configs = await SiteConfigModel.getMany(keys)
|
|
|
|
// 反序列化值
|
|
const result = {}
|
|
for (const [key, value] of Object.entries(configs)) {
|
|
result[key] = this.deserializeValue(value)
|
|
}
|
|
|
|
return result
|
|
} catch (error) {
|
|
logger.error(`批量获取配置失败:`, error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取所有配置
|
|
* @returns {Promise<Object>} 所有配置
|
|
*/
|
|
static async getAll() {
|
|
try {
|
|
const configs = await SiteConfigModel.getAll()
|
|
|
|
// 反序列化值
|
|
const result = {}
|
|
for (const [key, value] of Object.entries(configs)) {
|
|
result[key] = this.deserializeValue(value)
|
|
}
|
|
|
|
return result
|
|
} catch (error) {
|
|
logger.error(`获取所有配置失败:`, error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 批量设置配置
|
|
* @param {Object} configs - 配置对象
|
|
* @returns {Promise<Array>} 设置结果
|
|
*/
|
|
static async setMany(configs) {
|
|
try {
|
|
if (!configs || typeof configs !== 'object') {
|
|
throw new Error("配置对象不能为空")
|
|
}
|
|
|
|
const results = []
|
|
for (const [key, value] of Object.entries(configs)) {
|
|
try {
|
|
this.validateConfigKey(key)
|
|
const serializedValue = this.serializeValue(value)
|
|
const config = await SiteConfigModel.set(key, serializedValue)
|
|
results.push(this.formatConfigResponse(config))
|
|
} catch (error) {
|
|
logger.error(`设置配置失败 (${key}):`, error)
|
|
results.push({ key, error: error.message })
|
|
}
|
|
}
|
|
|
|
logger.info(`批量设置配置完成: ${Object.keys(configs).length} 个配置`)
|
|
return results
|
|
} catch (error) {
|
|
logger.error(`批量设置配置失败:`, error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 删除配置
|
|
* @param {string} key - 配置键
|
|
* @returns {Promise<boolean>} 删除结果
|
|
*/
|
|
static async delete(key) {
|
|
try {
|
|
const result = await SiteConfigModel.deleteByKey(key)
|
|
|
|
logger.info(`配置删除成功: ${key}`)
|
|
return result > 0
|
|
} catch (error) {
|
|
logger.error(`删除配置失败 (${key}):`, error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 检查配置是否存在
|
|
* @param {string} key - 配置键
|
|
* @returns {Promise<boolean>} 是否存在
|
|
*/
|
|
static async has(key) {
|
|
try {
|
|
return await SiteConfigModel.hasKey(key)
|
|
} catch (error) {
|
|
logger.error(`检查配置是否存在失败 (${key}):`, error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取配置统计
|
|
* @returns {Promise<Object>} 统计信息
|
|
*/
|
|
static async getStats() {
|
|
try {
|
|
return await SiteConfigModel.getConfigStats()
|
|
} catch (error) {
|
|
logger.error(`获取配置统计失败:`, error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取站点基本信息配置
|
|
* @returns {Promise<Object>} 站点基本信息
|
|
*/
|
|
static async getSiteInfo() {
|
|
try {
|
|
const keys = [
|
|
'site_name',
|
|
'site_description',
|
|
'site_keywords',
|
|
'site_author',
|
|
'site_url',
|
|
'site_logo',
|
|
'site_favicon',
|
|
'site_theme',
|
|
'site_language',
|
|
'site_timezone'
|
|
]
|
|
|
|
const configs = await this.getMany(keys)
|
|
|
|
return {
|
|
name: configs.site_name || '我的网站',
|
|
description: configs.site_description || '',
|
|
keywords: configs.site_keywords || '',
|
|
author: configs.site_author || '',
|
|
url: configs.site_url || '',
|
|
logo: configs.site_logo || '',
|
|
favicon: configs.site_favicon || '',
|
|
theme: configs.site_theme || 'default',
|
|
language: configs.site_language || 'zh-CN',
|
|
timezone: configs.site_timezone || 'Asia/Shanghai'
|
|
}
|
|
} catch (error) {
|
|
logger.error(`获取站点基本信息失败:`, error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 设置站点基本信息配置
|
|
* @param {Object} siteInfo - 站点信息
|
|
* @returns {Promise<Object>} 设置结果
|
|
*/
|
|
static async setSiteInfo(siteInfo) {
|
|
try {
|
|
const configs = {}
|
|
|
|
if (siteInfo.name) configs.site_name = siteInfo.name
|
|
if (siteInfo.description) configs.site_description = siteInfo.description
|
|
if (siteInfo.keywords) configs.site_keywords = siteInfo.keywords
|
|
if (siteInfo.author) configs.site_author = siteInfo.author
|
|
if (siteInfo.url) configs.site_url = siteInfo.url
|
|
if (siteInfo.logo) configs.site_logo = siteInfo.logo
|
|
if (siteInfo.favicon) configs.site_favicon = siteInfo.favicon
|
|
if (siteInfo.theme) configs.site_theme = siteInfo.theme
|
|
if (siteInfo.language) configs.site_language = siteInfo.language
|
|
if (siteInfo.timezone) configs.site_timezone = siteInfo.timezone
|
|
|
|
return await this.setMany(configs)
|
|
} catch (error) {
|
|
logger.error(`设置站点基本信息失败:`, error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取邮件配置
|
|
* @returns {Promise<Object>} 邮件配置
|
|
*/
|
|
static async getEmailConfig() {
|
|
try {
|
|
const keys = [
|
|
'email_host',
|
|
'email_port',
|
|
'email_secure',
|
|
'email_user',
|
|
'email_password',
|
|
'email_from',
|
|
'email_name'
|
|
]
|
|
|
|
const configs = await this.getMany(keys)
|
|
|
|
return {
|
|
host: configs.email_host || '',
|
|
port: parseInt(configs.email_port) || 587,
|
|
secure: configs.email_secure === 'true',
|
|
user: configs.email_user || '',
|
|
password: configs.email_password || '',
|
|
from: configs.email_from || '',
|
|
name: configs.email_name || ''
|
|
}
|
|
} catch (error) {
|
|
logger.error(`获取邮件配置失败:`, error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 设置邮件配置
|
|
* @param {Object} emailConfig - 邮件配置
|
|
* @returns {Promise<Object>} 设置结果
|
|
*/
|
|
static async setEmailConfig(emailConfig) {
|
|
try {
|
|
const configs = {}
|
|
|
|
if (emailConfig.host) configs.email_host = emailConfig.host
|
|
if (emailConfig.port) configs.email_port = emailConfig.port.toString()
|
|
if (emailConfig.secure !== undefined) configs.email_secure = emailConfig.secure.toString()
|
|
if (emailConfig.user) configs.email_user = emailConfig.user
|
|
if (emailConfig.password) configs.email_password = emailConfig.password
|
|
if (emailConfig.from) configs.email_from = emailConfig.from
|
|
if (emailConfig.name) configs.email_name = emailConfig.name
|
|
|
|
return await this.setMany(configs)
|
|
} catch (error) {
|
|
logger.error(`设置邮件配置失败:`, error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取系统配置
|
|
* @returns {Promise<Object>} 系统配置
|
|
*/
|
|
static async getSystemConfig() {
|
|
try {
|
|
const keys = [
|
|
'maintenance_mode',
|
|
'registration_enabled',
|
|
'email_verification_required',
|
|
'max_upload_size',
|
|
'allowed_file_types',
|
|
'session_timeout',
|
|
'password_min_length',
|
|
'login_attempts_limit'
|
|
]
|
|
|
|
const configs = await this.getMany(keys)
|
|
|
|
return {
|
|
maintenanceMode: configs.maintenance_mode === 'true',
|
|
registrationEnabled: configs.registration_enabled !== 'false',
|
|
emailVerificationRequired: configs.email_verification_required === 'true',
|
|
maxUploadSize: parseInt(configs.max_upload_size) || 10485760, // 10MB
|
|
allowedFileTypes: configs.allowed_file_types ? configs.allowed_file_types.split(',') : ['jpg', 'jpeg', 'png', 'gif', 'pdf'],
|
|
sessionTimeout: parseInt(configs.session_timeout) || 3600, // 1小时
|
|
passwordMinLength: parseInt(configs.password_min_length) || 6,
|
|
loginAttemptsLimit: parseInt(configs.login_attempts_limit) || 5
|
|
}
|
|
} catch (error) {
|
|
logger.error(`获取系统配置失败:`, error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 设置系统配置
|
|
* @param {Object} systemConfig - 系统配置
|
|
* @returns {Promise<Object>} 设置结果
|
|
*/
|
|
static async setSystemConfig(systemConfig) {
|
|
try {
|
|
const configs = {}
|
|
|
|
if (systemConfig.maintenanceMode !== undefined) configs.maintenance_mode = systemConfig.maintenanceMode.toString()
|
|
if (systemConfig.registrationEnabled !== undefined) configs.registration_enabled = systemConfig.registrationEnabled.toString()
|
|
if (systemConfig.emailVerificationRequired !== undefined) configs.email_verification_required = systemConfig.emailVerificationRequired.toString()
|
|
if (systemConfig.maxUploadSize) configs.max_upload_size = systemConfig.maxUploadSize.toString()
|
|
if (systemConfig.allowedFileTypes) configs.allowed_file_types = Array.isArray(systemConfig.allowedFileTypes) ? systemConfig.allowedFileTypes.join(',') : systemConfig.allowedFileTypes
|
|
if (systemConfig.sessionTimeout) configs.session_timeout = systemConfig.sessionTimeout.toString()
|
|
if (systemConfig.passwordMinLength) configs.password_min_length = systemConfig.passwordMinLength.toString()
|
|
if (systemConfig.loginAttemptsLimit) configs.login_attempts_limit = systemConfig.loginAttemptsLimit.toString()
|
|
|
|
return await this.setMany(configs)
|
|
} catch (error) {
|
|
logger.error(`设置系统配置失败:`, error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 重置配置为默认值
|
|
* @param {Array} keys - 要重置的配置键数组(可选,默认重置所有)
|
|
* @returns {Promise<Object>} 重置结果
|
|
*/
|
|
static async resetToDefaults(keys = null) {
|
|
try {
|
|
const defaultConfigs = {
|
|
site_name: '我的网站',
|
|
site_description: '欢迎来到我的网站',
|
|
site_keywords: '网站,博客,个人网站',
|
|
site_author: '网站管理员',
|
|
site_url: 'http://localhost:3000',
|
|
site_theme: 'default',
|
|
site_language: 'zh-CN',
|
|
site_timezone: 'Asia/Shanghai',
|
|
maintenance_mode: 'false',
|
|
registration_enabled: 'true',
|
|
email_verification_required: 'false',
|
|
max_upload_size: '10485760',
|
|
allowed_file_types: 'jpg,jpeg,png,gif,pdf',
|
|
session_timeout: '3600',
|
|
password_min_length: '6',
|
|
login_attempts_limit: '5'
|
|
}
|
|
|
|
const configsToReset = keys ?
|
|
Object.fromEntries(keys.filter(key => defaultConfigs[key]).map(key => [key, defaultConfigs[key]])) :
|
|
defaultConfigs
|
|
|
|
return await this.setMany(configsToReset)
|
|
} catch (error) {
|
|
logger.error(`重置配置为默认值失败:`, error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 验证配置键
|
|
* @param {string} key - 配置键
|
|
*/
|
|
static validateConfigKey(key) {
|
|
if (!key || typeof key !== 'string') {
|
|
throw new Error("配置键不能为空")
|
|
}
|
|
|
|
if (key.length > 100) {
|
|
throw new Error("配置键长度不能超过100个字符")
|
|
}
|
|
|
|
if (!/^[a-zA-Z0-9_]+$/.test(key)) {
|
|
throw new Error("配置键只能包含字母、数字和下划线")
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 序列化值
|
|
* @param {*} value - 要序列化的值
|
|
* @returns {string} 序列化后的字符串
|
|
*/
|
|
static serializeValue(value) {
|
|
if (value === null || value === undefined) {
|
|
return ''
|
|
}
|
|
|
|
if (typeof value === 'string') {
|
|
return value
|
|
}
|
|
|
|
if (typeof value === 'number' || typeof value === 'boolean') {
|
|
return value.toString()
|
|
}
|
|
|
|
return JSON.stringify(value)
|
|
}
|
|
|
|
/**
|
|
* 反序列化值
|
|
* @param {string} value - 要反序列化的字符串
|
|
* @returns {*} 反序列化后的值
|
|
*/
|
|
static deserializeValue(value) {
|
|
if (value === null || value === undefined || value === '') {
|
|
return null
|
|
}
|
|
|
|
// 尝试解析为JSON
|
|
try {
|
|
return JSON.parse(value)
|
|
} catch (e) {
|
|
// 如果不是有效的JSON,返回原字符串
|
|
return value
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 格式化配置响应数据
|
|
* @param {Object} config - 配置数据
|
|
* @returns {Object} 格式化后的配置数据
|
|
*/
|
|
static formatConfigResponse(config) {
|
|
return {
|
|
...config,
|
|
// 确保数字字段为数字类型
|
|
id: parseInt(config.id),
|
|
// 反序列化值
|
|
value: this.deserializeValue(config.value),
|
|
// 格式化日期字段
|
|
created_at: config.created_at,
|
|
updated_at: config.updated_at
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 导出配置
|
|
* @returns {Promise<Object>} 导出的配置
|
|
*/
|
|
static async exportConfig() {
|
|
try {
|
|
const configs = await this.getAll()
|
|
return {
|
|
exported_at: new Date().toISOString(),
|
|
configs
|
|
}
|
|
} catch (error) {
|
|
logger.error(`导出配置失败:`, error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 导入配置
|
|
* @param {Object} configData - 配置数据
|
|
* @returns {Promise<Object>} 导入结果
|
|
*/
|
|
static async importConfig(configData) {
|
|
try {
|
|
if (!configData || !configData.configs) {
|
|
throw new Error("无效的配置数据")
|
|
}
|
|
|
|
const results = await this.setMany(configData.configs)
|
|
|
|
logger.info(`配置导入完成: ${Object.keys(configData.configs).length} 个配置`)
|
|
return {
|
|
success: results.filter(r => !r.error).length,
|
|
failed: results.filter(r => r.error).length,
|
|
results
|
|
}
|
|
} catch (error) {
|
|
logger.error(`导入配置失败:`, error)
|
|
throw error
|
|
}
|
|
}
|
|
}
|
|
|
|
export default SiteConfigService
|
|
export { SiteConfigService }
|
|
|
|
|