5.4 KiB
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
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:
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>.zipusingZipOutputStream
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
BackupEntryfor the Status and Restore screens
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
CoroutineScopetied 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:
- Config is complete (
ConfigManager.isConfigured) - WoW is not running (unless
forceBackupWhileRunningis true)
- Config is complete (
- Delegates to
BackupEnginefor the actual work - After backup, calls
BackupHistory.pruneOldBackups() - Exposes state as
StateFlow<SchedulerState>:
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: "
- 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
- Set backup time to 1 minute from now, verify backup runs automatically
- Verify WTF and Interface folders are copied correctly to backup destination
- With compression enabled, verify a valid
.zipis created - With WoW running and force=false, verify backup is skipped with notification
- With WoW running and force=true, verify backup proceeds
- Verify old backups are pruned when exceeding history count
- "Backup Now" from tray menu triggers immediate backup
- Notifications appear for start/complete/fail (when enabled)
- Check logs for detailed backup operation entries