Skip to main content

fast_down_gui/
server.rs

1use crate::{
2    core::{App, start_new_entry},
3    os::wakeup_window,
4    ui::{EntryData, MainWindow},
5    utils::LogErr,
6};
7use axum::{Json, Router, extract::State, http::StatusCode, routing::post};
8use crossfire::{MTx, mpsc};
9use serde::{Deserialize, Serialize};
10use slint::{VecModel, Weak};
11use std::{rc::Rc, time::Duration};
12use tracing::{error, info};
13use url::Url;
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
16#[serde(rename_all = "camelCase")]
17pub struct DownloadOptions {
18    url: Url,
19    headers: Option<String>,
20}
21
22async fn download(
23    State(tx): State<MTx<mpsc::List<DownloadOptions>>>,
24    Json(payload): Json<DownloadOptions>,
25) -> StatusCode {
26    info!(payload = ?payload, "收到下载 HTTP 请求");
27    match tx.send(payload) {
28        Ok(()) => StatusCode::CREATED,
29        Err(e) => {
30            error!(err = ?e, "无法发送下载请求到主线程");
31            StatusCode::INTERNAL_SERVER_ERROR
32        }
33    }
34}
35
36pub async fn start_server(
37    app_core: App,
38    list_model: Rc<VecModel<EntryData>>,
39    ui: Weak<MainWindow>,
40) -> color_eyre::Result<()> {
41    let (tx, rx) = crossfire::mpsc::unbounded_async::<DownloadOptions>();
42    slint::spawn_local(async move {
43        while let Ok(e) = rx.recv().await {
44            let mut config = app_core.db.get_ui_download_config();
45            if let Some(s) = e.headers {
46                config.headers = s.into();
47            }
48            start_new_entry(&app_core, e.url, &config, &list_model);
49            let _ = ui.upgrade_in_event_loop(|ui| {
50                wakeup_window(&ui);
51            });
52        }
53    })
54    .log_err("启动后台 HTTP 请求处理服务失败")?;
55    tokio::spawn(async move {
56        let app = Router::new()
57            .route("/download", post(download))
58            .with_state(tx);
59        let listener = loop {
60            let res = tokio::net::TcpListener::bind("0.0.0.0:6121").await;
61            match res {
62                Ok(listener) => break listener,
63                Err(e) => error!(err = ?e, "Failed to bind to port 6121"),
64            }
65            tokio::time::sleep(Duration::from_secs(1)).await;
66        };
67        axum::serve(listener, app)
68            .await
69            .log_err("无法启动 server")
70            .unwrap();
71        info!("成功启动 server");
72    });
73    Ok(())
74}