Compare commits

...

3 Commits

Author SHA1 Message Date
spinline
9d0eb11f16 Refactor App routing to fix infinite recursion and stack overflow errors
All checks were successful
Build MIPS Binary / build (push) Successful in 4m8s
2026-02-07 16:10:06 +03:00
spinline
3c13f99652 Improve App initialization logic with better error handling and logging to prevent infinite loading state
All checks were successful
Build MIPS Binary / build (push) Successful in 4m6s
2026-02-07 15:45:51 +03:00
spinline
a948215538 Change cookie SameSite policy to Lax to fix login redirection issue
All checks were successful
Build MIPS Binary / build (push) Successful in 4m6s
2026-02-07 15:38:56 +03:00
4 changed files with 87 additions and 78 deletions

View File

@@ -74,7 +74,7 @@ pub async fn login_handler(
let cookie = Cookie::build(("auth_token", token)) let cookie = Cookie::build(("auth_token", token))
.path("/") .path("/")
.http_only(true) .http_only(true)
.same_site(SameSite::Strict) .same_site(SameSite::Lax)
.max_age(Duration::seconds(expires_in)) .max_age(Duration::seconds(expires_in))
.build(); .build();

View File

@@ -1,6 +1,4 @@
use crate::components::layout::sidebar::Sidebar; use crate::components::layout::protected::Protected;
use crate::components::layout::statusbar::StatusBar;
use crate::components::layout::toolbar::Toolbar;
use crate::components::toast::ToastContainer; use crate::components::toast::ToastContainer;
use crate::components::torrent::table::TorrentTable; use crate::components::torrent::table::TorrentTable;
use crate::components::auth::login::Login; use crate::components::auth::login::Login;
@@ -25,77 +23,66 @@ pub fn App() -> impl IntoView {
// Check Auth & Setup Status on load // Check Auth & Setup Status on load
create_effect(move |_| { create_effect(move |_| {
spawn_local(async move { spawn_local(async move {
logging::log!("App initialization started...");
// 1. Check Setup Status // 1. Check Setup Status
let setup_res = gloo_net::http::Request::get("/api/setup/status").send().await; let setup_res = gloo_net::http::Request::get("/api/setup/status").send().await;
if let Ok(resp) = setup_res {
if let Ok(status) = resp.json::<SetupStatus>().await { match setup_res {
if !status.completed { Ok(resp) => {
// Redirect to setup if not completed if resp.ok() {
let navigate = use_navigate(); match resp.json::<SetupStatus>().await {
navigate("/setup", Default::default()); Ok(status) => {
set_is_loading.set(false); if !status.completed {
return; logging::log!("Setup not completed, redirecting to /setup");
let navigate = use_navigate();
navigate("/setup", Default::default());
set_is_loading.set(false);
return;
}
}
Err(e) => logging::error!("Failed to parse setup status: {}", e),
}
} }
} }
Err(e) => logging::error!("Network error checking setup status: {}", e),
} }
// 2. Check Auth Status // 2. Check Auth Status
let auth_res = gloo_net::http::Request::get("/api/auth/check").send().await; let auth_res = gloo_net::http::Request::get("/api/auth/check").send().await;
if let Ok(resp) = auth_res {
if resp.status() == 200 {
set_is_authenticated.set(true);
// Initialize push notifications logic only if authenticated match auth_res {
// ... (Push notification logic moved here or kept global but guarded) Ok(resp) => {
} else { if resp.status() == 200 {
let navigate = use_navigate(); logging::log!("Authenticated!");
// If we are already on login or setup, don't redirect loop set_is_authenticated.set(true);
let pathname = window().location().pathname().unwrap_or_default(); } else {
if pathname != "/login" && pathname != "/setup" { logging::log!("Not authenticated, redirecting to /login");
navigate("/login", Default::default()); let navigate = use_navigate();
let pathname = window().location().pathname().unwrap_or_default();
if pathname != "/login" && pathname != "/setup" {
navigate("/login", Default::default());
}
} }
} }
Err(e) => logging::error!("Network error checking auth status: {}", e),
} }
set_is_loading.set(false); set_is_loading.set(false);
}); });
}); });
// Initialize push notifications after user grants permission (Only if authenticated) // Initialize push notifications (Only if authenticated)
create_effect(move |_| { create_effect(move |_| {
if is_authenticated.get() { if is_authenticated.get() {
spawn_local(async { spawn_local(async {
// ... (Push notification logic kept same, shortened for brevity in this replace)
// Wait a bit for service worker to be ready // Wait a bit for service worker to be ready
gloo_timers::future::TimeoutFuture::new(2000).await; gloo_timers::future::TimeoutFuture::new(2000).await;
// Check if running on iOS and not standalone if crate::utils::platform::supports_push_notifications() && !crate::utils::platform::is_safari() {
if let Some(ios_message) = crate::utils::platform::get_ios_notification_info() { crate::store::subscribe_to_push_notifications().await;
log::warn!("iOS detected: {}", ios_message);
if let Some(store) = use_context::<crate::store::TorrentStore>() {
crate::store::show_toast_with_signal(
store.notifications,
shared::NotificationLevel::Info,
ios_message,
);
}
return;
} }
if !crate::utils::platform::supports_push_notifications() {
return;
}
if crate::utils::platform::is_safari() {
if let Some(store) = use_context::<crate::store::TorrentStore>() {
crate::store::show_toast_with_signal(
store.notifications,
shared::NotificationLevel::Info,
"Bildirim izni için sağ alttaki ayarlar ⚙️ ikonuna basın.".to_string(),
);
}
return;
}
crate::store::subscribe_to_push_notifications().await;
}); });
} }
}); });
@@ -107,38 +94,29 @@ pub fn App() -> impl IntoView {
<Route path="/login" view=move || view! { <Login /> } /> <Route path="/login" view=move || view! { <Login /> } />
<Route path="/setup" view=move || view! { <Setup /> } /> <Route path="/setup" view=move || view! { <Setup /> } />
<Route path="/*" view=move || { <Route path="/" view=move || {
view! { view! {
<Show when=move || !is_loading.get() fallback=|| view! { <Show when=move || !is_loading.get() fallback=|| view! {
<div class="flex items-center justify-center h-screen bg-base-100"> <div class="flex items-center justify-center h-screen bg-base-100">
<span class="loading loading-spinner loading-lg"></span> <span class="loading loading-spinner loading-lg"></span>
</div> </div>
}> }>
<Show when=move || is_authenticated.get() fallback=|| view! { <Login /> }> <Show when=move || is_authenticated.get() fallback=|| ()>
// Protected Layout <Protected>
<div class="drawer lg:drawer-open h-full w-full"> <TorrentTable />
<input id="my-drawer" type="checkbox" class="drawer-toggle" /> </Protected>
</Show>
</Show>
}
}/>
<div class="drawer-content flex flex-col h-full overflow-hidden bg-base-100 text-base-content text-sm select-none"> <Route path="/settings" view=move || {
<Toolbar /> view! {
<Show when=move || !is_loading.get() fallback=|| ()>
<main class="flex-1 flex flex-col min-w-0 bg-base-100 overflow-hidden pb-8"> <Show when=move || is_authenticated.get() fallback=|| ()>
<Routes> <Protected>
<Route path="/" view=move || view! { <TorrentTable /> } /> <div class="p-4">"Settings Page (Coming Soon)"</div>
<Route path="/settings" view=move || view! { <div class="p-4">"Settings Page (Coming Soon)"</div> } /> </Protected>
</Routes>
</main>
<StatusBar />
</div>
<div class="drawer-side z-40 transition-none duration-0">
<label for="my-drawer" aria-label="close sidebar" class="drawer-overlay transition-none duration-0"></label>
<div class="menu p-0 min-h-full bg-base-200 text-base-content border-r border-base-300 transition-none duration-0">
<Sidebar />
</div>
</div>
</div>
</Show> </Show>
</Show> </Show>
} }

View File

@@ -1,3 +1,4 @@
pub mod sidebar; pub mod sidebar;
pub mod toolbar;
pub mod statusbar; pub mod statusbar;
pub mod toolbar;
pub mod protected;

View File

@@ -0,0 +1,30 @@
use leptos::*;
use crate::components::layout::sidebar::Sidebar;
use crate::components::layout::statusbar::StatusBar;
use crate::components::layout::toolbar::Toolbar;
#[component]
pub fn Protected(children: Children) -> impl IntoView {
view! {
<div class="drawer lg:drawer-open h-full w-full">
<input id="my-drawer" type="checkbox" class="drawer-toggle" />
<div class="drawer-content flex flex-col h-full overflow-hidden bg-base-100 text-base-content text-sm select-none">
<Toolbar />
<main class="flex-1 flex flex-col min-w-0 bg-base-100 overflow-hidden pb-8">
{children()}
</main>
<StatusBar />
</div>
<div class="drawer-side z-40 transition-none duration-0">
<label for="my-drawer" aria-label="close sidebar" class="drawer-overlay transition-none duration-0"></label>
<div class="menu p-0 min-h-full bg-base-200 text-base-content border-r border-base-300 transition-none duration-0">
<Sidebar />
</div>
</div>
</div>
}
}