modernize: migrate to Leptos 0.8 and Server Functions architecture, break backend->shared loop
Some checks failed
Build MIPS Binary / build (push) Failing after 1m27s
Some checks failed
Build MIPS Binary / build (push) Failing after 1m27s
This commit is contained in:
1261
Cargo.lock
generated
1261
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -4,15 +4,15 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[features]
|
||||
default = ["push-notifications", "swagger"]
|
||||
default = ["swagger"] # push-notifications kaldırıldı
|
||||
push-notifications = ["web-push", "openssl"]
|
||||
swagger = ["utoipa-swagger-ui"]
|
||||
|
||||
[dependencies]
|
||||
axum = { version = "0.8", features = ["macros", "ws"] }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tower = { version = "0.4", features = ["util", "timeout"] }
|
||||
tower-http = { version = "0.5", features = ["fs", "trace", "cors", "compression-full"] }
|
||||
tower = { version = "0.5", features = ["util", "timeout"] }
|
||||
tower-http = { version = "0.6", features = ["fs", "trace", "cors", "compression-full"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
tracing = "0.1"
|
||||
@@ -21,16 +21,15 @@ tokio-stream = "0.1"
|
||||
bytes = "1"
|
||||
futures = "0.3"
|
||||
quick-xml = { version = "0.31", features = ["serde", "serialize"] }
|
||||
# We might need `tokio-util` for codecs if we implement SCGI manually
|
||||
tokio-util = { version = "0.7", features = ["codec", "io"] }
|
||||
clap = { version = "4.4", features = ["derive", "env"] }
|
||||
rust-embed = "8.2"
|
||||
mime_guess = "2.0"
|
||||
shared = { path = "../shared" }
|
||||
shared = { path = "../shared", features = ["ssr"] }
|
||||
thiserror = "2.0.18"
|
||||
dotenvy = "0.15.7"
|
||||
utoipa = { version = "5.4.0", features = ["axum_extras"] }
|
||||
utoipa-swagger-ui = { version = "9.0.2", features = ["axum"], optional = true }
|
||||
utoipa-swagger-ui = { version = "9.0", features = ["axum"], optional = true }
|
||||
web-push = { version = "0.10", default-features = false, features = ["hyper-client"], optional = true }
|
||||
base64 = "0.22"
|
||||
openssl = { version = "0.10", features = ["vendored"], optional = true }
|
||||
@@ -42,3 +41,7 @@ anyhow = "1.0.101"
|
||||
time = { version = "0.3.47", features = ["serde", "formatting", "parsing"] }
|
||||
tower_governor = "0.8.0"
|
||||
governor = "0.10.4"
|
||||
|
||||
# Leptos
|
||||
leptos = { version = "0.8.15", features = ["nightly"] }
|
||||
leptos_axum = { version = "0.8.7" }
|
||||
@@ -1,7 +1,9 @@
|
||||
use crate::{
|
||||
use shared::{
|
||||
xmlrpc::{self, RpcParam},
|
||||
AppState,
|
||||
AddTorrentRequest, GlobalLimitRequest, SetFilePriorityRequest, SetLabelRequest, TorrentActionRequest,
|
||||
TorrentFile, TorrentPeer, TorrentTracker,
|
||||
};
|
||||
use crate::AppState;
|
||||
#[cfg(feature = "push-notifications")]
|
||||
use crate::push;
|
||||
use axum::{
|
||||
@@ -11,10 +13,6 @@ use axum::{
|
||||
BoxError,
|
||||
};
|
||||
use rust_embed::RustEmbed;
|
||||
use shared::{
|
||||
AddTorrentRequest, GlobalLimitRequest, SetFilePriorityRequest, SetLabelRequest, TorrentActionRequest,
|
||||
TorrentFile, TorrentPeer, TorrentTracker,
|
||||
};
|
||||
|
||||
pub mod auth;
|
||||
pub mod setup;
|
||||
|
||||
@@ -4,9 +4,9 @@ mod handlers;
|
||||
#[cfg(feature = "push-notifications")]
|
||||
mod push;
|
||||
mod rate_limit;
|
||||
mod scgi;
|
||||
mod sse;
|
||||
mod xmlrpc;
|
||||
|
||||
use shared::{scgi, xmlrpc};
|
||||
|
||||
use axum::error_handling::HandleErrorLayer;
|
||||
use axum::{
|
||||
@@ -59,6 +59,7 @@ async fn auth_middleware(
|
||||
if path.starts_with("/api/auth/login")
|
||||
|| path.starts_with("/api/auth/check") // Used by frontend to decide where to go
|
||||
|| path.starts_with("/api/setup")
|
||||
|| path.starts_with("/api/server_fns")
|
||||
|| path.starts_with("/swagger-ui")
|
||||
|| path.starts_with("/api-docs")
|
||||
|| !path.starts_with("/api/") // Allow static files (frontend)
|
||||
@@ -528,6 +529,7 @@ async fn main() {
|
||||
"/api/settings/global-limits",
|
||||
get(handlers::get_global_limit_handler).post(handlers::set_global_limit_handler),
|
||||
)
|
||||
.route("/api/server_fns/{*fn_name}", post(leptos_axum::handle_server_fns))
|
||||
.fallback(handlers::static_handler); // Serve static files for everything else
|
||||
|
||||
#[cfg(feature = "push-notifications")]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::xmlrpc::{
|
||||
use shared::xmlrpc::{
|
||||
parse_i64_response, parse_multicall_response, RpcParam, RtorrentClient, XmlRpcError,
|
||||
};
|
||||
use crate::AppState;
|
||||
|
||||
@@ -7,15 +7,15 @@ edition = "2021"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
leptos = { version = "0.6", features = ["csr"] }
|
||||
leptos_router = { version = "0.6", features = ["csr"] }
|
||||
leptos = { version = "0.8.7", features = ["csr", "nightly"] }
|
||||
leptos_router = { version = "0.8.7", features = ["nightly"] }
|
||||
|
||||
console_error_panic_hook = "0.1"
|
||||
console_log = "1"
|
||||
log = "0.4"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
gloo-net = "0.5"
|
||||
gloo-net = "0.6"
|
||||
gloo-timers = { version = "0.3", features = ["futures"] }
|
||||
wasm-bindgen = "0.2"
|
||||
wasm-bindgen-futures = "0.4"
|
||||
@@ -23,11 +23,11 @@ uuid = { version = "1", features = ["v4", "js"] }
|
||||
futures = "0.3"
|
||||
chrono = { version = "0.4", features = ["serde", "wasm-bindgen"] }
|
||||
web-sys = { version = "0.3", features = ["HtmlDivElement", "HtmlUListElement", "HtmlLiElement", "HtmlAnchorElement", "MouseEvent", "Event", "Window", "Document", "Element", "DomTokenList", "CssStyleDeclaration", "Storage", "TouchEvent", "TouchList", "Touch", "Navigator", "Notification", "NotificationOptions", "NotificationPermission", "ServiceWorkerContainer", "ServiceWorkerRegistration", "PushManager", "PushSubscription", "PushSubscriptionOptions", "PushSubscriptionOptionsInit", "HtmlDetailsElement"] }
|
||||
shared = { path = "../shared" }
|
||||
shared = { path = "../shared", features = ["hydrate"] }
|
||||
tailwind_fuse = "0.3.2"
|
||||
js-sys = "0.3.85"
|
||||
base64 = "0.22.1"
|
||||
serde-wasm-bindgen = "0.6.5"
|
||||
leptos-use = "0.13"
|
||||
leptos-use = "0.15"
|
||||
codee = "0.2"
|
||||
thiserror = "2.0"
|
||||
@@ -1,8 +1,32 @@
|
||||
[package]
|
||||
name = "shared"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
edition = "2021"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
ssr = [
|
||||
"dep:tokio",
|
||||
"dep:bytes",
|
||||
"dep:thiserror",
|
||||
"dep:quick-xml",
|
||||
"dep:leptos_axum",
|
||||
"leptos/ssr",
|
||||
"leptos_router/ssr",
|
||||
]
|
||||
hydrate = ["leptos/hydrate"]
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
utoipa = { version = "5.4.0", features = ["axum_extras"] }
|
||||
|
||||
# Leptos 0.8.7
|
||||
leptos = { version = "0.8.7", features = ["nightly"] }
|
||||
leptos_router = { version = "0.8.7", features = ["nightly"] }
|
||||
leptos_axum = { version = "0.8.7", optional = true }
|
||||
|
||||
# SSR Dependencies (XML-RPC & SCGI)
|
||||
tokio = { version = "1", features = ["full"], optional = true }
|
||||
bytes = { version = "1", optional = true }
|
||||
thiserror = { version = "2", optional = true }
|
||||
quick-xml = { version = "0.31", features = ["serde", "serialize"], optional = true }
|
||||
@@ -1,6 +1,14 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
#[cfg(feature = "ssr")]
|
||||
pub mod scgi;
|
||||
|
||||
#[cfg(feature = "ssr")]
|
||||
pub mod xmlrpc;
|
||||
|
||||
pub mod server_fns;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, ToSchema)]
|
||||
pub struct Torrent {
|
||||
pub hash: String,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![cfg(feature = "ssr")]
|
||||
|
||||
use bytes::Bytes;
|
||||
use std::collections::HashMap;
|
||||
use thiserror::Error;
|
||||
@@ -94,15 +96,33 @@ pub async fn send_request(socket_path: &str, request: ScgiRequest) -> Result<Byt
|
||||
.await
|
||||
.map_err(|_| ScgiError::Timeout)??;
|
||||
|
||||
let double_newline = b"\r\n\r\n";
|
||||
let mut response_vec = response;
|
||||
|
||||
// Improved header stripping: find the first occurrence of "<?xml" OR double newline
|
||||
let patterns = [
|
||||
&b"\r\n\r\n"[..],
|
||||
&b"\n\n"[..],
|
||||
&b"<?xml"[..] // If headers are missing or weird, find start of XML
|
||||
];
|
||||
|
||||
let mut found_pos = None;
|
||||
for (i, pattern) in patterns.iter().enumerate() {
|
||||
if let Some(pos) = response_vec
|
||||
.windows(double_newline.len())
|
||||
.position(|window| window == double_newline)
|
||||
.windows(pattern.len())
|
||||
.position(|window| window == *pattern)
|
||||
{
|
||||
Ok(Bytes::from(
|
||||
response_vec.split_off(pos + double_newline.len()),
|
||||
))
|
||||
// For XML pattern, we keep it. For newlines, we skip them.
|
||||
if i == 2 {
|
||||
found_pos = Some(pos);
|
||||
} else {
|
||||
found_pos = Some(pos + pattern.len());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(pos) = found_pos {
|
||||
Ok(Bytes::from(response_vec.split_off(pos)))
|
||||
} else {
|
||||
Ok(Bytes::from(response_vec))
|
||||
}
|
||||
26
shared/src/server_fns/mod.rs
Normal file
26
shared/src/server_fns/mod.rs
Normal file
@@ -0,0 +1,26 @@
|
||||
use leptos::*;
|
||||
use leptos::prelude::*;
|
||||
|
||||
#[cfg(feature = "ssr")]
|
||||
use crate::xmlrpc::{self, RtorrentClient};
|
||||
|
||||
#[server(GetVersion, "/api/server_fns")]
|
||||
pub async fn get_version() -> Result<String, ServerFnError> {
|
||||
let socket_path = std::env::var("RTORRENT_SOCKET").unwrap_or_else(|_| "/tmp/rtorrent.sock".to_string());
|
||||
|
||||
#[cfg(feature = "ssr")]
|
||||
{
|
||||
let client = RtorrentClient::new(&socket_path);
|
||||
match client.call("system.client_version", &[]).await {
|
||||
Ok(xml) => {
|
||||
let version = xmlrpc::parse_string_response(&xml).unwrap_or(xml);
|
||||
Ok(version)
|
||||
},
|
||||
Err(e) => Err(ServerFnError::ServerError(e.to_string())),
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "ssr"))]
|
||||
{
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
#![cfg(feature = "ssr")]
|
||||
|
||||
use crate::scgi::{send_request, ScgiError, ScgiRequest};
|
||||
use quick_xml::de::from_str;
|
||||
use quick_xml::se::to_string;
|
||||
Reference in New Issue
Block a user