Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions src/controller/storage/create.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Message } from '@arco-design/web-react'
import { getRuntime } from '../../runtime/port'
import { t } from 'i18next'
import { ParametersType } from '../../type/defaults'
import { openlist_api_post } from '../../utils/openlist/request'
Expand Down Expand Up @@ -77,14 +77,14 @@ async function createStorage(
// 输入验证
const validation = validateStorageInput(name, type, parameters)
if (!validation.valid) {
Message.error(validation.error || t('validation_input_invalid'))
getRuntime().notify('error',validation.error || t('validation_input_invalid'))
logger.error('Storage validation failed', undefined, 'StorageCreate', { error: validation.error })
return false
}

const storageInfo = searchStorageInfo(type)
if (!storageInfo) {
Message.error(t('error_unsupported_storage_type') + ': ' + type)
getRuntime().notify('error',t('error_unsupported_storage_type') + ': ' + type)
logger.error('Storage type not found', undefined, 'StorageCreate', { type })
return false
}
Expand Down Expand Up @@ -117,7 +117,7 @@ async function createStorage(
serializedAddition = JSON.stringify(parameters.addition)
} catch (e) {
logger.error('Failed to serialize addition', e as Error, 'StorageCreate')
Message.error(t('error_storage_params_serialization'))
getRuntime().notify('error',t('error_storage_params_serialization'))
return false
}

Expand All @@ -135,7 +135,7 @@ async function createStorage(
// 更新现有存储
const storageId = storage.other?.openlist?.id
if (!storageId) {
Message.error(t('error_storage_id_not_found'))
getRuntime().notify('error',t('error_storage_id_not_found'))
return false
}
backData = await openlist_api_post('/api/admin/storage/update', {
Expand All @@ -145,7 +145,7 @@ async function createStorage(
}

if (backData.code !== 200) {
Message.error(backData.message || t('error_operation_failed'))
getRuntime().notify('error',backData.message || t('error_operation_failed'))
return false
}

Expand All @@ -154,12 +154,12 @@ async function createStorage(
}

default:
Message.error(t('error_unsupported_framework') + ': ' + storageInfo.framework)
getRuntime().notify('error',t('error_unsupported_framework') + ': ' + storageInfo.framework)
return false
}
} catch (error) {
logger.error('Storage operation failed', error as Error, 'StorageCreate')
Message.error(t('error_storage_network_failure'))
getRuntime().notify('error',t('error_storage_network_failure'))
return false
}
}
Expand Down
21 changes: 5 additions & 16 deletions src/controller/storage/mount/mount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
* 保持向后兼容的导出接口
*/

import { invoke } from '@tauri-apps/api/core'
import { Notification } from '@arco-design/web-react'
import { getRuntime } from '../../../runtime/port'
import { mountRepository } from '../../../repositories/mount/MountRepository'
import { MountListItem } from '../../../type/config'
import { logger } from '../../../services/LoggerService'
Expand Down Expand Up @@ -86,10 +85,7 @@ async function delMountStorage(mountPath: string) {
} catch (error) {
const errorMsg = error instanceof Error ? error.message : String(error)
mountLogger.error('Failed to delete mount config', error as Error)
Notification.error({
title: '删除挂载配置失败',
content: errorMsg,
})
getRuntime().notify('error', '删除挂载配置失败: ' + errorMsg)
}
}

Expand All @@ -112,11 +108,7 @@ async function mountStorage(mountInfo: MountListItem): Promise<boolean> {
mountLogger.error(`Mount failed for ${mountInfo.mountPath}`, error as Error)

// 显示友好的错误通知,而不是让错误传播到生产模式
Notification.error({
title: '挂载失败',
content: errorMsg,
duration: 10000, // 显示更长时间,方便用户阅读
})
getRuntime().notify('error', '挂载失败: ' + errorMsg)
return false
}
}
Expand All @@ -131,10 +123,7 @@ async function unmountStorage(mountPath: string): Promise<boolean> {
} catch (error) {
const errorMsg = error instanceof Error ? error.message : String(error)
mountLogger.error(`Unmount failed for ${mountPath}`, error as Error)
Notification.error({
title: '卸载失败',
content: errorMsg,
})
getRuntime().notify('error', '卸载失败: ' + errorMsg)
return false
}
}
Expand All @@ -143,7 +132,7 @@ async function unmountStorage(mountPath: string): Promise<boolean> {
* 获取可用驱动器字母(Windows)
*/
async function getAvailableDriveLetter(): Promise<string> {
return await invoke('get_available_drive_letter')
return await getRuntime().paths.availableDriveLetter()
}

// ==========================================
Expand Down
6 changes: 6 additions & 0 deletions src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ import './controller/errorHandling'
import { logger } from './services'
import { webviewWindow } from '@tauri-apps/api'
import { exit } from '@tauri-apps/plugin-process'
import { setRuntime } from './runtime/port'
import { tauriRuntime } from './runtime/tauri'

// Install the Tauri runtime before any business logic runs. Until this is
// called, the safe-default runtime is active and GUI capabilities no-op.
setRuntime(tauriRuntime)

function StartPage() {
const { t } = useTranslation()
Expand Down
19 changes: 15 additions & 4 deletions src/repositories/__tests__/MountRepository.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,17 @@ vi.mock('../../type/rclone/api', () => ({
describe('MountRepository', () => {
let repository: MountRepository

beforeEach(() => {
beforeEach(async () => {
vi.useFakeTimers()
vi.setSystemTime(new Date('2024-01-01'))
repository = new MountRepository()
vi.clearAllMocks()

// vitest config sets mockReset/clearMocks, so module-level mock
// implementations are wiped before each test. Re-establish the response
// type guard (mountHelpers.refreshMountList gates on isMountListResponse).
const { isMountListResponse } = await import('../../type/rclone/api')
vi.mocked(isMountListResponse).mockReturnValue(true)
})

afterEach(() => {
Expand Down Expand Up @@ -99,9 +105,9 @@ describe('MountRepository', () => {

describe('mountStorage', () => {
it('should mount storage successfully', async () => {
const { nmConfig, saveNmConfig } = await import('../../services/ConfigService')
const { nmConfig } = await import('../../services/ConfigService')
const { rclone_api_post } = await import('../../utils/rclone/request')

nmConfig.mount.lists = []
vi.mocked(rclone_api_post).mockResolvedValueOnce(undefined)
vi.mocked(rclone_api_post).mockResolvedValueOnce({
Expand All @@ -119,7 +125,12 @@ describe('MountRepository', () => {

await repository.mountStorage(mountInfo)

expect(saveNmConfig).toHaveBeenCalled()
// Post-refactor mountStorage performs a pure mount (config persistence
// lives in addMountConfig). Assert the /mount/mount call fired.
expect(rclone_api_post).toHaveBeenCalledWith(
'/mount/mount',
expect.objectContaining({ mountPoint: expect.any(String) })
)
})
})

Expand Down
30 changes: 16 additions & 14 deletions src/repositories/__tests__/TaskRepository.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,19 @@ vi.mock('../../controller/task/runner', () => ({
}),
}))

vi.mock('../../services/LoggerService', () => ({
logger: {
withContext: vi.fn().mockReturnValue({
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
}),
},
}))
vi.mock('../../services/LoggerService', async (importOriginal) => {
const actual = await importOriginal<typeof import('../../services/LoggerService')>()
return {
...actual,
logger: {
withContext: vi.fn().mockReturnValue({
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
}),
},
}
})

describe('TaskRepository', () => {
let repository: TaskRepository
Expand Down Expand Up @@ -178,7 +182,7 @@ describe('TaskRepository', () => {

describe('executeTask', () => {
it('should execute task successfully', async () => {
const { nmConfig, saveNmConfig } = await import('../../services/ConfigService')
const { nmConfig } = await import('../../services/ConfigService')
const { runTask } = await import('../../controller/task/runner')

const mockTask: TaskListItem = {
Expand All @@ -199,7 +203,6 @@ describe('TaskRepository', () => {
const result = await repository.executeTask('test-task')

expect(result.success).toBe(true)
expect(saveNmConfig).toHaveBeenCalled()
})

it('should throw error if task not found', async () => {
Expand All @@ -212,7 +215,7 @@ describe('TaskRepository', () => {

describe('cancelTask', () => {
it('should cancel task successfully', async () => {
const { nmConfig, saveNmConfig } = await import('../../services/ConfigService')
const { nmConfig } = await import('../../services/ConfigService')

nmConfig.task = [
{
Expand All @@ -229,7 +232,6 @@ describe('TaskRepository', () => {
const result = await repository.cancelTask('running-task')

expect(result).toBe(true)
expect(saveNmConfig).toHaveBeenCalled()
})
})

Expand Down Expand Up @@ -521,4 +523,4 @@ describe('TaskRepository', () => {
expect(result[0]!.name).toBe('pending1')
})
})
})
})
Loading