feat: optimize bulk cipher restoration
This commit is contained in:
parent
6beec5f1ed
commit
b8a961a936
2 changed files with 23 additions and 20 deletions
|
|
@ -331,8 +331,17 @@ pub async fn restore_ciphers_bulk(
|
||||||
let now = Utc::now().format("%Y-%m-%dT%H:%M:%S%.3fZ").to_string();
|
let now = Utc::now().format("%Y-%m-%dT%H:%M:%S%.3fZ").to_string();
|
||||||
let batch_size = get_batch_size(&env);
|
let batch_size = get_batch_size(&env);
|
||||||
let ids = payload.ids;
|
let ids = payload.ids;
|
||||||
let mut update_statements: Vec<D1PreparedStatement> = Vec::with_capacity(ids.len());
|
|
||||||
|
|
||||||
|
if ids.is_empty() {
|
||||||
|
return Ok(Json(BulkRestoreResponse {
|
||||||
|
data: vec![],
|
||||||
|
object: "list".to_string(),
|
||||||
|
continuation_token: None,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Batch UPDATE operations
|
||||||
|
let mut update_statements: Vec<D1PreparedStatement> = Vec::with_capacity(ids.len());
|
||||||
for id in ids.iter() {
|
for id in ids.iter() {
|
||||||
let stmt = query!(
|
let stmt = query!(
|
||||||
&db,
|
&db,
|
||||||
|
|
@ -345,26 +354,20 @@ pub async fn restore_ciphers_bulk(
|
||||||
|
|
||||||
update_statements.push(stmt);
|
update_statements.push(stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
db::execute_in_batches(&db, update_statements, batch_size).await?;
|
db::execute_in_batches(&db, update_statements, batch_size).await?;
|
||||||
|
|
||||||
let mut restored_ciphers = Vec::with_capacity(ids.len());
|
// Batch SELECT using json_each() - avoid N+1 query problem
|
||||||
|
let ids_json = serde_json::to_string(&ids).map_err(|_| AppError::Internal)?;
|
||||||
|
|
||||||
for id in ids {
|
let restored_ciphers: Vec<Cipher> = db
|
||||||
let cipher_db: Option<crate::models::cipher::CipherDBModel> = query!(
|
.prepare("SELECT * FROM ciphers WHERE user_id = ?1 AND id IN (SELECT value FROM json_each(?2))")
|
||||||
&db,
|
.bind(&[claims.sub.clone().into(), ids_json.into()])?
|
||||||
"SELECT * FROM ciphers WHERE id = ?1 AND user_id = ?2",
|
.all()
|
||||||
id,
|
.await?
|
||||||
claims.sub
|
.results::<crate::models::cipher::CipherDBModel>()?
|
||||||
)
|
.into_iter()
|
||||||
.map_err(|_| AppError::Database)?
|
.map(|cipher| cipher.into())
|
||||||
.first(None)
|
.collect();
|
||||||
.await?;
|
|
||||||
|
|
||||||
if let Some(cipher) = cipher_db {
|
|
||||||
restored_ciphers.push(cipher.into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
db::touch_user_updated_at(&db, &claims.sub).await?;
|
db::touch_user_updated_at(&db, &claims.sub).await?;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -155,7 +155,7 @@ impl Into<Cipher> for CipherDBModel {
|
||||||
deleted_at: self.deleted_at,
|
deleted_at: self.deleted_at,
|
||||||
created_at: self.created_at,
|
created_at: self.created_at,
|
||||||
updated_at: self.updated_at,
|
updated_at: self.updated_at,
|
||||||
object: "default_object".to_string(),
|
object: "cipherDetails".to_string(),
|
||||||
organization_use_totp: false,
|
organization_use_totp: false,
|
||||||
edit: true,
|
edit: true,
|
||||||
view_password: true,
|
view_password: true,
|
||||||
|
|
@ -264,7 +264,7 @@ impl Serialize for Cipher {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_object() -> String {
|
fn default_object() -> String {
|
||||||
"cipher".to_string()
|
"cipherDetails".to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_true() -> bool {
|
fn default_true() -> bool {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue