opml parsing works??

This commit is contained in:
Greg Shuflin 2025-02-15 18:53:17 -08:00
parent 16ae4fc201
commit f2a2994fe3
2 changed files with 28 additions and 12 deletions

View File

@ -18,6 +18,12 @@ Current session management is basic and needs improvement:
- Consider using Font Awesome's SVG+JS version for better performance - Consider using Font Awesome's SVG+JS version for better performance
- Update CSS and HTML references to use local assets - Update CSS and HTML references to use local assets
## UI Improvements
- Fix sidebar scrollbar styling to match the main content area scrollbar
- Apply consistent scrollbar styling across the application
- Ensure scrollbar is visible but unobtrusive
- Match the color scheme and dimensions of the main content area scrollbar
- [ ] Add a timeout to external RSS feed fetching to prevent hanging on slow feeds - [ ] Add a timeout to external RSS feed fetching to prevent hanging on slow feeds
- Use reqwest's timeout feature - Use reqwest's timeout feature
- Consider making the timeout configurable - Consider making the timeout configurable

View File

@ -1,12 +1,16 @@
// Module for handling OPML feed list imports // Module for handling OPML feed list imports
use std::io::Cursor; use std::io::Cursor;
use std::io::Read;
use quick_xml::de::from_reader; use quick_xml::de::from_reader;
use rocket::data::ToByteUnit; use rocket::data::ToByteUnit;
use rocket::form::Form;
use rocket::fs::TempFile;
use rocket::http::Status; use rocket::http::Status;
use rocket::serde::json::Json; use rocket::serde::json::Json;
use rocket::serde::{Deserialize, Serialize}; use rocket::serde::{Deserialize, Serialize};
use rocket::tokio::io::AsyncReadExt;
use rocket::{post, Data}; use rocket::{post, Data};
use rocket_db_pools::Connection; use rocket_db_pools::Connection;
use tracing::{error, info}; use tracing::{error, info};
@ -67,30 +71,36 @@ impl OpmlOutline {
} }
} }
#[derive(FromForm)]
pub struct UploadForm<'f> {
file: TempFile<'f>,
}
/// Import feeds from an OPML file /// Import feeds from an OPML file
#[post("/import/opml", data = "<file>")] #[post("/import/opml", data = "<form>")]
pub async fn import_opml( pub async fn import_opml(
mut db: Connection<Db>, mut db: Connection<Db>,
user: AuthenticatedUser, user: AuthenticatedUser,
file: Data<'_>, mut form: Form<UploadForm<'_>>,
) -> Result<Json<ImportResponse>, Status> { ) -> Result<Json<ImportResponse>, Status> {
// Limit file size to 1MB // Read the file contents
let file_data = file.open(1.mebibytes()).into_bytes().await.map_err(|e| { let mut file = form.file.open().await.map_err(|e| {
error!("Failed to open OPML file: {}", e);
Status::BadRequest
})?;
let mut bytes = Vec::new();
file.read_to_end(&mut bytes).await.map_err(|e| {
error!("Failed to read OPML file: {}", e); error!("Failed to read OPML file: {}", e);
Status::BadRequest Status::BadRequest
})?; })?;
if !file_data.is_complete() { let cursor = Cursor::new(bytes.clone());
error!("OPML file too large");
return Err(Status::PayloadTooLarge);
}
let bytes = file_data.value;
let cursor = Cursor::new(bytes);
// Parse OPML // Parse OPML
let opml: Opml = from_reader(cursor).map_err(|e| { let opml: Opml = from_reader(cursor).map_err(|e| {
error!("Failed to parse OPML: {}", e); let preview = String::from_utf8_lossy(&bytes[..bytes.len().min(100)]);
error!("Failed to parse OPML: {}. File starts with: {}", e, preview);
Status::UnprocessableEntity Status::UnprocessableEntity
})?; })?;