- 完成 Repository 层实现,包括 BaseRepository、ConfigRepository、 StorageRepository、MountRepository、TaskRepository - 迁移所有 console.log/warn/error 到统一的 LoggerService - 实现 MountRepository 的原子性更新逻辑,支持配置变更检测 - 实现 TaskRepository 的任务状态管理和生命周期控制 - 添加完整的单元测试覆盖 StorageManager、FileManager、 TransferService 及各 Repository 模块 - 新增 CacheManager 的 localStorage 持久化功能 - 优化 ChunkTransferService 的并发控制和信号量实现 - 修复 MountRepository ID 生成逻辑中的特殊字符冲突问题 - 实现 TaskRepository.update 的真正就地更新而非删除重建 - 补充完整的类型定义文档和 JSDoc 注释
14 KiB
NetMount Architecture Documentation
Overview
NetMount is a unified cloud storage management and mounting application built with:
- Frontend: React + TypeScript + Vite
- Backend: Tauri (Rust)
- Dependencies: Rclone, OpenList
Architecture Layers
┌─────────────────────────────────────────────────────────────┐
│ Presentation Layer │
│ (React Components in src/page) │
└─────────────────────────────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────────────┐
│ Controller Layer │
│ (Business logic in src/controller/*) │
└─────────────────────────────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────────────┐
│ Service Layer │
│ (Core business services in src/services/*) │
│ - storage/: Storage management, file operations, transfers │
│ - ConfigService.ts: Configuration management │
│ - hook/: Event hooks │
│ - rclone/: Rclone integration │
│ - openlist/: OpenList integration │
└─────────────────────────────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────────────┐
│ Repository Layer │
│ (Data access abstraction - Future) │
└─────────────────────────────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────────────┐
│ Infrastructure Layer │
│ - utils/rclone/: Rclone API calls │
│ - utils/openlist/: OpenList API calls │
│ - utils/: Utility functions │
│ - type/: TypeScript type definitions │
│ - constants/: Application constants │
└─────────────────────────────────────────────────────────────┘
Module Structure
Storage Module (src/services/storage/)
The storage module has been refactored from a monolithic file into focused sub-modules:
StorageManager.ts
Core storage management operations:
reupStorage()- Refresh storage list from rclone/OpenListdelStorage()- Delete a storagegetStorageParams()- Get storage configurationsearchStorage()- Find storage by namefilterHideStorage()- Filter out hidden storagesconvertStoragePath()- Convert paths between formatsgetStorageSpace()- Get storage capacity infoformatPathRclone()- Format paths for rclonegetFileName()- Extract filename from path
FileManager.ts
File operations:
getFileList()- List files in a directorydelFile()- Delete a filedelDir()- Delete a directorymkDir()- Create a directoryuploadFileRequest()- Upload files with progress
TransferService.ts
Transfer operations between storages:
copyFile()- Copy filescopyDir()- Copy directoriesmoveFile()- Move filesmoveDir()- Move directoriessync()- Sync directories (with bisync option)
StorageService.ts
Callback registration pattern to avoid circular dependencies:
registerDeleteStorage()- Register delete implementationdeleteStorage()- Call registered delete function
ConfigService (src/services/ConfigService.ts)
Encapsulates global state (nmConfig, osInfo) with:
- Controlled access via getter/setter methods
- Subscription pattern for config changes
- Type-safe configuration updates
- Callback registration pattern to avoid circular dependencies
- Persistence (load/save to disk)
Backward Compatibility: Original exports (nmConfig, osInfo) still work via getters.
Repository Layer (src/repositories/)
The Repository layer abstracts data access operations, decoupling business logic from data sources:
Purpose
- Encapsulate all Tauri invoke calls
- Provide type-safe data access
- Enable caching and retry mechanisms
- Support testing with mock repositories
- Emit data change events
BaseRepository (base/BaseRepository.ts)
Abstract base class providing:
invokeCommand<R>()- Unified Tauri invoke wrapper with retry and loggingclearCache()- Cache managementaddChangeListener()- Data change subscriptionvalidate()- Entity validation helper
ConfigRepository (config/ConfigRepository.ts)
Configuration data access:
getConfig()- Load full configurationsaveConfig()- Persist configurationupdatePartialConfig()- Merge partial updatesgetConfigPath()/setConfigPath()- Path-based accessgetOsInfo()- Operating system info
StorageRepository (storage/StorageRepository.ts)
Storage data access:
getAll()- List all storagesgetById()- Get specific storagecreate()/update()/delete()- CRUD operationsgetStorageSpace()- Storage capacity infogetVisibleStorages()- Filter hidden storagesrefreshStorageList()- Sync from Rclone/OpenList
Usage Example
import { configRepository, storageRepository } from '@/repositories'
// Get configuration
const config = await configRepository.getConfig()
// Update configuration path
await configRepository.setConfigPath('settings.themeMode', 'dark')
// List storages
const storages = await storageRepository.getAll()
// Create storage
await storageRepository.create({
name: 'my-storage',
type: 's3',
framework: 'rclone'
})
// Subscribe to data changes
const unsubscribe = storageRepository.addChangeListener((event) => {
console.log(`Storage ${event.id} ${event.type}`)
})
Design Benefits
- Testability: Mock repositories in tests without touching Tauri
- Decoupling: Business logic isolated from data sources
- Caching: Automatic caching with configurable timeout
- Logging: Unified logging via LoggerService
- Retry: Automatic retry on transient failures
- Events: React to data changes in real-time
Utils Module (src/utils/)
Refactored into focused sub-modules:
format/
formatSize()- Format file sizesformatETA()- Format time estimatesformatPath()- Normalize paths
string/
randomString()- Generate random stringstakeMidStr()/takeRightStr()- String extractioncompareVersions()- Version comparison
file/
downloadFile()- File downloadsgetWinFspInstallState()/installWinFsp()/openWinFspInstaller()- WinFsp managementopenUrlInBrowser()- Open external URLsshowPathInExplorer()- Open file explorerfs_exist_dir()/fs_make_dir()- Directory operations
system/
set_devtools_state()- Toggle devtoolsrestartSelf()- Restart applicationsleep()- Async delaygetAvailablePorts()- Port availability
general.ts
isEmptyObject()- Object emptiness checkgetURLSearchParam()- URL parsinggetProperties()- Object property extractionmergeObjects()- Deep object merging
Constants (src/constants/index.ts)
Centralized application constants:
THEME_MODES/LANGUAGE_OPTIONS- UI optionsAPI_TIMEOUT- Request timeout settingsRETRY_CONFIG- Retry logic configurationWATCHDOG_CONFIG- Health check settingsSTATS_CONFIG- Statistics refresh intervalsUPDATE_CONFIG- Update check intervalsDEFAULT_LANGUAGE/FALLBACK_LANGUAGE- i18n defaults
Design Patterns
1. Circular Dependency Resolution
Problem: utils/rclone/process.ts imported delStorage from controller/storage/storage.ts, creating a circular dependency.
Solution: Callback registration pattern via StorageService.ts:
// utils/rclone/process.ts
import { deleteStorage } from '@/services/storage/StorageService'
await deleteStorage(name) // calls registered implementation
// controller/storage/storage.ts
import { registerDeleteStorage } from '@/services/storage/StorageService'
registerDeleteStorage(delStorage)
2. Global State Encapsulation
Problem: nmConfig, rcloneInfo, openlistInfo were exported as mutable objects, allowing any module to modify them.
Solution: ConfigService class with subscription pattern:
// New pattern (recommended)
import { configService } from '@/services/ConfigService'
configService.updateConfig({ settings: { themeMode: 'dark' }})
const unsubscribe = configService.subscribeConfig((new, old) => { ... })
// Backward compatible (still works)
import { nmConfig } from '@/services/config'
nmConfig.settings.themeMode = 'dark' // via getter
3. Module Re-export Pattern
Pattern: New modules export from index, old files re-export for backward compatibility:
// src/services/storage/index.ts - New structure
export { reupStorage, delStorage, ... } from './StorageManager'
export { getFileList, delFile, ... } from './FileManager'
export { copyFile, moveFile, ... } from './TransferService'
// src/controller/storage/storage.ts - Backward compatibility
export * from '../../services/storage'
Import Guidelines
Recommended Imports
Use direct imports to avoid circular dependency warnings:
// ✅ Good - Direct import from specific module
import { filterHideStorage } from '@/services/storage/StorageManager'
import { getFileList } from '@/services/storage/FileManager'
import { copyFile } from '@/services/storage/TransferService'
// ⚠️ Avoid - Index imports can cause circular dependency warnings in some cases
import { filterHideStorage, getFileList } from '@/services/storage'
Deprecated Imports (Still Work)
// Old path - maintained for backward compatibility
import { filterHideStorage } from '@/controller/storage/storage'
Refactoring Progress
Completed (P0 - High Priority)
- ✅ Deleted backup file (
storage.ts.bak) - ✅ Fixed circular dependencies (utils/rclone/process.ts ↔ controller/storage)
- ✅ Merged duplicate constants files
- ✅ Extracted magic numbers to named constants
- ✅ Created ConfigService for state encapsulation
- ✅ Split storage.ts into 3 focused modules
- ✅ Split utils.ts into 6 focused modules
- ✅ Updated imports to use new modular paths
- ✅ Migrated all console.log/warn/error to unified LoggerService
- ✅ Repository layer fully implemented (BaseRepository, ConfigRepository, StorageRepository, MountRepository, TaskRepository)
In Progress (P1 - Medium Priority)
- ✅ Repository layer for data access abstraction (已完成)
- ✅ Migrate console logs to unified LoggerService (已完成)
- ✅ Update remaining code to use new patterns (已完成)
Completed (P1 - Medium Priority)
- ✅ Added comprehensive unit tests for StorageManager
- ✅ Added comprehensive unit tests for FileManager
- ✅ Added comprehensive unit tests for TransferService
- ✅ Added unit tests for StorageRepository
- ✅ Added unit tests for ConfigRepository
- ✅ Added unit tests for TaskRepository
- ✅ Added unit tests for MountRepository
- ✅ Added unit tests for ErrorService
- ✅ Added integration tests for storage operations
Completed (P2 - Low Priority)
- ✅ Performance optimization guidance documented
- ✅ Code quality checks implemented
- ✅ Module size validation (<300 lines)
Code Quality Guidelines
Do's
- ✅ Use named constants instead of magic numbers
- ✅ Import from specific modules for new code
- ✅ Use ConfigService for configuration access
- ✅ Keep modules focused and under 300 lines
- ✅ Use TypeScript strict mode
- ✅ Add JSDoc comments for public APIs
Don'ts
- ❌ Import mutable state objects directly
- ❌ Create new circular dependencies
- ❌ Mix concerns in a single module
- ❌ Use
anytype - ❌ Suppress TypeScript errors with
@ts-ignore
Testing Strategy
Unit Tests (Future)
- Test each repository method in isolation
- Mock API calls
- Test edge cases and error handling
Integration Tests (Future)
- Test component interactions
- Test storage operations end-to-end
- Test configuration persistence
Migration Notes
For New Features
- Import from
@/services/storage/*directly - Use
ConfigServicefor configuration - Follow the module boundaries
For Existing Code
- No immediate changes required
- Gradual migration encouraged
- Old imports continue to work