Compare commits
No commits in common. "4125708354e6e0937d62a87f11b753d622c041d8" and "c3448371b5f6b331d35ecfffd2582114d3d95e6c" have entirely different histories.
4125708354
...
c3448371b5
2
doordesk/.gitignore → doordesk-rs/.gitignore
vendored
|
@ -2,6 +2,7 @@
|
|||
# will have compiled files and executables
|
||||
/target/
|
||||
pkg
|
||||
Cargo.lock
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
|
@ -11,3 +12,4 @@ node_modules/
|
|||
test-results/
|
||||
end2end/playwright-report/
|
||||
playwright/.cache/
|
||||
.sass-cache
|
|
@ -1,45 +1,51 @@
|
|||
[workspace]
|
||||
resolver = "2"
|
||||
members = ["app", "frontend", "server"]
|
||||
|
||||
# need to be applied only to wasm build
|
||||
[profile.release]
|
||||
codegen-units = 1
|
||||
lto = true
|
||||
opt-level = 'z'
|
||||
|
||||
[workspace.dependencies]
|
||||
leptos = { version = "0.5", features = ["nightly"] }
|
||||
leptos_meta = { version = "0.5", features = ["nightly"] }
|
||||
leptos_router = { version = "0.5", features = ["nightly"] }
|
||||
leptos_axum = { version = "0.5" }
|
||||
|
||||
axum = "0.6.20"
|
||||
cfg-if = "1"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
console_log = "1"
|
||||
http = "0.2.9"
|
||||
log = "0.4.20"
|
||||
simple_logger = "4.2.0"
|
||||
thiserror = "1"
|
||||
tokio = { version = "1.33.0", features = ["full"] }
|
||||
tower = { version = "0.4.13", features = ["full"] }
|
||||
tower-http = { version = "0.4", features = ["full"] }
|
||||
wasm-bindgen = "=0.2.89"
|
||||
|
||||
# See https://github.com/akesson/cargo-leptos for documentation of all the parameters.
|
||||
|
||||
# A leptos project defines which workspace members
|
||||
# that are used together frontend (lib) & server (bin)
|
||||
[[workspace.metadata.leptos]]
|
||||
# this name is used for the wasm, js and css file names
|
||||
[package]
|
||||
name = "doordesk"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# the package in the workspace that contains the server binary (binary crate)
|
||||
bin-package = "server"
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
# the package in the workspace that contains the frontend wasm binary (library crate)
|
||||
lib-package = "frontend"
|
||||
[dependencies]
|
||||
axum = { version = "0.6.4", optional = true }
|
||||
console_error_panic_hook = "0.1"
|
||||
console_log = "1"
|
||||
cfg-if = "1"
|
||||
leptos = { version = "0.4", features = ["nightly"] }
|
||||
leptos_axum = { version = "0.4", optional = true }
|
||||
leptos_meta = { version = "0.4", features = ["nightly"] }
|
||||
leptos_router = { version = "0.4", features = ["nightly"] }
|
||||
log = "0.4"
|
||||
simple_logger = "4"
|
||||
tokio = { version = "1.25.0", optional = true }
|
||||
tower = { version = "0.4.13", optional = true }
|
||||
tower-http = { version = "0.4", features = ["fs"], optional = true }
|
||||
wasm-bindgen = "=0.2.87"
|
||||
thiserror = "1.0.38"
|
||||
tracing = { version = "0.1.37", optional = true }
|
||||
http = "0.2.8"
|
||||
|
||||
[features]
|
||||
hydrate = ["leptos/hydrate", "leptos_meta/hydrate", "leptos_router/hydrate"]
|
||||
ssr = [
|
||||
"dep:axum",
|
||||
"dep:tokio",
|
||||
"dep:tower",
|
||||
"dep:tower-http",
|
||||
"dep:leptos_axum",
|
||||
"leptos/ssr",
|
||||
"leptos_meta/ssr",
|
||||
"leptos_router/ssr",
|
||||
"dep:tracing",
|
||||
]
|
||||
|
||||
[package.metadata.cargo-all-features]
|
||||
denylist = ["axum", "tokio", "tower", "tower-http", "leptos_axum"]
|
||||
skip_feature_sets = [["ssr", "hydrate"]]
|
||||
|
||||
[package.metadata.leptos]
|
||||
# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name
|
||||
output-name = "doordesk"
|
||||
|
||||
# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.
|
||||
site-root = "target/site"
|
||||
|
@ -49,7 +55,7 @@ site-root = "target/site"
|
|||
site-pkg-dir = "pkg"
|
||||
|
||||
# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to <site-root>/<site-pkg>/app.css
|
||||
# style-file = "style/main.scss"
|
||||
style-file = "style/main.scss"
|
||||
|
||||
# The tailwind input file.
|
||||
#
|
||||
|
@ -92,7 +98,7 @@ env = "DEV"
|
|||
# The features to use when compiling the bin target
|
||||
#
|
||||
# Optional. Can be over-ridden with the command line parameter --bin-features
|
||||
bin-features = []
|
||||
bin-features = ["ssr"]
|
||||
|
||||
# If the --no-default-features flag should be used when compiling the bin target
|
||||
#
|
||||
|
@ -102,7 +108,7 @@ bin-default-features = false
|
|||
# The features to use when compiling the lib target
|
||||
#
|
||||
# Optional. Can be over-ridden with the command line parameter --lib-features
|
||||
lib-features = []
|
||||
lib-features = ["hydrate"]
|
||||
|
||||
# If the --no-default-features flag should be used when compiling the lib target
|
||||
#
|
58
doordesk-rs/README.md
Normal file
|
@ -0,0 +1,58 @@
|
|||
# doordesk-rs
|
||||
|
||||
## Dev Server
|
||||
|
||||
```bash
|
||||
cargo install cargo-leptos
|
||||
```
|
||||
|
||||
```bash
|
||||
cargo leptos watch
|
||||
```
|
||||
|
||||
By default, `cargo-leptos` uses `nightly` Rust, `cargo-generate`, and `sass`. If you run into any trouble, you may need to install one or more of these tools.
|
||||
|
||||
1. `rustup toolchain install nightly --allow-downgrade` - make sure you have Rust nightly
|
||||
2. `rustup target add wasm32-unknown-unknown` - add the ability to compile Rust to WebAssembly
|
||||
3. `cargo install cargo-generate` - install `cargo-generate` binary (should be installed automatically in future)
|
||||
4. `npm install -g sass` - install `dart-sass` (should be optional in future
|
||||
|
||||
## Compiling for Release
|
||||
```bash
|
||||
cargo leptos build --release
|
||||
```
|
||||
|
||||
Will generate your server binary in target/server/release and your site package in target/site
|
||||
|
||||
## Testing Your Project
|
||||
```bash
|
||||
cargo leptos end-to-end
|
||||
```
|
||||
|
||||
```bash
|
||||
cargo leptos end-to-end --release
|
||||
```
|
||||
|
||||
Cargo-leptos uses Playwright as the end-to-end test tool.
|
||||
Tests are located in end2end/tests directory.
|
||||
|
||||
## Executing a Server on a Remote Machine Without the Toolchain
|
||||
After running a `cargo leptos build --release` the minimum files needed are:
|
||||
|
||||
1. The server binary located in `target/server/release`
|
||||
2. The `site` directory and all files within located in `target/site`
|
||||
|
||||
Copy these files to your remote server. The directory structure should be:
|
||||
```text
|
||||
start-axum
|
||||
site/
|
||||
```
|
||||
Set the following environment variables (updating for your project as needed):
|
||||
```text
|
||||
LEPTOS_OUTPUT_NAME="start-axum"
|
||||
LEPTOS_SITE_ROOT="site"
|
||||
LEPTOS_SITE_PKG_DIR="pkg"
|
||||
LEPTOS_SITE_ADDR="127.0.0.1:3000"
|
||||
LEPTOS_RELOAD_PORT="3001"
|
||||
```
|
||||
Finally, run the server binary.
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 685 B After Width: | Height: | Size: 685 B |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
3
doordesk-rs/rust-toolchain.toml
Normal file
|
@ -0,0 +1,3 @@
|
|||
|
||||
[toolchain]
|
||||
channel = "nightly"
|
95
doordesk-rs/src/app.rs
Normal file
|
@ -0,0 +1,95 @@
|
|||
use crate::error_template::{AppError, ErrorTemplate};
|
||||
use leptos::*;
|
||||
use leptos_meta::*;
|
||||
use leptos_router::*;
|
||||
|
||||
#[component]
|
||||
pub fn App(cx: Scope) -> impl IntoView {
|
||||
// Provides context that manages stylesheets, titles, meta tags, etc.
|
||||
provide_meta_context(cx);
|
||||
|
||||
view! {
|
||||
cx,
|
||||
|
||||
// injects a stylesheet into the document <head>
|
||||
// id=leptos means cargo-leptos will hot-reload this stylesheet
|
||||
<Stylesheet id="leptos" href="/pkg/doordesk.css"/>
|
||||
|
||||
// sets the document title
|
||||
<Title text="DoorDesk"/>
|
||||
|
||||
// content for this welcome page
|
||||
<Router fallback=|cx| {
|
||||
let mut outside_errors = Errors::default();
|
||||
outside_errors.insert_with_default_key(AppError::NotFound);
|
||||
view! { cx,
|
||||
<ErrorTemplate outside_errors/>
|
||||
}
|
||||
.into_view(cx)
|
||||
}>
|
||||
<nav class="bg-gradient-to-b from-zinc-800 to-zinc-900 shadow-lg sticky top-0">
|
||||
<ul class="container flex items-center p-3">
|
||||
<li class="mx-1.5 sm:mx-6">
|
||||
"DoorDesk"
|
||||
</li>
|
||||
<li class="mx-1.5 sm:mx-6">
|
||||
<A href="" exact=true>"Home"</A>
|
||||
</li>
|
||||
<li class="mx-1.5 sm:mx-6">
|
||||
<A href="/blog">"Blog"</A>
|
||||
</li>
|
||||
<li class="mx-1.5 sm:mx-6">
|
||||
<A href="/projects">"Projects"</A>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<main>
|
||||
<Routes>
|
||||
<Route path="/" view=|cx| view! { cx, <Home /> }/>
|
||||
<Route path="/blog" view=|cx| view! { cx, <Blog /> }/>
|
||||
<Route path="/projects" view=|cx| view! { cx, <Projects /> }/>
|
||||
</Routes>
|
||||
</main>
|
||||
<p class="text-center hover:rotate-180 duration-200 w-8 m-auto"><a href="https://open.spotify.com/playlist/3JRNw9gpt1w5ptsw8uDeYc?si=8f7e4191113f41f9">":)"</a></p><br />
|
||||
</Router>
|
||||
}
|
||||
}
|
||||
|
||||
/// Renders the home page of your application.
|
||||
#[component]
|
||||
fn Article(cx: Scope) -> impl IntoView {
|
||||
let (count, set_count) = create_signal(cx, 0);
|
||||
let on_click = move |_| set_count.update(|count| *count += 1);
|
||||
|
||||
view! { cx,
|
||||
<article class="bg-zinc-700 mx-auto p-7 my-5 w-11/12 max-w-screen-xl rounded-md shadow-1g bg-opacity-10">
|
||||
<h1 class="max-6-xs text-3xl text-orange-600 font-light capitalize">"ayo"</h1>
|
||||
<hr class="opacity-50" />
|
||||
<span class="opacity-50 text-xs pt-0 m-t pb-3.5">"today"</span>
|
||||
<div>
|
||||
<button on:click=on_click>"Click Me: " {count}</button>
|
||||
</div>
|
||||
</article>
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn Home(cx: Scope) -> impl IntoView {
|
||||
view! { cx,
|
||||
<Article />
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn Blog(cx: Scope) -> impl IntoView {
|
||||
view! { cx,
|
||||
<Article />
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn Projects(cx: Scope) -> impl IntoView {
|
||||
view! { cx,
|
||||
<Article />
|
||||
}
|
||||
}
|
|
@ -24,18 +24,19 @@ impl AppError {
|
|||
// Feel free to do more complicated things here than just displaying the error.
|
||||
#[component]
|
||||
pub fn ErrorTemplate(
|
||||
cx: Scope,
|
||||
#[prop(optional)] outside_errors: Option<Errors>,
|
||||
#[prop(optional)] errors: Option<RwSignal<Errors>>,
|
||||
) -> impl IntoView {
|
||||
let errors = match outside_errors {
|
||||
Some(e) => create_rw_signal(e),
|
||||
Some(e) => create_rw_signal(cx, e),
|
||||
None => match errors {
|
||||
Some(e) => e,
|
||||
None => panic!("No Errors found and we expected errors!"),
|
||||
},
|
||||
};
|
||||
// Get Errors from Signal
|
||||
let errors = errors.get_untracked();
|
||||
let errors = errors.get();
|
||||
|
||||
// Downcast lets us take a type that implements `std::error::Error`
|
||||
let errors: Vec<AppError> = errors
|
||||
|
@ -47,26 +48,32 @@ 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")] {
|
||||
let response = use_context::<ResponseOptions>();
|
||||
let response = use_context::<ResponseOptions>(cx);
|
||||
if let Some(response) = response {
|
||||
response.set_status(errors[0].status_code());
|
||||
}
|
||||
}}
|
||||
|
||||
view! {
|
||||
<h1>{if errors.len() > 1 { "Errors" } else { "Error" }}</h1>
|
||||
view! {cx,
|
||||
<h1>{if errors.len() > 1 {"Errors"} else {"Error"}}</h1>
|
||||
<For
|
||||
// a function that returns the items we're iterating over; a signal is fine
|
||||
each=move || { errors.clone().into_iter().enumerate() }
|
||||
each= move || {errors.clone().into_iter().enumerate()}
|
||||
// a unique key for each item as a reference
|
||||
key=|(index, _error)| *index
|
||||
// renders each item to a view
|
||||
children=move |error| {
|
||||
view= move |cx, error| {
|
||||
let error_string = error.1.to_string();
|
||||
let error_code = error.1.status_code();
|
||||
let error_code= error.1.status_code();
|
||||
view! {
|
||||
<h2>{error_code.to_string()}</h2>
|
||||
<p>"Error: " {error_string}</p>
|
||||
cx,
|
||||
<article class="bg-zinc-700 mx-auto p-7 my-5 w-11/12 max-w-screen-xl rounded-md shadow-1g bg-opacity-10">
|
||||
<h1 class="max-6-xs text-3xl text-orange-600 font-light capitalize">
|
||||
{error_code.to_string()}
|
||||
</h1>
|
||||
<hr class="opacity-50" />
|
||||
<p>"Error: " {error_string}</p>
|
||||
</article>
|
||||
}
|
||||
}
|
||||
/>
|
40
doordesk-rs/src/fileserv.rs
Normal file
|
@ -0,0 +1,40 @@
|
|||
use cfg_if::cfg_if;
|
||||
|
||||
cfg_if! { if #[cfg(feature = "ssr")] {
|
||||
use axum::{
|
||||
body::{boxed, Body, BoxBody},
|
||||
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::*;
|
||||
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();
|
||||
|
||||
if res.status() == StatusCode::OK {
|
||||
res.into_response()
|
||||
} else {
|
||||
let handler = leptos_axum::render_app_to_stream(options.to_owned(), move |cx| view!{cx, <App/>});
|
||||
handler(req).await.into_response()
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_static_file(uri: Uri, root: &str) -> Result<Response<BoxBody>, (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.map(boxed)),
|
||||
Err(err) => Err((
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Something went wrong: {err}"),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}}
|
21
doordesk-rs/src/lib.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
use cfg_if::cfg_if;
|
||||
pub mod app;
|
||||
pub mod error_template;
|
||||
pub mod fileserv;
|
||||
|
||||
cfg_if! { if #[cfg(feature = "hydrate")] {
|
||||
use leptos::*;
|
||||
use wasm_bindgen::prelude::wasm_bindgen;
|
||||
use crate::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();
|
||||
|
||||
leptos::mount_to_body(move |cx| {
|
||||
view! { cx, <App/> }
|
||||
});
|
||||
}
|
||||
}}
|
|
@ -1,13 +1,12 @@
|
|||
use app::*;
|
||||
use axum::{routing::post, Router};
|
||||
use fileserv::file_and_error_handler;
|
||||
use leptos::*;
|
||||
use leptos_axum::{generate_route_list, LeptosRoutes};
|
||||
|
||||
pub mod fileserv;
|
||||
|
||||
#[cfg(feature = "ssr")]
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
use axum::{routing::post, Router};
|
||||
use leptos::*;
|
||||
use leptos_axum::{generate_route_list, LeptosRoutes};
|
||||
use doordesk::app::*;
|
||||
use doordesk::fileserv::file_and_error_handler;
|
||||
|
||||
simple_logger::init_with_level(log::Level::Debug).expect("couldn't initialize logging");
|
||||
|
||||
// Setting get_configuration(None) means we'll be using cargo-leptos's env values
|
||||
|
@ -18,20 +17,27 @@ async fn main() {
|
|||
let conf = get_configuration(None).await.unwrap();
|
||||
let leptos_options = conf.leptos_options;
|
||||
let addr = leptos_options.site_addr;
|
||||
let routes = generate_route_list(App);
|
||||
let routes = generate_route_list(|cx| view! { cx, <App/> }).await;
|
||||
|
||||
// build our application with a route
|
||||
let app = Router::new()
|
||||
.route("/api/*fn_name", post(leptos_axum::handle_server_fns))
|
||||
.leptos_routes(&leptos_options, routes, App)
|
||||
.leptos_routes(&leptos_options, routes, |cx| view! { cx, <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::info!("listening on http://{}", &addr);
|
||||
log!("listening on http://{}", &addr);
|
||||
axum::Server::bind(&addr)
|
||||
.serve(app.into_make_service())
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "ssr"))]
|
||||
pub fn main() {
|
||||
// no client-side main function
|
||||
// unless we want this to work with e.g., Trunk for a purely client-side app
|
||||
// see lib.rs for hydration function instead
|
||||
}
|
8
doordesk-rs/src/routes/home.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
use leptos::{component, view, IntoView, Scope};
|
||||
|
||||
#[component]
|
||||
pub fn Home(cx: Scope) -> impl IntoView {
|
||||
view! { cx,
|
||||
<Article />
|
||||
}
|
||||
}
|
4
doordesk-rs/style/main.scss
Normal file
|
@ -0,0 +1,4 @@
|
|||
body {
|
||||
font-family: sans-serif;
|
||||
text-align: center;
|
||||
}
|
|
@ -2,11 +2,11 @@
|
|||
module.exports = {
|
||||
content: {
|
||||
relative: true,
|
||||
files: ["*.html", "./app/**/*.rs"],
|
||||
files: ["*.html", "./src/**/*.rs"],
|
||||
},
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
|
||||
|
2754
doordesk/Cargo.lock
generated
|
@ -1,87 +0,0 @@
|
|||
<picture>
|
||||
<source srcset="https://raw.githubusercontent.com/leptos-rs/leptos/main/docs/logos/Leptos_logo_Solid_White.svg" media="(prefers-color-scheme: dark)">
|
||||
<img src="https://raw.githubusercontent.com/leptos-rs/leptos/main/docs/logos/Leptos_logo_RGB.svg" alt="Leptos Logo">
|
||||
</picture>
|
||||
|
||||
# Leptos Axum Starter Template
|
||||
|
||||
This is a template for use with the [Leptos](https://github.com/leptos-rs/leptos) web framework and the [cargo-leptos](https://github.com/akesson/cargo-leptos) tool using [Axum](https://github.com/tokio-rs/axum).
|
||||
|
||||
## Creating your template repo
|
||||
|
||||
If you don't have `cargo-leptos` installed you can install it with
|
||||
|
||||
```bash
|
||||
cargo install cargo-leptos
|
||||
```
|
||||
|
||||
Then run
|
||||
```bash
|
||||
cargo leptos new --git https://github.com/leptos-rs/start-axum-workspace/
|
||||
```
|
||||
|
||||
to generate a new project template.
|
||||
|
||||
```bash
|
||||
cd {projectname}
|
||||
```
|
||||
|
||||
to go to your newly created project.
|
||||
Feel free to explore the project structure, but the best place to start with your application code is in `src/app.rs`.
|
||||
Addtionally, Cargo.toml may need updating as new versions of the dependencies are released, especially if things are not working after a `cargo update`.
|
||||
|
||||
## Running your project
|
||||
|
||||
```bash
|
||||
cargo leptos watch
|
||||
```
|
||||
|
||||
## Installing Additional Tools
|
||||
|
||||
By default, `cargo-leptos` uses `nightly` Rust, `cargo-generate`, and `sass`. If you run into any trouble, you may need to install one or more of these tools.
|
||||
|
||||
1. `rustup toolchain install nightly --allow-downgrade` - make sure you have Rust nightly
|
||||
2. `rustup default nightly` - setup nightly as default, or you can use rust-toolchain file later on
|
||||
3. `rustup target add wasm32-unknown-unknown` - add the ability to compile Rust to WebAssembly
|
||||
4. `cargo install cargo-generate` - install `cargo-generate` binary (should be installed automatically in future)
|
||||
5. `npm install -g sass` - install `dart-sass` (should be optional in future
|
||||
|
||||
## Compiling for Release
|
||||
```bash
|
||||
cargo leptos build --release
|
||||
```
|
||||
|
||||
Will generate your server binary in target/server/release and your site package in target/site
|
||||
|
||||
## Testing Your Project
|
||||
```bash
|
||||
cargo leptos end-to-end
|
||||
```
|
||||
|
||||
```bash
|
||||
cargo leptos end-to-end --release
|
||||
```
|
||||
|
||||
Cargo-leptos uses Playwright as the end-to-end test tool.
|
||||
Tests are located in end2end/tests directory.
|
||||
|
||||
## Executing a Server on a Remote Machine Without the Toolchain
|
||||
After running a `cargo leptos build --release` the minimum files needed are:
|
||||
|
||||
1. The server binary located in `target/server/release`
|
||||
2. The `site` directory and all files within located in `target/site`
|
||||
|
||||
Copy these files to your remote server. The directory structure should be:
|
||||
```text
|
||||
start-axum
|
||||
site/
|
||||
```
|
||||
Set the following enviornment variables (updating for your project as needed):
|
||||
```text
|
||||
LEPTOS_OUTPUT_NAME="start-axum"
|
||||
LEPTOS_SITE_ROOT="site"
|
||||
LEPTOS_SITE_PKG_DIR="pkg"
|
||||
LEPTOS_SITE_ADDR="127.0.0.1:3000"
|
||||
LEPTOS_RELOAD_PORT="3001"
|
||||
```
|
||||
Finally, run the server binary.
|
|
@ -1,23 +0,0 @@
|
|||
[package]
|
||||
name = "app"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
leptos.workspace = true
|
||||
leptos_meta.workspace = true
|
||||
leptos_router.workspace = true
|
||||
leptos_axum = { workspace = true, optional = true }
|
||||
|
||||
http.workspace = true
|
||||
cfg-if.workspace = true
|
||||
thiserror.workspace = true
|
||||
serde = "1.0.195"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
hydrate = ["leptos/hydrate", "leptos_meta/hydrate", "leptos_router/hydrate"]
|
||||
ssr = ["leptos/ssr", "leptos_meta/ssr", "leptos_router/ssr", "dep:leptos_axum"]
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
pub mod article;
|
||||
pub mod slingshot;
|
|
@ -1,42 +0,0 @@
|
|||
use crate::components::slingshot::*;
|
||||
use leptos::*;
|
||||
|
||||
#[component]
|
||||
pub fn Article() -> impl IntoView {
|
||||
let data = create_resource(
|
||||
|| (),
|
||||
|_| async move {
|
||||
logging::log!("loading data from slingshot");
|
||||
slingshot().await
|
||||
},
|
||||
);
|
||||
|
||||
view! {
|
||||
<Transition>
|
||||
<article class="p-7 my-5 mx-auto w-11/12 max-w-screen-xl bg-opacity-10 rounded-md bg-zinc-700 shadow-1g">
|
||||
<h1 class="text-3xl font-light text-orange-600 capitalize max-6-xs">
|
||||
{move || match data.get() {
|
||||
None => "Loading...".to_string(),
|
||||
Some(data) => data.unwrap().title,
|
||||
}}
|
||||
|
||||
</h1>
|
||||
<hr class="opacity-50"/>
|
||||
<span class="pt-0 pb-3.5 text-xs opacity-50 m-t">
|
||||
{move || match data.get() {
|
||||
None => "Loading...".to_string(),
|
||||
Some(data) => data.unwrap().date,
|
||||
}}
|
||||
|
||||
</span>
|
||||
<div>
|
||||
{move || match data.get() {
|
||||
None => "Loading...".to_string(),
|
||||
Some(data) => data.unwrap().content,
|
||||
}}
|
||||
|
||||
</div>
|
||||
</article>
|
||||
</Transition>
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
use leptos::*;
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ArticleData {
|
||||
pub content_type: String,
|
||||
pub title: String,
|
||||
pub date: String, // make datetime?
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
#[server(Slingshot)]
|
||||
pub async fn slingshot() -> Result<ArticleData, ServerFnError> {
|
||||
let data = ArticleData {
|
||||
content_type: String::from("Blog"),
|
||||
title: String::from("Test article"),
|
||||
date: String::from("12/21/2022"),
|
||||
content: String::from("Testicles"),
|
||||
};
|
||||
|
||||
Ok(data)
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
use crate::error_template::{AppError, ErrorTemplate};
|
||||
|
||||
//use crate::routes::{blog::*, home::*, projects::*};
|
||||
use leptos::*;
|
||||
use leptos_meta::*;
|
||||
use leptos_router::*;
|
||||
|
||||
pub mod error_template;
|
||||
pub mod components;
|
||||
pub mod routes;
|
||||
|
||||
use crate::routes::{home::*, blog::*, projects::*};
|
||||
|
||||
#[component]
|
||||
pub fn App() -> impl IntoView {
|
||||
// Provides context that manages stylesheets, titles, meta tags, etc.
|
||||
provide_meta_context();
|
||||
|
||||
view! {
|
||||
<Stylesheet id="leptos" href="/pkg/doordesk.css"/>
|
||||
|
||||
// sets the document title
|
||||
<Title text="doordesk"/>
|
||||
|
||||
// content for this welcome page
|
||||
<Router fallback=|| {
|
||||
let mut outside_errors = Errors::default();
|
||||
outside_errors.insert_with_default_key(AppError::NotFound);
|
||||
view! { <ErrorTemplate outside_errors/> }.into_view()
|
||||
}>
|
||||
<nav class="bg-gradient-to-b from-zinc-800 to-zinc-900 shadow-lg sticky top-0">
|
||||
<ul class="container flex items-center p-3">
|
||||
<li class="mx-1.5 sm:mx-6">"DoorDesk"</li>
|
||||
<li class="mx-1.5 sm:mx-6">
|
||||
<A href="" exact=true>
|
||||
"Home"
|
||||
</A>
|
||||
</li>
|
||||
<li class="mx-1.5 sm:mx-6">
|
||||
<A href="/blog">"Blog"</A>
|
||||
</li>
|
||||
<li class="mx-1.5 sm:mx-6">
|
||||
<A href="/projects">"Projects"</A>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<main>
|
||||
<Routes>
|
||||
<Route path="" view=Home/>
|
||||
<Route path="blog" view=Blog/>
|
||||
<Route path="projects" view=Projects/>
|
||||
</Routes>
|
||||
</main>
|
||||
<p class="text-center hover:rotate-180 duration-200 w-8 m-auto">
|
||||
<a href="https://open.spotify.com/playlist/3JRNw9gpt1w5ptsw8uDeYc?si=8f7e4191113f41f9">
|
||||
":)"
|
||||
</a>
|
||||
</p>
|
||||
<br/>
|
||||
</Router>
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
pub mod home;
|
||||
pub mod blog;
|
||||
pub mod projects;
|
|
@ -1,7 +0,0 @@
|
|||
use crate::components::article::*;
|
||||
use leptos::*;
|
||||
|
||||
#[component]
|
||||
pub fn Blog() -> impl IntoView {
|
||||
view! { <Article/> }
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
use crate::components::article::*;
|
||||
use leptos::*;
|
||||
|
||||
#[component]
|
||||
pub fn Home() -> impl IntoView {
|
||||
view! { <Article/> }
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
use crate::components::article::*;
|
||||
use leptos::*;
|
||||
|
||||
#[component]
|
||||
pub fn Projects() -> impl IntoView {
|
||||
view! { <Article/> }
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
[package]
|
||||
name = "frontend"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
app = { path = "../app", default-features = false, features = ["hydrate"] }
|
||||
leptos = { workspace = true, features = [ "hydrate" ] }
|
||||
|
||||
console_error_panic_hook.workspace = true
|
||||
console_log.workspace = true
|
||||
log.workspace = true
|
||||
wasm-bindgen.workspace = true
|
|
@ -1,12 +0,0 @@
|
|||
use app::*;
|
||||
use leptos::*;
|
||||
use wasm_bindgen::prelude::wasm_bindgen;
|
||||
|
||||
#[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();
|
||||
|
||||
leptos::mount_to_body(App);
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
[toolchain]
|
||||
channel = "nightly"
|
|
@ -1 +0,0 @@
|
|||
edition = "2021"
|
|
@ -1,18 +0,0 @@
|
|||
[package]
|
||||
name = "server"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
app = { path = "../app", default-features = false, features = ["ssr"] }
|
||||
leptos = { workspace = true, features = [ "ssr" ]}
|
||||
leptos_axum.workspace = true
|
||||
|
||||
axum.workspace = true
|
||||
simple_logger.workspace = true
|
||||
tokio.workspace = true
|
||||
tower.workspace = true
|
||||
tower-http.workspace = true
|
||||
log.workspace = true
|
|
@ -1,46 +0,0 @@
|
|||
use app::error_template::AppError;
|
||||
use app::error_template::ErrorTemplate;
|
||||
use app::App;
|
||||
use axum::response::Response as AxumResponse;
|
||||
use axum::{
|
||||
body::{boxed, Body, BoxBody},
|
||||
extract::State,
|
||||
http::{Request, Response, StatusCode, Uri},
|
||||
response::IntoResponse,
|
||||
};
|
||||
use leptos::*;
|
||||
use tower::ServiceExt;
|
||||
use tower_http::services::ServeDir;
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_static_file(uri: Uri, root: &str) -> Result<Response<BoxBody>, (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.map(boxed)),
|
||||
Err(err) => Err((
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Something went wrong: {err}"),
|
||||
)),
|
||||
}
|
||||
}
|
|
@ -7,19 +7,19 @@
|
|||
},
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^10.4.16",
|
||||
"postcss": "^8.4.32",
|
||||
"solid-start-node": "^0.2.32",
|
||||
"tailwindcss": "^3.3.6",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"postcss": "^8.4.24",
|
||||
"solid-start-node": "^0.2.26",
|
||||
"tailwindcss": "^3.3.2",
|
||||
"typescript": "^4.9.5",
|
||||
"vite": "^4.5.1"
|
||||
"vite": "^4.3.9"
|
||||
},
|
||||
"dependencies": {
|
||||
"@solidjs/meta": "^0.28.7",
|
||||
"@solidjs/router": "^0.8.4",
|
||||
"solid-js": "^1.8.7",
|
||||
"solid-start": "^0.2.32",
|
||||
"undici": "^5.28.2"
|
||||
"@solidjs/meta": "^0.28.5",
|
||||
"@solidjs/router": "^0.8.2",
|
||||
"solid-js": "^1.7.7",
|
||||
"solid-start": "^0.2.26",
|
||||
"undici": "^5.22.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
|
|
1973
frontend/pnpm-lock.yaml
generated
|
@ -16,7 +16,7 @@ import {
|
|||
|
||||
import "./root.css";
|
||||
|
||||
export const DENNIS = "https://dennis.doordesk.net";
|
||||
export const DENNIS = "http://localhost:9696";
|
||||
|
||||
|
||||
export default function Root() {
|
||||
|
|