#[cfg(server)]
use super::admin_auth::AdminAuthenticatedUser;
use crate::adapters::{AdminDatabase, AdminRecord, AdminSite, ImportFormat, ImportResponse};
#[cfg(server)]
use reinhardt_di::Depends;
#[cfg(server)]
use reinhardt_pages::server_fn::ServerFnRequest;
use reinhardt_pages::server_fn::{ServerFnError, server_fn};
#[cfg(server)]
use std::collections::HashMap;
#[cfg(server)]
use super::error::{AdminAuth, MapServerFnError, ModelPermission};
#[cfg(server)]
use super::limits::{MAX_IMPORT_FILE_SIZE, MAX_IMPORT_RECORDS};
#[server_fn]
pub async fn import_data(
model_name: String,
format: crate::adapters::ImportFormat,
data: Vec<u8>,
#[inject] site: Depends<AdminSite>,
#[inject] db: Depends<AdminDatabase>,
#[inject] http_request: ServerFnRequest,
#[inject] AdminAuthenticatedUser(user): AdminAuthenticatedUser,
) -> Result<crate::adapters::ImportResponse, ServerFnError> {
let auth = AdminAuth::from_request(&http_request);
let model_admin = site.get_model_admin(&model_name).map_server_fn_error()?;
auth.require_model_permission(model_admin.as_ref(), user.as_ref(), ModelPermission::Add)
.await?;
if data.len() > MAX_IMPORT_FILE_SIZE {
return Err(ServerFnError::application(format!(
"Import file size ({} bytes) exceeds maximum allowed size ({} bytes)",
data.len(),
MAX_IMPORT_FILE_SIZE
)));
}
let table_name = model_admin.table_name();
let pk_field = model_admin.pk_field();
let records: Vec<HashMap<String, serde_json::Value>> = match format {
ImportFormat::JSON => serde_json::from_slice(&data)
.map_err(|_| ServerFnError::deserialization("Invalid JSON format in import data"))?,
ImportFormat::CSV => {
let mut rdr = csv::Reader::from_reader(&data[..]);
rdr.deserialize()
.collect::<Result<Vec<_>, _>>()
.map_err(|_| ServerFnError::deserialization("Invalid CSV format in import data"))?
}
ImportFormat::TSV => {
let mut rdr = csv::ReaderBuilder::new()
.delimiter(b'\t')
.from_reader(&data[..]);
rdr.deserialize()
.collect::<Result<Vec<_>, _>>()
.map_err(|_| ServerFnError::deserialization("Invalid TSV format in import data"))?
}
};
if records.len() > MAX_IMPORT_RECORDS {
return Err(ServerFnError::application(format!(
"Import record count ({}) exceeds maximum allowed count ({})",
records.len(),
MAX_IMPORT_RECORDS
)));
}
let mut imported = 0;
let mut failed = 0;
let mut errors = Vec::new();
for (index, record) in records.into_iter().enumerate() {
match db
.create::<AdminRecord>(table_name, Some(pk_field), record)
.await
{
Ok(_) => imported += 1,
Err(_) => {
failed += 1;
errors.push(format!("Record {}: import failed", index + 1));
}
}
}
Ok(ImportResponse {
success: failed == 0,
imported,
updated: 0, skipped: 0,
failed,
message: if failed == 0 {
format!("Successfully imported {} {} records", imported, model_name)
} else {
format!(
"Imported {} {} records, {} failed",
imported, model_name, failed
)
},
errors: if errors.is_empty() {
None
} else {
Some(errors)
},
})
}