Add import support
This commit is contained in:
parent
42976295df
commit
ff3ab17077
5 changed files with 166 additions and 3 deletions
113
src/handlers/import.rs
Normal file
113
src/handlers/import.rs
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
use axum::{extract::State, Json};
|
||||
use chrono::Utc;
|
||||
use std::sync::Arc;
|
||||
use uuid::Uuid;
|
||||
use worker::{query, Env};
|
||||
|
||||
use crate::auth::Claims;
|
||||
use crate::db;
|
||||
use crate::error::AppError;
|
||||
use crate::models::cipher::{Cipher, CipherData};
|
||||
use crate::models::folder::Folder;
|
||||
use crate::models::import::ImportRequest;
|
||||
|
||||
#[worker::send]
|
||||
pub async fn import_data(
|
||||
claims: Claims,
|
||||
State(env): State<Arc<Env>>,
|
||||
Json(mut payload): Json<ImportRequest>,
|
||||
) -> Result<Json<()>, AppError> {
|
||||
let db = db::get_db(&env)?;
|
||||
let now = Utc::now();
|
||||
let now = now.format("%Y-%m-%dT%H:%M:%S%.3fZ").to_string();
|
||||
|
||||
for import_folder in &payload.folders {
|
||||
let folder = Folder {
|
||||
id: import_folder.id.clone(),
|
||||
user_id: claims.sub.clone(),
|
||||
name: import_folder.name.clone(),
|
||||
created_at: now.clone(),
|
||||
updated_at: now.clone(),
|
||||
};
|
||||
|
||||
query!(
|
||||
&db,
|
||||
"INSERT OR IGNORE INTO folders (id, user_id, name, created_at, updated_at) VALUES (?1, ?2, ?3, ?4, ?5)",
|
||||
folder.id,
|
||||
folder.user_id,
|
||||
folder.name,
|
||||
folder.created_at,
|
||||
folder.updated_at
|
||||
)
|
||||
.map_err(|_| AppError::Database)?
|
||||
.run()
|
||||
.await?;
|
||||
}
|
||||
|
||||
for relationship in payload.folder_relationships {
|
||||
if let Some(cipher) = payload.ciphers.get_mut(relationship.key) {
|
||||
if let Some(folder) = payload.folders.get(relationship.value) {
|
||||
cipher.folder_id = Some(folder.id.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for import_cipher in payload.ciphers {
|
||||
if import_cipher.encrypted_for != claims.sub {
|
||||
return Err(AppError::BadRequest("Cipher encrypted for wrong user".to_string()));
|
||||
}
|
||||
|
||||
let cipher_data = CipherData {
|
||||
name: import_cipher.name,
|
||||
notes: import_cipher.notes,
|
||||
login: import_cipher.login,
|
||||
card: import_cipher.card,
|
||||
identity: import_cipher.identity,
|
||||
secure_note: import_cipher.secure_note,
|
||||
fields: import_cipher.fields,
|
||||
password_history: import_cipher.password_history,
|
||||
reprompt: import_cipher.reprompt,
|
||||
};
|
||||
|
||||
let data_value = serde_json::to_value(&cipher_data).map_err(|_| AppError::Internal)?;
|
||||
|
||||
let cipher = Cipher {
|
||||
id: Uuid::new_v4().to_string(),
|
||||
user_id: Some(claims.sub.clone()),
|
||||
organization_id: import_cipher.organization_id.clone(),
|
||||
r#type: import_cipher.r#type,
|
||||
data: data_value,
|
||||
favorite: import_cipher.favorite,
|
||||
folder_id: import_cipher.folder_id.clone(),
|
||||
deleted_at: None,
|
||||
created_at: now.clone(),
|
||||
updated_at: now.clone(),
|
||||
object: "cipher".to_string(),
|
||||
organization_use_totp: false,
|
||||
edit: true,
|
||||
view_password: true,
|
||||
collection_ids: None,
|
||||
};
|
||||
|
||||
let data = serde_json::to_string(&cipher.data).map_err(|_| AppError::Internal)?;
|
||||
|
||||
query!(
|
||||
&db,
|
||||
"INSERT OR IGNORE INTO ciphers (id, user_id, organization_id, type, data, favorite, folder_id, created_at, updated_at)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)",
|
||||
cipher.id,
|
||||
cipher.user_id,
|
||||
cipher.organization_id,
|
||||
cipher.r#type,
|
||||
data,
|
||||
cipher.favorite,
|
||||
cipher.folder_id,
|
||||
cipher.created_at,
|
||||
cipher.updated_at,
|
||||
).map_err(|_|AppError::Database)?
|
||||
.run()
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(Json(()))
|
||||
}
|
||||
|
|
@ -4,3 +4,4 @@ pub mod config;
|
|||
pub mod identity;
|
||||
pub mod sync;
|
||||
pub mod folders;
|
||||
pub mod import;
|
||||
|
|
|
|||
47
src/models/import.rs
Normal file
47
src/models/import.rs
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
use serde::Deserialize;
|
||||
use serde_json::Value;
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ImportCipher {
|
||||
#[serde(rename = "type")]
|
||||
pub r#type: i32,
|
||||
pub folder_id: Option<String>,
|
||||
pub organization_id: Option<String>,
|
||||
pub name: String,
|
||||
pub notes: Option<String>,
|
||||
pub favorite: bool,
|
||||
pub login: Option<Value>,
|
||||
pub card: Option<Value>,
|
||||
pub identity: Option<Value>,
|
||||
pub secure_note: Option<Value>,
|
||||
pub fields: Option<Value>,
|
||||
pub password_history: Option<Value>,
|
||||
pub reprompt: Option<i32>,
|
||||
pub last_known_revision_date: Option<String>,
|
||||
pub encrypted_for: String,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ImportFolder {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct FolderRelationship {
|
||||
pub key: usize,
|
||||
pub value: usize,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ImportRequest {
|
||||
pub ciphers: Vec<ImportCipher>,
|
||||
pub folders: Vec<ImportFolder>,
|
||||
#[serde(default)]
|
||||
pub folder_relationships: Vec<FolderRelationship>,
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
pub mod user;
|
||||
pub mod sync;
|
||||
pub mod cipher;
|
||||
pub mod folder;
|
||||
pub mod sync;
|
||||
pub mod user;
|
||||
pub mod import;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use axum::{
|
|||
use std::sync::Arc;
|
||||
use worker::Env;
|
||||
|
||||
use crate::handlers::{accounts, ciphers, config, identity, sync, folders};
|
||||
use crate::handlers::{accounts, ciphers, config, identity, sync, folders, import};
|
||||
|
||||
pub fn api_router(env: Env) -> Router {
|
||||
let app_state = Arc::new(env);
|
||||
|
|
@ -26,6 +26,7 @@ pub fn api_router(env: Env) -> Router {
|
|||
.route("/api/sync", get(sync::get_sync_data))
|
||||
// Ciphers CRUD
|
||||
.route("/api/ciphers/create", post(ciphers::create_cipher))
|
||||
.route("/api/ciphers/import", post(import::import_data))
|
||||
.route("/api/ciphers/{id}", put(ciphers::update_cipher))
|
||||
.route("/api/ciphers/{id}/delete", put(ciphers::delete_cipher))
|
||||
// Folders CRUD
|
||||
|
|
|
|||
Loading…
Reference in a new issue