better error handling

This commit is contained in:
Joe Ardent 2025-07-28 13:46:50 -07:00
parent 6558e18dec
commit 9fff83a721
3 changed files with 68 additions and 49 deletions

View file

@ -128,10 +128,9 @@ impl App {
.state
.get()
.unwrap()
.upload_requests
.lock()
.get_upload_request(id)
.await
.get(&id).ok_or(LocalSendError::SessionInactive)?.clone();
.ok_or(LocalSendError::SessionInactive)?;
// TODO: replace this with ratatui widget dialog
let upload_confirmed = MessageDialogBuilder::default()

View file

@ -59,7 +59,7 @@ pub struct JoecalState {
pub running_state: Arc<Mutex<RunningState>>,
pub socket: Arc<UdpSocket>,
pub client: reqwest::Client,
pub upload_requests: Arc<Mutex<HashMap<Julid, UnboundedSender<UploadDialog>>>>,
upload_requests: Arc<Mutex<HashMap<Julid, UnboundedSender<UploadDialog>>>>,
shutdown_sender: OnceLock<ShutdownSender>,
// the receiving end will be held by the application so it can update the UI based on backend
// events
@ -130,6 +130,9 @@ impl JoecalState {
Listeners::Udp
}
});
// TODO: add a task that periodically clears out the upload requests if
// they're too old; the keys are julids so they have the time in them
}
pub async fn stop(&self) {
@ -147,6 +150,24 @@ impl JoecalState {
let mut peers = self.peers.lock().await;
peers.clear();
}
pub async fn get_upload_request(&self, id: Julid) -> Option<UnboundedSender<UploadDialog>> {
self.upload_requests.lock().await.get(&id).cloned()
}
pub async fn clear_upload_request(&self, id: Julid) {
let _ = self.upload_requests.lock().await.remove(&id);
}
/// Add a transmitter for an upload request confirmation dialog that the
/// application frontend can use to tell the Axum handler whether or not to
/// accept the upload.
///
/// IMPORTANT! Be sure to call `clear_upload_request(id)` when you're done
/// getting an answer back/before you exit!
pub async fn add_upload_request(&self, id: Julid, tx: UnboundedSender<UploadDialog>) {
self.upload_requests.lock().await.entry(id).insert_entry(tx);
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]

View file

@ -197,12 +197,8 @@ pub async fn register_prepare_upload(
let id = Julid::new();
let (tx, mut rx) = unbounded_channel();
state
.upload_requests
.lock()
.await
.entry(id)
.insert_entry(tx);
// be sure to clear this request before this function exits!
state.add_upload_request(id, tx).await;
let dialog_send = state.transfer_event_tx.send(TransferEvent::UploadRequest {
alias: req.info.alias.clone(),
@ -211,52 +207,55 @@ pub async fn register_prepare_upload(
match dialog_send {
Ok(_) => {}
Err(_e) => {
let _ = state.upload_requests.lock().await.remove(&id);
state.clear_upload_request(id).await;
return StatusCode::INTERNAL_SERVER_ERROR.into_response();
}
}
// safe to unwrap because it's only `None` when there are no more transmitters,
// and we still have the `tx` we created earlier
let result = rx.recv().await.unwrap();
let _ = state.upload_requests.lock().await.remove(&id);
let confirmation = rx.recv().await;
state.clear_upload_request(id).await;
if result == UploadDialog::UploadConfirm {
let session_id = id.as_string();
let Some(confirmation) = confirmation else {
// the frontend must have dropped the tx before trying to send a reply back
return StatusCode::INTERNAL_SERVER_ERROR.into_response();
};
let file_tokens: HashMap<String, String> = req
.files
.keys()
.map(|id| (id.clone(), Julid::new().to_string())) // Replace with actual token logic
.collect();
let session = Session {
session_id: session_id.clone(),
files: req.files.clone(),
file_tokens: file_tokens.clone(),
receiver: state.device.clone(),
sender: req.info.clone(),
status: SessionStatus::Active,
addr,
};
state
.sessions
.lock()
.await
.insert(session_id.clone(), session);
(
StatusCode::OK,
Json(PrepareUploadResponse {
session_id,
files: file_tokens,
}),
)
.into_response()
} else {
StatusCode::FORBIDDEN.into_response()
if confirmation != UploadDialog::UploadConfirm {
return StatusCode::FORBIDDEN.into_response();
}
let session_id = id.as_string();
let file_tokens: HashMap<String, String> = req
.files
.keys()
.map(|id| (id.clone(), Julid::new().to_string())) // Replace with actual token logic
.collect();
let session = Session {
session_id: session_id.clone(),
files: req.files.clone(),
file_tokens: file_tokens.clone(),
receiver: state.device.clone(),
sender: req.info.clone(),
status: SessionStatus::Active,
addr,
};
state
.sessions
.lock()
.await
.insert(session_id.clone(), session);
(
StatusCode::OK,
Json(PrepareUploadResponse {
session_id,
files: file_tokens,
}),
)
.into_response()
}
pub async fn register_upload(