This commit is contained in:
2026-08-14 16:34:17 +08:00
parent e0b0e1d8c8
commit f63acae353
53 changed files with 2488 additions and 350 deletions
+88 -4
View File
@@ -77,6 +77,13 @@
<el-form-item label="数据库名"><el-input v-model.trim="config.data.database.name" /></el-form-item>
<el-form-item label="SQLite 目录"><el-input v-model.trim="config.data.database.path" /></el-form-item>
<el-form-item label="连接参数"><el-input v-model.trim="config.data.database.config" /></el-form-item>
<el-form-item label="表前缀"><el-input v-model.trim="config.data.database.prefix" /></el-form-item>
<el-form-item label="存储引擎"><el-input v-model.trim="config.data.database.engine" placeholder="InnoDB" /></el-form-item>
<el-form-item label="GORM 日志级别"><el-select v-model="config.data.database.log_mode" class="!w-full"><el-option label="Silent" value="silent" /><el-option label="Error" value="error" /><el-option label="Warn" value="warn" /><el-option label="Info" value="info" /></el-select></el-form-item>
<el-form-item label="最大空闲连接"><el-input-number v-model="config.data.database.max_idle_conns" :min="0" class="!w-full" /></el-form-item>
<el-form-item label="最大打开连接"><el-input-number v-model="config.data.database.max_open_conns" :min="0" class="!w-full" /></el-form-item>
<el-form-item label="连接最长复用(秒)"><el-input-number v-model="config.data.database.conn_max_lifetime" :min="0" class="!w-full" /></el-form-item>
<el-form-item label="表名选项"><el-switch v-model="config.data.database.singular" active-text="使用单数表名" /></el-form-item>
<p class="md:col-span-2 text-sm text-gray-500">保存后点击“重载服务”,数据库连接会按新配置重建。</p>
</el-form>
</el-tab-pane>
@@ -85,11 +92,59 @@
<el-form label-position="top" class="grid grid-cols-1 md:grid-cols-2 gap-x-5">
<el-form-item label="网络"><el-input v-model.trim="config.data.redis.network" placeholder="tcp" /></el-form-item>
<el-form-item label="地址"><el-input v-model.trim="config.data.redis.addr" placeholder="127.0.0.1:6379;留空关闭 Redis" /></el-form-item>
<el-form-item label="实例名"><el-input v-model.trim="config.data.redis.name" /></el-form-item>
<el-form-item label="密码"><el-input v-model="config.data.redis.password" show-password /></el-form-item>
<el-form-item label="数据库"><el-input-number v-model="config.data.redis.db" :min="0" class="!w-full" /></el-form-item>
<el-form-item label="集群模式"><el-switch v-model="config.data.redis.use_cluster" /></el-form-item>
<el-form-item v-if="config.data.redis.use_cluster" label="集群节点(每行一个)" class="md:col-span-2"><el-input v-model="redisClusterText" type="textarea" :rows="4" /></el-form-item>
<el-form-item label="读取超时"><el-input v-model.trim="config.data.redis.read_timeout" placeholder="例如 200ms" /></el-form-item>
<el-form-item label="写入超时"><el-input v-model.trim="config.data.redis.write_timeout" placeholder="例如 200ms" /></el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="MongoDB / 多数据库" name="advancedDatabase">
<el-form label-position="top" class="grid grid-cols-1 md:grid-cols-2 gap-x-5">
<el-form-item label="MongoDB 开关"><el-switch v-model="config.admin.system.useMongo" active-text="启用 MongoDB" /></el-form-item>
<el-form-item label="数据库名"><el-input v-model.trim="config.data.mongo.database" /></el-form-item>
<el-form-item label="默认集合"><el-input v-model.trim="config.data.mongo.coll" /></el-form-item>
<el-form-item label="连接选项"><el-input v-model.trim="config.data.mongo.options" /></el-form-item>
<el-form-item label="用户名"><el-input v-model.trim="config.data.mongo.username" /></el-form-item>
<el-form-item label="密码"><el-input v-model="config.data.mongo.password" show-password /></el-form-item>
<el-form-item label="认证库"><el-input v-model.trim="config.data.mongo.auth_source" /></el-form-item>
<el-form-item label="最小连接池"><el-input-number v-model="config.data.mongo.min_pool_size" :min="0" class="!w-full" /></el-form-item>
<el-form-item label="最大连接池"><el-input-number v-model="config.data.mongo.max_pool_size" :min="0" class="!w-full" /></el-form-item>
<el-form-item label="连接超时(毫秒)"><el-input-number v-model="config.data.mongo.connect_timeout_ms" :min="0" class="!w-full" /></el-form-item>
<el-form-item label="Socket 超时(毫秒)"><el-input-number v-model="config.data.mongo.socket_timeout_ms" :min="0" class="!w-full" /></el-form-item>
<el-form-item label="MongoDB 节点 JSON" class="md:col-span-2"><el-input v-model="mongoHostsText" type="textarea" :rows="4" placeholder='[{"host":"127.0.0.1","port":"27017"}]' /></el-form-item>
<el-form-item label="多数据库配置 JSON" class="md:col-span-2"><el-input v-model="databaseListText" type="textarea" :rows="6" /></el-form-item>
<el-form-item label="多 Redis 配置 JSON" class="md:col-span-2"><el-input v-model="redisListText" type="textarea" :rows="6" /></el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="日志" name="logging">
<el-form label-position="top" class="grid grid-cols-1 md:grid-cols-2 gap-x-5">
<el-form-item label="日志级别"><el-select v-model="config.admin.zap.level" class="!w-full"><el-option label="Debug" value="debug" /><el-option label="Info" value="info" /><el-option label="Warn" value="warn" /><el-option label="Error" value="error" /></el-select></el-form-item>
<el-form-item label="格式"><el-select v-model="config.admin.zap.format" class="!w-full"><el-option label="JSON" value="json" /><el-option label="Console" value="console" /></el-select></el-form-item>
<el-form-item label="日志目录"><el-input v-model.trim="config.admin.zap.director" /></el-form-item>
<el-form-item label="日志前缀"><el-input v-model="config.admin.zap.prefix" /></el-form-item>
<el-form-item label="级别编码器"><el-select v-model="config.admin.zap.encode_level" class="!w-full"><el-option label="lowercase" value="LowercaseLevelEncoder" /><el-option label="lowercase color" value="LowercaseColorLevelEncoder" /><el-option label="capital" value="CapitalLevelEncoder" /><el-option label="capital color" value="CapitalColorLevelEncoder" /></el-select></el-form-item>
<el-form-item label="堆栈字段"><el-input v-model.trim="config.admin.zap.stacktrace_key" /></el-form-item>
<el-form-item label="保留天数"><el-input-number v-model="config.admin.zap.retention_day" :min="0" class="!w-full" /></el-form-item>
<el-form-item label="访问日志最大字节"><el-input-number v-model="config.admin.zap.access_log_max_bytes" :min="1024" class="!w-full" /></el-form-item>
<el-form-item label="输出选项" class="md:col-span-2"><div class="flex flex-wrap gap-5"><el-switch v-model="config.admin.zap.log_in_console" active-text="控制台" /><el-switch v-model="config.admin.zap.show_line" active-text="调用行" /><el-switch v-model="config.admin.zap.access_req_body" active-text="请求体" /><el-switch v-model="config.admin.zap.access_resp_data" active-text="响应体" /><el-switch v-model="config.admin.zap.access_req_headers" active-text="请求头" /></div></el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="应用 / CORS" name="application">
<el-form label-position="top" class="grid grid-cols-1 md:grid-cols-2 gap-x-5">
<el-form-item label="节点标识"><el-input v-model.trim="config.admin.app.node" /></el-form-item>
<el-form-item label="应用 ID"><el-input v-model.trim="config.admin.app.app_id" /></el-form-item>
<el-form-item label="运行环境"><el-input v-model.trim="config.admin.app.env" /></el-form-item>
<el-form-item label="CORS 模式"><el-select v-model="config.admin.cors.mode" class="!w-full"><el-option label="白名单" value="whitelist" /><el-option label="全部放行" value="allow-all" /></el-select></el-form-item>
<el-form-item label="CORS 白名单 JSON" class="md:col-span-2"><el-input v-model="corsRulesText" type="textarea" :rows="7" /></el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="对象存储" name="storage">
<el-form label-position="top" class="grid grid-cols-1 md:grid-cols-2 gap-x-5">
<el-form-item label="存储类型" class="md:col-span-2">
@@ -172,7 +227,9 @@
jwt: { signingKey: '******', expiresTime: '168h', bufferTime: '24h', issuer: 'kra' },
captcha: { keyLong: 6, imgWidth: 240, imgHeight: 80, storeExpiration: '3m' },
local: { storePath: 'uploads/file', pathPrefix: 'uploads/file' }, media: { sessionTtl: 24 },
system: { useRedis: false, useMultipoint: false, useStrictAuth: false, disableAutoMigrate: false },
zap: { level: 'info', prefix: '[kra] ', format: 'json', director: 'logs', encode_level: 'LowercaseLevelEncoder', stacktrace_key: 'stacktrace', show_line: true, log_in_console: true, retention_day: 7, access_req_body: true, access_resp_data: true, access_req_headers: false, access_log_max_bytes: 32768, file_only_modules: [] },
cors: { mode: 'whitelist', whitelist: [] }, app: { node: '', app_id: 'kra', env: 'development' },
system: { useRedis: false, useMultipoint: false, useStrictAuth: false, disableAutoMigrate: false, useMongo: false },
storage: { type: 'local', qiniu: {}, aliyun_oss: {}, huawei_obs: {}, tencent_cos: {}, aws_s3: {}, cloudflare_r2: {}, minio: {} }
},
email: {
@@ -180,8 +237,9 @@
'is-ssl': true, 'is-loginauth': false
},
data: {
database: { driver: 'mysql', source: '', host: '127.0.0.1', port: '3306', user: '', password: '******', name: '', config: '', path: '' },
redis: { network: 'tcp', addr: '', read_timeout: '200ms', write_timeout: '200ms' }
database: { driver: 'mysql', source: '', host: '127.0.0.1', port: '3306', user: '', password: '******', name: '', config: '', path: '', prefix: '', engine: 'InnoDB', log_mode: 'info', max_idle_conns: 10, max_open_conns: 100, conn_max_lifetime: 3600, singular: false },
redis: { network: 'tcp', addr: '', name: 'default', password: '******', db: 0, use_cluster: false, cluster_addrs: [], read_timeout: '200ms', write_timeout: '200ms' },
database_list: [], redis_list: [], mongo: { coll: '', options: '', database: '', username: '', password: '******', auth_source: '', min_pool_size: 0, max_pool_size: 0, connect_timeout_ms: 0, socket_timeout_ms: 0, is_zap: false, hosts: [] }
}
})
const storageTypes = [
@@ -192,6 +250,11 @@
]
const storageKeys = { 'aliyun-oss': 'aliyun_oss', 'huawei-obs': 'huawei_obs', 'tencent-cos': 'tencent_cos', 'aws-s3': 'aws_s3', 'cloudflare-r2': 'cloudflare_r2', minio: 'minio' }
const currentObjectStorage = computed(() => config.value.admin.storage[storageKeys[config.value.admin.storage.type]])
const redisClusterText = ref('')
const mongoHostsText = ref('[]')
const databaseListText = ref('[]')
const redisListText = ref('[]')
const corsRulesText = ref('[]')
const initForm = async () => {
const res = await getSystemConfig()
@@ -203,6 +266,9 @@
local: { ...config.value.admin.local, ...res.data.config.admin?.local }
, media: { ...config.value.admin.media, ...res.data.config.admin?.media }
, system: { ...config.value.admin.system, ...res.data.config.admin?.system }
, zap: { ...config.value.admin.zap, ...res.data.config.admin?.zap }
, cors: { ...config.value.admin.cors, ...res.data.config.admin?.cors }
, app: { ...config.value.admin.app, ...res.data.config.admin?.app }
, storage: { ...config.value.admin.storage, ...res.data.config.admin?.storage,
qiniu: { ...config.value.admin.storage.qiniu, ...res.data.config.admin?.storage?.qiniu },
aliyun_oss: { ...config.value.admin.storage.aliyun_oss, ...res.data.config.admin?.storage?.aliyun_oss },
@@ -216,15 +282,33 @@
email: { ...config.value.email, ...res.data.config.email },
data: {
database: { ...config.value.data.database, ...res.data.config.data?.database },
redis: { ...config.value.data.redis, ...res.data.config.data?.redis }
redis: { ...config.value.data.redis, ...res.data.config.data?.redis },
database_list: res.data.config.data?.database_list || [],
redis_list: res.data.config.data?.redis_list || [],
mongo: { ...config.value.data.mongo, ...res.data.config.data?.mongo }
}
}
redisClusterText.value = (config.value.data.redis.cluster_addrs || []).join('\n')
mongoHostsText.value = JSON.stringify(config.value.data.mongo.hosts || [], null, 2)
databaseListText.value = JSON.stringify(config.value.data.database_list || [], null, 2)
redisListText.value = JSON.stringify(config.value.data.redis_list || [], null, 2)
corsRulesText.value = JSON.stringify(config.value.admin.cors.whitelist || [], null, 2)
}
}
const update = async () => {
saving.value = true
try {
try {
config.value.data.redis.cluster_addrs = redisClusterText.value.split('\n').map(item => item.trim()).filter(Boolean)
config.value.data.mongo.hosts = JSON.parse(mongoHostsText.value || '[]')
config.value.data.database_list = JSON.parse(databaseListText.value || '[]')
config.value.data.redis_list = JSON.parse(redisListText.value || '[]')
config.value.admin.cors.whitelist = JSON.parse(corsRulesText.value || '[]')
} catch {
ElMessage.error('MongoDB 或多数据库 JSON 格式不正确')
return
}
const res = await setSystemConfig({ config: config.value })
if (res.code === 0) {
ElMessage.success('配置保存成功')