mirror of
https://github.com/LCE-Hub/LCE-Emerald-Launcher.git
synced 2026-07-25 18:47:10 +00:00
135 lines
4.6 KiB
Rust
135 lines
4.6 KiB
Rust
use once_cell::sync::Lazy;
|
|
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
|
|
use tokio::net::TcpListener;
|
|
use tokio_util::sync::CancellationToken;
|
|
use tauri::{AppHandle, Emitter};
|
|
const REGISTRY_URL: &str = "https://raw.githubusercontent.com/LCE-Hub/LCE-Workshop/refs/heads/main";
|
|
static CLIENT: Lazy<reqwest::Client> = Lazy::new(|| reqwest::Client::new());
|
|
pub struct Guard {
|
|
cancel: Option<CancellationToken>,
|
|
}
|
|
|
|
impl Guard {
|
|
pub fn new(cancel: CancellationToken) -> Self {
|
|
Self { cancel: Some(cancel) }
|
|
}
|
|
}
|
|
|
|
impl Drop for Guard {
|
|
fn drop(&mut self) {
|
|
if let Some(cancel) = self.cancel.take() {
|
|
cancel.cancel();
|
|
}
|
|
}
|
|
}
|
|
|
|
pub async fn start(app: AppHandle) -> CancellationToken {
|
|
let cancel = CancellationToken::new();
|
|
let server_cancel = cancel.clone();
|
|
tokio::spawn(async move {
|
|
serve(app, server_cancel).await;
|
|
});
|
|
|
|
cancel
|
|
}
|
|
|
|
async fn serve(app: AppHandle, cancel: CancellationToken) {
|
|
let listener = match TcpListener::bind("127.0.0.1:5582").await {
|
|
Ok(l) => l,
|
|
Err(e) => {
|
|
let _ = app.emit("backend-error", format!("Workshop server failed to bind: {e}"));
|
|
eprintln!("[WorkshopServer] Failed to bind: {e}");
|
|
return;
|
|
}
|
|
};
|
|
|
|
loop {
|
|
tokio::select! {
|
|
result = listener.accept() => {
|
|
match result {
|
|
Ok((stream, _)) => {
|
|
tokio::spawn(handle(stream));
|
|
}
|
|
Err(e) => {
|
|
let _ = app.emit("backend-error", format!("Workshop server accept error: {e}"));
|
|
eprintln!("[WorkshopServer] Accept error: {e}");
|
|
}
|
|
}
|
|
}
|
|
_ = cancel.cancelled() => break,
|
|
}
|
|
}
|
|
}
|
|
|
|
async fn handle(stream: tokio::net::TcpStream) {
|
|
let (reader, mut writer) = stream.into_split();
|
|
let mut buf_reader = BufReader::new(reader);
|
|
let mut request_line = String::new();
|
|
if buf_reader.read_line(&mut request_line).await.is_err() {
|
|
return;
|
|
}
|
|
let request_line = request_line.trim();
|
|
loop {
|
|
let mut line = String::new();
|
|
if buf_reader.read_line(&mut line).await.is_err() {
|
|
return;
|
|
}
|
|
if line == "\r\n" || line == "\n" {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if request_line.starts_with("GET /workshop/") && request_line.ends_with(" HTTP/1.1") {
|
|
let path = &request_line["GET /workshop/".len()..request_line.len() - " HTTP/1.1".len()];
|
|
match fetch_workshop_file(path).await {
|
|
Ok(body) => {
|
|
let body_bytes = &body;
|
|
let content_type = if path.ends_with(".json") {
|
|
"application/json"
|
|
} else if path.ends_with(".png") {
|
|
"image/png"
|
|
} else {
|
|
"application/octet-stream"
|
|
};
|
|
let response = format!(
|
|
"HTTP/1.1 200 OK\r\nContent-Type: {}\r\nContent-Length: {}\r\nAccess-Control-Allow-Origin: *\r\nConnection: close\r\n\r\n",
|
|
content_type,
|
|
body.len(),
|
|
);
|
|
let _ = writer.write_all(response.as_bytes()).await;
|
|
let _ = writer.write_all(body_bytes).await;
|
|
}
|
|
Err(e) => {
|
|
let status = if e.contains("404") { 404 } else { 502 };
|
|
let body = format!("{{\"error\": \"{e}\"}}");
|
|
let response = format!(
|
|
"HTTP/1.1 {} {}\r\nContent-Type: application/json\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}",
|
|
status,
|
|
if status == 404 { "Not Found" } else { "Bad Gateway" },
|
|
body.len(),
|
|
body
|
|
);
|
|
let _ = writer.write_all(response.as_bytes()).await;
|
|
}
|
|
}
|
|
} else {
|
|
let body = "{\"error\": \"not found\"}";
|
|
let response = format!(
|
|
"HTTP/1.1 404 Not Found\r\nContent-Type: application/json\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}",
|
|
body.len(),
|
|
body
|
|
);
|
|
let _ = writer.write_all(response.as_bytes()).await;
|
|
}
|
|
}
|
|
|
|
async fn fetch_workshop_file(path: &str) -> Result<Vec<u8>, String> {
|
|
let url = format!("{}/{}", REGISTRY_URL, path);
|
|
let resp = CLIENT.get(&url).send().await.map_err(|e| e.to_string())?;
|
|
if resp.status().is_success() {
|
|
resp.bytes().await.map(|b| b.to_vec()).map_err(|e| e.to_string())
|
|
} else {
|
|
Err(format!("GitHub returned {}", resp.status()))
|
|
}
|
|
}
|