add manage page
This commit is contained in:
+4
-15
@@ -5,6 +5,8 @@ use leptos_router::{
|
||||
StaticSegment,
|
||||
};
|
||||
|
||||
use crate::manage::ManagePage;
|
||||
|
||||
pub fn shell(options: LeptosOptions) -> impl IntoView {
|
||||
view! {
|
||||
<!DOCTYPE html>
|
||||
@@ -34,28 +36,15 @@ pub fn App() -> impl IntoView {
|
||||
<Stylesheet id="leptos" href="/pkg/code_submission.css"/>
|
||||
|
||||
// sets the document title
|
||||
<Title text="Welcome to Leptos"/>
|
||||
<Title text="Manage Server"/>
|
||||
|
||||
// content for this welcome page
|
||||
<Router>
|
||||
<main>
|
||||
<Routes fallback=|| "Page not found.".into_view()>
|
||||
<Route path=StaticSegment("") view=HomePage/>
|
||||
<Route path=StaticSegment("manage") view=ManagePage/>
|
||||
</Routes>
|
||||
</main>
|
||||
</Router>
|
||||
}
|
||||
}
|
||||
|
||||
/// Renders the home page of your application.
|
||||
#[component]
|
||||
fn HomePage() -> impl IntoView {
|
||||
// Creates a reactive value to update the button
|
||||
let count = RwSignal::new(0);
|
||||
let on_click = move |_| *count.write() += 1;
|
||||
|
||||
view! {
|
||||
<h1>"Welcome to Leptos!"</h1>
|
||||
<button on:click=on_click>"Click Me: " {count}</button>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod app;
|
||||
pub mod manage;
|
||||
|
||||
#[cfg(feature = "hydrate")]
|
||||
#[wasm_bindgen::prelude::wasm_bindgen]
|
||||
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
use leptos::prelude::*;
|
||||
|
||||
#[cfg(feature = "ssr")]
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
#[cfg(feature = "ssr")]
|
||||
static SERVER_LIVE: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
#[cfg(feature = "ssr")]
|
||||
const STATUS_SECRET_KEY: &str = "password";
|
||||
|
||||
#[server]
|
||||
async fn get_live_state() -> Result<bool, ServerFnError> {
|
||||
Ok(SERVER_LIVE.load(Ordering::SeqCst))
|
||||
}
|
||||
|
||||
#[server]
|
||||
async fn set_live_state(live: bool, secret_key: String) -> Result<(), ServerFnError> {
|
||||
if secret_key != STATUS_SECRET_KEY {
|
||||
return Err(ServerFnError::ServerError("Invalid secret key".to_string()));
|
||||
}
|
||||
|
||||
SERVER_LIVE.store(live, Ordering::SeqCst);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Renders the server management page.
|
||||
#[component]
|
||||
pub fn ManagePage() -> impl IntoView {
|
||||
let live_state = Resource::new(|| (), |_| get_live_state());
|
||||
|
||||
view! {
|
||||
<section class="status-card">
|
||||
<p class="eyebrow">"CONTROL PANEL"</p>
|
||||
<h1>"Server status"</h1>
|
||||
<p class="intro">"Manage whether your server is accepting live traffic."</p>
|
||||
<Suspense fallback=|| view! { <p class="loading">"Loading server status..."</p> }>
|
||||
{move || Suspend::new(async move {
|
||||
match live_state.await {
|
||||
Ok(is_live) => view! { <LiveToggle initial=is_live/> }.into_any(),
|
||||
Err(error) => view! {
|
||||
<p class="error-message" role="alert">
|
||||
"Unable to load server status: " {error.to_string()}
|
||||
</p>
|
||||
}.into_any(),
|
||||
}
|
||||
})}
|
||||
</Suspense>
|
||||
</section>
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn LiveToggle(initial: bool) -> impl IntoView {
|
||||
let (live, set_live) = signal(initial);
|
||||
let (server_live, set_server_live) = signal(initial);
|
||||
let (updating, set_updating) = signal(false);
|
||||
let (secret_key, set_secret_key) = signal(String::new());
|
||||
let (error, set_error) = signal(None::<String>);
|
||||
|
||||
let on_click = move |_| {
|
||||
let previous = live.get_untracked();
|
||||
let checked = !previous;
|
||||
let key = secret_key.get_untracked();
|
||||
set_live.set(checked);
|
||||
set_updating.set(true);
|
||||
set_error.set(None);
|
||||
|
||||
leptos::task::spawn_local(async move {
|
||||
match set_live_state(checked, key).await {
|
||||
Ok(()) => set_server_live.set(checked),
|
||||
Err(error) => {
|
||||
set_live.set(previous);
|
||||
set_error.set(Some(error.to_string()));
|
||||
}
|
||||
}
|
||||
set_updating.set(false);
|
||||
});
|
||||
};
|
||||
|
||||
view! {
|
||||
<div class="status-controls">
|
||||
<label class="key-label" for="status-secret-key">"Secret key"</label>
|
||||
<input
|
||||
id="status-secret-key"
|
||||
class="key-input"
|
||||
type="password"
|
||||
placeholder="Enter your secret key"
|
||||
autocomplete="current-password"
|
||||
prop:disabled=updating
|
||||
on:input=move |event| set_secret_key.set(event_target_value(&event))
|
||||
/>
|
||||
<button
|
||||
class="status-button"
|
||||
class:go-live=move || !live.get()
|
||||
class:take-offline=move || live.get()
|
||||
type="button"
|
||||
prop:disabled=updating
|
||||
on:click=on_click
|
||||
>
|
||||
{move || if updating.get() {
|
||||
"Updating..."
|
||||
} else if live.get() {
|
||||
"Take Offline"
|
||||
} else {
|
||||
"Go Live"
|
||||
}}
|
||||
</button>
|
||||
<p
|
||||
class="status-indicator"
|
||||
class:is-live=move || server_live.get()
|
||||
aria-live="polite"
|
||||
>
|
||||
<span class="status-dot"></span>
|
||||
"Server status: "
|
||||
<strong>{move || if server_live.get() { "Live" } else { "Offline" }}</strong>
|
||||
</p>
|
||||
{move || error.get().map(|message| view! {
|
||||
<p class="error-message" role="alert">{message}</p>
|
||||
})}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user