From 9074234fd3d26ef757f150bc7859f33e1ccd363a Mon Sep 17 00:00:00 2001 From: bponders12-cell Date: Tue, 1 Sep 2026 21:20:38 -0400 Subject: [PATCH] fix: require exact telemetry bundle CSV schema during restore --- lib/services/sync/cloud_restore_service.dart | 41 ++++++++------------ 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/lib/services/sync/cloud_restore_service.dart b/lib/services/sync/cloud_restore_service.dart index 47a1350..2716aee 100644 --- a/lib/services/sync/cloud_restore_service.dart +++ b/lib/services/sync/cloud_restore_service.dart @@ -62,11 +62,7 @@ class CloudRestoreService { } final metadataResult = await _metadata.reconcileMetadata(); - final snapshot = await _firestore - .collection('users') - .doc(user.uid) - .collection('rides') - .get(); + final snapshot = await _firestore.collection('users').doc(user.uid).collection('rides').get(); var telemetryRestored = 0; var telemetrySkipped = 0; @@ -80,37 +76,26 @@ class CloudRestoreService { continue; } - final localRide = await (_db.select(_db.rides) - ..where((ride) => ride.cloudRideId.equals(cloudRideId))) - .getSingleOrNull(); + final localRide = await (_db.select(_db.rides)..where((ride) => ride.cloudRideId.equals(cloudRideId))).getSingleOrNull(); if (localRide == null) { telemetryFailed++; continue; } - final telemetryCount = await (_db.select(_db.telemetrySamples) - ..where((sample) => sample.rideId.equals(localRide.id))) - .get() - .then((rows) => rows.length); + final telemetryCount = await (_db.select(_db.telemetrySamples)..where((sample) => sample.rideId.equals(localRide.id))).get().then((rows) => rows.length); if (telemetryCount > 0) { telemetrySkipped++; continue; } - final result = await _r2DownloadService.downloadTelemetryBundle( - idToken: idToken, - cloudRideId: cloudRideId, - ); + final result = await _r2DownloadService.downloadTelemetryBundle(idToken: idToken, cloudRideId: cloudRideId); if (!result.success || result.zipBytes == null) { telemetryFailed++; continue; } try { - final restoredCount = await _restoreTelemetryCsv( - localRide.id, - result.zipBytes!, - ); + final restoredCount = await _restoreTelemetryCsv(localRide.id, result.zipBytes!); if (restoredCount > 0) { telemetryRestored++; } else { @@ -138,8 +123,7 @@ class CloudRestoreService { final lines = const LineSplitter().convert(csv); if (lines.length < 2) return 0; - final header = lines.first.split(','); - final expected = [ + const expected = [ 'timestamp', 'latitude', 'longitude', @@ -155,8 +139,8 @@ class CloudRestoreService { 'longitudinalG', 'lateralG', ]; - if (header.length != expected.length || !List.generate(header.length, (i) => header[i]) - .every((value) => expected.contains(value))) { + final header = lines.first.split(','); + if (header.length != expected.length || !_sameStringList(header, expected)) { throw const FormatException('Unsupported telemetry.csv schema.'); } @@ -189,7 +173,6 @@ class CloudRestoreService { } if (companions.isEmpty) return 0; - await _db.transaction(() async { for (final companion in companions) { await _db.into(_db.telemetrySamples).insert(companion); @@ -206,5 +189,13 @@ class CloudRestoreService { return drift.Value(parsed); } + bool _sameStringList(List actual, List expected) { + if (actual.length != expected.length) return false; + for (var i = 0; i < expected.length; i++) { + if (actual[i] != expected[i]) return false; + } + return true; + } + bool _isValidCloudRideId(String id) => RegExp(r'^ride_[A-Za-z0-9_-]+$').hasMatch(id); }