From 23de8a6fc6620e480c2d359e0a515dfe2fd4e2a0 Mon Sep 17 00:00:00 2001 From: Atridad Lahiji Date: Wed, 15 Oct 2025 18:17:19 -0600 Subject: [PATCH] [All Platforms] 2.1.0 - Sync Optimizations --- android/app/build.gradle.kts | 4 +- .../ascently/data/format/BackupFormat.kt | 19 +- .../ascently/data/health/HealthConnectStub.kt | 16 - .../data/repository/ClimbRepository.kt | 10 +- .../ascently/data/sync/DeltaSyncModels.kt | 30 ++ .../atridad/ascently/data/sync/SyncService.kt | 310 +++++++++++++++-- .../ascently/utils/MigrationManager.kt | 4 - .../atridad/openclimb/SyncMergeLogicTest.kt | 4 - docs/astro.config.mjs | 1 + docs/package.json | 2 +- docs/pnpm-lock.yaml | 258 +++++++------- docs/pnpm-workspace.yaml | 3 - docs/src/content/docs/reference/example.md | 51 --- docs/src/content/docs/sync/api-reference.md | 152 +++++++++ docs/src/content/docs/sync/overview.md | 41 ++- docs/src/content/docs/sync/quick-start.md | 166 +++++++-- ios/Ascently.xcodeproj/project.pbxproj | 16 +- .../UserInterfaceState.xcuserstate | Bin 229840 -> 231929 bytes ios/Ascently/ContentView.swift | 1 - ios/Ascently/Models/BackupFormat.swift | 13 - ios/Ascently/Models/DeltaSyncFormat.swift | 26 ++ ios/Ascently/Services/HealthKitService.swift | 3 - ios/Ascently/Services/SyncService.swift | 321 +++++++++++++++++- ios/Ascently/Utils/DataStateManager.swift | 12 +- ios/Ascently/Utils/ImageManager.swift | 1 - ios/Ascently/Utils/ImageNamingUtils.swift | 1 - ios/Ascently/Utils/ZipUtils.swift | 86 +++-- .../ViewModels/ClimbingDataManager.swift | 72 ++-- .../Views/AddEdit/AddEditProblemView.swift | 1 - ios/Ascently/Views/SettingsView.swift | 10 +- ios/AscentlyTests/AscentlyTests.swift | 4 - sync/main.go | 309 ++++++++++++++++- 32 files changed, 1538 insertions(+), 409 deletions(-) create mode 100644 android/app/src/main/java/com/atridad/ascently/data/sync/DeltaSyncModels.kt delete mode 100644 docs/pnpm-workspace.yaml delete mode 100644 docs/src/content/docs/reference/example.md create mode 100644 docs/src/content/docs/sync/api-reference.md create mode 100644 ios/Ascently/Models/DeltaSyncFormat.swift diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index ff92738..e87f432 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -16,8 +16,8 @@ android { applicationId = "com.atridad.ascently" minSdk = 31 targetSdk = 36 - versionCode = 41 - versionName = "2.0.1" + versionCode = 42 + versionName = "2.1.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } diff --git a/android/app/src/main/java/com/atridad/ascently/data/format/BackupFormat.kt b/android/app/src/main/java/com/atridad/ascently/data/format/BackupFormat.kt index 2d96088..ce3513d 100644 --- a/android/app/src/main/java/com/atridad/ascently/data/format/BackupFormat.kt +++ b/android/app/src/main/java/com/atridad/ascently/data/format/BackupFormat.kt @@ -32,13 +32,12 @@ data class BackupGym( val supportedClimbTypes: List, val difficultySystems: List, @kotlinx.serialization.SerialName("customDifficultyGrades") - val customDifficultyGrades: List = emptyList(), + val customDifficultyGrades: List? = null, val notes: String? = null, val createdAt: String, val updatedAt: String ) { companion object { - /** Create BackupGym from native Android Gym model */ fun fromGym(gym: Gym): BackupGym { return BackupGym( id = gym.id, @@ -46,7 +45,7 @@ data class BackupGym( location = gym.location, supportedClimbTypes = gym.supportedClimbTypes, difficultySystems = gym.difficultySystems, - customDifficultyGrades = gym.customDifficultyGrades, + customDifficultyGrades = gym.customDifficultyGrades.ifEmpty { null }, notes = gym.notes, createdAt = gym.createdAt, updatedAt = gym.updatedAt @@ -54,7 +53,6 @@ data class BackupGym( } } - /** Convert to native Android Gym model */ fun toGym(): Gym { return Gym( id = id, @@ -62,7 +60,7 @@ data class BackupGym( location = location, supportedClimbTypes = supportedClimbTypes, difficultySystems = difficultySystems, - customDifficultyGrades = customDifficultyGrades, + customDifficultyGrades = customDifficultyGrades ?: emptyList(), notes = notes, createdAt = createdAt, updatedAt = updatedAt @@ -79,7 +77,7 @@ data class BackupProblem( val description: String? = null, val climbType: ClimbType, val difficulty: DifficultyGrade, - val tags: List = emptyList(), + val tags: List? = null, val location: String? = null, val imagePaths: List? = null, val isActive: Boolean = true, @@ -89,7 +87,6 @@ data class BackupProblem( val updatedAt: String ) { companion object { - /** Create BackupProblem from native Android Problem model */ fun fromProblem(problem: Problem): BackupProblem { return BackupProblem( id = problem.id, @@ -112,7 +109,6 @@ data class BackupProblem( } } - /** Convert to native Android Problem model */ fun toProblem(): Problem { return Problem( id = id, @@ -121,7 +117,7 @@ data class BackupProblem( description = description, climbType = climbType, difficulty = difficulty, - tags = tags, + tags = tags ?: emptyList(), location = location, imagePaths = imagePaths ?: emptyList(), isActive = isActive, @@ -132,7 +128,6 @@ data class BackupProblem( ) } - /** Create a copy with updated image paths for import processing */ fun withUpdatedImagePaths(newImagePaths: List): BackupProblem { return copy(imagePaths = newImagePaths.ifEmpty { null }) } @@ -153,7 +148,6 @@ data class BackupClimbSession( val updatedAt: String ) { companion object { - /** Create BackupClimbSession from native Android ClimbSession model */ fun fromClimbSession(session: ClimbSession): BackupClimbSession { return BackupClimbSession( id = session.id, @@ -170,7 +164,6 @@ data class BackupClimbSession( } } - /** Convert to native Android ClimbSession model */ fun toClimbSession(): ClimbSession { return ClimbSession( id = id, @@ -203,7 +196,6 @@ data class BackupAttempt( val updatedAt: String? = null ) { companion object { - /** Create BackupAttempt from native Android Attempt model */ fun fromAttempt(attempt: Attempt): BackupAttempt { return BackupAttempt( id = attempt.id, @@ -221,7 +213,6 @@ data class BackupAttempt( } } - /** Convert to native Android Attempt model */ fun toAttempt(): Attempt { return Attempt( id = id, diff --git a/android/app/src/main/java/com/atridad/ascently/data/health/HealthConnectStub.kt b/android/app/src/main/java/com/atridad/ascently/data/health/HealthConnectStub.kt index ff08b79..305b1dd 100644 --- a/android/app/src/main/java/com/atridad/ascently/data/health/HealthConnectStub.kt +++ b/android/app/src/main/java/com/atridad/ascently/data/health/HealthConnectStub.kt @@ -66,7 +66,6 @@ class HealthConnectManager(private val context: Context) { } } - /** Check if Health Connect is available on this device */ fun isHealthConnectAvailable(): Flow = flow { try { if (!_isCompatible.value) { @@ -82,10 +81,6 @@ class HealthConnectManager(private val context: Context) { } } - /** - * Enable or disable Health Connect integration and automatically request permissions if - * enabling - */ suspend fun setEnabled(enabled: Boolean) { preferences.edit().putBoolean("enabled", enabled).apply() _isEnabled.value = enabled @@ -105,13 +100,11 @@ class HealthConnectManager(private val context: Context) { } } - /** Update the permissions granted state */ fun setPermissionsGranted(granted: Boolean) { preferences.edit().putBoolean("permissions", granted).apply() _hasPermissions.value = granted } - /** Check if all required permissions are granted */ suspend fun hasAllPermissions(): Boolean { return try { if (!_isCompatible.value || healthConnectClient == null) { @@ -132,7 +125,6 @@ class HealthConnectManager(private val context: Context) { } } - /** Check if Health Connect is ready for use */ suspend fun isReady(): Boolean { return try { if (!_isEnabled.value || !_isCompatible.value || healthConnectClient == null) @@ -148,12 +140,10 @@ class HealthConnectManager(private val context: Context) { } } - /** Get permission request contract */ fun getPermissionRequestContract(): ActivityResultContract, Set> { return PermissionController.createRequestPermissionResultContract() } - /** Get required permissions as strings */ fun getRequiredPermissions(): Set { return try { REQUIRED_PERMISSIONS.map { it }.toSet() @@ -163,7 +153,6 @@ class HealthConnectManager(private val context: Context) { } } - /** Sync a completed climbing session to Health Connect (only when auto-sync is enabled) */ @SuppressLint("RestrictedApi") suspend fun syncCompletedSession( session: ClimbSession, @@ -271,7 +260,6 @@ class HealthConnectManager(private val context: Context) { } } - /** Auto-sync a completed session if enabled - this is the only way to sync sessions */ suspend fun autoSyncCompletedSession( session: ClimbSession, gymName: String, @@ -293,7 +281,6 @@ class HealthConnectManager(private val context: Context) { } } - /** Estimate calories burned during climbing */ private fun estimateCaloriesForClimbing(durationMinutes: Long, attemptCount: Int): Double { val baseCaloriesPerMinute = 8.0 val intensityMultiplier = @@ -305,7 +292,6 @@ class HealthConnectManager(private val context: Context) { return durationMinutes * baseCaloriesPerMinute * intensityMultiplier } - /** Create heart rate data */ @SuppressLint("RestrictedApi") private fun createHeartRateRecord( startTime: Instant, @@ -347,9 +333,7 @@ class HealthConnectManager(private val context: Context) { } } - /** Check if ready for use */ fun isReadySync(): Boolean { return _isEnabled.value && _hasPermissions.value } - } diff --git a/android/app/src/main/java/com/atridad/ascently/data/repository/ClimbRepository.kt b/android/app/src/main/java/com/atridad/ascently/data/repository/ClimbRepository.kt index 6d8260c..7668c36 100644 --- a/android/app/src/main/java/com/atridad/ascently/data/repository/ClimbRepository.kt +++ b/android/app/src/main/java/com/atridad/ascently/data/repository/ClimbRepository.kt @@ -251,23 +251,15 @@ class ClimbRepository(database: AscentlyDatabase, private val context: Context) } } - /** - * Sets the callback for auto-sync functionality. This should be called by the SyncService to - * register itself for auto-sync triggers. - */ fun setAutoSyncCallback(callback: (() -> Unit)?) { autoSyncCallback = callback } - /** - * Triggers auto-sync if enabled. This is called after any data modification to keep data - * synchronized across devices automatically. - */ private fun triggerAutoSync() { autoSyncCallback?.invoke() } - private fun trackDeletion(itemId: String, itemType: String) { + fun trackDeletion(itemId: String, itemType: String) { val currentDeletions = getDeletedItems().toMutableList() val newDeletion = DeletedItem(id = itemId, type = itemType, deletedAt = DateFormatUtils.nowISO8601()) diff --git a/android/app/src/main/java/com/atridad/ascently/data/sync/DeltaSyncModels.kt b/android/app/src/main/java/com/atridad/ascently/data/sync/DeltaSyncModels.kt new file mode 100644 index 0000000..d6f1563 --- /dev/null +++ b/android/app/src/main/java/com/atridad/ascently/data/sync/DeltaSyncModels.kt @@ -0,0 +1,30 @@ +package com.atridad.ascently.data.sync + +import com.atridad.ascently.data.format.BackupAttempt +import com.atridad.ascently.data.format.BackupClimbSession +import com.atridad.ascently.data.format.BackupGym +import com.atridad.ascently.data.format.BackupProblem +import com.atridad.ascently.data.format.DeletedItem +import kotlinx.serialization.Serializable + +/** Request structure for delta sync - sends only changes since last sync */ +@Serializable +data class DeltaSyncRequest( + val lastSyncTime: String, + val gyms: List, + val problems: List, + val sessions: List, + val attempts: List, + val deletedItems: List +) + +/** Response structure for delta sync - receives only changes from server */ +@Serializable +data class DeltaSyncResponse( + val serverTime: String, + val gyms: List, + val problems: List, + val sessions: List, + val attempts: List, + val deletedItems: List +) diff --git a/android/app/src/main/java/com/atridad/ascently/data/sync/SyncService.kt b/android/app/src/main/java/com/atridad/ascently/data/sync/SyncService.kt index 4b0e207..66f4cf8 100644 --- a/android/app/src/main/java/com/atridad/ascently/data/sync/SyncService.kt +++ b/android/app/src/main/java/com/atridad/ascently/data/sync/SyncService.kt @@ -19,6 +19,9 @@ import com.atridad.ascently.utils.ImageNamingUtils import com.atridad.ascently.utils.ImageUtils import java.io.IOException import java.io.Serializable +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Locale import java.util.concurrent.TimeUnit import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -63,6 +66,7 @@ class SyncService(private val context: Context, private val repository: ClimbRep prettyPrint = true ignoreUnknownKeys = true explicitNulls = false + coerceInputValues = true } // State @@ -195,26 +199,33 @@ class SyncService(private val context: Context, private val repository: ClimbRep serverBackup.sessions.isNotEmpty() || serverBackup.attempts.isNotEmpty() - when { - !hasLocalData && hasServerData -> { - Log.d(TAG, "No local data found, performing full restore from server") - val imagePathMapping = syncImagesFromServer(serverBackup) - importBackupToRepository(serverBackup, imagePathMapping) - Log.d(TAG, "Full restore completed") - } - hasLocalData && !hasServerData -> { - Log.d(TAG, "No server data found, uploading local data to server") - uploadData(localBackup) - syncImagesForBackup(localBackup) - Log.d(TAG, "Initial upload completed") - } - hasLocalData && hasServerData -> { - Log.d(TAG, "Both local and server data exist, merging (server wins)") - mergeDataSafely(serverBackup) - Log.d(TAG, "Merge completed") - } - else -> { - Log.d(TAG, "No data to sync") + // If both client and server have been synced before, use delta sync + val lastSyncTimeStr = sharedPreferences.getString(Keys.LAST_SYNC_TIME, null) + if (hasLocalData && hasServerData && lastSyncTimeStr != null) { + Log.d(TAG, "Using delta sync for incremental updates") + performDeltaSync(lastSyncTimeStr) + } else { + when { + !hasLocalData && hasServerData -> { + Log.d(TAG, "No local data found, performing full restore from server") + val imagePathMapping = syncImagesFromServer(serverBackup) + importBackupToRepository(serverBackup, imagePathMapping) + Log.d(TAG, "Full restore completed") + } + hasLocalData && !hasServerData -> { + Log.d(TAG, "No server data found, uploading local data to server") + uploadData(localBackup) + syncImagesForBackup(localBackup) + Log.d(TAG, "Initial upload completed") + } + hasLocalData && hasServerData -> { + Log.d(TAG, "Both local and server data exist, merging (server wins)") + mergeDataSafely(serverBackup) + Log.d(TAG, "Merge completed") + } + else -> { + Log.d(TAG, "No data to sync") + } } } @@ -230,6 +241,265 @@ class SyncService(private val context: Context, private val repository: ClimbRep } } + private suspend fun performDeltaSync(lastSyncTimeStr: String) { + Log.d(TAG, "Starting delta sync with lastSyncTime=$lastSyncTimeStr") + + // Parse last sync time to filter modified items + val lastSyncDate = parseISO8601(lastSyncTimeStr) ?: Date(0) + + // Collect items modified since last sync + val allGyms = repository.getAllGyms().first() + val modifiedGyms = + allGyms + .filter { gym -> parseISO8601(gym.updatedAt)?.after(lastSyncDate) == true } + .map { BackupGym.fromGym(it) } + + val allProblems = repository.getAllProblems().first() + val modifiedProblems = + allProblems + .filter { problem -> + parseISO8601(problem.updatedAt)?.after(lastSyncDate) == true + } + .map { problem -> + val backupProblem = BackupProblem.fromProblem(problem) + val normalizedImagePaths = + problem.imagePaths.mapIndexed { index, _ -> + ImageNamingUtils.generateImageFilename(problem.id, index) + } + if (normalizedImagePaths.isNotEmpty()) { + backupProblem.copy(imagePaths = normalizedImagePaths) + } else { + backupProblem + } + } + + val allSessions = repository.getAllSessions().first() + val modifiedSessions = + allSessions + .filter { session -> + parseISO8601(session.updatedAt)?.after(lastSyncDate) == true + } + .map { BackupClimbSession.fromClimbSession(it) } + + val allAttempts = repository.getAllAttempts().first() + val modifiedAttempts = + allAttempts + .filter { attempt -> + parseISO8601(attempt.createdAt)?.after(lastSyncDate) == true + } + .map { BackupAttempt.fromAttempt(it) } + + val allDeletions = repository.getDeletedItems() + val modifiedDeletions = + allDeletions.filter { item -> + parseISO8601(item.deletedAt)?.after(lastSyncDate) == true + } + + Log.d( + TAG, + "Delta sync sending: gyms=${modifiedGyms.size}, problems=${modifiedProblems.size}, sessions=${modifiedSessions.size}, attempts=${modifiedAttempts.size}, deletions=${modifiedDeletions.size}" + ) + + // Create delta request + val deltaRequest = + DeltaSyncRequest( + lastSyncTime = lastSyncTimeStr, + gyms = modifiedGyms, + problems = modifiedProblems, + sessions = modifiedSessions, + attempts = modifiedAttempts, + deletedItems = modifiedDeletions + ) + + val requestBody = + json.encodeToString(DeltaSyncRequest.serializer(), deltaRequest) + .toRequestBody("application/json".toMediaType()) + + val request = + Request.Builder() + .url("$serverUrl/sync/delta") + .header("Authorization", "Bearer $authToken") + .post(requestBody) + .build() + + val deltaResponse = + withContext(Dispatchers.IO) { + try { + httpClient.newCall(request).execute().use { response -> + if (response.isSuccessful) { + val body = response.body?.string() + if (!body.isNullOrEmpty()) { + json.decodeFromString(DeltaSyncResponse.serializer(), body) + } else { + throw SyncException.InvalidResponse("Empty response body") + } + } else { + handleHttpError(response.code) + } + } + } catch (e: IOException) { + throw SyncException.NetworkError(e.message ?: "Network error") + } + } + + Log.d( + TAG, + "Delta sync received: gyms=${deltaResponse.gyms.size}, problems=${deltaResponse.problems.size}, sessions=${deltaResponse.sessions.size}, attempts=${deltaResponse.attempts.size}, deletions=${deltaResponse.deletedItems.size}" + ) + + // Apply server changes to local data + applyDeltaResponse(deltaResponse) + + // Sync only modified problem images + syncModifiedImages(modifiedProblems) + } + + private fun parseISO8601(dateString: String): Date? { + return try { + val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.US) + format.parse(dateString) + } catch (e: Exception) { + null + } + } + + private suspend fun applyDeltaResponse(response: DeltaSyncResponse) { + // Temporarily disable auto-sync to prevent recursive sync triggers + repository.setAutoSyncCallback(null) + + try { + // Download images for new/modified problems from server + val imagePathMapping = mutableMapOf() + for (problem in response.problems) { + problem.imagePaths?.forEach { imagePath -> + val serverFilename = imagePath.substringAfterLast('/') + try { + val localImagePath = downloadImage(serverFilename) + if (localImagePath != null) { + imagePathMapping[imagePath] = localImagePath + } + } catch (e: Exception) { + Log.w(TAG, "Failed to download image $imagePath: ${e.message}") + } + } + } + + // Merge gyms - check if exists and compare timestamps + val existingGyms = repository.getAllGyms().first() + for (backupGym in response.gyms) { + val existing = existingGyms.find { it.id == backupGym.id } + if (existing == null || backupGym.updatedAt >= existing.updatedAt) { + val gym = backupGym.toGym() + if (existing != null) { + repository.updateGym(gym) + } else { + repository.insertGym(gym) + } + } + } + + // Merge problems + val existingProblems = repository.getAllProblems().first() + for (backupProblem in response.problems) { + val updatedImagePaths = + backupProblem.imagePaths?.map { oldPath -> + imagePathMapping[oldPath] ?: oldPath + } + val problemToMerge = backupProblem.copy(imagePaths = updatedImagePaths) + val problem = problemToMerge.toProblem() + + val existing = existingProblems.find { it.id == backupProblem.id } + if (existing == null || backupProblem.updatedAt >= existing.updatedAt) { + if (existing != null) { + repository.updateProblem(problem) + } else { + repository.insertProblem(problem) + } + } + } + + // Merge sessions + val existingSessions = repository.getAllSessions().first() + for (backupSession in response.sessions) { + val session = backupSession.toClimbSession() + val existing = existingSessions.find { it.id == backupSession.id } + if (existing == null || backupSession.updatedAt >= existing.updatedAt) { + if (existing != null) { + repository.updateSession(session) + } else { + repository.insertSession(session) + } + } + } + + // Merge attempts + val existingAttempts = repository.getAllAttempts().first() + for (backupAttempt in response.attempts) { + val attempt = backupAttempt.toAttempt() + val existing = existingAttempts.find { it.id == backupAttempt.id } + if (existing == null || backupAttempt.createdAt >= existing.createdAt) { + if (existing != null) { + repository.updateAttempt(attempt) + } else { + repository.insertAttempt(attempt) + } + } + } + + // Apply deletions + applyDeletions(response.deletedItems) + + // Update deletion records + val allDeletions = repository.getDeletedItems() + response.deletedItems + repository.clearDeletedItems() + allDeletions.distinctBy { "${it.type}:${it.id}" }.forEach { + repository.trackDeletion(it.id, it.type) + } + } finally { + // Re-enable auto-sync + repository.setAutoSyncCallback { serviceScope.launch { triggerAutoSync() } } + } + } + + private suspend fun applyDeletions( + deletions: List + ) { + val existingGyms = repository.getAllGyms().first() + val existingProblems = repository.getAllProblems().first() + val existingSessions = repository.getAllSessions().first() + val existingAttempts = repository.getAllAttempts().first() + + for (item in deletions) { + when (item.type) { + "gym" -> { + existingGyms.find { it.id == item.id }?.let { repository.deleteGym(it) } + } + "problem" -> { + existingProblems.find { it.id == item.id }?.let { repository.deleteProblem(it) } + } + "session" -> { + existingSessions.find { it.id == item.id }?.let { repository.deleteSession(it) } + } + "attempt" -> { + existingAttempts.find { it.id == item.id }?.let { repository.deleteAttempt(it) } + } + } + } + } + + private suspend fun syncModifiedImages(modifiedProblems: List) { + if (modifiedProblems.isEmpty()) return + + Log.d(TAG, "Syncing images for ${modifiedProblems.size} modified problems") + + for (backupProblem in modifiedProblems) { + backupProblem.imagePaths?.forEach { imagePath -> + val filename = imagePath.substringAfterLast('/') + uploadImage(imagePath, filename) + } + } + } + private suspend fun downloadData(): ClimbDataBackup { val request = Request.Builder() diff --git a/android/app/src/main/java/com/atridad/ascently/utils/MigrationManager.kt b/android/app/src/main/java/com/atridad/ascently/utils/MigrationManager.kt index 7a30c0a..6532668 100644 --- a/android/app/src/main/java/com/atridad/ascently/utils/MigrationManager.kt +++ b/android/app/src/main/java/com/atridad/ascently/utils/MigrationManager.kt @@ -5,10 +5,6 @@ import android.content.SharedPreferences import android.util.Log import androidx.core.content.edit -/** - * Handles migration of data from OpenClimb to Ascently This includes SharedPreferences, database - * names, and other local storage - */ class MigrationManager(private val context: Context) { companion object { diff --git a/android/app/src/test/java/com/atridad/openclimb/SyncMergeLogicTest.kt b/android/app/src/test/java/com/atridad/openclimb/SyncMergeLogicTest.kt index f80fd7a..43d3f2b 100644 --- a/android/app/src/test/java/com/atridad/openclimb/SyncMergeLogicTest.kt +++ b/android/app/src/test/java/com/atridad/openclimb/SyncMergeLogicTest.kt @@ -457,10 +457,6 @@ class SyncMergeLogicTest { @Test fun `test active sessions excluded from sync`() { - // Test scenario: Active sessions should not be included in sync data - // This tests the new behavior where active sessions are excluded from sync - // until they are completed - val allLocalSessions = listOf( BackupClimbSession( diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs index e9f949d..2eb0806 100644 --- a/docs/astro.config.mjs +++ b/docs/astro.config.mjs @@ -40,6 +40,7 @@ export default defineConfig({ items: [ { label: "Overview", slug: "sync/overview" }, { label: "Quick Start", slug: "sync/quick-start" }, + { label: "API Reference", slug: "sync/api-reference" }, ], }, { diff --git a/docs/package.json b/docs/package.json index 1410c49..28ea69f 100644 --- a/docs/package.json +++ b/docs/package.json @@ -26,7 +26,7 @@ }, "dependencies": { "@astrojs/node": "^9.5.0", - "@astrojs/starlight": "^0.36.0", + "@astrojs/starlight": "^0.36.1", "astro": "^5.14.5", "sharp": "^0.34.4" } diff --git a/docs/pnpm-lock.yaml b/docs/pnpm-lock.yaml index 50e9c0c..6a4d1ed 100644 --- a/docs/pnpm-lock.yaml +++ b/docs/pnpm-lock.yaml @@ -12,8 +12,8 @@ importers: specifier: ^9.5.0 version: 9.5.0(astro@5.14.5(@types/node@24.7.2)(rollup@4.52.4)(typescript@5.9.3)) '@astrojs/starlight': - specifier: ^0.36.0 - version: 0.36.0(astro@5.14.5(@types/node@24.7.2)(rollup@4.52.4)(typescript@5.9.3)) + specifier: ^0.36.1 + version: 0.36.1(astro@5.14.5(@types/node@24.7.2)(rollup@4.52.4)(typescript@5.9.3)) astro: specifier: ^5.14.5 version: 5.14.5(@types/node@24.7.2)(rollup@4.52.4)(typescript@5.9.3) @@ -50,8 +50,8 @@ packages: '@astrojs/sitemap@3.6.0': resolution: {integrity: sha512-4aHkvcOZBWJigRmMIAJwRQXBS+ayoP5z40OklTXYXhUDhwusz+DyDl+nSshY6y9DvkVEavwNcFO8FD81iGhXjg==} - '@astrojs/starlight@0.36.0': - resolution: {integrity: sha512-aVJVBfvFuE2avsMDhmRzn6I5GjDhUwIQFlu3qH9a1C0fNsPYDw2asxHQODAD7EfGiKGvvHCJgHb+9jbJ8lCfNQ==} + '@astrojs/starlight@0.36.1': + resolution: {integrity: sha512-Fmt8mIsAIZN18Y4YQDI6p521GsYGe4hYxh9jWmz0pHBXnS5J7Na3TSXNya4eyIymCcKkuiKFbs7b/knsdGVYPg==} peerDependencies: astro: ^5.5.0 @@ -91,158 +91,158 @@ packages: '@emnapi/runtime@1.5.0': resolution: {integrity: sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==} - '@esbuild/aix-ppc64@0.25.10': - resolution: {integrity: sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==} + '@esbuild/aix-ppc64@0.25.11': + resolution: {integrity: sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.25.10': - resolution: {integrity: sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==} + '@esbuild/android-arm64@0.25.11': + resolution: {integrity: sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.25.10': - resolution: {integrity: sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==} + '@esbuild/android-arm@0.25.11': + resolution: {integrity: sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.25.10': - resolution: {integrity: sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==} + '@esbuild/android-x64@0.25.11': + resolution: {integrity: sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.25.10': - resolution: {integrity: sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==} + '@esbuild/darwin-arm64@0.25.11': + resolution: {integrity: sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.25.10': - resolution: {integrity: sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==} + '@esbuild/darwin-x64@0.25.11': + resolution: {integrity: sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.25.10': - resolution: {integrity: sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==} + '@esbuild/freebsd-arm64@0.25.11': + resolution: {integrity: sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.10': - resolution: {integrity: sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==} + '@esbuild/freebsd-x64@0.25.11': + resolution: {integrity: sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.25.10': - resolution: {integrity: sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==} + '@esbuild/linux-arm64@0.25.11': + resolution: {integrity: sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.25.10': - resolution: {integrity: sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==} + '@esbuild/linux-arm@0.25.11': + resolution: {integrity: sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.25.10': - resolution: {integrity: sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==} + '@esbuild/linux-ia32@0.25.11': + resolution: {integrity: sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.25.10': - resolution: {integrity: sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==} + '@esbuild/linux-loong64@0.25.11': + resolution: {integrity: sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.25.10': - resolution: {integrity: sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==} + '@esbuild/linux-mips64el@0.25.11': + resolution: {integrity: sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.25.10': - resolution: {integrity: sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==} + '@esbuild/linux-ppc64@0.25.11': + resolution: {integrity: sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.25.10': - resolution: {integrity: sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==} + '@esbuild/linux-riscv64@0.25.11': + resolution: {integrity: sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.25.10': - resolution: {integrity: sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==} + '@esbuild/linux-s390x@0.25.11': + resolution: {integrity: sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.25.10': - resolution: {integrity: sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==} + '@esbuild/linux-x64@0.25.11': + resolution: {integrity: sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.25.10': - resolution: {integrity: sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==} + '@esbuild/netbsd-arm64@0.25.11': + resolution: {integrity: sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.10': - resolution: {integrity: sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==} + '@esbuild/netbsd-x64@0.25.11': + resolution: {integrity: sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.25.10': - resolution: {integrity: sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==} + '@esbuild/openbsd-arm64@0.25.11': + resolution: {integrity: sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.10': - resolution: {integrity: sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==} + '@esbuild/openbsd-x64@0.25.11': + resolution: {integrity: sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openharmony-arm64@0.25.10': - resolution: {integrity: sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==} + '@esbuild/openharmony-arm64@0.25.11': + resolution: {integrity: sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] - '@esbuild/sunos-x64@0.25.10': - resolution: {integrity: sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==} + '@esbuild/sunos-x64@0.25.11': + resolution: {integrity: sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.25.10': - resolution: {integrity: sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==} + '@esbuild/win32-arm64@0.25.11': + resolution: {integrity: sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.25.10': - resolution: {integrity: sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==} + '@esbuild/win32-ia32@0.25.11': + resolution: {integrity: sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.25.10': - resolution: {integrity: sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==} + '@esbuild/win32-x64@0.25.11': + resolution: {integrity: sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -811,8 +811,8 @@ packages: resolution: {integrity: sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ==} engines: {node: '>=18'} - devalue@5.3.2: - resolution: {integrity: sha512-UDsjUbpQn9kvm68slnrs+mfxwFkIflOhkanmyabZ8zOYk8SMEIbJ3TK+88g70hSIeytu4y18f0z/hYHMTrXIWw==} + devalue@5.4.1: + resolution: {integrity: sha512-YtoaOfsqjbZQKGIMRYDWKjUmSB4VJ/RElB+bXZawQAQYAo4xu08GKTMVlsZDTF6R2MbAgjcAQRPI5eIyRAT2OQ==} devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} @@ -861,8 +861,8 @@ packages: esast-util-from-js@2.0.1: resolution: {integrity: sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==} - esbuild@0.25.10: - resolution: {integrity: sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==} + esbuild@0.25.11: + resolution: {integrity: sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==} engines: {node: '>=18'} hasBin: true @@ -1344,8 +1344,8 @@ packages: resolution: {integrity: sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==} engines: {node: '>=14.16'} - package-manager-detector@1.4.0: - resolution: {integrity: sha512-rRZ+pR1Usc+ND9M2NkmCvE/LYJS+8ORVV9X0KuNSY/gFsp7RBHJM/ADh9LYq4Vvfq6QkKrW6/weuh8SMEtN5gw==} + package-manager-detector@1.4.1: + resolution: {integrity: sha512-dSMiVLBEA4XaNJ0PRb4N5cV/SEP4BWrWZKBmfF+OUm2pQTiZ6DDkKeWaltwu3JRhLoy59ayIkJ00cx9K9CaYTg==} pagefind@1.4.0: resolution: {integrity: sha512-z2kY1mQlL4J8q5EIsQkLzQjilovKzfNVhX8De6oyE6uHpfFtyBaqUpcl/XzJC/4fjD8vBDyh1zolimIcVrCn9g==} @@ -1753,8 +1753,8 @@ packages: vfile@6.0.3: resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} - vite@6.3.7: - resolution: {integrity: sha512-mQYaKepA0NGMBsz8Xktt3tJUG5ELE2iT7IJ+ssXI6nxVdE2sFc/d/6w/JByqMLvWg8hNKHpPgzjgOkrhpKFnrA==} + vite@6.4.0: + resolution: {integrity: sha512-oLnWs9Hak/LOlKjeSpOwD6JMks8BeICEdYMJBf6P4Lac/pO9tKiv/XhXnAM7nNfSkZahjlCZu9sS50zL8fSnsw==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: @@ -1922,7 +1922,7 @@ snapshots: stream-replace-string: 2.0.0 zod: 3.25.76 - '@astrojs/starlight@0.36.0(astro@5.14.5(@types/node@24.7.2)(rollup@4.52.4)(typescript@5.9.3))': + '@astrojs/starlight@0.36.1(astro@5.14.5(@types/node@24.7.2)(rollup@4.52.4)(typescript@5.9.3))': dependencies: '@astrojs/markdown-remark': 6.3.8 '@astrojs/mdx': 4.3.7(astro@5.14.5(@types/node@24.7.2)(rollup@4.52.4)(typescript@5.9.3)) @@ -1993,82 +1993,82 @@ snapshots: tslib: 2.8.1 optional: true - '@esbuild/aix-ppc64@0.25.10': + '@esbuild/aix-ppc64@0.25.11': optional: true - '@esbuild/android-arm64@0.25.10': + '@esbuild/android-arm64@0.25.11': optional: true - '@esbuild/android-arm@0.25.10': + '@esbuild/android-arm@0.25.11': optional: true - '@esbuild/android-x64@0.25.10': + '@esbuild/android-x64@0.25.11': optional: true - '@esbuild/darwin-arm64@0.25.10': + '@esbuild/darwin-arm64@0.25.11': optional: true - '@esbuild/darwin-x64@0.25.10': + '@esbuild/darwin-x64@0.25.11': optional: true - '@esbuild/freebsd-arm64@0.25.10': + '@esbuild/freebsd-arm64@0.25.11': optional: true - '@esbuild/freebsd-x64@0.25.10': + '@esbuild/freebsd-x64@0.25.11': optional: true - '@esbuild/linux-arm64@0.25.10': + '@esbuild/linux-arm64@0.25.11': optional: true - '@esbuild/linux-arm@0.25.10': + '@esbuild/linux-arm@0.25.11': optional: true - '@esbuild/linux-ia32@0.25.10': + '@esbuild/linux-ia32@0.25.11': optional: true - '@esbuild/linux-loong64@0.25.10': + '@esbuild/linux-loong64@0.25.11': optional: true - '@esbuild/linux-mips64el@0.25.10': + '@esbuild/linux-mips64el@0.25.11': optional: true - '@esbuild/linux-ppc64@0.25.10': + '@esbuild/linux-ppc64@0.25.11': optional: true - '@esbuild/linux-riscv64@0.25.10': + '@esbuild/linux-riscv64@0.25.11': optional: true - '@esbuild/linux-s390x@0.25.10': + '@esbuild/linux-s390x@0.25.11': optional: true - '@esbuild/linux-x64@0.25.10': + '@esbuild/linux-x64@0.25.11': optional: true - '@esbuild/netbsd-arm64@0.25.10': + '@esbuild/netbsd-arm64@0.25.11': optional: true - '@esbuild/netbsd-x64@0.25.10': + '@esbuild/netbsd-x64@0.25.11': optional: true - '@esbuild/openbsd-arm64@0.25.10': + '@esbuild/openbsd-arm64@0.25.11': optional: true - '@esbuild/openbsd-x64@0.25.10': + '@esbuild/openbsd-x64@0.25.11': optional: true - '@esbuild/openharmony-arm64@0.25.10': + '@esbuild/openharmony-arm64@0.25.11': optional: true - '@esbuild/sunos-x64@0.25.10': + '@esbuild/sunos-x64@0.25.11': optional: true - '@esbuild/win32-arm64@0.25.10': + '@esbuild/win32-arm64@0.25.11': optional: true - '@esbuild/win32-ia32@0.25.10': + '@esbuild/win32-ia32@0.25.11': optional: true - '@esbuild/win32-x64@0.25.10': + '@esbuild/win32-x64@0.25.11': optional: true '@expressive-code/core@0.41.3': @@ -2453,12 +2453,12 @@ snapshots: cssesc: 3.0.0 debug: 4.4.3 deterministic-object-hash: 2.0.2 - devalue: 5.3.2 + devalue: 5.4.1 diff: 5.2.0 dlv: 1.1.3 dset: 3.1.4 es-module-lexer: 1.7.0 - esbuild: 0.25.10 + esbuild: 0.25.11 estree-walker: 3.0.3 flattie: 1.1.1 fontace: 0.3.1 @@ -2474,7 +2474,7 @@ snapshots: neotraverse: 0.6.18 p-limit: 6.2.0 p-queue: 8.1.1 - package-manager-detector: 1.4.0 + package-manager-detector: 1.4.1 picomatch: 4.0.3 prompts: 2.4.2 rehype: 13.0.2 @@ -2489,8 +2489,8 @@ snapshots: unist-util-visit: 5.0.0 unstorage: 1.17.1 vfile: 6.0.3 - vite: 6.3.7(@types/node@24.7.2) - vitefu: 1.1.1(vite@6.3.7(@types/node@24.7.2)) + vite: 6.4.0(@types/node@24.7.2) + vitefu: 1.1.1(vite@6.4.0(@types/node@24.7.2)) xxhash-wasm: 1.1.0 yargs-parser: 21.1.1 yocto-spinner: 0.2.3 @@ -2638,7 +2638,7 @@ snapshots: dependencies: base-64: 1.0.0 - devalue@5.3.2: {} + devalue@5.4.1: {} devlop@1.1.0: dependencies: @@ -2680,34 +2680,34 @@ snapshots: esast-util-from-estree: 2.0.0 vfile-message: 4.0.3 - esbuild@0.25.10: + esbuild@0.25.11: optionalDependencies: - '@esbuild/aix-ppc64': 0.25.10 - '@esbuild/android-arm': 0.25.10 - '@esbuild/android-arm64': 0.25.10 - '@esbuild/android-x64': 0.25.10 - '@esbuild/darwin-arm64': 0.25.10 - '@esbuild/darwin-x64': 0.25.10 - '@esbuild/freebsd-arm64': 0.25.10 - '@esbuild/freebsd-x64': 0.25.10 - '@esbuild/linux-arm': 0.25.10 - '@esbuild/linux-arm64': 0.25.10 - '@esbuild/linux-ia32': 0.25.10 - '@esbuild/linux-loong64': 0.25.10 - '@esbuild/linux-mips64el': 0.25.10 - '@esbuild/linux-ppc64': 0.25.10 - '@esbuild/linux-riscv64': 0.25.10 - '@esbuild/linux-s390x': 0.25.10 - '@esbuild/linux-x64': 0.25.10 - '@esbuild/netbsd-arm64': 0.25.10 - '@esbuild/netbsd-x64': 0.25.10 - '@esbuild/openbsd-arm64': 0.25.10 - '@esbuild/openbsd-x64': 0.25.10 - '@esbuild/openharmony-arm64': 0.25.10 - '@esbuild/sunos-x64': 0.25.10 - '@esbuild/win32-arm64': 0.25.10 - '@esbuild/win32-ia32': 0.25.10 - '@esbuild/win32-x64': 0.25.10 + '@esbuild/aix-ppc64': 0.25.11 + '@esbuild/android-arm': 0.25.11 + '@esbuild/android-arm64': 0.25.11 + '@esbuild/android-x64': 0.25.11 + '@esbuild/darwin-arm64': 0.25.11 + '@esbuild/darwin-x64': 0.25.11 + '@esbuild/freebsd-arm64': 0.25.11 + '@esbuild/freebsd-x64': 0.25.11 + '@esbuild/linux-arm': 0.25.11 + '@esbuild/linux-arm64': 0.25.11 + '@esbuild/linux-ia32': 0.25.11 + '@esbuild/linux-loong64': 0.25.11 + '@esbuild/linux-mips64el': 0.25.11 + '@esbuild/linux-ppc64': 0.25.11 + '@esbuild/linux-riscv64': 0.25.11 + '@esbuild/linux-s390x': 0.25.11 + '@esbuild/linux-x64': 0.25.11 + '@esbuild/netbsd-arm64': 0.25.11 + '@esbuild/netbsd-x64': 0.25.11 + '@esbuild/openbsd-arm64': 0.25.11 + '@esbuild/openbsd-x64': 0.25.11 + '@esbuild/openharmony-arm64': 0.25.11 + '@esbuild/sunos-x64': 0.25.11 + '@esbuild/win32-arm64': 0.25.11 + '@esbuild/win32-ia32': 0.25.11 + '@esbuild/win32-x64': 0.25.11 escape-html@1.0.3: {} @@ -3595,7 +3595,7 @@ snapshots: p-timeout@6.1.4: {} - package-manager-detector@1.4.0: {} + package-manager-detector@1.4.1: {} pagefind@1.4.0: optionalDependencies: @@ -4116,9 +4116,9 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite@6.3.7(@types/node@24.7.2): + vite@6.4.0(@types/node@24.7.2): dependencies: - esbuild: 0.25.10 + esbuild: 0.25.11 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 @@ -4128,9 +4128,9 @@ snapshots: '@types/node': 24.7.2 fsevents: 2.3.3 - vitefu@1.1.1(vite@6.3.7(@types/node@24.7.2)): + vitefu@1.1.1(vite@6.4.0(@types/node@24.7.2)): optionalDependencies: - vite: 6.3.7(@types/node@24.7.2) + vite: 6.4.0(@types/node@24.7.2) web-namespaces@2.0.1: {} diff --git a/docs/pnpm-workspace.yaml b/docs/pnpm-workspace.yaml deleted file mode 100644 index d0b7dbe..0000000 --- a/docs/pnpm-workspace.yaml +++ /dev/null @@ -1,3 +0,0 @@ -onlyBuiltDependencies: - - esbuild - - sharp diff --git a/docs/src/content/docs/reference/example.md b/docs/src/content/docs/reference/example.md deleted file mode 100644 index e7194a1..0000000 --- a/docs/src/content/docs/reference/example.md +++ /dev/null @@ -1,51 +0,0 @@ ---- -title: Sync Server API -description: API endpoints for the Ascently sync server ---- - -The sync server provides a minimal REST API for data synchronization. - -## Authentication - -All endpoints require an `Authorization: Bearer ` header. - -## Endpoints - -### Data Sync - -**GET /sync** -- Download `ascently.json` file -- Returns: JSON data file or 404 if no data exists - -**POST /sync** -- Upload `ascently.json` file -- Body: JSON data -- Returns: Success confirmation - -### Images - -**GET /images/{imageName}** -- Download an image file -- Returns: Image file or 404 if not found - -**POST /images/{imageName}** -- Upload an image file -- Body: Image data -- Returns: Success confirmation - -## Example Usage - -```bash -# Download data -curl -H "Authorization: Bearer your-token" \ - http://localhost:8080/sync - -# Upload data -curl -X POST \ - -H "Authorization: Bearer your-token" \ - -H "Content-Type: application/json" \ - -d @ascently.json \ - http://localhost:8080/sync -``` - -See `main.go` in the sync directory for implementation details. \ No newline at end of file diff --git a/docs/src/content/docs/sync/api-reference.md b/docs/src/content/docs/sync/api-reference.md new file mode 100644 index 0000000..d299fd7 --- /dev/null +++ b/docs/src/content/docs/sync/api-reference.md @@ -0,0 +1,152 @@ +--- +title: API Reference +description: Complete API documentation for the Ascently sync server +--- + +Complete reference for all sync server endpoints. + +## Authentication + +All endpoints require a bearer token in the `Authorization` header: + +``` +Authorization: Bearer your-auth-token +``` + +Unauthorized requests return `401 Unauthorized`. + +## Endpoints + +### Health Check + +**`GET /health`** + +Check if the server is running. + +**Response:** +```json +{ + "status": "ok", + "version": "2.0.0" +} +``` + +### Full Sync - Download + +**`GET /sync`** + +Download the entire dataset from the server. + +**Response:** +```json +{ + "exportedAt": "2024-01-15T10:30:00.000Z", + "version": "2.0", + "formatVersion": "2.0", + "gyms": [...], + "problems": [...], + "sessions": [...], + "attempts": [...], + "deletedItems": [...] +} +``` + +Returns `200 OK` with the backup data, or `404 Not Found` if no data exists. + +### Full Sync - Upload + +**`POST /sync`** + +Upload your entire dataset to the server. This overwrites all server data. + +**Request Body:** +```json +{ + "exportedAt": "2024-01-15T10:30:00.000Z", + "version": "2.0", + "formatVersion": "2.0", + "gyms": [...], + "problems": [...], + "sessions": [...], + "attempts": [...], + "deletedItems": [...] +} +``` + +**Response:** +``` +200 OK +``` + +### Delta Sync + +**`POST /sync/delta`** + +Sync only changed data since your last sync. Much faster than full sync. + +**Request Body:** +```json +{ + "lastSyncTime": "2024-01-15T10:00:00.000Z", + "gyms": [...], + "problems": [...], + "sessions": [...], + "attempts": [...], + "deletedItems": [...] +} +``` + +Include only items modified after `lastSyncTime`. The server merges your changes with its data using last-write-wins based on `updatedAt` timestamps. + +**Response:** +```json +{ + "serverTime": "2024-01-15T10:30:00.000Z", + "gyms": [...], + "problems": [...], + "sessions": [...], + "attempts": [...], + "deletedItems": [...] +} +``` + +Returns only server items modified after your `lastSyncTime`. Save `serverTime` as your new `lastSyncTime` for the next delta sync. + +### Image Upload + +**`POST /images/upload?filename={name}`** + +Upload an image file. + +**Query Parameters:** +- `filename`: Image filename (e.g., `problem_abc123_0.jpg`) + +**Request Body:** +Binary image data (JPEG, PNG, GIF, or WebP) + +**Response:** +``` +200 OK +``` + +### Image Download + +**`GET /images/download?filename={name}`** + +Download an image file. + +**Query Parameters:** +- `filename`: Image filename + +**Response:** +Binary image data with appropriate `Content-Type` header. + +Returns `404 Not Found` if the image doesn't exist. + +## Notes + +- All timestamps are ISO 8601 format with milliseconds +- Active sessions (status `active`) are excluded from sync +- Images are stored separately and referenced by filename +- The server stores everything in a single `ascently.json` file +- No versioning or history - last write wins diff --git a/docs/src/content/docs/sync/overview.md b/docs/src/content/docs/sync/overview.md index 38c331a..b531569 100644 --- a/docs/src/content/docs/sync/overview.md +++ b/docs/src/content/docs/sync/overview.md @@ -3,28 +3,49 @@ title: Self-Hosted Sync Overview description: Learn about Ascently's optional sync server for cross-device data synchronization --- -You can run your own sync server to keep your data in sync across devices. The server is lightweight and easy to set up using Docker. +Run your own sync server to keep your data in sync across devices. The server is lightweight and easy to set up with Docker. ## How It Works -This server uses a single `ascently.json` file for your data and a directory for images. The last client to upload wins, overwriting the old data. Authentication is just a static bearer token. +The server stores your data in a single `ascently.json` file and images in a directory. It's simple: last write wins. Authentication is a static bearer token you set. -## API +## Features -All endpoints require an `Authorization: Bearer ` header. +- **Delta sync**: Only syncs changed data +- **Image sync**: Automatically syncs problem images +- **Conflict resolution**: Last-write-wins based on timestamps +- **Cross-platform**: Works with iOS and Android clients +- **Privacy**: Your data, your server, no analytics -- `GET /sync`: Download `ascently.json` -- `POST /sync`: Upload `ascently.json` -- `GET /images/{imageName}`: Download an image -- `POST /images/{imageName}`: Upload an image +## API Endpoints + +- `GET /health` - Health check +- `GET /sync` - Download full dataset +- `POST /sync` - Upload full dataset +- `POST /sync/delta` - Sync only changes (recommended) +- `POST /images/upload?filename={name}` - Upload image +- `GET /images/download?filename={name}` - Download image + +All endpoints require `Authorization: Bearer ` header. + +See the [API Reference](/sync/api-reference/) for complete documentation. ## Getting Started -The easiest way to get started is with the [Quick Start guide](/sync/quick-start/) using Docker Compose. +Check out the [Quick Start guide](/sync/quick-start/) to get your server running with Docker Compose. You'll need: - Docker and Docker Compose - A secure authentication token - A place to store your data -The server will be available at `http://localhost:8080` by default. Configure your clients with your server URL and auth token to start syncing. +The server will be available at `http://localhost:8080` by default. Configure your Ascently apps with your server URL and auth token to start syncing. + +## How Sync Works + +1. **First sync**: Client uploads or downloads full dataset +2. **Subsequent syncs**: Client uses delta sync to only transfer changed data +3. **Conflicts**: Resolved automatically using timestamps (newer wins) +4. **Images**: Synced automatically with problem data + +Active sessions are excluded from sync until completed. diff --git a/docs/src/content/docs/sync/quick-start.md b/docs/src/content/docs/sync/quick-start.md index 590c36f..247f140 100644 --- a/docs/src/content/docs/sync/quick-start.md +++ b/docs/src/content/docs/sync/quick-start.md @@ -3,7 +3,7 @@ title: Quick Start description: Get your Ascently sync server running with Docker Compose --- -Get your Ascently sync server up and running using Docker Compose. +Get your sync server running in minutes with Docker Compose. ## Prerequisites @@ -12,50 +12,158 @@ Get your Ascently sync server up and running using Docker Compose. ## Setup -1. Create a `.env` file with your configuration: +1. Create a `docker-compose.yml` file: -```env -IMAGE=git.atri.dad/atridad/ascently-sync:latest -APP_PORT=8080 -AUTH_TOKEN=your-super-secret-token -DATA_FILE=/data/ascently.json -IMAGES_DIR=/data/images -ROOT_DIR=./ascently-data +```yaml +version: '3.8' + +services: + ascently-sync: + image: git.atri.dad/atridad/ascently-sync:latest + ports: + - "8080:8080" + environment: + - AUTH_TOKEN=${AUTH_TOKEN} + - DATA_FILE=/data/ascently.json + - IMAGES_DIR=/data/images + volumes: + - ./ascently-data:/data + restart: unless-stopped ``` -Set `AUTH_TOKEN` to a long, random string. `ROOT_DIR` is where the server will store its data on your machine. +2. Create a `.env` file in the same directory: -2. Use the provided `docker-compose.yml` in the `sync/` directory: +```env +AUTH_TOKEN=your-super-secret-token-here +``` + +Replace `your-super-secret-token-here` with a secure random token (see below). + +3. Start the server: ```bash -cd sync/ docker-compose up -d ``` The server will be available at `http://localhost:8080`. -## Configure Your Clients +## Generate a Secure Token -Configure your Ascently apps with: -- **Server URL**: `http://your-server-ip:8080` (or your domain) -- **Auth Token**: The token from your `.env` file - -Enable sync and perform your first sync to start synchronizing data across devices. - -## Generating a Secure Token - -Generate a secure authentication token: +Use this command to generate a secure authentication token: ```bash -# On Linux/macOS openssl rand -base64 32 ``` -Keep this token secure and don't share it publicly. +Copy the output and paste it into your `.env` file as the `AUTH_TOKEN`. -## Accessing Remotely +Keep this token secret and don't commit it to version control. -For remote access, you'll need to: -- Set up port forwarding on your router (port 8080) -- Use your public IP address or set up a domain name -- Consider using HTTPS with a reverse proxy for security +## Configure Your Apps + +Open Ascently on your iOS or Android device: + +1. Go to **Settings** +2. Scroll to **Sync Configuration** +3. Enter your **Server URL**: `http://your-server-ip:8080` +4. Enter your **Auth Token**: (the token from your `.env` file) +5. Tap **Test Connection** to verify it works +6. Enable **Auto Sync** +7. Tap **Sync Now** to perform your first sync + +Repeat this on all your devices to keep them in sync. + +## Verify It's Working + +Check the server logs: + +```bash +docker-compose logs -f ascently-sync +``` + +You should see logs like: + +``` +Delta sync from 192.168.1.100: lastSyncTime=2024-01-15T10:00:00.000Z, gyms=1, problems=5, sessions=2, attempts=10, deletedItems=0 +``` + +## Remote Access + +To access your server remotely: + +### Option 1: Port Forwarding + +1. Forward port 8080 on your router to your server +2. Find your public IP address +3. Use `http://your-public-ip:8080` as the server URL + +### Option 2: Domain Name (Recommended) + +1. Get a domain name and point it to your server +2. Set up a reverse proxy (nginx, Caddy, Traefik) +3. Enable HTTPS with Let's Encrypt +4. Use `https://sync.yourdomain.com` as the server URL + +Example nginx config with HTTPS: + +```nginx +server { + listen 443 ssl http2; + server_name sync.yourdomain.com; + + ssl_certificate /etc/letsencrypt/live/sync.yourdomain.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/sync.yourdomain.com/privkey.pem; + + location / { + proxy_pass http://localhost:8080; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + } +} +``` + +## Updating + +Pull the latest image and restart: + +```bash +docker-compose pull +docker-compose up -d +``` + +Your data is stored in `./ascently-data` and persists across updates. + +## Troubleshooting + +### Connection Failed + +- Check the server is running: `docker-compose ps` +- Verify the auth token matches on server and client +- Check firewall settings and port forwarding +- Test locally first with `http://localhost:8080` + +### Sync Errors + +- Check server logs: `docker-compose logs ascently-sync` +- Verify your device has internet connection +- Try disabling and re-enabling sync +- Perform a manual sync from Settings + +### Data Location + +All data is stored in `./ascently-data/`: + +``` +ascently-data/ +├── ascently.json # Your climb data +└── images/ # Problem images +``` + +You can back this up or move it to another server. + +## Next Steps + +- Read the [API Reference](/sync/api-reference/) for advanced usage +- Set up automated backups of your `ascently-data` directory +- Configure HTTPS for secure remote access +- Monitor server logs for sync activity \ No newline at end of file diff --git a/ios/Ascently.xcodeproj/project.pbxproj b/ios/Ascently.xcodeproj/project.pbxproj index 79b2c42..24ebef4 100644 --- a/ios/Ascently.xcodeproj/project.pbxproj +++ b/ios/Ascently.xcodeproj/project.pbxproj @@ -465,7 +465,7 @@ CODE_SIGN_ENTITLEMENTS = Ascently/Ascently.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 26; + CURRENT_PROJECT_VERSION = 27; DEVELOPMENT_TEAM = 4BC9Y2LL4B; DRIVERKIT_DEPLOYMENT_TARGET = 24.6; ENABLE_PREVIEWS = YES; @@ -487,7 +487,7 @@ "@executable_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 15.6; - MARKETING_VERSION = 2.0.0; + MARKETING_VERSION = 2.1.0; PRODUCT_BUNDLE_IDENTIFIER = com.atridad.Ascently; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -513,7 +513,7 @@ CODE_SIGN_ENTITLEMENTS = Ascently/Ascently.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 26; + CURRENT_PROJECT_VERSION = 27; DEVELOPMENT_TEAM = 4BC9Y2LL4B; DRIVERKIT_DEPLOYMENT_TARGET = 24.6; ENABLE_PREVIEWS = YES; @@ -535,7 +535,7 @@ "@executable_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 15.6; - MARKETING_VERSION = 2.0.0; + MARKETING_VERSION = 2.1.0; PRODUCT_BUNDLE_IDENTIFIER = com.atridad.Ascently; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -602,7 +602,7 @@ ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; CODE_SIGN_ENTITLEMENTS = SessionStatusLiveExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 26; + CURRENT_PROJECT_VERSION = 27; DEVELOPMENT_TEAM = 4BC9Y2LL4B; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = SessionStatusLive/Info.plist; @@ -613,7 +613,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 2.0.0; + MARKETING_VERSION = 2.1.0; PRODUCT_BUNDLE_IDENTIFIER = com.atridad.Ascently.SessionStatusLive; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -632,7 +632,7 @@ ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; CODE_SIGN_ENTITLEMENTS = SessionStatusLiveExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 26; + CURRENT_PROJECT_VERSION = 27; DEVELOPMENT_TEAM = 4BC9Y2LL4B; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = SessionStatusLive/Info.plist; @@ -643,7 +643,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 2.0.0; + MARKETING_VERSION = 2.1.0; PRODUCT_BUNDLE_IDENTIFIER = com.atridad.Ascently.SessionStatusLive; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; diff --git a/ios/Ascently.xcodeproj/project.xcworkspace/xcuserdata/atridad.xcuserdatad/UserInterfaceState.xcuserstate b/ios/Ascently.xcodeproj/project.xcworkspace/xcuserdata/atridad.xcuserdatad/UserInterfaceState.xcuserstate index 6e022e188d20c5950afc03999c36cca506928aa7..528f7359bc0ffabbd5cd31d4b2bba5bbaddb16c7 100644 GIT binary patch literal 231929 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 z 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 i9Od^Fn6zU9iR);$=)v~H& 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�N}c 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 Gym { let formatter = ISO8601DateFormatter() formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] @@ -137,7 +134,6 @@ struct BackupProblem: Codable { let createdAt: String let updatedAt: String - /// Initialize from native iOS Problem model init(from problem: Problem) { self.id = problem.id.uuidString self.gymId = problem.gymId.uuidString @@ -158,7 +154,6 @@ struct BackupProblem: Codable { self.updatedAt = formatter.string(from: problem.updatedAt) } - /// Initialize with explicit parameters for import init( id: String, gymId: String, @@ -191,7 +186,6 @@ struct BackupProblem: Codable { self.updatedAt = updatedAt } - /// Convert to native iOS Problem model func toProblem() throws -> Problem { let formatter = ISO8601DateFormatter() formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] @@ -224,7 +218,6 @@ struct BackupProblem: Codable { ) } - /// Create a copy with updated image paths for import processing func withUpdatedImagePaths(_ newImagePaths: [String]) -> BackupProblem { return BackupProblem( id: self.id, @@ -258,7 +251,6 @@ struct BackupClimbSession: Codable { let createdAt: String let updatedAt: String - /// Initialize from native iOS ClimbSession model init(from session: ClimbSession) { self.id = session.id.uuidString self.gymId = session.gymId.uuidString @@ -275,7 +267,6 @@ struct BackupClimbSession: Codable { self.updatedAt = formatter.string(from: session.updatedAt) } - /// Initialize with explicit parameters for import init( id: String, gymId: String, @@ -300,7 +291,6 @@ struct BackupClimbSession: Codable { self.updatedAt = updatedAt } - /// Convert to native iOS ClimbSession model func toClimbSession() throws -> ClimbSession { let formatter = ISO8601DateFormatter() formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] @@ -347,7 +337,6 @@ struct BackupAttempt: Codable { let createdAt: String let updatedAt: String? - /// Initialize from native iOS Attempt model init(from attempt: Attempt) { self.id = attempt.id.uuidString self.sessionId = attempt.sessionId.uuidString @@ -365,7 +354,6 @@ struct BackupAttempt: Codable { self.updatedAt = formatter.string(from: attempt.updatedAt) } - /// Initialize with explicit parameters for import init( id: String, sessionId: String, @@ -392,7 +380,6 @@ struct BackupAttempt: Codable { self.updatedAt = updatedAt } - /// Convert to native iOS Attempt model func toAttempt() throws -> Attempt { let formatter = ISO8601DateFormatter() formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] diff --git a/ios/Ascently/Models/DeltaSyncFormat.swift b/ios/Ascently/Models/DeltaSyncFormat.swift new file mode 100644 index 0000000..951f39d --- /dev/null +++ b/ios/Ascently/Models/DeltaSyncFormat.swift @@ -0,0 +1,26 @@ +// +// DeltaSyncFormat.swift +// Ascently +// +// Delta sync structures for incremental data synchronization +// + +import Foundation + +struct DeltaSyncRequest: Codable { + let lastSyncTime: String + let gyms: [BackupGym] + let problems: [BackupProblem] + let sessions: [BackupClimbSession] + let attempts: [BackupAttempt] + let deletedItems: [DeletedItem] +} + +struct DeltaSyncResponse: Codable { + let serverTime: String + let gyms: [BackupGym] + let problems: [BackupProblem] + let sessions: [BackupClimbSession] + let attempts: [BackupAttempt] + let deletedItems: [DeletedItem] +} diff --git a/ios/Ascently/Services/HealthKitService.swift b/ios/Ascently/Services/HealthKitService.swift index 1f561d8..9cc3983 100644 --- a/ios/Ascently/Services/HealthKitService.swift +++ b/ios/Ascently/Services/HealthKitService.swift @@ -31,7 +31,6 @@ class HealthKitService: ObservableObject { } } - /// Restore active workout state private func restoreActiveWorkout() { if let startDate = userDefaults.object(forKey: workoutStartDateKey) as? Date, let sessionIdString = userDefaults.string(forKey: workoutSessionIdKey), @@ -43,7 +42,6 @@ class HealthKitService: ObservableObject { } } - /// Persist active workout state private func persistActiveWorkout() { if let startDate = currentWorkoutStartDate, let sessionId = currentWorkoutSessionId { userDefaults.set(startDate, forKey: workoutStartDateKey) @@ -54,7 +52,6 @@ class HealthKitService: ObservableObject { } } - /// Verify and restore health integration func verifyAndRestoreIntegration() async { guard isEnabled else { return } diff --git a/ios/Ascently/Services/SyncService.swift b/ios/Ascently/Services/SyncService.swift index 72b56bd..ea98eec 100644 --- a/ios/Ascently/Services/SyncService.swift +++ b/ios/Ascently/Services/SyncService.swift @@ -136,6 +136,314 @@ class SyncService: ObservableObject { } } + func performDeltaSync(dataManager: ClimbingDataManager) async throws { + guard isConfigured else { + throw SyncError.notConfigured + } + + guard let url = URL(string: "\(serverURL)/sync/delta") else { + throw SyncError.invalidURL + } + + // Get last sync time, or use epoch if never synced + let lastSync = lastSyncTime ?? Date(timeIntervalSince1970: 0) + let formatter = ISO8601DateFormatter() + let lastSyncString = formatter.string(from: lastSync) + + // Collect items modified since last sync + let modifiedGyms = dataManager.gyms.filter { gym in + gym.updatedAt > lastSync + }.map { BackupGym(from: $0) } + + let modifiedProblems = dataManager.problems.filter { problem in + problem.updatedAt > lastSync + }.map { problem -> BackupProblem in + var backupProblem = BackupProblem(from: problem) + if !problem.imagePaths.isEmpty { + let normalizedPaths = problem.imagePaths.enumerated().map { index, _ in + ImageNamingUtils.generateImageFilename( + problemId: problem.id.uuidString, imageIndex: index) + } + return BackupProblem( + id: backupProblem.id, + gymId: backupProblem.gymId, + name: backupProblem.name, + description: backupProblem.description, + climbType: backupProblem.climbType, + difficulty: backupProblem.difficulty, + tags: backupProblem.tags, + location: backupProblem.location, + imagePaths: normalizedPaths, + isActive: backupProblem.isActive, + dateSet: backupProblem.dateSet, + notes: backupProblem.notes, + createdAt: backupProblem.createdAt, + updatedAt: backupProblem.updatedAt + ) + } + return backupProblem + } + + let modifiedSessions = dataManager.sessions.filter { session in + session.status != .active && session.updatedAt > lastSync + }.map { BackupClimbSession(from: $0) } + + let activeSessionIds = Set( + dataManager.sessions.filter { $0.status == .active }.map { $0.id }) + let modifiedAttempts = dataManager.attempts.filter { attempt in + !activeSessionIds.contains(attempt.sessionId) && attempt.createdAt > lastSync + }.map { BackupAttempt(from: $0) } + + let modifiedDeletions = dataManager.getDeletedItems().filter { item in + if let deletedDate = formatter.date(from: item.deletedAt) { + return deletedDate > lastSync + } + return false + } + + print( + "iOS DELTA SYNC: Sending gyms=\(modifiedGyms.count), problems=\(modifiedProblems.count), sessions=\(modifiedSessions.count), attempts=\(modifiedAttempts.count), deletions=\(modifiedDeletions.count)" + ) + + // Create delta request + let deltaRequest = DeltaSyncRequest( + lastSyncTime: lastSyncString, + gyms: modifiedGyms, + problems: modifiedProblems, + sessions: modifiedSessions, + attempts: modifiedAttempts, + deletedItems: modifiedDeletions + ) + + let encoder = JSONEncoder() + encoder.dateEncodingStrategy = .iso8601 + let jsonData = try encoder.encode(deltaRequest) + + var request = URLRequest(url: url) + request.httpMethod = "POST" + request.setValue("Bearer \(authToken)", forHTTPHeaderField: "Authorization") + request.setValue("application/json", forHTTPHeaderField: "Content-Type") + request.setValue("application/json", forHTTPHeaderField: "Accept") + request.httpBody = jsonData + + let (data, response) = try await URLSession.shared.data(for: request) + + guard let httpResponse = response as? HTTPURLResponse else { + throw SyncError.invalidResponse + } + + switch httpResponse.statusCode { + case 200: + break + case 401: + throw SyncError.unauthorized + default: + throw SyncError.serverError(httpResponse.statusCode) + } + + let decoder = JSONDecoder() + let deltaResponse = try decoder.decode(DeltaSyncResponse.self, from: data) + + print( + "iOS DELTA SYNC: Received gyms=\(deltaResponse.gyms.count), problems=\(deltaResponse.problems.count), sessions=\(deltaResponse.sessions.count), attempts=\(deltaResponse.attempts.count), deletions=\(deltaResponse.deletedItems.count)" + ) + + // Apply server changes to local data + try await applyDeltaResponse(deltaResponse, dataManager: dataManager) + + // Sync only modified problem images + try await syncModifiedImages(modifiedProblems: modifiedProblems, dataManager: dataManager) + + // Update last sync time to server time + if let serverTime = formatter.date(from: deltaResponse.serverTime) { + lastSyncTime = serverTime + userDefaults.set(lastSyncTime, forKey: Keys.lastSyncTime) + } + } + + private func applyDeltaResponse(_ response: DeltaSyncResponse, dataManager: ClimbingDataManager) + async throws + { + let formatter = ISO8601DateFormatter() + + // Download images for new/modified problems from server + var imagePathMapping: [String: String] = [:] + for problem in response.problems { + guard let imagePaths = problem.imagePaths, !imagePaths.isEmpty else { continue } + + for (index, imagePath) in imagePaths.enumerated() { + let serverFilename = URL(fileURLWithPath: imagePath).lastPathComponent + + do { + let imageData = try await downloadImage(filename: serverFilename) + let consistentFilename = ImageNamingUtils.generateImageFilename( + problemId: problem.id, imageIndex: index) + let imageManager = ImageManager.shared + _ = try imageManager.saveImportedImage(imageData, filename: consistentFilename) + imagePathMapping[serverFilename] = consistentFilename + } catch SyncError.imageNotFound { + print("Image not found on server: \(serverFilename)") + continue + } catch { + print("Failed to download image \(serverFilename): \(error)") + continue + } + } + } + + // Merge gyms + for backupGym in response.gyms { + if let index = dataManager.gyms.firstIndex(where: { $0.id.uuidString == backupGym.id }) + { + let existing = dataManager.gyms[index] + if backupGym.updatedAt >= formatter.string(from: existing.updatedAt) { + dataManager.gyms[index] = try backupGym.toGym() + } + } else { + dataManager.gyms.append(try backupGym.toGym()) + } + } + + // Merge problems + for backupProblem in response.problems { + var problemToMerge = backupProblem + if !imagePathMapping.isEmpty, let imagePaths = backupProblem.imagePaths { + let updatedPaths = imagePaths.compactMap { imagePathMapping[$0] ?? $0 } + problemToMerge = BackupProblem( + id: backupProblem.id, + gymId: backupProblem.gymId, + name: backupProblem.name, + description: backupProblem.description, + climbType: backupProblem.climbType, + difficulty: backupProblem.difficulty, + tags: backupProblem.tags, + location: backupProblem.location, + imagePaths: updatedPaths, + isActive: backupProblem.isActive, + dateSet: backupProblem.dateSet, + notes: backupProblem.notes, + createdAt: backupProblem.createdAt, + updatedAt: backupProblem.updatedAt + ) + } + + if let index = dataManager.problems.firstIndex(where: { + $0.id.uuidString == problemToMerge.id + }) { + let existing = dataManager.problems[index] + if problemToMerge.updatedAt >= formatter.string(from: existing.updatedAt) { + dataManager.problems[index] = try problemToMerge.toProblem() + } + } else { + dataManager.problems.append(try problemToMerge.toProblem()) + } + } + + // Merge sessions + for backupSession in response.sessions { + if let index = dataManager.sessions.firstIndex(where: { + $0.id.uuidString == backupSession.id + }) { + let existing = dataManager.sessions[index] + if backupSession.updatedAt >= formatter.string(from: existing.updatedAt) { + dataManager.sessions[index] = try backupSession.toClimbSession() + } + } else { + dataManager.sessions.append(try backupSession.toClimbSession()) + } + } + + // Merge attempts + for backupAttempt in response.attempts { + if let index = dataManager.attempts.firstIndex(where: { + $0.id.uuidString == backupAttempt.id + }) { + let existing = dataManager.attempts[index] + if backupAttempt.createdAt >= formatter.string(from: existing.createdAt) { + dataManager.attempts[index] = try backupAttempt.toAttempt() + } + } else { + dataManager.attempts.append(try backupAttempt.toAttempt()) + } + } + + // Apply deletions + let allDeletions = dataManager.getDeletedItems() + response.deletedItems + let uniqueDeletions = Array(Set(allDeletions)) + applyDeletionsToDataManager(deletions: uniqueDeletions, dataManager: dataManager) + + // Save all changes + dataManager.saveGyms() + dataManager.saveProblems() + dataManager.saveSessions() + dataManager.saveAttempts() + + // Update deletion records + dataManager.clearDeletedItems() + if let data = try? JSONEncoder().encode(uniqueDeletions) { + UserDefaults.standard.set(data, forKey: "ascently_deleted_items") + } + + DataStateManager.shared.updateDataState() + } + + private func applyDeletionsToDataManager( + deletions: [DeletedItem], dataManager: ClimbingDataManager + ) { + let deletedGymIds = Set(deletions.filter { $0.type == "gym" }.map { $0.id }) + let deletedProblemIds = Set(deletions.filter { $0.type == "problem" }.map { $0.id }) + let deletedSessionIds = Set(deletions.filter { $0.type == "session" }.map { $0.id }) + let deletedAttemptIds = Set(deletions.filter { $0.type == "attempt" }.map { $0.id }) + + dataManager.gyms.removeAll { deletedGymIds.contains($0.id.uuidString) } + dataManager.problems.removeAll { deletedProblemIds.contains($0.id.uuidString) } + dataManager.sessions.removeAll { deletedSessionIds.contains($0.id.uuidString) } + dataManager.attempts.removeAll { deletedAttemptIds.contains($0.id.uuidString) } + } + + private func syncModifiedImages( + modifiedProblems: [BackupProblem], dataManager: ClimbingDataManager + ) async throws { + guard !modifiedProblems.isEmpty else { return } + + print("iOS DELTA SYNC: Syncing images for \(modifiedProblems.count) modified problems") + + for backupProblem in modifiedProblems { + guard + let problem = dataManager.problems.first(where: { + $0.id.uuidString == backupProblem.id + }) + else { + continue + } + + for (index, imagePath) in problem.imagePaths.enumerated() { + let filename = URL(fileURLWithPath: imagePath).lastPathComponent + let consistentFilename = ImageNamingUtils.generateImageFilename( + problemId: problem.id.uuidString, imageIndex: index) + + let imageManager = ImageManager.shared + let fullPath = imageManager.imagesDirectory.appendingPathComponent(filename).path + + if let imageData = imageManager.loadImageData(fromPath: fullPath) { + do { + if filename != consistentFilename { + let newPath = imageManager.imagesDirectory.appendingPathComponent( + consistentFilename + ).path + try? FileManager.default.moveItem(atPath: fullPath, toPath: newPath) + } + + try await uploadImage(filename: consistentFilename, imageData: imageData) + print("Uploaded modified problem image: \(consistentFilename)") + } catch { + print("Failed to upload image \(consistentFilename): \(error)") + } + } + } + } + } + func uploadImage(filename: String, imageData: Data) async throws { guard isConfigured else { throw SyncError.notConfigured @@ -246,6 +554,17 @@ class SyncService: ObservableObject { !serverBackup.gyms.isEmpty || !serverBackup.problems.isEmpty || !serverBackup.sessions.isEmpty || !serverBackup.attempts.isEmpty + // If both client and server have been synced before, use delta sync + if hasLocalData && hasServerData && lastSyncTime != nil { + print("iOS SYNC: Using delta sync for incremental updates") + try await performDeltaSync(dataManager: dataManager) + + // Update last sync time + lastSyncTime = Date() + userDefaults.set(lastSyncTime, forKey: Keys.lastSyncTime) + return + } + if !hasLocalData && hasServerData { // Case 1: No local data - do full restore from server print("iOS SYNC: Case 1 - No local data, performing full restore from server") @@ -286,7 +605,6 @@ class SyncService: ObservableObject { } } - /// Parses ISO8601 timestamp to milliseconds for comparison private func parseISO8601ToMillis(timestamp: String) -> Int64 { let formatter = ISO8601DateFormatter() if let date = formatter.date(from: timestamp) { @@ -1150,7 +1468,6 @@ class SyncService: ObservableObject { // Get active session IDs to protect their attempts let activeSessionIds = Set( local.compactMap { attempt in - // This is a simplified check - in a real implementation you'd want to cross-reference with sessions return attempt.sessionId }.filter { sessionId in // Check if this session ID belongs to an active session diff --git a/ios/Ascently/Utils/DataStateManager.swift b/ios/Ascently/Utils/DataStateManager.swift index 69c05b4..307d33d 100644 --- a/ios/Ascently/Utils/DataStateManager.swift +++ b/ios/Ascently/Utils/DataStateManager.swift @@ -37,46 +37,36 @@ class DataStateManager { print("iOS Data state updated to: \(now)") } - /// Gets the current data state timestamp. This represents when any data was last modified - /// locally. func getLastModified() -> String { if let storedTimestamp = userDefaults.string(forKey: Keys.lastModified) { print("iOS DataStateManager returning stored timestamp: \(storedTimestamp)") return storedTimestamp } - // If no timestamp is stored, return epoch time to indicate very old data - // This ensures server data will be considered newer than uninitialized local data let epochTime = "1970-01-01T00:00:00.000Z" - print("WARNING: No data state timestamp found - returning epoch time: \(epochTime)") + print("No data state timestamp found - returning epoch time: \(epochTime)") return epochTime } - /// Sets the data state timestamp to a specific value. Used when importing data from server to - /// sync the state. func setLastModified(_ timestamp: String) { userDefaults.set(timestamp, forKey: Keys.lastModified) print("Data state set to: \(timestamp)") } - /// Resets the data state (for testing or complete data wipe). func reset() { userDefaults.removeObject(forKey: Keys.lastModified) userDefaults.removeObject(forKey: Keys.initialized) print("Data state reset") } - /// Checks if the data state has been initialized. private func isInitialized() -> Bool { return userDefaults.bool(forKey: Keys.initialized) } - /// Marks the data state as initialized. private func markAsInitialized() { userDefaults.set(true, forKey: Keys.initialized) } - /// Gets debug information about the current state. func getDebugInfo() -> String { return "DataState(lastModified=\(getLastModified()), initialized=\(isInitialized()))" } diff --git a/ios/Ascently/Utils/ImageManager.swift b/ios/Ascently/Utils/ImageManager.swift index 2edae1a..22fde9e 100644 --- a/ios/Ascently/Utils/ImageManager.swift +++ b/ios/Ascently/Utils/ImageManager.swift @@ -690,7 +690,6 @@ class ImageManager { } private func cleanupOrphanedFiles() { - // This would need access to the data manager to check which files are actually referenced print("Cleanup would require coordination with data manager") } diff --git a/ios/Ascently/Utils/ImageNamingUtils.swift b/ios/Ascently/Utils/ImageNamingUtils.swift index ad339d7..1421361 100644 --- a/ios/Ascently/Utils/ImageNamingUtils.swift +++ b/ios/Ascently/Utils/ImageNamingUtils.swift @@ -108,7 +108,6 @@ class ImageNamingUtils { ) } - /// Generates the canonical filename that should be used for a problem image static func getCanonicalImageFilename(problemId: String, imageIndex: Int) -> String { return generateImageFilename(problemId: problemId, imageIndex: imageIndex) } diff --git a/ios/Ascently/Utils/ZipUtils.swift b/ios/Ascently/Utils/ZipUtils.swift index c64647f..d47b1bf 100644 --- a/ios/Ascently/Utils/ZipUtils.swift +++ b/ios/Ascently/Utils/ZipUtils.swift @@ -18,6 +18,7 @@ struct ZipUtils { var fileEntries: [(name: String, data: Data, offset: UInt32)] = [] var currentOffset: UInt32 = 0 + // Add metadata let metadata = createMetadata( exportData: exportData, referencedImagePaths: referencedImagePaths) let metadataData = metadata.data(using: .utf8) ?? Data() @@ -29,6 +30,7 @@ struct ZipUtils { currentOffset: ¤tOffset ) + // Encode JSON data let encoder = JSONEncoder() encoder.outputFormatting = .prettyPrinted encoder.dateEncodingStrategy = .custom { date, encoder in @@ -46,44 +48,49 @@ struct ZipUtils { currentOffset: ¤tOffset ) - print("Processing \(referencedImagePaths.count) referenced image paths") + // Process images in batches for better performance + print("Processing \(referencedImagePaths.count) images for export") var successfulImages = 0 + let batchSize = 10 + let sortedPaths = Array(referencedImagePaths).sorted() + + // Pre-allocate capacity for better memory performance + zipData.reserveCapacity(zipData.count + (referencedImagePaths.count * 200_000)) // Estimate 200KB per image + + for (index, imagePath) in sortedPaths.enumerated() { + if index % batchSize == 0 { + print("Processing images \(index)/\(sortedPaths.count)") + } - for imagePath in referencedImagePaths { - print("Processing image path: \(imagePath)") let imageURL = URL(fileURLWithPath: imagePath) let imageName = imageURL.lastPathComponent - print("Image name: \(imageName)") - if FileManager.default.fileExists(atPath: imagePath) { - print("Image file exists at: \(imagePath)") - do { - let imageData = try Data(contentsOf: imageURL) - print("Image data size: \(imageData.count) bytes") - if imageData.count > 0 { - let imageEntryName = "\(IMAGES_DIR_NAME)/\(imageName)" - try addFileToZip( - filename: imageEntryName, - fileData: imageData, - zipData: &zipData, - fileEntries: &fileEntries, - currentOffset: ¤tOffset - ) - successfulImages += 1 - print("Successfully added image to ZIP: \(imageEntryName)") - } else { - print("Image data is empty for: \(imagePath)") - } - } catch { - print("Failed to read image data for \(imagePath): \(error)") + guard FileManager.default.fileExists(atPath: imagePath) else { + continue + } + + do { + let imageData = try Data(contentsOf: imageURL) + if imageData.count > 0 { + let imageEntryName = "\(IMAGES_DIR_NAME)/\(imageName)" + try addFileToZip( + filename: imageEntryName, + fileData: imageData, + zipData: &zipData, + fileEntries: &fileEntries, + currentOffset: ¤tOffset + ) + successfulImages += 1 } - } else { - print("Image file does not exist at: \(imagePath)") + } catch { + print("Failed to read image: \(imageName)") } } - print("Export completed: \(successfulImages)/\(referencedImagePaths.count) images included") + print("Export: included \(successfulImages)/\(referencedImagePaths.count) images") + // Build central directory + centralDirectory.reserveCapacity(fileEntries.count * 100) // Estimate 100 bytes per entry for entry in fileEntries { let centralDirEntry = createCentralDirectoryEntry( filename: entry.name, @@ -372,12 +379,12 @@ struct ZipUtils { return data } - private static func calculateCRC32(data: Data) -> UInt32 { + // CRC32 lookup table for faster calculation + private static let crc32Table: [UInt32] = { let polynomial: UInt32 = 0xEDB8_8320 - var crc: UInt32 = 0xFFFF_FFFF - - for byte in data { - crc ^= UInt32(byte) + var table = [UInt32](repeating: 0, count: 256) + for i in 0..<256 { + var crc = UInt32(i) for _ in 0..<8 { if crc & 1 != 0 { crc = (crc >> 1) ^ polynomial @@ -385,6 +392,19 @@ struct ZipUtils { crc >>= 1 } } + table[i] = crc + } + return table + }() + + private static func calculateCRC32(data: Data) -> UInt32 { + var crc: UInt32 = 0xFFFF_FFFF + + data.withUnsafeBytes { (bytes: UnsafeRawBufferPointer) in + for byte in bytes { + let index = Int((crc ^ UInt32(byte)) & 0xFF) + crc = (crc >> 8) ^ crc32Table[index] + } } return ~crc diff --git a/ios/Ascently/ViewModels/ClimbingDataManager.swift b/ios/Ascently/ViewModels/ClimbingDataManager.swift index 81d9a9e..67d3f96 100644 --- a/ios/Ascently/ViewModels/ClimbingDataManager.swift +++ b/ios/Ascently/ViewModels/ClimbingDataManager.swift @@ -653,9 +653,6 @@ class ClimbingDataManager: ObservableObject { return gym(withId: mostUsedGymId) } - /// Clean up orphaned data - removes attempts that reference non-existent sessions - /// and removes duplicate attempts. This ensures data integrity and prevents - /// orphaned attempts from appearing in widgets private func cleanupOrphanedData() { let validSessionIds = Set(sessions.map { $0.id }) let validProblemIds = Set(problems.map { $0.id }) @@ -761,8 +758,6 @@ class ClimbingDataManager: ObservableObject { } } - /// Validate data integrity and return a report - /// This can be called manually to check for issues func validateDataIntegrity() -> String { let validSessionIds = Set(sessions.map { $0.id }) let validProblemIds = Set(problems.map { $0.id }) @@ -801,8 +796,6 @@ class ClimbingDataManager: ObservableObject { return report } - /// Manually trigger cleanup of orphaned data - /// This can be called from settings or debug menu func manualDataCleanup() { cleanupOrphanedData() successMessage = "Data cleanup completed" @@ -830,12 +823,12 @@ class ClimbingDataManager: ObservableObject { } } - func exportData() -> Data? { + func exportData() async -> Data? { do { + // Create backup objects on main thread (they access MainActor-isolated properties) let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSS" - // Create export data with normalized image paths let exportData = ClimbDataBackup( exportedAt: dateFormatter.string(from: Date()), version: "2.0", @@ -846,19 +839,30 @@ class ClimbingDataManager: ObservableObject { attempts: attempts.map { BackupAttempt(from: $0) } ) - // Collect actual image paths from disk for the ZIP - let referencedImagePaths = collectReferencedImagePaths() - print("Starting export with \(referencedImagePaths.count) images") + // Get image manager path info on main thread + let imagesDirectory = ImageManager.shared.imagesDirectory.path + let problemsForImages = problems - let zipData = try ZipUtils.createExportZip( - exportData: exportData, - referencedImagePaths: referencedImagePaths - ) + // Move heavy I/O operations to background thread + let zipData = try await Task.detached(priority: .userInitiated) { + // Collect actual image paths from disk for the ZIP + let referencedImagePaths = await Self.collectReferencedImagePathsStatic( + problems: problemsForImages, + imagesDirectory: imagesDirectory) + print("Starting export with \(referencedImagePaths.count) images") - print("Export completed successfully") - successMessage = "Export completed with \(referencedImagePaths.count) images" + let zipData = try await ZipUtils.createExportZip( + exportData: exportData, + referencedImagePaths: referencedImagePaths + ) + + print("Export completed successfully") + return (zipData, referencedImagePaths.count) + }.value + + successMessage = "Export completed with \(zipData.1) images" clearMessageAfterDelay() - return zipData + return zipData.0 } catch { let errorMessage = "Export failed: \(error.localizedDescription)" print("ERROR: \(errorMessage)") @@ -955,36 +959,36 @@ class ClimbingDataManager: ObservableObject { extension ClimbingDataManager { private func collectReferencedImagePaths() -> Set { + let imagesDirectory = ImageManager.shared.imagesDirectory.path + return Self.collectReferencedImagePathsStatic( + problems: problems, + imagesDirectory: imagesDirectory) + } + + private static func collectReferencedImagePathsStatic( + problems: [Problem], imagesDirectory: String + ) -> Set { var imagePaths = Set() - print("Starting image path collection...") - print("Total problems: \(problems.count)") + var missingCount = 0 for problem in problems { if !problem.imagePaths.isEmpty { - print( - "Problem '\(problem.name ?? "Unnamed")' has \(problem.imagePaths.count) images" - ) for imagePath in problem.imagePaths { - print(" - Stored path: \(imagePath)") - // Extract just the filename (migration should have normalized these) let filename = URL(fileURLWithPath: imagePath).lastPathComponent - let fullPath = ImageManager.shared.getFullPath(from: filename) - print(" - Full disk path: \(fullPath)") + let fullPath = (imagesDirectory as NSString).appendingPathComponent(filename) if FileManager.default.fileExists(atPath: fullPath) { - print(" ✓ File exists") imagePaths.insert(fullPath) } else { - print(" ✗ WARNING: File not found at \(fullPath)") - // Still add it to let ZipUtils handle the logging + missingCount += 1 imagePaths.insert(fullPath) } } } } - print("Collected \(imagePaths.count) total image paths for export") + print("Export: Collected \(imagePaths.count) images (\(missingCount) missing)") return imagePaths } @@ -1273,7 +1277,9 @@ extension ClimbingDataManager { ) { [weak self] notification in if let updateCount = notification.userInfo?["updateCount"] as? Int { print("🔔 Image migration completed with \(updateCount) updates - reloading data") - self?.loadProblems() + Task { @MainActor in + self?.loadProblems() + } } } } diff --git a/ios/Ascently/Views/AddEdit/AddEditProblemView.swift b/ios/Ascently/Views/AddEdit/AddEditProblemView.swift index a913f75..af3ee55 100644 --- a/ios/Ascently/Views/AddEdit/AddEditProblemView.swift +++ b/ios/Ascently/Views/AddEdit/AddEditProblemView.swift @@ -103,7 +103,6 @@ struct AddEditProblemView: View { setupInitialGym() } .onChange(of: dataManager.gyms) { - // Ensure a gym is selected when gyms are loaded or changed if selectedGym == nil && !dataManager.gyms.isEmpty { selectedGym = dataManager.gyms.first } diff --git a/ios/Ascently/Views/SettingsView.swift b/ios/Ascently/Views/SettingsView.swift index 5cbdd3a..e7ee043 100644 --- a/ios/Ascently/Views/SettingsView.swift +++ b/ios/Ascently/Views/SettingsView.swift @@ -180,10 +180,12 @@ struct DataManagementSection: View { private func exportDataAsync() { isExporting = true Task { - let data = await MainActor.run { dataManager.exportData() } - isExporting = false - if let data = data { - activeSheet = .export(data) + let data = await dataManager.exportData() + await MainActor.run { + isExporting = false + if let data = data { + activeSheet = .export(data) + } } } } diff --git a/ios/AscentlyTests/AscentlyTests.swift b/ios/AscentlyTests/AscentlyTests.swift index 71fbe0f..cbbbda8 100644 --- a/ios/AscentlyTests/AscentlyTests.swift +++ b/ios/AscentlyTests/AscentlyTests.swift @@ -256,10 +256,6 @@ final class AscentlyTests: XCTestCase { // MARK: - Active Session Preservation Tests func testActiveSessionPreservationDuringImport() throws { - // Test that active sessions are preserved during import operations - // This tests the fix for the bug where active sessions disappear after sync - - // Simulate an active session that exists locally but not in import data let activeSessionId = UUID() let gymId = UUID() diff --git a/sync/main.go b/sync/main.go index 190aaf8..6872fa8 100644 --- a/sync/main.go +++ b/sync/main.go @@ -13,7 +13,7 @@ import ( "time" ) -const VERSION = "2.0.0" +const VERSION = "2.1.0" func min(a, b int) int { if a < b { @@ -39,6 +39,24 @@ type ClimbDataBackup struct { DeletedItems []DeletedItem `json:"deletedItems"` } +type DeltaSyncRequest struct { + LastSyncTime string `json:"lastSyncTime"` + Gyms []BackupGym `json:"gyms"` + Problems []BackupProblem `json:"problems"` + Sessions []BackupClimbSession `json:"sessions"` + Attempts []BackupAttempt `json:"attempts"` + DeletedItems []DeletedItem `json:"deletedItems"` +} + +type DeltaSyncResponse struct { + ServerTime string `json:"serverTime"` + Gyms []BackupGym `json:"gyms"` + Problems []BackupProblem `json:"problems"` + Sessions []BackupClimbSession `json:"sessions"` + Attempts []BackupAttempt `json:"attempts"` + DeletedItems []DeletedItem `json:"deletedItems"` +} + type BackupGym struct { ID string `json:"id"` Name string `json:"name"` @@ -154,6 +172,174 @@ func (s *SyncServer) loadData() (*ClimbDataBackup, error) { return &backup, nil } +func (s *SyncServer) mergeGyms(existing []BackupGym, updates []BackupGym) []BackupGym { + gymMap := make(map[string]BackupGym) + for _, gym := range existing { + gymMap[gym.ID] = gym + } + + for _, gym := range updates { + if existingGym, exists := gymMap[gym.ID]; exists { + // Keep newer version based on updatedAt timestamp + if gym.UpdatedAt >= existingGym.UpdatedAt { + gymMap[gym.ID] = gym + } + } else { + gymMap[gym.ID] = gym + } + } + + result := make([]BackupGym, 0, len(gymMap)) + for _, gym := range gymMap { + result = append(result, gym) + } + return result +} + +func (s *SyncServer) mergeProblems(existing []BackupProblem, updates []BackupProblem) []BackupProblem { + problemMap := make(map[string]BackupProblem) + for _, problem := range existing { + problemMap[problem.ID] = problem + } + + for _, problem := range updates { + if existingProblem, exists := problemMap[problem.ID]; exists { + if problem.UpdatedAt >= existingProblem.UpdatedAt { + problemMap[problem.ID] = problem + } + } else { + problemMap[problem.ID] = problem + } + } + + result := make([]BackupProblem, 0, len(problemMap)) + for _, problem := range problemMap { + result = append(result, problem) + } + return result +} + +func (s *SyncServer) mergeSessions(existing []BackupClimbSession, updates []BackupClimbSession) []BackupClimbSession { + sessionMap := make(map[string]BackupClimbSession) + for _, session := range existing { + sessionMap[session.ID] = session + } + + for _, session := range updates { + if existingSession, exists := sessionMap[session.ID]; exists { + if session.UpdatedAt >= existingSession.UpdatedAt { + sessionMap[session.ID] = session + } + } else { + sessionMap[session.ID] = session + } + } + + result := make([]BackupClimbSession, 0, len(sessionMap)) + for _, session := range sessionMap { + result = append(result, session) + } + return result +} + +func (s *SyncServer) mergeAttempts(existing []BackupAttempt, updates []BackupAttempt) []BackupAttempt { + attemptMap := make(map[string]BackupAttempt) + for _, attempt := range existing { + attemptMap[attempt.ID] = attempt + } + + for _, attempt := range updates { + if existingAttempt, exists := attemptMap[attempt.ID]; exists { + if attempt.CreatedAt >= existingAttempt.CreatedAt { + attemptMap[attempt.ID] = attempt + } + } else { + attemptMap[attempt.ID] = attempt + } + } + + result := make([]BackupAttempt, 0, len(attemptMap)) + for _, attempt := range attemptMap { + result = append(result, attempt) + } + return result +} + +func (s *SyncServer) mergeDeletedItems(existing []DeletedItem, updates []DeletedItem) []DeletedItem { + deletedMap := make(map[string]DeletedItem) + for _, item := range existing { + key := item.Type + ":" + item.ID + deletedMap[key] = item + } + + for _, item := range updates { + key := item.Type + ":" + item.ID + if existingItem, exists := deletedMap[key]; exists { + if item.DeletedAt >= existingItem.DeletedAt { + deletedMap[key] = item + } + } else { + deletedMap[key] = item + } + } + + result := make([]DeletedItem, 0, len(deletedMap)) + for _, item := range deletedMap { + result = append(result, item) + } + return result +} + +func (s *SyncServer) applyDeletions(backup *ClimbDataBackup, deletedItems []DeletedItem) { + deletedMap := make(map[string]map[string]bool) + for _, item := range deletedItems { + if deletedMap[item.Type] == nil { + deletedMap[item.Type] = make(map[string]bool) + } + deletedMap[item.Type][item.ID] = true + } + + if deletedMap["gym"] != nil { + filtered := []BackupGym{} + for _, gym := range backup.Gyms { + if !deletedMap["gym"][gym.ID] { + filtered = append(filtered, gym) + } + } + backup.Gyms = filtered + } + + if deletedMap["problem"] != nil { + filtered := []BackupProblem{} + for _, problem := range backup.Problems { + if !deletedMap["problem"][problem.ID] { + filtered = append(filtered, problem) + } + } + backup.Problems = filtered + } + + if deletedMap["session"] != nil { + filtered := []BackupClimbSession{} + for _, session := range backup.Sessions { + if !deletedMap["session"][session.ID] { + filtered = append(filtered, session) + } + } + backup.Sessions = filtered + } + + if deletedMap["attempt"] != nil { + filtered := []BackupAttempt{} + for _, attempt := range backup.Attempts { + if !deletedMap["attempt"][attempt.ID] { + filtered = append(filtered, attempt) + } + } + backup.Attempts = filtered + } +} + func (s *SyncServer) saveData(backup *ClimbDataBackup) error { backup.ExportedAt = time.Now().UTC().Format(time.RFC3339) @@ -167,7 +353,6 @@ func (s *SyncServer) saveData(backup *ClimbDataBackup) error { return err } - // Ensure images directory exists if err := os.MkdirAll(s.imagesDir, 0755); err != nil { return err } @@ -315,6 +500,123 @@ func (s *SyncServer) handleImageDownload(w http.ResponseWriter, r *http.Request) w.Write(imageData) } +func (s *SyncServer) handleDeltaSync(w http.ResponseWriter, r *http.Request) { + if !s.authenticate(r) { + log.Printf("Unauthorized delta sync attempt from %s", r.RemoteAddr) + http.Error(w, "Unauthorized", http.StatusUnauthorized) + return + } + + if r.Method != http.MethodPost { + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + return + } + + var deltaRequest DeltaSyncRequest + if err := json.NewDecoder(r.Body).Decode(&deltaRequest); err != nil { + log.Printf("Invalid JSON from %s: %v", r.RemoteAddr, err) + http.Error(w, "Invalid JSON", http.StatusBadRequest) + return + } + + log.Printf("Delta sync from %s: lastSyncTime=%s, gyms=%d, problems=%d, sessions=%d, attempts=%d, deletedItems=%d", + r.RemoteAddr, deltaRequest.LastSyncTime, + len(deltaRequest.Gyms), len(deltaRequest.Problems), + len(deltaRequest.Sessions), len(deltaRequest.Attempts), + len(deltaRequest.DeletedItems)) + + // Load current server data + serverBackup, err := s.loadData() + if err != nil { + log.Printf("Failed to load data: %v", err) + http.Error(w, "Failed to load data", http.StatusInternalServerError) + return + } + + // Merge client changes into server data + serverBackup.Gyms = s.mergeGyms(serverBackup.Gyms, deltaRequest.Gyms) + serverBackup.Problems = s.mergeProblems(serverBackup.Problems, deltaRequest.Problems) + serverBackup.Sessions = s.mergeSessions(serverBackup.Sessions, deltaRequest.Sessions) + serverBackup.Attempts = s.mergeAttempts(serverBackup.Attempts, deltaRequest.Attempts) + serverBackup.DeletedItems = s.mergeDeletedItems(serverBackup.DeletedItems, deltaRequest.DeletedItems) + + // Apply deletions to remove deleted items + s.applyDeletions(serverBackup, serverBackup.DeletedItems) + + // Save merged data + if err := s.saveData(serverBackup); err != nil { + log.Printf("Failed to save data: %v", err) + http.Error(w, "Failed to save data", http.StatusInternalServerError) + return + } + + // Parse client's last sync time + clientLastSync, err := time.Parse(time.RFC3339, deltaRequest.LastSyncTime) + if err != nil { + // If parsing fails, send everything + clientLastSync = time.Time{} + } + + // Prepare response with items modified since client's last sync + response := DeltaSyncResponse{ + ServerTime: time.Now().UTC().Format(time.RFC3339), + Gyms: []BackupGym{}, + Problems: []BackupProblem{}, + Sessions: []BackupClimbSession{}, + Attempts: []BackupAttempt{}, + DeletedItems: []DeletedItem{}, + } + + // Filter gyms modified after client's last sync + for _, gym := range serverBackup.Gyms { + gymTime, err := time.Parse(time.RFC3339, gym.UpdatedAt) + if err == nil && gymTime.After(clientLastSync) { + response.Gyms = append(response.Gyms, gym) + } + } + + // Filter problems modified after client's last sync + for _, problem := range serverBackup.Problems { + problemTime, err := time.Parse(time.RFC3339, problem.UpdatedAt) + if err == nil && problemTime.After(clientLastSync) { + response.Problems = append(response.Problems, problem) + } + } + + // Filter sessions modified after client's last sync + for _, session := range serverBackup.Sessions { + sessionTime, err := time.Parse(time.RFC3339, session.UpdatedAt) + if err == nil && sessionTime.After(clientLastSync) { + response.Sessions = append(response.Sessions, session) + } + } + + // Filter attempts created after client's last sync + for _, attempt := range serverBackup.Attempts { + attemptTime, err := time.Parse(time.RFC3339, attempt.CreatedAt) + if err == nil && attemptTime.After(clientLastSync) { + response.Attempts = append(response.Attempts, attempt) + } + } + + // Filter deletions after client's last sync + for _, deletedItem := range serverBackup.DeletedItems { + deletedTime, err := time.Parse(time.RFC3339, deletedItem.DeletedAt) + if err == nil && deletedTime.After(clientLastSync) { + response.DeletedItems = append(response.DeletedItems, deletedItem) + } + } + + log.Printf("Delta sync response to %s: gyms=%d, problems=%d, sessions=%d, attempts=%d, deletedItems=%d", + r.RemoteAddr, + len(response.Gyms), len(response.Problems), + len(response.Sessions), len(response.Attempts), + len(response.DeletedItems)) + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(response) +} + func (s *SyncServer) handleSync(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: @@ -354,6 +656,7 @@ func main() { } http.HandleFunc("/sync", server.handleSync) + http.HandleFunc("/sync/delta", server.handleDeltaSync) http.HandleFunc("/health", server.handleHealth) http.HandleFunc("/images/upload", server.handleImageUpload) http.HandleFunc("/images/download", server.handleImageDownload) @@ -362,6 +665,8 @@ func main() { fmt.Printf("Data file: %s\n", dataFile) fmt.Printf("Images directory: %s\n", imagesDir) fmt.Printf("Health check available at /health\n") + fmt.Printf("Delta sync: POST /sync/delta (incremental sync)\n") + fmt.Printf("Full sync: GET /sync (download all), PUT /sync (upload all)\n") fmt.Printf("Image upload: POST /images/upload?filename=\n") fmt.Printf("Image download: GET /images/download?filename=\n")