mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-08 10:27:46 +08:00
refactor(workspace): enforce layered crate boundaries
This commit is contained in:
@@ -180,20 +180,22 @@ onMounted(() => {
|
||||
|
||||
async function loadConfig() {
|
||||
try {
|
||||
const [moduleStatus, deviceKey, serverUrl, template] = await Promise.all([
|
||||
const [moduleStatus, configs] = await Promise.all([
|
||||
modulesApi.getStatus('bark_push'),
|
||||
adminApi.getSystemConfig(CONFIG_KEYS.device_key),
|
||||
adminApi.getSystemConfig(CONFIG_KEYS.server_url),
|
||||
adminApi.getSystemConfig(CONFIG_KEYS.template),
|
||||
adminApi.getAllSystemConfigs({ cacheTtlMs: 30_000 }),
|
||||
])
|
||||
const configsByKey = new Map(configs.map(config => [config.key, config]))
|
||||
const deviceKey = configsByKey.get(CONFIG_KEYS.device_key)
|
||||
const serverUrl = configsByKey.get(CONFIG_KEYS.server_url)
|
||||
const template = configsByKey.get(CONFIG_KEYS.template)
|
||||
|
||||
enabled.value = moduleStatus.enabled === true
|
||||
deviceKeyIsSet.value = deviceKey.is_set === true
|
||||
deviceKeyIsSet.value = deviceKey?.is_set === true
|
||||
deviceKeyInput.value = ''
|
||||
serverUrlInput.value = typeof serverUrl.value === 'string' && serverUrl.value.trim()
|
||||
serverUrlInput.value = typeof serverUrl?.value === 'string' && serverUrl.value.trim()
|
||||
? serverUrl.value
|
||||
: DEFAULT_SERVER_URL
|
||||
templateInput.value = typeof template.value === 'string' ? template.value : ''
|
||||
templateInput.value = typeof template?.value === 'string' ? template.value : ''
|
||||
} catch (err) {
|
||||
error(parseApiError(err, '加载 Bark 推送配置失败'))
|
||||
log.error('加载 Bark 推送配置失败:', err)
|
||||
|
||||
@@ -532,42 +532,33 @@ onMounted(() => {
|
||||
|
||||
async function loadConfig() {
|
||||
try {
|
||||
const [
|
||||
moduleStatus,
|
||||
emailEnabled,
|
||||
recipients,
|
||||
defaultChannel,
|
||||
items,
|
||||
serverChanModuleStatus,
|
||||
serverChanKey,
|
||||
barkModuleStatus,
|
||||
barkDeviceKey,
|
||||
smtpHost,
|
||||
smtpFromEmail,
|
||||
] = await Promise.all([
|
||||
modulesApi.getStatus('important_notification'),
|
||||
adminApi.getSystemConfig(CONFIG_KEYS.email_enabled),
|
||||
adminApi.getSystemConfig(CONFIG_KEYS.email_recipients),
|
||||
adminApi.getSystemConfig(CONFIG_KEYS.default_channel),
|
||||
adminApi.getSystemConfig(CONFIG_KEYS.items),
|
||||
modulesApi.getStatus('server_chan_push'),
|
||||
adminApi.getSystemConfig(CONFIG_KEYS.server_chan_send_key),
|
||||
modulesApi.getStatus('bark_push'),
|
||||
adminApi.getSystemConfig(CONFIG_KEYS.bark_device_key),
|
||||
adminApi.getSystemConfig('smtp_host'),
|
||||
adminApi.getSystemConfig('smtp_from_email'),
|
||||
const [moduleStatuses, configs] = await Promise.all([
|
||||
modulesApi.getAllStatus(),
|
||||
adminApi.getAllSystemConfigs({ cacheTtlMs: 30_000 }),
|
||||
])
|
||||
const moduleStatus = moduleStatuses.important_notification
|
||||
const serverChanModuleStatus = moduleStatuses.server_chan_push
|
||||
const barkModuleStatus = moduleStatuses.bark_push
|
||||
const configsByKey = new Map(configs.map(config => [config.key, config]))
|
||||
const emailEnabled = configsByKey.get(CONFIG_KEYS.email_enabled)
|
||||
const recipients = configsByKey.get(CONFIG_KEYS.email_recipients)
|
||||
const defaultChannel = configsByKey.get(CONFIG_KEYS.default_channel)
|
||||
const items = configsByKey.get(CONFIG_KEYS.items)
|
||||
const serverChanKey = configsByKey.get(CONFIG_KEYS.server_chan_send_key)
|
||||
const barkDeviceKey = configsByKey.get(CONFIG_KEYS.bark_device_key)
|
||||
const smtpHost = configsByKey.get('smtp_host')
|
||||
const smtpFromEmail = configsByKey.get('smtp_from_email')
|
||||
|
||||
config.value.enabled = moduleStatus.enabled === true
|
||||
config.value.email_enabled = emailEnabled.value === true
|
||||
config.value.email_recipients = normalizeRecipients(recipients.value)
|
||||
config.value.default_channel = normalizeDefaultChannel(defaultChannel.value)
|
||||
config.value.items = normalizeItems(items.value)
|
||||
config.value.email_enabled = emailEnabled?.value === true
|
||||
config.value.email_recipients = normalizeRecipients(recipients?.value)
|
||||
config.value.default_channel = normalizeDefaultChannel(defaultChannel?.value)
|
||||
config.value.items = normalizeItems(items?.value)
|
||||
serverChanStatus.value = serverChanModuleStatus
|
||||
serverChanKeyIsSet.value = serverChanKey.is_set === true
|
||||
serverChanKeyIsSet.value = serverChanKey?.is_set === true
|
||||
barkStatus.value = barkModuleStatus
|
||||
barkKeyIsSet.value = barkDeviceKey.is_set === true
|
||||
smtpConfigured.value = isNonEmptyString(smtpHost.value) && isNonEmptyString(smtpFromEmail.value)
|
||||
barkKeyIsSet.value = barkDeviceKey?.is_set === true
|
||||
smtpConfigured.value = isNonEmptyString(smtpHost?.value) && isNonEmptyString(smtpFromEmail?.value)
|
||||
if (!config.value.items.some(item => item.key === testItemKey.value)) {
|
||||
testItemKey.value = config.value.items[0]?.key || ''
|
||||
}
|
||||
|
||||
@@ -154,16 +154,18 @@ onMounted(() => {
|
||||
|
||||
async function loadConfig() {
|
||||
try {
|
||||
const [moduleStatus, sendKey, template] = await Promise.all([
|
||||
const [moduleStatus, configs] = await Promise.all([
|
||||
modulesApi.getStatus('server_chan_push'),
|
||||
adminApi.getSystemConfig(CONFIG_KEYS.send_key),
|
||||
adminApi.getSystemConfig(CONFIG_KEYS.template),
|
||||
adminApi.getAllSystemConfigs({ cacheTtlMs: 30_000 }),
|
||||
])
|
||||
const configsByKey = new Map(configs.map(config => [config.key, config]))
|
||||
const sendKey = configsByKey.get(CONFIG_KEYS.send_key)
|
||||
const template = configsByKey.get(CONFIG_KEYS.template)
|
||||
|
||||
enabled.value = moduleStatus.enabled === true
|
||||
sendKeyIsSet.value = sendKey.is_set === true
|
||||
sendKeyIsSet.value = sendKey?.is_set === true
|
||||
sendKeyInput.value = ''
|
||||
templateInput.value = typeof template.value === 'string' ? template.value : ''
|
||||
templateInput.value = typeof template?.value === 'string' ? template.value : ''
|
||||
} catch (err) {
|
||||
error(parseApiError(err, '加载 Server 酱推送配置失败'))
|
||||
log.error('加载 Server 酱推送配置失败:', err)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
const { getSystemConfigMock, updateSystemConfigMock, runS3BackupMock, errorMock, successMock } =
|
||||
const { getAllSystemConfigsMock, updateSystemConfigMock, runS3BackupMock, errorMock, successMock } =
|
||||
vi.hoisted(() => ({
|
||||
getSystemConfigMock: vi.fn(),
|
||||
getAllSystemConfigsMock: vi.fn(),
|
||||
updateSystemConfigMock: vi.fn(),
|
||||
runS3BackupMock: vi.fn(),
|
||||
errorMock: vi.fn(),
|
||||
@@ -11,7 +11,7 @@ const { getSystemConfigMock, updateSystemConfigMock, runS3BackupMock, errorMock,
|
||||
|
||||
vi.mock('@/api/admin', () => ({
|
||||
adminApi: {
|
||||
getSystemConfig: getSystemConfigMock,
|
||||
getAllSystemConfigs: getAllSystemConfigsMock,
|
||||
updateSystemConfig: updateSystemConfigMock,
|
||||
runS3Backup: runS3BackupMock,
|
||||
},
|
||||
@@ -28,7 +28,7 @@ import { useS3BackupConfig } from '../composables/useS3BackupConfig'
|
||||
|
||||
describe('useS3BackupConfig', () => {
|
||||
beforeEach(() => {
|
||||
getSystemConfigMock.mockReset()
|
||||
getAllSystemConfigsMock.mockReset()
|
||||
updateSystemConfigMock.mockReset()
|
||||
runS3BackupMock.mockReset()
|
||||
errorMock.mockReset()
|
||||
@@ -36,15 +36,10 @@ describe('useS3BackupConfig', () => {
|
||||
})
|
||||
|
||||
it('loads write-only secret as configured without exposing the value', async () => {
|
||||
getSystemConfigMock.mockImplementation(async (key: string) => {
|
||||
if (key === 'backup_s3_secret_access_key') {
|
||||
return { key, value: null, is_set: true }
|
||||
}
|
||||
if (key === 'backup_s3_scope') {
|
||||
return { key, value: 'data' }
|
||||
}
|
||||
return { key, value: null }
|
||||
})
|
||||
getAllSystemConfigsMock.mockResolvedValue([
|
||||
{ key: 'backup_s3_secret_access_key', value: null, is_set: true },
|
||||
{ key: 'backup_s3_scope', value: 'data' },
|
||||
])
|
||||
|
||||
const backup = useS3BackupConfig()
|
||||
await backup.loadS3BackupConfig()
|
||||
@@ -55,12 +50,9 @@ describe('useS3BackupConfig', () => {
|
||||
})
|
||||
|
||||
it('keeps an existing secret when saving with an empty secret field', async () => {
|
||||
getSystemConfigMock.mockImplementation(async (key: string) => {
|
||||
if (key === 'backup_s3_secret_access_key') {
|
||||
return { key, value: null, is_set: true }
|
||||
}
|
||||
return { key, value: null }
|
||||
})
|
||||
getAllSystemConfigsMock.mockResolvedValue([
|
||||
{ key: 'backup_s3_secret_access_key', value: null, is_set: true },
|
||||
])
|
||||
updateSystemConfigMock.mockResolvedValue({})
|
||||
|
||||
const backup = useS3BackupConfig()
|
||||
@@ -76,15 +68,10 @@ describe('useS3BackupConfig', () => {
|
||||
|
||||
it('reloads server state when saving fails before writing a new secret', async () => {
|
||||
let loadRound = 0
|
||||
getSystemConfigMock.mockImplementation(async (key: string) => {
|
||||
if (key === 'backup_s3_secret_access_key') {
|
||||
return { key, value: null, is_set: false }
|
||||
}
|
||||
if (key === 'backup_s3_bucket') {
|
||||
return { key, value: loadRound === 0 ? 'old-bucket' : 'server-bucket' }
|
||||
}
|
||||
return { key, value: null }
|
||||
})
|
||||
getAllSystemConfigsMock.mockImplementation(async () => [
|
||||
{ key: 'backup_s3_secret_access_key', value: null, is_set: false },
|
||||
{ key: 'backup_s3_bucket', value: loadRound === 0 ? 'old-bucket' : 'server-bucket' },
|
||||
])
|
||||
updateSystemConfigMock.mockImplementation(async (key: string) => {
|
||||
if (key === 'backup_s3_bucket') {
|
||||
loadRound += 1
|
||||
|
||||
@@ -143,38 +143,37 @@ export function useS3BackupConfig() {
|
||||
loading.value = true
|
||||
try {
|
||||
const next = defaultS3BackupConfig()
|
||||
await Promise.all(CONFIG_KEYS.map(async (key) => {
|
||||
try {
|
||||
const response = await adminApi.getSystemConfig(key)
|
||||
const field = FIELD_BY_CONFIG_KEY[key]
|
||||
if (field === 'secretAccessKey') {
|
||||
next.secretAccessKey = ''
|
||||
next.secretAccessKeyIsSet = !!response.is_set
|
||||
return
|
||||
}
|
||||
if (response.value === null || response.value === undefined) return
|
||||
if (field === 'enabled' || field === 'pathStyle') {
|
||||
next[field] = booleanValue(response.value, next[field])
|
||||
} else if (
|
||||
field === 'scheduleInterval' ||
|
||||
field === 'scheduleMinute' ||
|
||||
field === 'scheduleHour' ||
|
||||
field === 'scheduleWeekday' ||
|
||||
field === 'scheduleMonthDay' ||
|
||||
field === 'retentionCount'
|
||||
) {
|
||||
next[field] = numberValue(response.value, next[field])
|
||||
} else if (field === 'scope') {
|
||||
next.scope = scopeValue(response.value, next.scope)
|
||||
} else if (field === 'scheduleUnit') {
|
||||
next.scheduleUnit = scheduleUnitValue(response.value, next.scheduleUnit)
|
||||
} else {
|
||||
next[field] = stringValue(response.value, next[field] as string)
|
||||
}
|
||||
} catch {
|
||||
// 单个配置缺失时使用默认值
|
||||
const configs = await adminApi.getAllSystemConfigs({ cacheTtlMs: 30_000 })
|
||||
const configsByKey = new Map(configs.map((item) => [item.key, item]))
|
||||
for (const key of CONFIG_KEYS) {
|
||||
const response = configsByKey.get(key)
|
||||
if (!response) continue
|
||||
const field = FIELD_BY_CONFIG_KEY[key]
|
||||
if (field === 'secretAccessKey') {
|
||||
next.secretAccessKey = ''
|
||||
next.secretAccessKeyIsSet = !!response.is_set
|
||||
continue
|
||||
}
|
||||
}))
|
||||
if (response.value === null || response.value === undefined) continue
|
||||
if (field === 'enabled' || field === 'pathStyle') {
|
||||
next[field] = booleanValue(response.value, next[field])
|
||||
} else if (
|
||||
field === 'scheduleInterval' ||
|
||||
field === 'scheduleMinute' ||
|
||||
field === 'scheduleHour' ||
|
||||
field === 'scheduleWeekday' ||
|
||||
field === 'scheduleMonthDay' ||
|
||||
field === 'retentionCount'
|
||||
) {
|
||||
next[field] = numberValue(response.value, next[field])
|
||||
} else if (field === 'scope') {
|
||||
next.scope = scopeValue(response.value, next.scope)
|
||||
} else if (field === 'scheduleUnit') {
|
||||
next.scheduleUnit = scheduleUnitValue(response.value, next.scheduleUnit)
|
||||
} else {
|
||||
next[field] = stringValue(response.value, next[field] as string)
|
||||
}
|
||||
}
|
||||
config.value = next
|
||||
originalConfig.value = cloneConfig(next)
|
||||
} catch (err) {
|
||||
|
||||
Reference in New Issue
Block a user