Config screen

This commit is contained in:
Rukira 2026-03-04 14:19:19 +00:00
parent ac88e62abe
commit 4b3c512a9d
28 changed files with 2015 additions and 59 deletions

View file

@ -0,0 +1,144 @@
# Feature 3: Backup
## Context
This is the core functionality — automatically backing up WoW's WTF and Interface folders on a daily schedule. The backup runs in the background, respects configuration, and notifies the user of results.
## Dependencies
- **Depends on**: Feature 0 (ConfigManager, logging, platform), Feature 2 (config must be complete)
- **Depended on by**: Feature 4 (Status — displays backup state), Feature 5 (Restore — reads backup history)
## Key Decisions
- **Compression**: ZIP format using `java.util.zip` (no extra deps)
- **Process detection**: `ProcessHandle.allProcesses()` to check if WoW is running
- **Scheduling**: Coroutine-based scheduler, checks every minute if it's time to run
- **Backup naming**: Timestamped folders like `2024-01-15_03-00-00/` (or `.zip`)
## Implementation Steps
### Step 1: WoW process detection
**Files to create:**
- `composeApp/src/jvmMain/kotlin/com/rukira/wowbackup/backup/WoWProcessDetector.kt`
```kotlin
object WoWProcessDetector {
fun isWoWRunning(): Boolean {
return ProcessHandle.allProcesses()
.filter { it.isAlive }
.anyMatch { handle ->
val name = handle.info().command().orElse("").lowercase()
name.contains("world of warcraft") || name.contains("wow.exe") || name.contains("wow-64")
}
}
}
```
### Step 2: File copy engine
**Files to create:**
- `composeApp/src/jvmMain/kotlin/com/rukira/wowbackup/backup/BackupEngine.kt`
Responsibilities:
- Copy selected folders (WTF, Interface) from WoW install to backup destination
- Support two modes: plain copy and ZIP compression
- Report progress: total files count, files copied so far, current file name
- Cancellable via coroutine cancellation
- Return a `BackupResult` (success/failure with details)
Progress model:
```kotlin
data class BackupProgress(
val totalFiles: Int,
val completedFiles: Int,
val currentFile: String,
)
sealed class BackupResult {
data class Success(val backupPath: File, val sizeBytes: Long, val durationMs: Long) : BackupResult()
data class Failure(val reason: String, val exception: Throwable? = null) : BackupResult()
}
```
For plain copy:
- Walk source directories, replicate structure in `<backupDir>/<timestamp>/`
For ZIP:
- Walk source directories, write to `<backupDir>/<timestamp>.zip` using `ZipOutputStream`
### Step 3: Backup history management
**Files to create:**
- `composeApp/src/jvmMain/kotlin/com/rukira/wowbackup/backup/BackupHistory.kt`
Responsibilities:
- List existing backups in the backup directory (sorted by date, newest first)
- Parse timestamps from folder/zip names
- Prune old backups exceeding `backupHistoryCount`
- Return list of `BackupEntry` for the Status and Restore screens
```kotlin
data class BackupEntry(
val path: File,
val timestamp: LocalDateTime,
val isCompressed: Boolean,
val sizeBytes: Long,
)
```
### Step 4: Backup scheduler
**Files to create:**
- `composeApp/src/jvmMain/kotlin/com/rukira/wowbackup/backup/BackupScheduler.kt`
Coroutine-based scheduler that:
- Runs in a `CoroutineScope` tied to the application lifecycle
- Checks every 60 seconds if the current time matches `backupTimeOfDay` (within a 1-minute window)
- Tracks whether today's backup has already run (to avoid duplicate runs)
- Before starting backup, checks:
1. Config is complete (`ConfigManager.isConfigured`)
2. WoW is not running (unless `forceBackupWhileRunning` is true)
- Delegates to `BackupEngine` for the actual work
- After backup, calls `BackupHistory.pruneOldBackups()`
- Exposes state as `StateFlow<SchedulerState>`:
```kotlin
data class SchedulerState(
val lastBackupTime: LocalDateTime? = null,
val lastBackupResult: BackupResult? = null,
val nextBackupTime: LocalDateTime? = null,
val isRunning: Boolean = false,
val currentProgress: BackupProgress? = null,
val isWoWRunning: Boolean = false,
)
```
### Step 5: Notifications integration
**Files to create:**
- `composeApp/src/jvmMain/kotlin/com/rukira/wowbackup/backup/BackupNotifier.kt`
Uses `TrayState.sendNotification()` (from Compose Desktop's Tray) to send native notifications:
- Backup started: "Backup in progress..."
- Backup completed: "Backup complete (X files, Y MB)"
- Backup failed: "Backup failed: <reason>"
- Backup skipped (WoW running): "Backup skipped — WoW is running"
Only sends if `notificationsEnabled` is true in config.
### Step 6: Wire scheduler into app lifecycle
**Files to modify:**
- `composeApp/src/jvmMain/kotlin/com/rukira/wowbackup/main.kt`
Start `BackupScheduler` when app launches. Stop when app exits. Pass `TrayState` to `BackupNotifier`.
### Step 7: Manual backup trigger
Add a "Backup Now" item to the tray context menu that triggers an immediate backup (bypassing the schedule, still respecting the WoW-running check).
**Files to modify:**
- `composeApp/src/jvmMain/kotlin/com/rukira/wowbackup/main.kt` — add menu item
## Verification
1. Set backup time to 1 minute from now, verify backup runs automatically
2. Verify WTF and Interface folders are copied correctly to backup destination
3. With compression enabled, verify a valid `.zip` is created
4. With WoW running and force=false, verify backup is skipped with notification
5. With WoW running and force=true, verify backup proceeds
6. Verify old backups are pruned when exceeding history count
7. "Backup Now" from tray menu triggers immediate backup
8. Notifications appear for start/complete/fail (when enabled)
9. Check logs for detailed backup operation entries