better error handling
This commit is contained in:
parent
6558e18dec
commit
9fff83a721
3 changed files with 68 additions and 49 deletions
|
@ -128,10 +128,9 @@ impl App {
|
||||||
.state
|
.state
|
||||||
.get()
|
.get()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.upload_requests
|
.get_upload_request(id)
|
||||||
.lock()
|
|
||||||
.await
|
.await
|
||||||
.get(&id).ok_or(LocalSendError::SessionInactive)?.clone();
|
.ok_or(LocalSendError::SessionInactive)?;
|
||||||
|
|
||||||
// TODO: replace this with ratatui widget dialog
|
// TODO: replace this with ratatui widget dialog
|
||||||
let upload_confirmed = MessageDialogBuilder::default()
|
let upload_confirmed = MessageDialogBuilder::default()
|
||||||
|
|
23
src/lib.rs
23
src/lib.rs
|
@ -59,7 +59,7 @@ pub struct JoecalState {
|
||||||
pub running_state: Arc<Mutex<RunningState>>,
|
pub running_state: Arc<Mutex<RunningState>>,
|
||||||
pub socket: Arc<UdpSocket>,
|
pub socket: Arc<UdpSocket>,
|
||||||
pub client: reqwest::Client,
|
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>,
|
shutdown_sender: OnceLock<ShutdownSender>,
|
||||||
// the receiving end will be held by the application so it can update the UI based on backend
|
// the receiving end will be held by the application so it can update the UI based on backend
|
||||||
// events
|
// events
|
||||||
|
@ -130,6 +130,9 @@ impl JoecalState {
|
||||||
Listeners::Udp
|
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) {
|
pub async fn stop(&self) {
|
||||||
|
@ -147,6 +150,24 @@ impl JoecalState {
|
||||||
let mut peers = self.peers.lock().await;
|
let mut peers = self.peers.lock().await;
|
||||||
peers.clear();
|
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)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
|
|
@ -197,12 +197,8 @@ pub async fn register_prepare_upload(
|
||||||
|
|
||||||
let id = Julid::new();
|
let id = Julid::new();
|
||||||
let (tx, mut rx) = unbounded_channel();
|
let (tx, mut rx) = unbounded_channel();
|
||||||
state
|
// be sure to clear this request before this function exits!
|
||||||
.upload_requests
|
state.add_upload_request(id, tx).await;
|
||||||
.lock()
|
|
||||||
.await
|
|
||||||
.entry(id)
|
|
||||||
.insert_entry(tx);
|
|
||||||
|
|
||||||
let dialog_send = state.transfer_event_tx.send(TransferEvent::UploadRequest {
|
let dialog_send = state.transfer_event_tx.send(TransferEvent::UploadRequest {
|
||||||
alias: req.info.alias.clone(),
|
alias: req.info.alias.clone(),
|
||||||
|
@ -211,52 +207,55 @@ pub async fn register_prepare_upload(
|
||||||
match dialog_send {
|
match dialog_send {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(_e) => {
|
Err(_e) => {
|
||||||
let _ = state.upload_requests.lock().await.remove(&id);
|
state.clear_upload_request(id).await;
|
||||||
return StatusCode::INTERNAL_SERVER_ERROR.into_response();
|
return StatusCode::INTERNAL_SERVER_ERROR.into_response();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// safe to unwrap because it's only `None` when there are no more transmitters,
|
let confirmation = rx.recv().await;
|
||||||
// and we still have the `tx` we created earlier
|
state.clear_upload_request(id).await;
|
||||||
let result = rx.recv().await.unwrap();
|
|
||||||
let _ = state.upload_requests.lock().await.remove(&id);
|
|
||||||
|
|
||||||
if result == UploadDialog::UploadConfirm {
|
let Some(confirmation) = confirmation else {
|
||||||
let session_id = id.as_string();
|
// 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
|
if confirmation != UploadDialog::UploadConfirm {
|
||||||
.files
|
return StatusCode::FORBIDDEN.into_response();
|
||||||
.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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(
|
pub async fn register_upload(
|
||||||
|
|
Loading…
Reference in a new issue