remove cfg-if from all examples

This commit is contained in:
Greg Johnston 2024-01-10 21:43:19 -05:00
parent 31b2b9e94c
commit 1d1de4ac38
55 changed files with 1000 additions and 1160 deletions

View file

@ -17,7 +17,6 @@ broadcaster = "1"
console_log = "1"
console_error_panic_hook = "0.1"
futures = "0.3"
cfg-if = "1"
lazy_static = "1"
leptos = { path = "../../leptos" }
leptos_actix = { path = "../../integrations/actix", optional = true }

View file

@ -1,34 +1,35 @@
use cfg_if::cfg_if;
use leptos::*;
use leptos_meta::*;
use leptos_router::*;
#[cfg(feature = "ssr")]
use tracing::instrument;
cfg_if! {
if #[cfg(feature = "ssr")] {
use std::sync::atomic::{AtomicI32, Ordering};
use broadcaster::BroadcastChannel;
use once_cell::sync::OnceCell;
#[cfg(feature = "ssr")]
mod ssr_imports {
pub use broadcaster::BroadcastChannel;
pub use once_cell::sync::OnceCell;
pub use std::sync::atomic::{AtomicI32, Ordering};
static COUNT: AtomicI32 = AtomicI32::new(0);
pub static COUNT: AtomicI32 = AtomicI32::new(0);
lazy_static::lazy_static! {
pub static ref COUNT_CHANNEL: BroadcastChannel<i32> = BroadcastChannel::new();
}
lazy_static::lazy_static! {
pub static ref COUNT_CHANNEL: BroadcastChannel<i32> = BroadcastChannel::new();
}
static LOG_INIT: OnceCell<()> = OnceCell::new();
fn init_logging() {
LOG_INIT.get_or_init(|| {
simple_logger::SimpleLogger::new().env().init().unwrap();
});
}
static LOG_INIT: OnceCell<()> = OnceCell::new();
pub fn init_logging() {
LOG_INIT.get_or_init(|| {
simple_logger::SimpleLogger::new().env().init().unwrap();
});
}
}
#[server]
#[cfg_attr(feature = "ssr", instrument)]
pub async fn get_server_count() -> Result<i32, ServerFnError> {
use ssr_imports::*;
Ok(COUNT.load(Ordering::Relaxed))
}
@ -38,6 +39,8 @@ pub async fn adjust_server_count(
delta: i32,
msg: String,
) -> Result<i32, ServerFnError> {
use ssr_imports::*;
let new = COUNT.load(Ordering::Relaxed) + delta;
COUNT.store(new, Ordering::Relaxed);
_ = COUNT_CHANNEL.send(&new).await;
@ -48,6 +51,8 @@ pub async fn adjust_server_count(
#[server]
#[cfg_attr(feature = "ssr", instrument)]
pub async fn clear_server_count() -> Result<i32, ServerFnError> {
use ssr_imports::*;
COUNT.store(0, Ordering::Relaxed);
_ = COUNT_CHANNEL.send(&0).await;
Ok(0)
@ -55,7 +60,7 @@ pub async fn clear_server_count() -> Result<i32, ServerFnError> {
#[component]
pub fn Counters() -> impl IntoView {
#[cfg(feature = "ssr")]
init_logging();
ssr_imports::init_logging();
provide_meta_context();
view! {

View file

@ -1,21 +1,13 @@
use cfg_if::cfg_if;
pub mod counters;
// Needs to be in lib.rs AFAIK because wasm-bindgen needs us to be compiling a lib. I may be wrong.
cfg_if! {
if #[cfg(feature = "hydrate")] {
use leptos::*;
use wasm_bindgen::prelude::wasm_bindgen;
use crate::counters::*;
#[cfg(feature = "hydrate")]
#[wasm_bindgen::prelude::wasm_bindgen]
pub fn hydrate() {
use crate::counters::*;
use leptos::*;
#[wasm_bindgen]
pub fn hydrate() {
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
mount_to_body(|| {
view! { <Counters/> }
});
}
}
mount_to_body(Counters);
}

View file

@ -1,10 +1,5 @@
use cfg_if::cfg_if;
mod counters;
// boilerplate to run in different modes
cfg_if! {
// server-only stuff
if #[cfg(feature = "ssr")] {
use leptos::*;
use actix_files::{Files};
use actix_web::*;
@ -59,13 +54,4 @@ cfg_if! {
.run()
.await
}
}
// client-only main for Trunk
else {
pub fn main() {
// isomorphic counters cannot work in a Client-Side-Rendered only
// app as a server is required to maintain state
}
}
}

View file

@ -15,7 +15,6 @@ actix-files = { version = "0.6", optional = true }
actix-web = { version = "4", optional = true, features = ["macros"] }
console_log = "1"
console_error_panic_hook = "0.1"
cfg-if = "1"
leptos = { path = "../../leptos", features = ["nightly"] }
leptos_meta = { path = "../../meta", features = ["nightly"] }
leptos_actix = { path = "../../integrations/actix", optional = true }

View file

@ -1,4 +1,3 @@
use cfg_if::cfg_if;
use leptos::*;
use leptos_meta::*;
use leptos_router::*;
@ -33,16 +32,10 @@ pub fn App() -> impl IntoView {
}
}
// Needs to be in lib.rs AFAIK because wasm-bindgen needs us to be compiling a lib. I may be wrong.
cfg_if! {
if #[cfg(feature = "hydrate")] {
use wasm_bindgen::prelude::wasm_bindgen;
#[wasm_bindgen]
pub fn hydrate() {
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
leptos::mount_to_body(App);
}
}
#[cfg(feature = "hydrate")]
#[wasm_bindgen::prelude::wasm_bindgen]
pub fn hydrate() {
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
leptos::mount_to_body(App);
}

View file

@ -1,55 +1,56 @@
use cfg_if::cfg_if;
use leptos::*;
// server-only stuff
#[cfg(feature = "ssr")]
mod ssr_imports {
pub use actix_files::Files;
pub use actix_web::*;
pub use hackernews::App;
pub use leptos_actix::{generate_route_list, LeptosRoutes};
// boilerplate to run in different modes
cfg_if! {
// server-only stuff
if #[cfg(feature = "ssr")] {
use actix_files::{Files};
use actix_web::*;
use hackernews::{App};
use leptos_actix::{LeptosRoutes, generate_route_list};
#[get("/style.css")]
async fn css() -> impl Responder {
actix_files::NamedFile::open_async("./style.css").await
}
#[get("/favicon.ico")]
async fn favicon() -> impl Responder {
actix_files::NamedFile::open_async("./target/site//favicon.ico").await
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
// Setting this to None means we'll be using cargo-leptos and its env vars.
let conf = get_configuration(None).await.unwrap();
let addr = conf.leptos_options.site_addr;
// Generate the list of routes in your Leptos App
let routes = generate_route_list(App);
HttpServer::new(move || {
let leptos_options = &conf.leptos_options;
let site_root = &leptos_options.site_root;
App::new()
.service(css)
.service(favicon)
.leptos_routes(leptos_options.to_owned(), routes.to_owned(), App)
.service(Files::new("/", site_root))
//.wrap(middleware::Compress::default())
})
.bind(&addr)?
.run()
.await
}
} else {
fn main() {
use hackernews::{App};
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
mount_to_body(App)
}
#[get("/style.css")]
pub async fn css() -> impl Responder {
actix_files::NamedFile::open_async("./style.css").await
}
#[get("/favicon.ico")]
pub async fn favicon() -> impl Responder {
actix_files::NamedFile::open_async("./target/site//favicon.ico").await
}
}
#[cfg(feature = "ssr")]
#[actix_web::main]
async fn main() -> std::io::Result<()> {
use leptos::get_configuration;
use ssr_imports::*;
// Setting this to None means we'll be using cargo-leptos and its env vars.
let conf = get_configuration(None).await.unwrap();
let addr = conf.leptos_options.site_addr;
// Generate the list of routes in your Leptos App
let routes = generate_route_list(App);
HttpServer::new(move || {
let leptos_options = &conf.leptos_options;
let site_root = &leptos_options.site_root;
App::new()
.service(css)
.service(favicon)
.leptos_routes(leptos_options.to_owned(), routes.to_owned(), App)
.service(Files::new("/", site_root))
//.wrap(middleware::Compress::default())
})
.bind(&addr)?
.run()
.await
}
// CSR-only setup
#[cfg(not(feature = "ssr"))]
fn main() {
use hackernews::App;
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
mount_to_body(App)
}

View file

@ -13,7 +13,6 @@ lto = true
[dependencies]
console_log = "1.0"
console_error_panic_hook = "0.1"
cfg-if = "1.0"
leptos = { path = "../../leptos", features = ["nightly"] }
leptos_axum = { path = "../../integrations/axum", optional = true }
leptos_meta = { path = "../../meta", features = ["nightly"] }

View file

@ -1,44 +1,48 @@
use cfg_if::cfg_if;
use crate::error_template::error_template;
use axum::{
body::Body,
extract::State,
http::{Request, Response, StatusCode, Uri},
response::{IntoResponse, Response as AxumResponse},
};
use leptos::LeptosOptions;
use tower::ServiceExt;
use tower_http::services::ServeDir;
cfg_if! {
if #[cfg(feature = "ssr")] {
use axum::{
body::Body,
extract::State,
response::IntoResponse,
http::{Request, Response, StatusCode, Uri},
};
use axum::response::Response as AxumResponse;
use tower::ServiceExt;
use tower_http::services::ServeDir;
use leptos::{LeptosOptions};
use crate::error_template::error_template;
pub async fn file_and_error_handler(
uri: Uri,
State(options): State<LeptosOptions>,
req: Request<Body>,
) -> AxumResponse {
let root = options.site_root.clone();
let res = get_static_file(uri.clone(), &root).await.unwrap();
pub async fn file_and_error_handler(uri: Uri, State(options): State<LeptosOptions>, req: Request<Body>) -> AxumResponse {
let root = options.site_root.clone();
let res = get_static_file(uri.clone(), &root).await.unwrap();
if res.status() == StatusCode::OK {
res.into_response()
} else{
let handler = leptos_axum::render_app_to_stream(options.to_owned(), || error_template( None));
handler(req).await.into_response()
}
if res.status() == StatusCode::OK {
res.into_response()
} else {
let handler =
leptos_axum::render_app_to_stream(options.to_owned(), || {
error_template(None)
});
handler(req).await.into_response()
}
}
async fn get_static_file(uri: Uri, root: &str) -> Result<Response<Body>, (StatusCode, String)> {
let req = Request::builder().uri(uri.clone()).body(Body::empty()).unwrap();
// `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`
// This path is relative to the cargo root
match ServeDir::new(root).oneshot(req).await {
Ok(res) => Ok(res.into_response()),
Err(err) => Err((
StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong: {}", err),
)),
}
async fn get_static_file(
uri: Uri,
root: &str,
) -> Result<Response<Body>, (StatusCode, String)> {
let req = Request::builder()
.uri(uri.clone())
.body(Body::empty())
.unwrap();
// `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`
// This path is relative to the cargo root
match ServeDir::new(root).oneshot(req).await {
Ok(res) => Ok(res.into_response()),
Err(err) => Err((
StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong: {}", err),
)),
}
}
}

View file

@ -1,64 +1,68 @@
use cfg_if::cfg_if;
use axum::{
body::Body,
http::{Request, Response, StatusCode, Uri},
response::IntoResponse,
};
use tower::ServiceExt;
use tower_http::services::ServeDir;
cfg_if! {
if #[cfg(feature = "ssr")] {
use axum::{
body::Body,
http::{Request, Response, StatusCode, Uri},
response::IntoResponse,
};
use tower::ServiceExt;
use tower_http::services::ServeDir;
pub async fn file_handler(
uri: Uri,
) -> Result<Response<Body>, (StatusCode, String)> {
let res = get_static_file(uri.clone(), "/pkg").await?;
pub async fn file_handler(uri: Uri) -> Result<Response<Body>, (StatusCode, String)> {
let res = get_static_file(uri.clone(), "/pkg").await?;
if res.status() == StatusCode::NOT_FOUND {
// try with `.html`
// TODO: handle if the Uri has query parameters
match format!("{}.html", uri).parse() {
Ok(uri_html) => get_static_file(uri_html, "/pkg").await,
Err(_) => Err((StatusCode::INTERNAL_SERVER_ERROR, "Invalid URI".to_string())),
}
} else {
Ok(res)
if res.status() == StatusCode::NOT_FOUND {
// try with `.html`
// TODO: handle if the Uri has query parameters
match format!("{}.html", uri).parse() {
Ok(uri_html) => get_static_file(uri_html, "/pkg").await,
Err(_) => Err((
StatusCode::INTERNAL_SERVER_ERROR,
"Invalid URI".to_string(),
)),
}
}
pub async fn get_static_file_handler(uri: Uri) -> Result<Response<Body>, (StatusCode, String)> {
let res = get_static_file(uri.clone(), "/static").await?;
if res.status() == StatusCode::NOT_FOUND {
Err((StatusCode::INTERNAL_SERVER_ERROR, "Invalid URI".to_string()))
} else {
Ok(res)
}
}
async fn get_static_file(uri: Uri, base: &str) -> Result<Response<Body>, (StatusCode, String)> {
let req = Request::builder().uri(&uri).body(Body::empty()).unwrap();
// `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`
// When run normally, the root should be the crate root
if base == "/static" {
match ServeDir::new("./static").oneshot(req).await {
Ok(res) => Ok(res.into_response()),
Err(err) => Err((
StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong: {}", err),
))
}
} else if base == "/pkg" {
match ServeDir::new("./pkg").oneshot(req).await {
Ok(res) => Ok(res.into_response()),
Err(err) => Err((
StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong: {}", err),
)),
}
} else{
Err((StatusCode::NOT_FOUND, "Not Found".to_string()))
}
}
} else {
Ok(res)
}
}
pub async fn get_static_file_handler(
uri: Uri,
) -> Result<Response<Body>, (StatusCode, String)> {
let res = get_static_file(uri.clone(), "/static").await?;
if res.status() == StatusCode::NOT_FOUND {
Err((StatusCode::INTERNAL_SERVER_ERROR, "Invalid URI".to_string()))
} else {
Ok(res)
}
}
async fn get_static_file(
uri: Uri,
base: &str,
) -> Result<Response<Body>, (StatusCode, String)> {
let req = Request::builder().uri(&uri).body(Body::empty()).unwrap();
// `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`
// When run normally, the root should be the crate root
if base == "/static" {
match ServeDir::new("./static").oneshot(req).await {
Ok(res) => Ok(res.into_response()),
Err(err) => Err((
StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong: {}", err),
)),
}
} else if base == "/pkg" {
match ServeDir::new("./pkg").oneshot(req).await {
Ok(res) => Ok(res.into_response()),
Err(err) => Err((
StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong: {}", err),
)),
}
} else {
Err((StatusCode::NOT_FOUND, "Not Found".to_string()))
}
}

View file

@ -1,10 +1,11 @@
use cfg_if::cfg_if;
use leptos::{component, view, IntoView};
use leptos_meta::*;
use leptos_router::*;
mod api;
pub mod error_template;
#[cfg(feature = "ssr")]
pub mod fallback;
#[cfg(feature = "ssr")]
pub mod handlers;
mod routes;
use routes::{nav::*, stories::*, story::*, users::*};
@ -12,38 +13,28 @@ use routes::{nav::*, stories::*, story::*, users::*};
#[component]
pub fn App() -> impl IntoView {
provide_meta_context();
view! {
<>
<Link rel="shortcut icon" type_="image/ico" href="/favicon.ico"/>
<Stylesheet id="leptos" href="/pkg/hackernews_axum.css"/>
<Meta name="description" content="Leptos implementation of a HackerNews demo."/>
<Router>
<Nav />
<main>
<Routes>
<Route path="users/:id" view=User/>
<Route path="stories/:id" view=Story/>
<Route path=":stories?" view=Stories/>
</Routes>
</main>
</Router>
</>
<Link rel="shortcut icon" type_="image/ico" href="/favicon.ico"/>
<Stylesheet id="leptos" href="/pkg/hackernews_axum.css"/>
<Meta name="description" content="Leptos implementation of a HackerNews demo."/>
<Router>
<Nav />
<main>
<Routes>
<Route path="users/:id" view=User/>
<Route path="stories/:id" view=Story/>
<Route path=":stories?" view=Stories/>
</Routes>
</main>
</Router>
}
}
// Needs to be in lib.rs AFAIK because wasm-bindgen needs us to be compiling a lib. I may be wrong.
cfg_if! {
if #[cfg(feature = "hydrate")] {
use wasm_bindgen::prelude::wasm_bindgen;
#[wasm_bindgen]
pub fn hydrate() {
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
leptos::mount_to_body(move || {
view! { <App/> }
});
}
}
#[cfg(feature = "hydrate")]
#[wasm_bindgen::prelude::wasm_bindgen]
pub fn hydrate() {
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
leptos::mount_to_body(App);
}

View file

@ -1,54 +1,41 @@
use cfg_if::cfg_if;
use leptos::{logging::log, *};
// boilerplate to run in different modes
cfg_if! {
if #[cfg(feature = "ssr")] {
use axum::{
Router,
routing::get,
};
#[cfg(feature = "ssr")]
#[tokio::main]
async fn main() {
use axum::{routing::get, Router};
use hackernews_axum::{fallback::file_and_error_handler, *};
use leptos::get_configuration;
use leptos_axum::{generate_route_list, LeptosRoutes};
use hackernews_axum::fallback::file_and_error_handler;
#[tokio::main]
async fn main() {
use hackernews_axum::*;
let conf = get_configuration(Some("Cargo.toml")).await.unwrap();
let leptos_options = conf.leptos_options;
let addr = leptos_options.site_addr;
let routes = generate_route_list(App);
let conf = get_configuration(Some("Cargo.toml")).await.unwrap();
let leptos_options = conf.leptos_options;
let addr = leptos_options.site_addr;
let routes = generate_route_list(App);
simple_logger::init_with_level(log::Level::Debug)
.expect("couldn't initialize logging");
simple_logger::init_with_level(log::Level::Debug).expect("couldn't initialize logging");
// build our application with a route
let app = Router::new()
// build our application with a route
let app = Router::new()
.route("/favicon.ico", get(file_and_error_handler))
.leptos_routes(&leptos_options, routes, || view! { <App/> } )
.leptos_routes(&leptos_options, routes, App)
.fallback(file_and_error_handler)
.with_state(leptos_options);
// run our app with hyper
// `axum::Server` is a re-export of `hyper::Server`
log!("listening on {}", addr);
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
axum::serve(listener, app.into_make_service())
.await
.unwrap();
}
// run our app with hyper
// `axum::Server` is a re-export of `hyper::Server`
println!("listening on {}", addr);
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
axum::serve(listener, app.into_make_service())
.await
.unwrap();
}
// client-only stuff for Trunk
else {
use hackernews_axum::*;
// client-only stuff for Trunk
#[cfg(not(feature = "ssr"))]
pub fn main() {
use hackernews_axum::*;
pub fn main() {
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
mount_to_body(|| {
view! { <App/> }
});
}
}
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
mount_to_body(App);
}

View file

@ -13,7 +13,6 @@ lto = true
[dependencies]
console_log = "1.0"
console_error_panic_hook = "0.1"
cfg-if = "1.0"
leptos = { path = "../../leptos", features = [
"nightly",
"experimental-islands",

View file

@ -1,44 +1,48 @@
use cfg_if::cfg_if;
use crate::error_template::error_template;
use axum::{
body::Body,
extract::State,
http::{Request, Response, StatusCode, Uri},
response::{IntoResponse, Response as AxumResponse},
};
use leptos::LeptosOptions;
use tower::ServiceExt;
use tower_http::services::ServeDir;
cfg_if! {
if #[cfg(feature = "ssr")] {
use axum::{
body::Body,
extract::State,
response::IntoResponse,
http::{Request, Response, StatusCode, Uri},
};
use axum::response::Response as AxumResponse;
use tower::ServiceExt;
use tower_http::services::ServeDir;
use leptos::{LeptosOptions};
use crate::error_template::error_template;
pub async fn file_and_error_handler(
uri: Uri,
State(options): State<LeptosOptions>,
req: Request<Body>,
) -> AxumResponse {
let root = options.site_root.clone();
let res = get_static_file(uri.clone(), &root).await.unwrap();
pub async fn file_and_error_handler(uri: Uri, State(options): State<LeptosOptions>, req: Request<Body>) -> AxumResponse {
let root = options.site_root.clone();
let res = get_static_file(uri.clone(), &root).await.unwrap();
if res.status() == StatusCode::OK {
res.into_response()
} else{
let handler = leptos_axum::render_app_to_stream(options.to_owned(), || error_template( None));
handler(req).await.into_response()
}
if res.status() == StatusCode::OK {
res.into_response()
} else {
let handler =
leptos_axum::render_app_to_stream(options.to_owned(), || {
error_template(None)
});
handler(req).await.into_response()
}
}
async fn get_static_file(uri: Uri, root: &str) -> Result<Response<Body>, (StatusCode, String)> {
let req = Request::builder().uri(uri.clone()).body(Body::empty()).unwrap();
// `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`
// This path is relative to the cargo root
match ServeDir::new(root).oneshot(req).await {
Ok(res) => Ok(res.into_response()),
Err(err) => Err((
StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong: {}", err),
)),
}
async fn get_static_file(
uri: Uri,
root: &str,
) -> Result<Response<Body>, (StatusCode, String)> {
let req = Request::builder()
.uri(uri.clone())
.body(Body::empty())
.unwrap();
// `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`
// This path is relative to the cargo root
match ServeDir::new(root).oneshot(req).await {
Ok(res) => Ok(res.into_response()),
Err(err) => Err((
StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong: {}", err),
)),
}
}
}

View file

@ -1,63 +0,0 @@
use cfg_if::cfg_if;
cfg_if! {
if #[cfg(feature = "ssr")] {
use axum::{
body::Body,
http::{Request, Response, StatusCode, Uri},
};
use tower::ServiceExt;
use tower_http::services::ServeDir;
pub async fn file_handler(uri: Uri) -> Result<Response<Body>, (StatusCode, String)> {
let res = get_static_file(uri.clone(), "/pkg").await?;
if res.status() == StatusCode::NOT_FOUND {
// try with `.html`
// TODO: handle if the Uri has query parameters
match format!("{}.html", uri).parse() {
Ok(uri_html) => get_static_file(uri_html, "/pkg").await,
Err(_) => Err((StatusCode::INTERNAL_SERVER_ERROR, "Invalid URI".to_string())),
}
} else {
Ok(res)
}
}
pub async fn get_static_file_handler(uri: Uri) -> Result<Response<Body>, (StatusCode, String)> {
let res = get_static_file(uri.clone(), "/static").await?;
if res.status() == StatusCode::NOT_FOUND {
Err((StatusCode::INTERNAL_SERVER_ERROR, "Invalid URI".to_string()))
} else {
Ok(res)
}
}
async fn get_static_file(uri: Uri, base: &str) -> Result<Response<Body>, (StatusCode, String)> {
let req = Request::builder().uri(&uri).body(Body::empty()).unwrap();
// `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`
// When run normally, the root should be the crate root
if base == "/static" {
match ServeDir::new("./static").oneshot(req).await {
Ok(res) => Ok(res.into_response()),
Err(err) => Err((
StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong: {}", err),
))
}
} else if base == "/pkg" {
match ServeDir::new("./pkg").oneshot(req).await {
Ok(res) => Ok(res.into_response()),
Err(err) => Err((
StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong: {}", err),
)),
}
} else{
Err((StatusCode::NOT_FOUND, "Not Found".to_string()))
}
}
}
}

View file

@ -1,11 +1,11 @@
#![feature(lazy_cell)]
use cfg_if::cfg_if;
use leptos::*;
use leptos_meta::*;
use leptos_router::*;
mod api;
pub mod error_template;
#[cfg(feature = "ssr")]
pub mod fallback;
mod routes;
use routes::{nav::*, stories::*, story::*, users::*};
@ -31,16 +31,10 @@ pub fn App() -> impl IntoView {
}
}
// Needs to be in lib.rs AFAIK because wasm-bindgen needs us to be compiling a lib. I may be wrong.
cfg_if! {
if #[cfg(feature = "hydrate")] {
use wasm_bindgen::prelude::wasm_bindgen;
#[wasm_bindgen]
pub fn hydrate() {
#[cfg(debug_assertions)]
console_error_panic_hook::set_once();
leptos::leptos_dom::HydrationCtx::stop_hydrating();
}
}
#[cfg(feature = "hydrate")]
#[wasm_bindgen::prelude::wasm_bindgen]
pub fn hydrate() {
#[cfg(debug_assertions)]
console_error_panic_hook::set_once();
leptos::leptos_dom::HydrationCtx::stop_hydrating();
}

View file

@ -1,16 +1,11 @@
#[cfg(feature = "ssr")]
mod ssr_imports {
pub use axum::{routing::get, Router};
pub use hackernews_islands::fallback::file_and_error_handler;
pub use leptos::*;
pub use leptos_axum::{generate_route_list, LeptosRoutes};
}
#[cfg(feature = "ssr")]
#[tokio::main]
async fn main() {
pub use axum::{routing::get, Router};
pub use hackernews_islands::fallback::file_and_error_handler;
use hackernews_islands::*;
use ssr_imports::*;
pub use leptos::get_configuration;
pub use leptos_axum::{generate_route_list, LeptosRoutes};
let conf = get_configuration(Some("Cargo.toml")).await.unwrap();
let leptos_options = conf.leptos_options;
@ -40,7 +35,5 @@ pub fn main() {
use leptos::*;
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
mount_to_body(|| {
view! { <App/> }
});
mount_to_body(App);
}

View file

@ -13,7 +13,6 @@ lto = true
[dependencies]
console_log = "1.0"
console_error_panic_hook = "0.1"
cfg-if = "1.0"
leptos = { path = "../../leptos", features = ["nightly"] }
leptos_axum = { path = "../../integrations/axum", default-features = false, optional = true }
leptos_meta = { path = "../../meta", features = ["nightly"] }

View file

@ -1,39 +1,43 @@
use cfg_if::cfg_if;
use crate::error_template::error_template;
use axum::{
body::Body,
extract::State,
http::{Request, Response, StatusCode, Uri},
response::{IntoResponse, Response as AxumResponse},
};
//use tower::ServiceExt;
use leptos::LeptosOptions;
cfg_if! {
if #[cfg(feature = "ssr")] {
use axum::{
body::Body,
extract::State,
response::IntoResponse,
http::{Request, Response, StatusCode, Uri},
};
use axum::response::Response as AxumResponse;
//use tower::ServiceExt;
use leptos::{LeptosOptions};
use crate::error_template::error_template;
pub async fn file_and_error_handler(
uri: Uri,
State(options): State<LeptosOptions>,
req: Request<Body>,
) -> AxumResponse {
let root = options.site_root.clone();
let res = get_static_file(uri.clone(), &root).await.unwrap();
pub async fn file_and_error_handler(uri: Uri, State(options): State<LeptosOptions>, req: Request<Body>) -> AxumResponse {
let root = options.site_root.clone();
let res = get_static_file(uri.clone(), &root).await.unwrap();
if res.status() == StatusCode::OK {
res.into_response()
} else{
let handler = leptos_axum::render_app_to_stream(options.to_owned(), || error_template(None));
handler(req).await.into_response()
}
if res.status() == StatusCode::OK {
res.into_response()
} else {
let handler =
leptos_axum::render_app_to_stream(options.to_owned(), || {
error_template(None)
});
handler(req).await.into_response()
}
async fn get_static_file(uri: Uri, root: &str) -> Result<Response<Body>, (StatusCode, String)> {
let req = Request::builder().uri(uri.clone()).body(Body::empty()).unwrap();
// `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`
// This path is relative to the cargo root
_ = req;
_ = root;
todo!()
}
}
async fn get_static_file(
uri: Uri,
root: &str,
) -> Result<Response<Body>, (StatusCode, String)> {
let req = Request::builder()
.uri(uri.clone())
.body(Body::empty())
.unwrap();
// `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`
// This path is relative to the cargo root
_ = req;
_ = root;
todo!()
}

View file

@ -1,9 +1,9 @@
use cfg_if::cfg_if;
use leptos::{component, view, IntoView};
use leptos_meta::*;
use leptos_router::*;
mod api;
pub mod error_template;
#[cfg(feature = "ssr")]
pub mod fallback;
mod routes;
use routes::{nav::*, stories::*, story::*, users::*};
@ -29,24 +29,22 @@ pub fn App() -> impl IntoView {
}
}
cfg_if! {
if #[cfg(feature = "hydrate")] {
#[wasm_bindgen]
pub fn hydrate() {
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
leptos::mount_to_body(move || {
view! { <App/> }
});
}
} else if #[cfg(feature = "ssr")] {
#[cfg(feature = "hydrate")]
#[wasm_bindgen]
pub fn hydrate() {
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
leptos::mount_to_body(App);
}
use axum::{
Router,
};
use leptos_axum::{generate_route_list, LeptosRoutes};
#[cfg(feature = "ssr")]
mod ssr_imports {
use crate::App;
use axum::Router;
use leptos::*;
use leptos_axum::{generate_route_list, LeptosRoutes};
use log::{info, Level};
use wasm_bindgen::prelude::wasm_bindgen;
#[wasm_bindgen]
pub struct Handler(axum_js_fetch::App);
@ -57,13 +55,16 @@ cfg_if! {
console_log::init_with_level(Level::Debug);
console_error_panic_hook::set_once();
let leptos_options = LeptosOptions::builder().output_name("client").site_pkg_dir("pkg").build();
let leptos_options = LeptosOptions::builder()
.output_name("client")
.site_pkg_dir("pkg")
.build();
let routes = generate_route_list(App);
// build our application with a route
let app: axum::Router = Router::new()
.leptos_routes(&leptos_options, routes, || view! { <App/> } )
.with_state(leptos_options);
.leptos_routes(&leptos_options, routes, App)
.with_state(leptos_options);
info!("creating handler instance");
@ -75,4 +76,3 @@ cfg_if! {
}
}
}
}

View file

@ -12,7 +12,6 @@ console_log = "1.0"
rand = { version = "0.8", features = ["min_const_gen"], optional = true }
console_error_panic_hook = "0.1"
futures = "0.3"
cfg-if = "1.0"
leptos = { path = "../../leptos", features = ["nightly"] }
leptos_meta = { path = "../../meta", features = ["nightly"] }
leptos_axum = { path = "../../integrations/axum", optional = true }

View file

@ -1,17 +1,7 @@
use cfg_if::cfg_if;
use leptos::*;
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
cfg_if! {
if #[cfg(feature = "ssr")] {
use sqlx::SqlitePool;
use axum_session_auth::{SessionSqlitePool, Authentication, HasPermission};
use bcrypt::{hash, verify, DEFAULT_COST};
use crate::todo::{pool, auth};
pub type AuthSession = axum_session_auth::AuthSession<User, i64, SessionSqlitePool, SqlitePool>;
}}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct User {
pub id: i64,
@ -33,17 +23,33 @@ impl Default for User {
}
}
cfg_if! {
if #[cfg(feature = "ssr")] {
use async_trait::async_trait;
#[cfg(feature = "ssr")]
pub mod ssr {
pub use super::User;
pub use axum_session_auth::{
Authentication, HasPermission, SessionSqlitePool,
};
pub use sqlx::SqlitePool;
pub use std::collections::HashSet;
pub type AuthSession = axum_session_auth::AuthSession<
User,
i64,
SessionSqlitePool,
SqlitePool,
>;
pub use crate::todo::ssr::{auth, pool};
pub use async_trait::async_trait;
pub use bcrypt::{hash, verify, DEFAULT_COST};
impl User {
pub async fn get(id: i64, pool: &SqlitePool) -> Option<Self> {
let sqluser = sqlx::query_as::<_, SqlUser>("SELECT * FROM users WHERE id = ?")
.bind(id)
.fetch_one(pool)
.await
.ok()?;
let sqluser = sqlx::query_as::<_, SqlUser>(
"SELECT * FROM users WHERE id = ?",
)
.bind(id)
.fetch_one(pool)
.await
.ok()?;
//lets just get all the tokens the user can use, we will only use the full permissions if modifing them.
let sql_user_perms = sqlx::query_as::<_, SqlPermissionTokens>(
@ -57,12 +63,17 @@ if #[cfg(feature = "ssr")] {
Some(sqluser.into_user(Some(sql_user_perms)))
}
pub async fn get_from_username(name: String, pool: &SqlitePool) -> Option<Self> {
let sqluser = sqlx::query_as::<_, SqlUser>("SELECT * FROM users WHERE username = ?")
.bind(name)
.fetch_one(pool)
.await
.ok()?;
pub async fn get_from_username(
name: String,
pool: &SqlitePool,
) -> Option<Self> {
let sqluser = sqlx::query_as::<_, SqlUser>(
"SELECT * FROM users WHERE username = ?",
)
.bind(name)
.fetch_one(pool)
.await
.ok()?;
//lets just get all the tokens the user can use, we will only use the full permissions if modifing them.
let sql_user_perms = sqlx::query_as::<_, SqlPermissionTokens>(
@ -84,7 +95,10 @@ if #[cfg(feature = "ssr")] {
#[async_trait]
impl Authentication<User, i64, SqlitePool> for User {
async fn load_user(userid: i64, pool: Option<&SqlitePool>) -> Result<User, anyhow::Error> {
async fn load_user(
userid: i64,
pool: Option<&SqlitePool>,
) -> Result<User, anyhow::Error> {
let pool = pool.unwrap();
User::get(userid, pool)
@ -120,7 +134,10 @@ if #[cfg(feature = "ssr")] {
}
impl SqlUser {
pub fn into_user(self, sql_user_perms: Option<Vec<SqlPermissionTokens>>) -> User {
pub fn into_user(
self,
sql_user_perms: Option<Vec<SqlPermissionTokens>>,
) -> User {
User {
id: self.id,
username: self.username,
@ -137,15 +154,16 @@ if #[cfg(feature = "ssr")] {
}
}
}
}
#[server(Foo, "/api")]
#[server]
pub async fn foo() -> Result<String, ServerFnError> {
Ok(String::from("Bar!"))
}
#[server(GetUser, "/api")]
#[server]
pub async fn get_user() -> Result<Option<User>, ServerFnError> {
use crate::todo::ssr::auth;
let auth = auth()?;
Ok(auth.current_user)
@ -157,6 +175,8 @@ pub async fn login(
password: String,
remember: Option<String>,
) -> Result<(), ServerFnError> {
use self::ssr::*;
let pool = pool()?;
let auth = auth()?;
@ -184,6 +204,8 @@ pub async fn signup(
password_confirmation: String,
remember: Option<String>,
) -> Result<(), ServerFnError> {
use self::ssr::*;
let pool = pool()?;
let auth = auth()?;
@ -218,6 +240,8 @@ pub async fn signup(
#[server(Logout, "/api")]
pub async fn logout() -> Result<(), ServerFnError> {
use self::ssr::*;
let auth = auth()?;
auth.logout_user();

View file

@ -1,5 +1,4 @@
use crate::errors::TodoAppError;
use cfg_if::cfg_if;
use leptos::{Errors, *};
#[cfg(feature = "ssr")]
use leptos_axum::ResponseOptions;
@ -29,13 +28,12 @@ pub fn ErrorTemplate(
// Only the response code for the first error is actually sent from the server
// this may be customized by the specific application
cfg_if! {
if #[cfg(feature="ssr")]{
#[cfg(feature = "ssr")]
{
let response = use_context::<ResponseOptions>();
if let Some(response) = response{
response.set_status(errors[0].status_code());
if let Some(response) = response {
response.set_status(errors[0].status_code());
}
}
}
view! {

View file

@ -1,47 +1,50 @@
use cfg_if::cfg_if;
use crate::{error_template::ErrorTemplate, errors::TodoAppError};
use axum::{
body::Body,
extract::State,
http::{Request, Response, StatusCode, Uri},
response::{IntoResponse, Response as AxumResponse},
};
use leptos::{view, Errors, LeptosOptions};
use tower::ServiceExt;
use tower_http::services::ServeDir;
cfg_if! {
if #[cfg(feature = "ssr")] {
use axum::{
body::Body,
extract::State,
response::IntoResponse,
http::{Request, Response, StatusCode, Uri},
};
use axum::response::Response as AxumResponse;
use tower::ServiceExt;
use tower_http::services::ServeDir;
use leptos::{LeptosOptions, Errors, view};
use crate::error_template::ErrorTemplate;
use crate::errors::TodoAppError;
pub async fn file_and_error_handler(
uri: Uri,
State(options): State<LeptosOptions>,
req: Request<Body>,
) -> AxumResponse {
let root = options.site_root.clone();
let res = get_static_file(uri.clone(), &root).await.unwrap();
pub async fn file_and_error_handler(uri: Uri, State(options): State<LeptosOptions>, req: Request<Body>) -> AxumResponse {
let root = options.site_root.clone();
let res = get_static_file(uri.clone(), &root).await.unwrap();
if res.status() == StatusCode::OK {
res.into_response()
} else{
let mut errors = Errors::default();
errors.insert_with_default_key(TodoAppError::NotFound);
let handler = leptos_axum::render_app_to_stream(options.to_owned(), move || view!{<ErrorTemplate outside_errors=errors.clone()/>});
handler(req).await.into_response()
}
if res.status() == StatusCode::OK {
res.into_response()
} else {
let mut errors = Errors::default();
errors.insert_with_default_key(TodoAppError::NotFound);
let handler = leptos_axum::render_app_to_stream(
options.to_owned(),
move || view! {<ErrorTemplate outside_errors=errors.clone()/>},
);
handler(req).await.into_response()
}
}
async fn get_static_file(uri: Uri, root: &str) -> Result<Response<Body>, (StatusCode, String)> {
let req = Request::builder().uri(uri.clone()).body(Body::empty()).unwrap();
// `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`
// This path is relative to the cargo root
match ServeDir::new(root).oneshot(req).await {
Ok(res) => Ok(res.into_response()),
Err(err) => Err((
StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong: {err}"),
)),
}
async fn get_static_file(
uri: Uri,
root: &str,
) -> Result<Response<Body>, (StatusCode, String)> {
let req = Request::builder()
.uri(uri.clone())
.body(Body::empty())
.unwrap();
// `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`
// This path is relative to the cargo root
match ServeDir::new(root).oneshot(req).await {
Ok(res) => Ok(res.into_response()),
Err(err) => Err((
StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong: {err}"),
)),
}
}
}

View file

@ -1,27 +1,18 @@
use cfg_if::cfg_if;
pub mod auth;
pub mod error_template;
pub mod errors;
#[cfg(feature = "ssr")]
pub mod fallback;
#[cfg(feature = "ssr")]
pub mod state;
pub mod todo;
// Needs to be in lib.rs AFAIK because wasm-bindgen needs us to be compiling a lib. I may be wrong.
cfg_if! {
if #[cfg(feature = "hydrate")] {
use wasm_bindgen::prelude::wasm_bindgen;
use crate::todo::*;
use leptos::view;
#[cfg(feature = "hydrate")]
#[wasm_bindgen::prelude::wasm_bindgen]
pub fn hydrate() {
use crate::todo::*;
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
#[wasm_bindgen]
pub fn hydrate() {
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
leptos::mount_to_body(|| {
view! { <TodoApp/> }
});
}
}
leptos::mount_to_body(TodoApp);
}

View file

@ -1,118 +1,131 @@
use cfg_if::cfg_if;
use axum::{
body::Body as AxumBody,
extract::{Path, State},
http::Request,
response::{IntoResponse, Response},
routing::get,
Router,
};
use axum_session::{SessionConfig, SessionLayer, SessionStore};
use axum_session_auth::{AuthConfig, AuthSessionLayer, SessionSqlitePool};
use leptos::{get_configuration, logging::log, provide_context};
use leptos_axum::{
generate_route_list, handle_server_fns_with_context, LeptosRoutes,
};
use session_auth_axum::{
auth::{ssr::AuthSession, User},
fallback::file_and_error_handler,
state::AppState,
todo::*,
};
use sqlx::{sqlite::SqlitePoolOptions, SqlitePool};
// boilerplate to run in different modes
cfg_if! {
if #[cfg(feature = "ssr")] {
use axum::{
response::{Response, IntoResponse},
routing::get,
extract::{Path, State},
http::{Request},
body::Body as AxumBody,
Router,
};
use session_auth_axum::todo::*;
use session_auth_axum::auth::*;
use session_auth_axum::state::AppState;
use session_auth_axum::fallback::file_and_error_handler;
use leptos_axum::{generate_route_list, LeptosRoutes, handle_server_fns_with_context};
use leptos::{logging::log, provide_context, get_configuration};
use sqlx::{SqlitePool, sqlite::SqlitePoolOptions};
use axum_session::{SessionConfig, SessionLayer, SessionStore};
use axum_session_auth::{AuthSessionLayer, AuthConfig, SessionSqlitePool};
async fn server_fn_handler(
State(app_state): State<AppState>,
auth_session: AuthSession,
path: Path<String>,
request: Request<AxumBody>,
) -> impl IntoResponse {
log!("{:?}", path);
async fn server_fn_handler(State(app_state): State<AppState>, auth_session: AuthSession, path: Path<String>,
request: Request<AxumBody>) -> impl IntoResponse {
log!("{:?}", path);
handle_server_fns_with_context( move || {
handle_server_fns_with_context(
move || {
provide_context(auth_session.clone());
provide_context(app_state.pool.clone());
}, request).await
},
request,
)
.await
}
async fn leptos_routes_handler(
auth_session: AuthSession,
State(app_state): State<AppState>,
req: Request<AxumBody>,
) -> Response {
let handler = leptos_axum::render_route_with_context(
app_state.leptos_options.clone(),
app_state.routes.clone(),
move || {
provide_context(auth_session.clone());
provide_context(app_state.pool.clone());
},
TodoApp,
);
handler(req).await.into_response()
}
#[tokio::main]
async fn main() {
simple_logger::init_with_level(log::Level::Info)
.expect("couldn't initialize logging");
let pool = SqlitePoolOptions::new()
.connect("sqlite:Todos.db")
.await
.expect("Could not make pool.");
// Auth section
let session_config =
SessionConfig::default().with_table_name("axum_sessions");
let auth_config = AuthConfig::<i64>::default();
let session_store = SessionStore::<SessionSqlitePool>::new(
Some(pool.clone().into()),
session_config,
)
.await
.unwrap();
if let Err(e) = sqlx::migrate!().run(&pool).await {
eprintln!("{e:?}");
}
async fn leptos_routes_handler(auth_session: AuthSession, State(app_state): State<AppState>, req: Request<AxumBody>) -> Response{
let handler = leptos_axum::render_route_with_context(app_state.leptos_options.clone(),
app_state.routes.clone(),
move || {
provide_context(auth_session.clone());
provide_context(app_state.pool.clone());
},
TodoApp
);
handler(req).await.into_response()
}
// Explicit server function registration is no longer required
// on the main branch. On 0.3.0 and earlier, uncomment the lines
// below to register the server functions.
// _ = GetTodos::register();
// _ = AddTodo::register();
// _ = DeleteTodo::register();
// _ = Login::register();
// _ = Logout::register();
// _ = Signup::register();
// _ = GetUser::register();
// _ = Foo::register();
#[tokio::main]
async fn main() {
simple_logger::init_with_level(log::Level::Info).expect("couldn't initialize logging");
// Setting this to None means we'll be using cargo-leptos and its env vars
let conf = get_configuration(None).await.unwrap();
let leptos_options = conf.leptos_options;
let addr = leptos_options.site_addr;
let routes = generate_route_list(TodoApp);
let pool = SqlitePoolOptions::new()
.connect("sqlite:Todos.db")
.await
.expect("Could not make pool.");
let app_state = AppState {
leptos_options,
pool: pool.clone(),
routes: routes.clone(),
};
// Auth section
let session_config = SessionConfig::default().with_table_name("axum_sessions");
let auth_config = AuthConfig::<i64>::default();
let session_store = SessionStore::<SessionSqlitePool>::new(Some(pool.clone().into()), session_config).await.unwrap();
if let Err(e) = sqlx::migrate!()
.run(&pool)
.await {
eprintln!("{e:?}");
}
// Explicit server function registration is no longer required
// on the main branch. On 0.3.0 and earlier, uncomment the lines
// below to register the server functions.
// _ = GetTodos::register();
// _ = AddTodo::register();
// _ = DeleteTodo::register();
// _ = Login::register();
// _ = Logout::register();
// _ = Signup::register();
// _ = GetUser::register();
// _ = Foo::register();
// Setting this to None means we'll be using cargo-leptos and its env vars
let conf = get_configuration(None).await.unwrap();
let leptos_options = conf.leptos_options;
let addr = leptos_options.site_addr;
let routes = generate_route_list(TodoApp);
let app_state = AppState{
leptos_options,
pool: pool.clone(),
routes: routes.clone(),
};
// build our application with a route
let app = Router::new()
.route("/api/*fn_name", get(server_fn_handler).post(server_fn_handler))
.leptos_routes_with_handler(routes, get(leptos_routes_handler) )
// build our application with a route
let app = Router::new()
.route(
"/api/*fn_name",
get(server_fn_handler).post(server_fn_handler),
)
.leptos_routes_with_handler(routes, get(leptos_routes_handler))
.fallback(file_and_error_handler)
.layer(AuthSessionLayer::<User, i64, SessionSqlitePool, SqlitePool>::new(Some(pool.clone()))
.with_config(auth_config))
.layer(
AuthSessionLayer::<User, i64, SessionSqlitePool, SqlitePool>::new(
Some(pool.clone()),
)
.with_config(auth_config),
)
.layer(SessionLayer::new(session_store))
.with_state(app_state);
// run our app with hyper
// `axum::Server` is a re-export of `hyper::Server`
log!("listening on http://{}", &addr);
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
axum::serve(listener, app.into_make_service())
.await
.unwrap();
}
}
// client-only stuff for Trunk
else {
pub fn main() {
// This example cannot be built as a trunk standalone CSR-only app.
// Only the server may directly connect to the database.
}
}
// run our app with hyper
// `axum::Server` is a re-export of `hyper::Server`
log!("listening on http://{}", &addr);
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
axum::serve(listener, app.into_make_service())
.await
.unwrap();
}

View file

@ -1,18 +1,13 @@
use cfg_if::cfg_if;
cfg_if! {
if #[cfg(feature = "ssr")] {
use leptos::LeptosOptions;
use sqlx::SqlitePool;
use axum::extract::FromRef;
use leptos::LeptosOptions;
use leptos_router::RouteListing;
use sqlx::SqlitePool;
/// This takes advantage of Axum's SubStates feature by deriving FromRef. This is the only way to have more than one
/// item in Axum's State. Leptos requires you to have leptosOptions in your State struct for the leptos route handlers
#[derive(FromRef, Debug, Clone)]
pub struct AppState{
pub struct AppState {
pub leptos_options: LeptosOptions,
pub pool: SqlitePool,
pub routes: Vec<RouteListing>,
}
}
}

View file

@ -1,5 +1,4 @@
use crate::{auth::*, error_template::ErrorTemplate};
use cfg_if::cfg_if;
use leptos::*;
use leptos_meta::*;
use leptos_router::*;
@ -14,40 +13,41 @@ pub struct Todo {
completed: bool,
}
cfg_if! {
if #[cfg(feature = "ssr")] {
#[cfg(feature = "ssr")]
pub mod ssr {
use super::Todo;
use crate::auth::{ssr::AuthSession, User};
use leptos::*;
use sqlx::SqlitePool;
use sqlx::SqlitePool;
use futures::future::join_all;
pub fn pool() -> Result<SqlitePool, ServerFnError> {
use_context::<SqlitePool>()
.ok_or_else(|| ServerFnError::ServerError("Pool missing.".into()))
}
pub fn pool() -> Result<SqlitePool, ServerFnError> {
use_context::<SqlitePool>()
.ok_or_else(|| ServerFnError::ServerError("Pool missing.".into()))
}
pub fn auth() -> Result<AuthSession, ServerFnError> {
use_context::<AuthSession>().ok_or_else(|| {
ServerFnError::ServerError("Auth session missing.".into())
})
}
pub fn auth() -> Result<AuthSession, ServerFnError> {
use_context::<AuthSession>()
.ok_or_else(|| ServerFnError::ServerError("Auth session missing.".into()))
}
#[derive(sqlx::FromRow, Clone)]
pub struct SqlTodo {
id: u32,
user_id: i64,
title: String,
created_at: String,
completed: bool,
}
#[derive(sqlx::FromRow, Clone)]
pub struct SqlTodo {
id: u32,
user_id: i64,
title: String,
created_at: String,
completed: bool,
}
impl SqlTodo {
pub async fn into_todo(self, pool: &SqlitePool) -> Todo {
Todo {
id: self.id,
user: User::get(self.user_id, pool).await,
title: self.title,
created_at: self.created_at,
completed: self.completed,
}
impl SqlTodo {
pub async fn into_todo(self, pool: &SqlitePool) -> Todo {
Todo {
id: self.id,
user: User::get(self.user_id, pool).await,
title: self.title,
created_at: self.created_at,
completed: self.completed,
}
}
}
@ -55,6 +55,9 @@ cfg_if! {
#[server(GetTodos, "/api")]
pub async fn get_todos() -> Result<Vec<Todo>, ServerFnError> {
use self::ssr::{pool, SqlTodo};
use futures::future::join_all;
let pool = pool()?;
Ok(join_all(
@ -69,6 +72,8 @@ pub async fn get_todos() -> Result<Vec<Todo>, ServerFnError> {
#[server(AddTodo, "/api")]
pub async fn add_todo(title: String) -> Result<(), ServerFnError> {
use self::ssr::*;
let user = get_user().await?;
let pool = pool()?;
@ -93,6 +98,8 @@ pub async fn add_todo(title: String) -> Result<(), ServerFnError> {
// The struct name and path prefix arguments are optional.
#[server]
pub async fn delete_todo(id: u16) -> Result<(), ServerFnError> {
use self::ssr::*;
let pool = pool()?;
Ok(sqlx::query("DELETE FROM todos WHERE id = $1")

View file

@ -11,7 +11,6 @@ actix-files = { version = "0.6", optional = true }
actix-web = { version = "4", optional = true, features = ["macros"] }
console_error_panic_hook = "0.1"
console_log = "1"
cfg-if = "1"
lazy_static = "1"
leptos = { path = "../../leptos", features = ["nightly"] }
leptos_meta = { path = "../../meta", features = ["nightly"] }

View file

@ -1,25 +1,15 @@
#![feature(result_flattening)]
pub mod app;
use cfg_if::cfg_if;
cfg_if! {
if #[cfg(feature = "hydrate")] {
#[cfg(feature = "hydrate")]
#[wasm_bindgen::prelude::wasm_bindgen]
pub fn hydrate() {
use app::*;
use wasm_bindgen::prelude::wasm_bindgen;
// initializes logging using the `log` crate
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
#[wasm_bindgen]
pub fn hydrate() {
use app::*;
use leptos::*;
// initializes logging using the `log` crate
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
leptos::mount_to_body(move || {
view! { <App/> }
});
}
}
leptos::mount_to_body(App);
}

View file

@ -23,11 +23,7 @@ async fn main() -> std::io::Result<()> {
let site_root = &leptos_options.site_root;
App::new()
.leptos_routes(
leptos_options.to_owned(),
routes.to_owned(),
App,
)
.leptos_routes(leptos_options.to_owned(), routes.to_owned(), App)
.service(Files::new("/", site_root))
//.wrap(middleware::Compress::default())
})

View file

@ -9,7 +9,6 @@ crate-type = ["cdylib", "rlib"]
[dependencies]
console_error_panic_hook = "0.1"
console_log = "1"
cfg-if = "1"
lazy_static = "1"
leptos = { path = "../../leptos", features = ["nightly"] }
leptos_meta = { path = "../../meta", features = ["nightly"] }

View file

@ -1,43 +1,48 @@
use cfg_if::cfg_if;
use crate::app::App;
use axum::{
body::Body,
extract::State,
http::{Request, Response, StatusCode, Uri},
response::{IntoResponse, Response as AxumResponse},
};
use leptos::{view, LeptosOptions};
use tower::ServiceExt;
use tower_http::services::ServeDir;
cfg_if! { if #[cfg(feature = "ssr")] {
use axum::{
body::Body,
extract::State,
response::IntoResponse,
http::{Request, Response, StatusCode, Uri},
};
use axum::response::Response as AxumResponse;
use tower::ServiceExt;
use tower_http::services::ServeDir;
use leptos::{LeptosOptions, view};
use crate::app::App;
pub async fn file_and_error_handler(
uri: Uri,
State(options): State<LeptosOptions>,
req: Request<Body>,
) -> AxumResponse {
let root = options.site_root.clone();
let res = get_static_file(uri.clone(), &root).await.unwrap();
pub async fn file_and_error_handler(uri: Uri, State(options): State<LeptosOptions>, req: Request<Body>) -> AxumResponse {
let root = options.site_root.clone();
let res = get_static_file(uri.clone(), &root).await.unwrap();
if res.status() == StatusCode::OK {
res.into_response()
} else{
let handler = leptos_axum::render_app_to_stream(
options.to_owned(),
move || view!{ <App/> }
);
handler(req).await.into_response()
}
if res.status() == StatusCode::OK {
res.into_response()
} else {
let handler = leptos_axum::render_app_to_stream(
options.to_owned(),
move || view! { <App/> },
);
handler(req).await.into_response()
}
}
async fn get_static_file(uri: Uri, root: &str) -> Result<Response<Body>, (StatusCode, String)> {
let req = Request::builder().uri(uri.clone()).body(Body::empty()).unwrap();
// `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`
// This path is relative to the cargo root
match ServeDir::new(root).oneshot(req).await {
Ok(res) => Ok(res.into_response()),
Err(err) => Err((
StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong: {err}"),
)),
}
async fn get_static_file(
uri: Uri,
root: &str,
) -> Result<Response<Body>, (StatusCode, String)> {
let req = Request::builder()
.uri(uri.clone())
.body(Body::empty())
.unwrap();
// `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`
// This path is relative to the cargo root
match ServeDir::new(root).oneshot(req).await {
Ok(res) => Ok(res.into_response()),
Err(err) => Err((
StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong: {err}"),
)),
}
}}
}

View file

@ -1,26 +1,18 @@
#![feature(result_flattening)]
pub mod app;
#[cfg(feature = "ssr")]
pub mod fallback;
use cfg_if::cfg_if;
cfg_if! {
if #[cfg(feature = "hydrate")] {
#[cfg(feature = "hydrate")]
#[wasm_bindgen::prelude::wasm_bindgen]
pub fn hydrate() {
use app::*;
use wasm_bindgen::prelude::wasm_bindgen;
// initializes logging using the `log` crate
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
#[wasm_bindgen]
pub fn hydrate() {
use app::*;
use leptos::*;
// initializes logging using the `log` crate
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
leptos::mount_to_body(move || {
view! { <App/> }
});
}
}
leptos::mount_to_body(App);
}

View file

@ -1,7 +1,7 @@
#[cfg(feature = "ssr")]
#[tokio::main]
async fn main() {
use axum::{routing::post, Router};
use axum::Router;
use leptos::{logging::log, *};
use leptos_axum::{generate_route_list, LeptosRoutes};
use ssr_modes_axum::{app::*, fallback::file_and_error_handler};

View file

@ -11,7 +11,6 @@ actix-files = { version = "0.6", optional = true }
actix-web = { version = "4", optional = true, features = ["macros"] }
console_error_panic_hook = "0.1"
console_log = "1"
cfg-if = "1"
leptos = { path = "../../leptos", features = ["serde"] }
leptos_actix = { path = "../../integrations/actix", optional = true }
leptos_router = { path = "../../router" }

View file

@ -1,23 +1,12 @@
pub mod app;
use cfg_if::cfg_if;
cfg_if! {
if #[cfg(feature = "hydrate")] {
#[wasm_bindgen::prelude::wasm_bindgen]
pub fn hydrate() {
use app::*;
use wasm_bindgen::prelude::wasm_bindgen;
// initializes logging using the `log` crate
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
#[wasm_bindgen]
pub fn hydrate() {
use app::*;
use leptos::*;
// initializes logging using the `log` crate
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
leptos::mount_to_body(|| {
view! { <App/> }
});
}
}
leptos::mount_to_body(App);
}

View file

@ -17,11 +17,7 @@ async fn main() -> std::io::Result<()> {
let site_root = &leptos_options.site_root;
App::new()
.leptos_routes(
leptos_options.to_owned(),
routes.to_owned(),
App,
)
.leptos_routes(leptos_options.to_owned(), routes.to_owned(), App)
.service(Files::new("/", site_root))
//.wrap(middleware::Compress::default())
})

View file

@ -16,7 +16,6 @@ leptos_meta = { path = "../../meta", features = ["nightly"] }
leptos_router = { path = "../../router", features = ["nightly"] }
gloo-net = { version = "0.2", features = ["http"] }
log = "0.4"
cfg-if = "1.0"
# dependecies for client (enable when csr or hydrate set)
wasm-bindgen = { version = "0.2", optional = true }

View file

@ -1,39 +1,27 @@
mod app;
use cfg_if::cfg_if;
cfg_if! {
if #[cfg(feature = "hydrate")] {
use wasm_bindgen::prelude::wasm_bindgen;
use crate::app::*;
use leptos::*;
#[cfg(feature = "hydrate")]
#[wasm_bindgen::prelude::wasm_bindgen]
pub fn hydrate() {
use crate::app::App;
use leptos::{logging, mount_to_body};
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
#[wasm_bindgen]
pub fn hydrate() {
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
logging::log!("hydrate mode - hydrating");
logging::log!("hydrate mode - hydrating");
leptos::mount_to_body(|| {
view! { <App/> }
});
}
}
else if #[cfg(feature = "csr")] {
use wasm_bindgen::prelude::wasm_bindgen;
#[wasm_bindgen(start)]
pub fn main() {
use app::*;
use leptos::*;
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
logging::log!("csr mode - mounting to body");
mount_to_body(|| {
view! { <App /> }
});
}
}
mount_to_body(App);
}
#[cfg(feature = "csr")]
#[wasm_bindgen::prelude::wasm_bindgen(start)]
pub fn main() {
use crate::app::App;
use leptos::{logging, mount_to_body};
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
logging::log!("csr mode - mounting to body");
mount_to_body(App);
}

View file

@ -1,40 +1,35 @@
mod app;
use cfg_if::cfg_if;
cfg_if! {
if #[cfg(feature = "ssr")] {
use actix_files::Files;
use actix_web::*;
use leptos::*;
use crate::app::*;
use leptos_actix::{generate_route_list, LeptosRoutes};
use crate::app::*;
use actix_files::Files;
use actix_web::*;
use leptos::*;
use leptos_actix::{generate_route_list, LeptosRoutes};
#[actix_web::main]
async fn main() -> std::io::Result<()> {
#[actix_web::main]
async fn main() -> std::io::Result<()> {
// Setting this to None means we'll be using cargo-leptos and its env vars.
let conf = get_configuration(None).await.unwrap();
// Setting this to None means we'll be using cargo-leptos and its env vars.
let conf = get_configuration(None).await.unwrap();
let addr = conf.leptos_options.site_addr;
let addr = conf.leptos_options.site_addr;
// Generate the list of routes in your Leptos App
let routes = generate_route_list(|| view! { <App/> });
// Generate the list of routes in your Leptos App
let routes = generate_route_list(|| view! { <App/> });
HttpServer::new(move || {
let leptos_options = &conf.leptos_options;
let site_root = &leptos_options.site_root;
let routes = &routes;
App::new()
.leptos_routes(leptos_options.to_owned(), routes.to_owned(), || view! { <App/> })
.service(Files::new("/", site_root))
.wrap(middleware::Compress::default())
})
.bind(&addr)?
.run()
.await
}
}
else {
pub fn main() {}
}
HttpServer::new(move || {
let leptos_options = &conf.leptos_options;
let site_root = &leptos_options.site_root;
let routes = &routes;
App::new()
.leptos_routes(
leptos_options.to_owned(),
routes.to_owned(),
|| view! { <App/> },
)
.service(Files::new("/", site_root))
.wrap(middleware::Compress::default())
})
.bind(&addr)?
.run()
.await
}

View file

@ -10,7 +10,6 @@ crate-type = ["cdylib", "rlib"]
axum = { version = "0.7", optional = true }
console_error_panic_hook = "0.1"
console_log = "1"
cfg-if = "1"
leptos = { path = "../../leptos", features = ["nightly"] }
leptos_meta = { path = "../../meta", features = ["nightly"] }
leptos_axum = { path = "../../integrations/axum", optional = true }

View file

@ -1,43 +1,48 @@
use cfg_if::cfg_if;
use crate::app::App;
use axum::{
body::Body,
extract::State,
http::{Request, Response, StatusCode, Uri},
response::{IntoResponse, Response as AxumResponse},
};
use leptos::{view, LeptosOptions};
use tower::ServiceExt;
use tower_http::services::ServeDir;
cfg_if! { if #[cfg(feature = "ssr")] {
use axum::{
body::Body,
extract::State,
response::IntoResponse,
http::{Request, Response, StatusCode, Uri},
};
use axum::response::Response as AxumResponse;
use tower::ServiceExt;
use tower_http::services::ServeDir;
use leptos::{LeptosOptions, view};
use crate::app::App;
pub async fn file_and_error_handler(
uri: Uri,
State(options): State<LeptosOptions>,
req: Request<Body>,
) -> AxumResponse {
let root = options.site_root.clone();
let res = get_static_file(uri.clone(), &root).await.unwrap();
pub async fn file_and_error_handler(uri: Uri, State(options): State<LeptosOptions>, req: Request<Body>) -> AxumResponse {
let root = options.site_root.clone();
let res = get_static_file(uri.clone(), &root).await.unwrap();
if res.status() == StatusCode::OK {
res.into_response()
} else{
let handler = leptos_axum::render_app_to_stream(
options.to_owned(),
move || view!{ <App/> }
);
handler(req).await.into_response()
}
if res.status() == StatusCode::OK {
res.into_response()
} else {
let handler = leptos_axum::render_app_to_stream(
options.to_owned(),
move || view! { <App/> },
);
handler(req).await.into_response()
}
}
async fn get_static_file(uri: Uri, root: &str) -> Result<Response<Body>, (StatusCode, String)> {
let req = Request::builder().uri(uri.clone()).body(Body::empty()).unwrap();
// `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`
// This path is relative to the cargo root
match ServeDir::new(root).oneshot(req).await {
Ok(res) => Ok(res.into_response()),
Err(err) => Err((
StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong: {err}"),
)),
}
async fn get_static_file(
uri: Uri,
root: &str,
) -> Result<Response<Body>, (StatusCode, String)> {
let req = Request::builder()
.uri(uri.clone())
.body(Body::empty())
.unwrap();
// `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`
// This path is relative to the cargo root
match ServeDir::new(root).oneshot(req).await {
Ok(res) => Ok(res.into_response()),
Err(err) => Err((
StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong: {err}"),
)),
}
}}
}

View file

@ -1,20 +1,16 @@
use cfg_if::cfg_if;
pub mod app;
#[cfg(feature = "ssr")]
pub mod fallback;
cfg_if! { if #[cfg(feature = "hydrate")] {
use leptos::*;
use wasm_bindgen::prelude::wasm_bindgen;
use crate::app::*;
#[cfg(feature = "hydrate")]
#[wasm_bindgen::prelude::wasm_bindgen]
pub fn hydrate() {
use crate::app::App;
#[wasm_bindgen]
pub fn hydrate() {
// initializes logging using the `log` crate
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
// initializes logging using the `log` crate
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
leptos::mount_to_body(move || {
view! { <App/> }
});
}
}}
leptos::mount_to_body(App);
}

View file

@ -1,7 +1,7 @@
#[cfg(feature = "ssr")]
#[tokio::main]
async fn main() {
use axum::{routing::post, Router};
use axum::Router;
use leptos::*;
use leptos_axum::{generate_route_list, LeptosRoutes};
use leptos_tailwind::{app::*, fallback::file_and_error_handler};

View file

@ -15,7 +15,6 @@ console_log = "1.0.0"
console_error_panic_hook = "0.1.7"
serde = { version = "1.0.152", features = ["derive"] }
futures = "0.3.25"
cfg-if = "1.0.0"
leptos = { path = "../../leptos", features = ["nightly"] }
leptos_actix = { path = "../../integrations/actix", optional = true }
leptos_meta = { path = "../../meta", features = ["nightly"] }

View file

@ -1,18 +1,11 @@
use cfg_if::cfg_if;
pub mod todo;
// Needs to be in lib.rs AFAIK because wasm-bindgen needs us to be compiling a lib. I may be wrong.
cfg_if! {
if #[cfg(feature = "hydrate")] {
use wasm_bindgen::prelude::wasm_bindgen;
use crate::todo::*;
#[cfg(feature = "hydrate")]
#[wasm_bindgen::prelude::wasm_bindgen]
pub fn hydrate() {
use crate::todo::*;
console_error_panic_hook::set_once();
_ = console_log::init_with_level(log::Level::Debug);
#[wasm_bindgen]
pub fn hydrate() {
console_error_panic_hook::set_once();
_ = console_log::init_with_level(log::Level::Debug);
leptos::mount_to_body(TodoApp);
}
}
leptos::mount_to_body(TodoApp);
}

View file

@ -1,62 +1,61 @@
use cfg_if::cfg_if;
mod todo;
// boilerplate to run in different modes
cfg_if! {
// server-only stuff
if #[cfg(feature = "ssr")] {
use actix_files::{Files};
use actix_web::*;
use crate::todo::*;
use leptos::*;
use leptos_actix::{generate_route_list, LeptosRoutes};
#[cfg(feature = "ssr")]
mod ssr {
pub use crate::todo::*;
pub use actix_files::Files;
pub use actix_web::*;
pub use leptos::*;
pub use leptos_actix::{generate_route_list, LeptosRoutes};
#[get("/style.css")]
async fn css() -> impl Responder {
actix_files::NamedFile::open_async("./style.css").await
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let mut conn = db().await.expect("couldn't connect to DB");
sqlx::migrate!()
.run(&mut conn)
.await
.expect("could not run SQLx migrations");
// Explicit server function registration is no longer required
// on the main branch. On 0.3.0 and earlier, uncomment the lines
// below to register the server functions.
// _ = GetTodos::register();
// _ = AddTodo::register();
// _ = DeleteTodo::register();
// Setting this to None means we'll be using cargo-leptos and its env vars.
let conf = get_configuration(None).await.unwrap();
let addr = conf.leptos_options.site_addr;
// Generate the list of routes in your Leptos App
let routes = generate_route_list(TodoApp);
HttpServer::new(move || {
let leptos_options = &conf.leptos_options;
let site_root = &leptos_options.site_root;
let routes = &routes;
App::new()
.service(css)
.leptos_routes(leptos_options.to_owned(), routes.to_owned(), TodoApp)
.service(Files::new("/", site_root))
//.wrap(middleware::Compress::default())
})
.bind(addr)?
.run()
.await
}
} else {
fn main() {
// no client-side main function
}
#[get("/style.css")]
pub async fn css() -> impl Responder {
actix_files::NamedFile::open_async("./style.css").await
}
}
#[cfg(feature = "ssr")]
#[actix_web::main]
async fn main() -> std::io::Result<()> {
use self::{ssr::*, todo::ssr::*};
let mut conn = db().await.expect("couldn't connect to DB");
sqlx::migrate!()
.run(&mut conn)
.await
.expect("could not run SQLx migrations");
// Explicit server function registration is no longer required
// on the main branch. On 0.3.0 and earlier, uncomment the lines
// below to register the server functions.
// _ = GetTodos::register();
// _ = AddTodo::register();
// _ = DeleteTodo::register();
// Setting this to None means we'll be using cargo-leptos and its env vars.
let conf = get_configuration(None).await.unwrap();
let addr = conf.leptos_options.site_addr;
// Generate the list of routes in your Leptos App
let routes = generate_route_list(TodoApp);
HttpServer::new(move || {
let leptos_options = &conf.leptos_options;
let site_root = &leptos_options.site_root;
let routes = &routes;
App::new()
.service(css)
.leptos_routes(
leptos_options.to_owned(),
routes.to_owned(),
TodoApp,
)
.service(Files::new("/", site_root))
//.wrap(middleware::Compress::default())
})
.bind(addr)?
.run()
.await
}

View file

@ -1,4 +1,3 @@
use cfg_if::cfg_if;
use leptos::*;
use leptos_meta::*;
use leptos_router::*;
@ -12,13 +11,14 @@ pub struct Todo {
completed: bool,
}
cfg_if! {
if #[cfg(feature = "ssr")] {
use sqlx::{Connection, SqliteConnection};
#[cfg(feature = "ssr")]
pub mod ssr {
pub use actix_web::HttpRequest;
pub use leptos::ServerFnError;
pub use sqlx::{Connection, SqliteConnection};
pub async fn db() -> Result<SqliteConnection, ServerFnError> {
Ok(SqliteConnection::connect("sqlite:Todos.db").await?)
}
pub async fn db() -> Result<SqliteConnection, ServerFnError> {
Ok(SqliteConnection::connect("sqlite:Todos.db").await?)
}
}
@ -26,8 +26,10 @@ cfg_if! {
/// to the server and the server response will be encoded with CBOR. Good for binary data that doesn't encode well via the default methods
#[server(encoding = "Cbor")]
pub async fn get_todos() -> Result<Vec<Todo>, ServerFnError> {
use self::ssr::*;
// this is just an example of how to access server context injected in the handlers
let req = use_context::<actix_web::HttpRequest>();
let req = use_context::<HttpRequest>();
if let Some(req) = req {
println!("req.path = {:#?}", req.path());
@ -48,6 +50,8 @@ pub async fn get_todos() -> Result<Vec<Todo>, ServerFnError> {
#[server]
pub async fn add_todo(title: String) -> Result<(), ServerFnError> {
use self::ssr::*;
let mut conn = db().await?;
// fake API delay
@ -66,6 +70,8 @@ pub async fn add_todo(title: String) -> Result<(), ServerFnError> {
// The struct name and path prefix arguments are optional.
#[server]
pub async fn delete_todo(id: u16) -> Result<(), ServerFnError> {
use self::ssr::*;
let mut conn = db().await?;
Ok(sqlx::query("DELETE FROM todos WHERE id = $1")
@ -78,8 +84,8 @@ pub async fn delete_todo(id: u16) -> Result<(), ServerFnError> {
#[component]
pub fn TodoApp() -> impl IntoView {
provide_meta_context();
view! {
view! {
<Link rel="shortcut icon" type_="image/ico" href="/favicon.ico"/>
<Stylesheet id="leptos" href="/pkg/todo_app_sqlite.css"/>
<Router>

View file

@ -10,7 +10,6 @@ crate-type = ["cdylib", "rlib"]
console_log = "1.0"
console_error_panic_hook = "0.1"
futures = "0.3"
cfg-if = "1.0"
http = "1.0"
leptos = { path = "../../leptos", features = ["nightly"] }
server_fn = { path = "../../server_fn", features = ["serde-lite"] }

View file

@ -1,5 +1,4 @@
use crate::errors::TodoAppError;
use cfg_if::cfg_if;
use leptos::{Errors, *};
#[cfg(feature = "ssr")]
use leptos_axum::ResponseOptions;
@ -29,13 +28,12 @@ pub fn ErrorTemplate(
// Only the response code for the first error is actually sent from the server
// this may be customized by the specific application
cfg_if! {
if #[cfg(feature="ssr")]{
#[cfg(feature = "ssr")]
{
let response = use_context::<ResponseOptions>();
if let Some(response) = response{
response.set_status(errors[0].status_code());
if let Some(response) = response {
response.set_status(errors[0].status_code());
}
}
}
view! {
@ -50,7 +48,6 @@ pub fn ErrorTemplate(
let error_string = error.1.to_string();
let error_code= error.1.status_code();
view! {
<h2>{error_code.to_string()}</h2>
<p>"Error: " {error_string}</p>
}

View file

@ -1,47 +1,50 @@
use cfg_if::cfg_if;
use crate::{error_template::ErrorTemplate, errors::TodoAppError};
use axum::{
body::Body,
extract::State,
http::{Request, Response, StatusCode, Uri},
response::{IntoResponse, Response as AxumResponse},
};
use leptos::{view, Errors, LeptosOptions};
use tower::ServiceExt;
use tower_http::services::ServeDir;
cfg_if! {
if #[cfg(feature = "ssr")] {
use axum::{
body::Body,
extract::State,
response::IntoResponse,
http::{Request, Response, StatusCode, Uri},
};
use axum::response::Response as AxumResponse;
use tower::ServiceExt;
use tower_http::services::ServeDir;
use leptos::{LeptosOptions, Errors, view};
use crate::error_template::ErrorTemplate;
use crate::errors::TodoAppError;
pub async fn file_and_error_handler(
uri: Uri,
State(options): State<LeptosOptions>,
req: Request<Body>,
) -> AxumResponse {
let root = options.site_root.clone();
let res = get_static_file(uri.clone(), &root).await.unwrap();
pub async fn file_and_error_handler(uri: Uri, State(options): State<LeptosOptions>, req: Request<Body>) -> AxumResponse {
let root = options.site_root.clone();
let res = get_static_file(uri.clone(), &root).await.unwrap();
if res.status() == StatusCode::OK {
res.into_response()
} else{
let mut errors = Errors::default();
errors.insert_with_default_key(TodoAppError::NotFound);
let handler = leptos_axum::render_app_to_stream(options.to_owned(), move || view!{<ErrorTemplate outside_errors=errors.clone()/>});
handler(req).await.into_response()
}
if res.status() == StatusCode::OK {
res.into_response()
} else {
let mut errors = Errors::default();
errors.insert_with_default_key(TodoAppError::NotFound);
let handler = leptos_axum::render_app_to_stream(
options.to_owned(),
move || view! {<ErrorTemplate outside_errors=errors.clone()/>},
);
handler(req).await.into_response()
}
}
async fn get_static_file(uri: Uri, root: &str) -> Result<Response<Body>, (StatusCode, String)> {
let req = Request::builder().uri(uri.clone()).body(Body::empty()).unwrap();
// `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`
// This path is relative to the cargo root
match ServeDir::new(root).oneshot(req).await {
Ok(res) => Ok(res.into_response()),
Err(err) => Err((
StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong: {err}"),
)),
}
async fn get_static_file(
uri: Uri,
root: &str,
) -> Result<Response<Body>, (StatusCode, String)> {
let req = Request::builder()
.uri(uri.clone())
.body(Body::empty())
.unwrap();
// `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`
// This path is relative to the cargo root
match ServeDir::new(root).oneshot(req).await {
Ok(res) => Ok(res.into_response()),
Err(err) => Err((
StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong: {err}"),
)),
}
}
}

View file

@ -1,24 +1,15 @@
use cfg_if::cfg_if;
pub mod error_template;
pub mod errors;
#[cfg(feature = "ssr")]
pub mod fallback;
pub mod todo;
// Needs to be in lib.rs AFAIK because wasm-bindgen needs us to be compiling a lib. I may be wrong.
cfg_if! {
if #[cfg(feature = "hydrate")] {
use leptos::*;
use wasm_bindgen::prelude::wasm_bindgen;
use crate::todo::*;
#[wasm_bindgen::prelude::wasm_bindgen]
pub fn hydrate() {
use crate::todo::TodoApp;
#[wasm_bindgen]
pub fn hydrate() {
_ = console_log::init_with_level(log::Level::Error);
console_error_panic_hook::set_once();
_ = console_log::init_with_level(log::Level::Error);
console_error_panic_hook::set_once();
leptos::mount_to_body(|| {
view! { <TodoApp/> }
});
}
}
leptos::mount_to_body(TodoApp);
}

View file

@ -1,77 +1,62 @@
use cfg_if::cfg_if;
// boilerplate to run in different modes
cfg_if! {
if #[cfg(feature = "ssr")] {
use leptos::*;
use axum::{
routing::get,
extract::{State, Path},
http::Request,
response::{IntoResponse, Response},
Router,
};
use axum::body::Body;
use crate::todo::*;
use todo_app_sqlite_axum::*;
use crate::fallback::file_and_error_handler;
use leptos_axum::{generate_route_list, LeptosRoutes};
use crate::{fallback::file_and_error_handler, todo::*};
use axum::{
body::Body,
extract::{Path, State},
http::Request,
response::{IntoResponse, Response},
routing::get,
Router,
};
use leptos::*;
use leptos_axum::{generate_route_list, LeptosRoutes};
use todo_app_sqlite_axum::*;
//Define a handler to test extractor with state
async fn custom_handler(Path(id): Path<String>, State(options): State<LeptosOptions>, req: Request<Body>) -> Response{
let handler = leptos_axum::render_app_to_stream_with_context(options,
move || {
provide_context(id.clone());
},
|| view! { <TodoApp/> }
);
handler(req).await.into_response()
//Define a handler to test extractor with state
async fn custom_handler(
Path(id): Path<String>,
State(options): State<LeptosOptions>,
req: Request<Body>,
) -> Response {
let handler = leptos_axum::render_app_to_stream_with_context(
options,
move || {
provide_context(id.clone());
},
TodoApp,
);
handler(req).await.into_response()
}
#[tokio::main]
async fn main() {
use crate::todo::ssr::db;
simple_logger::init_with_level(log::Level::Error)
.expect("couldn't initialize logging");
let mut conn = db().await.expect("couldn't connect to DB");
if let Err(e) = sqlx::migrate!().run(&mut conn).await {
eprintln!("{e:?}");
}
#[tokio::main]
async fn main() {
simple_logger::init_with_level(log::Level::Error).expect("couldn't initialize logging");
// Setting this to None means we'll be using cargo-leptos and its env vars
let conf = get_configuration(None).await.unwrap();
let leptos_options = conf.leptos_options;
let addr = leptos_options.site_addr;
let routes = generate_route_list(TodoApp);
let _conn = db().await.expect("couldn't connect to DB");
/* sqlx::migrate!()
.run(&mut conn)
.await
.expect("could not run SQLx migrations"); */
// Explicit server function registration is no longer required
// on the main branch. On 0.3.0 and earlier, uncomment the lines
// below to register the server functions.
// _ = GetTodos::register();
// _ = AddTodo::register();
// _ = DeleteTodo::register();
// Setting this to None means we'll be using cargo-leptos and its env vars
let conf = get_configuration(None).await.unwrap();
let leptos_options = conf.leptos_options;
let addr = leptos_options.site_addr;
let routes = generate_route_list(TodoApp);
// build our application with a route
let app = Router::new()
// build our application with a route
let app = Router::new()
.route("/special/:id", get(custom_handler))
.leptos_routes(&leptos_options, routes, || view! { <TodoApp/> } )
.leptos_routes(&leptos_options, routes, || view! { <TodoApp/> })
.fallback(file_and_error_handler)
.with_state(leptos_options);
// run our app with hyper
// `axum::Server` is a re-export of `hyper::Server`
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
logging::log!("listening on http://{}", &addr);
axum::serve(listener, app.into_make_service())
.await
.unwrap();
}
}
// client-only stuff for Trunk
else {
pub fn main() {
// This example cannot be built as a trunk standalone CSR-only app.
// Only the server may directly connect to the database.
}
}
// run our app with hyper
// `axum::Server` is a re-export of `hyper::Server`
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
logging::log!("listening on http://{}", &addr);
axum::serve(listener, app.into_make_service())
.await
.unwrap();
}

View file

@ -1,5 +1,4 @@
use crate::error_template::ErrorTemplate;
use cfg_if::cfg_if;
use leptos::*;
use leptos_meta::*;
use leptos_router::*;
@ -14,19 +13,20 @@ pub struct Todo {
completed: bool,
}
cfg_if! {
if #[cfg(feature = "ssr")] {
use sqlx::{Connection, SqliteConnection};
// use http::{header::SET_COOKIE, HeaderMap, HeaderValue, StatusCode};
#[cfg(feature = "ssr")]
pub mod ssr {
// use http::{header::SET_COOKIE, HeaderMap, HeaderValue, StatusCode};
use leptos::ServerFnError;
use sqlx::{Connection, SqliteConnection};
pub async fn db() -> Result<SqliteConnection, ServerFnError> {
Ok(SqliteConnection::connect("sqlite:Todos.db").await?)
}
pub async fn db() -> Result<SqliteConnection, ServerFnError> {
Ok(SqliteConnection::connect("sqlite:Todos.db").await?)
}
}
#[server(GetTodos, "/api")]
#[server]
pub async fn get_todos() -> Result<Vec<Todo>, ServerFnError> {
use self::ssr::*;
use http::request::Parts;
// this is just an example of how to access server context injected in the handlers
@ -55,8 +55,9 @@ pub async fn get_todos() -> Result<Vec<Todo>, ServerFnError> {
Ok(todos)
}
#[server(AddTodo, "/api")]
#[server]
pub async fn add_todo(title: String) -> Result<(), ServerFnError> {
use self::ssr::*;
let mut conn = db().await?;
// fake API delay
@ -72,9 +73,9 @@ pub async fn add_todo(title: String) -> Result<(), ServerFnError> {
}
}
// The struct name and path prefix arguments are optional.
#[server(output = SerdeLite)]
pub async fn delete_todo(id: u16) -> Result<(), ServerFnError> {
use self::ssr::*;
let mut conn = db().await?;
Ok(sqlx::query("DELETE FROM todos WHERE id = $1")