uploads: centralise via ChatHubApi::save_upload, refactor handlers
Nightly Build / build (push) Successful in 6m41s

Extract shared upload seam in skald-core, move Telegram and web
handlers to use it. Simplify media attachment routing. Clean up
unused deps and dead code.
This commit is contained in:
2026-07-22 19:19:29 +01:00
parent e70c4a90f3
commit 6f0461f7f5
15 changed files with 350 additions and 214 deletions
+37
View File
@@ -4,6 +4,7 @@ use std::sync::{Arc, OnceLock, Weak};
use std::time::Duration;
use async_trait::async_trait;
use core_api::message_meta::Attachment;
use sqlx::SqlitePool;
use tokio::sync::{Mutex, broadcast, mpsc};
use tokio_util::sync::CancellationToken;
@@ -234,6 +235,32 @@ impl ChatHub {
self.session_mgr.get_or_create_handler(session_id).await
}
/// Persist an uploaded file for `source_id` into the owner's home
/// (`~/uploads/{session}/`) and return its [`Attachment`]. The single entry
/// point every surface (web handler, channel plugins) routes through, so
/// uploads can't drift on placement or on the recorded agent path — see
/// [`crate::uploads::save_to_home`]. Resolves the source's active session (so
/// the upload shares the directory the following message references).
pub async fn save_upload(
&self,
source_id: &str,
file_name: &str,
client_mime: Option<String>,
bytes: &[u8],
) -> anyhow::Result<Attachment> {
let handler = self.session_handler(source_id).await?;
let fs = handler.user_fs();
let att = crate::uploads::save_to_home(
&fs,
handler.session_id,
file_name,
client_mime,
bytes,
)
.await?;
Ok(att)
}
/// Returns the handler for a specific `session_id`, creating one lazily if needed.
/// Used to resolve a pending tool against the session that actually owns it,
/// independent of any source's "active" session.
@@ -781,6 +808,16 @@ impl ChatHubApi for ChatHub {
self.send_message(source_id, prompt, opts).await
}
async fn save_upload(
&self,
source_id: &str,
file_name: &str,
client_mime: Option<String>,
bytes: &[u8],
) -> anyhow::Result<Attachment> {
self.save_upload(source_id, file_name, client_mime, bytes).await
}
async fn clear(&self, source_id: &str) -> anyhow::Result<i64> {
self.clear(source_id).await
}