@@ -17,8 +17,8 @@ use crate::{WebData, get_db_conn};
1717const MEBIBYTE : usize = 1024 * 1024 ;
1818const MAX_ENCRYPTED_SYNC_BYTES : usize = 64 * MEBIBYTE ;
1919const MAX_ENCRYPTED_SYNC_ACCOUNT_BYTES : usize = 128 * MEBIBYTE ;
20- // Deprecated playbackSpeeds is read into settings by current clients and must
21- // not be required to finish legacy document migration .
20+ // Current clients can acknowledge saved playback speeds in settings. Older
21+ // clients still require the deprecated collection to resume partial migrations .
2222const LEGACY_ENCRYPTED_COLLECTIONS : [ & str ; 5 ] = [
2323 "subscriptions" ,
2424 "playlists" ,
@@ -27,6 +27,14 @@ const LEGACY_ENCRYPTED_COLLECTIONS: [&str; 5] = [
2727 "playlistBookmarks" ,
2828] ;
2929
30+ #[ derive( Debug , Default , serde:: Deserialize , utoipa:: IntoParams ) ]
31+ #[ into_params( parameter_in = Query ) ]
32+ struct EncryptedSyncManifestQuery {
33+ /// The requesting client has successfully synced playback speeds into settings.
34+ #[ serde( default ) ]
35+ playback_speeds_in_settings : bool ,
36+ }
37+
3038pub struct EncryptedSyncHandler { }
3139
3240impl ScopedHandler for EncryptedSyncHandler {
@@ -74,11 +82,12 @@ fn collection_limit(collection: &str) -> HandlerResult<usize> {
7482 }
7583}
7684
77- #[ utoipa:: path( responses( ( status = OK , body = EncryptedSyncManifest ) ) , security( ( "api_jwt_token" = [ ] ) ) ) ]
85+ #[ utoipa:: path( params ( EncryptedSyncManifestQuery ) , responses( ( status = OK , body = EncryptedSyncManifest ) ) , security( ( "api_jwt_token" = [ ] ) ) ) ]
7886#[ get( "" ) ]
7987async fn get_encrypted_sync_manifest (
8088 account : Account ,
8189 pool : WebData ,
90+ query : web:: Query < EncryptedSyncManifestQuery > ,
8291) -> HandlerResult < impl Responder > {
8392 let mut conn = get_db_conn ! ( pool) ;
8493 let documents = encrypted_sync:: get_all ( & mut conn, & account. id )
@@ -92,7 +101,11 @@ async fn get_encrypted_sync_manifest(
92101 . iter ( )
93102 . any ( |document| document. collection == * collection)
94103 } ) ;
95- let legacy_encrypted_data = !has_all_migrated_collections
104+ let has_migrated_playback_speeds = query. playback_speeds_in_settings
105+ || documents
106+ . iter ( )
107+ . any ( |document| document. collection == "playbackSpeeds" ) ;
108+ let legacy_encrypted_data = !( has_all_migrated_collections && has_migrated_playback_speeds)
96109 && encrypted_sync:: get_legacy_encrypted ( & mut conn, & account. id )
97110 . await
98111 . map_err ( |_| HandlerError :: InternalDatabaseError ) ?
@@ -301,6 +314,15 @@ mod migration_tests {
301314 . status( )
302315 . is_success( )
303316 ) ;
317+ // Even an existing settings ciphertext cannot prove that speeds
318+ // were migrated: the user may have excluded that setting.
319+ let request = test:: TestRequest :: get ( ) . uri ( "/sync" ) . to_request ( ) ;
320+ request. extensions_mut ( ) . insert ( account. clone ( ) ) ;
321+ let manifest: serde_json:: Value = test:: call_and_read_body_json ( & app, request) . await ;
322+ assert_eq ! (
323+ manifest[ "legacy_encrypted_data" ] ,
324+ collection != "playbackSpeeds"
325+ ) ;
304326 }
305327 for deleted in [ false , true ] {
306328 if deleted {
@@ -312,6 +334,12 @@ mod migration_tests {
312334 request. extensions_mut ( ) . insert ( account. clone ( ) ) ;
313335 let manifest: serde_json:: Value = test:: call_and_read_body_json ( & app, request) . await ;
314336 assert_eq ! ( manifest[ "legacy_data" ] , false ) ;
337+ assert_eq ! ( manifest[ "legacy_encrypted_data" ] , deleted) ;
338+ let request = test:: TestRequest :: get ( )
339+ . uri ( "/sync?playback_speeds_in_settings=true" )
340+ . to_request ( ) ;
341+ request. extensions_mut ( ) . insert ( account. clone ( ) ) ;
342+ let manifest: serde_json:: Value = test:: call_and_read_body_json ( & app, request) . await ;
315343 assert_eq ! ( manifest[ "legacy_encrypted_data" ] , false ) ;
316344 assert_eq ! (
317345 manifest[ "collections" ]
0 commit comments