Wire in a database

This commit is contained in:
Thomas Gideon 2024-09-02 17:43:49 -04:00
parent 232a9877e6
commit c7e4983e46
7 changed files with 252 additions and 28 deletions

2
.gitignore vendored
View file

@ -11,3 +11,5 @@ node_modules/
test-results/ test-results/
end2end/playwright-report/ end2end/playwright-report/
playwright/.cache/ playwright/.cache/
.envrc

152
Cargo.lock generated
View file

@ -44,6 +44,55 @@ version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
[[package]]
name = "anstream"
version = "0.6.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
[[package]]
name = "anstyle-parse"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
dependencies = [
"anstyle",
"windows-sys 0.52.0",
]
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.86" version = "1.0.86"
@ -328,6 +377,12 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "186dce98367766de751c42c4f03970fc60fc012296e706ccbb9d5df9b6c1e271" checksum = "186dce98367766de751c42c4f03970fc60fc012296e706ccbb9d5df9b6c1e271"
[[package]]
name = "colorchoice"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
[[package]] [[package]]
name = "concurrent-queue" name = "concurrent-queue"
version = "2.5.0" version = "2.5.0"
@ -563,6 +618,29 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "env_filter"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab"
dependencies = [
"log",
"regex",
]
[[package]]
name = "env_logger"
version = "0.11.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d"
dependencies = [
"anstream",
"anstyle",
"env_filter",
"humantime",
"log",
]
[[package]] [[package]]
name = "equivalent" name = "equivalent"
version = "1.0.1" version = "1.0.1"
@ -937,6 +1015,12 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]] [[package]]
name = "hyper" name = "hyper"
version = "1.4.1" version = "1.4.1"
@ -1018,6 +1102,12 @@ version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767" checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767"
[[package]]
name = "is_terminal_polyfill"
version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]] [[package]]
name = "itertools" name = "itertools"
version = "0.12.1" version = "0.12.1"
@ -1898,6 +1988,40 @@ dependencies = [
"thiserror", "thiserror",
] ]
[[package]]
name = "rust-embed"
version = "8.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa66af4a4fdd5e7ebc276f115e895611a34739a9c1c01028383d612d550953c0"
dependencies = [
"rust-embed-impl",
"rust-embed-utils",
"walkdir",
]
[[package]]
name = "rust-embed-impl"
version = "8.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6125dbc8867951125eec87294137f4e9c2c96566e61bf72c45095a7c77761478"
dependencies = [
"proc-macro2",
"quote",
"rust-embed-utils",
"syn 2.0.77",
"walkdir",
]
[[package]]
name = "rust-embed-utils"
version = "8.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e5347777e9aacb56039b0e1f28785929a8a3b709e87482e7442c72e7c12529d"
dependencies = [
"sha2",
"walkdir",
]
[[package]] [[package]]
name = "rustc-demangle" name = "rustc-demangle"
version = "0.1.24" version = "0.1.24"
@ -2198,6 +2322,15 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "signal-hook-registry"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "signature" name = "signature"
version = "2.2.0" version = "2.2.0"
@ -2327,6 +2460,7 @@ dependencies = [
"tokio-stream", "tokio-stream",
"tracing", "tracing",
"url", "url",
"uuid",
"webpki-roots", "webpki-roots",
] ]
@ -2408,6 +2542,7 @@ dependencies = [
"stringprep", "stringprep",
"thiserror", "thiserror",
"tracing", "tracing",
"uuid",
"whoami", "whoami",
] ]
@ -2446,6 +2581,7 @@ dependencies = [
"stringprep", "stringprep",
"thiserror", "thiserror",
"tracing", "tracing",
"uuid",
"whoami", "whoami",
] ]
@ -2470,6 +2606,7 @@ dependencies = [
"sqlx-core", "sqlx-core",
"tracing", "tracing",
"url", "url",
"uuid",
] ]
[[package]] [[package]]
@ -2600,6 +2737,7 @@ dependencies = [
"libc", "libc",
"mio", "mio",
"pin-project-lite", "pin-project-lite",
"signal-hook-registry",
"socket2", "socket2",
"tokio-macros", "tokio-macros",
"windows-sys 0.52.0", "windows-sys 0.52.0",
@ -2625,6 +2763,7 @@ dependencies = [
"futures-core", "futures-core",
"pin-project-lite", "pin-project-lite",
"tokio", "tokio",
"tokio-util",
] ]
[[package]] [[package]]
@ -2864,6 +3003,12 @@ version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]] [[package]]
name = "uuid" name = "uuid"
version = "1.10.0" version = "1.10.0"
@ -2871,6 +3016,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314"
dependencies = [ dependencies = [
"getrandom", "getrandom",
"serde",
] ]
[[package]] [[package]]
@ -2994,17 +3140,23 @@ dependencies = [
"anyhow", "anyhow",
"axum", "axum",
"console_error_panic_hook", "console_error_panic_hook",
"env_logger",
"http", "http",
"leptos", "leptos",
"leptos_axum", "leptos_axum",
"leptos_meta", "leptos_meta",
"leptos_router", "leptos_router",
"log",
"rust-embed",
"serde",
"sqlx", "sqlx",
"thiserror", "thiserror",
"tokio", "tokio",
"tokio-stream",
"tower", "tower",
"tower-http", "tower-http",
"tracing", "tracing",
"uuid",
"wasm-bindgen", "wasm-bindgen",
] ]

View file

@ -20,8 +20,14 @@ wasm-bindgen = "=0.2.93"
thiserror = "1" thiserror = "1"
tracing = { version = "0.1", optional = true } tracing = { version = "0.1", optional = true }
http = "1" http = "1"
sqlx = { version = "0.8.1", features = ["postgres", "runtime-tokio", "tls-rustls-ring"], optional = true } sqlx = { version = "0.8.1", features = ["postgres", "runtime-tokio", "tls-rustls-ring", "uuid"], optional = true }
anyhow = "1.0.86" anyhow = "1.0.86"
log = { version = "0.4.22", optional = true }
env_logger = { version = "0.11.5", optional = true }
rust-embed = { version = "8.5.0", optional = true }
uuid = { version = "1.10.0", features = ["serde", "v4"] }
serde = { version = "1.0.209", features = ["derive"] }
tokio-stream = { version = "0.1.15", optional = true, features = ["full"] }
[features] [features]
hydrate = ["leptos/hydrate", "leptos_meta/hydrate", "leptos_router/hydrate"] hydrate = ["leptos/hydrate", "leptos_meta/hydrate", "leptos_router/hydrate"]
@ -35,8 +41,16 @@ ssr = [
"leptos_meta/ssr", "leptos_meta/ssr",
"leptos_router/ssr", "leptos_router/ssr",
"dep:tracing", "dep:tracing",
"dep:sqlx" "dep:sqlx",
"dep:log",
"dep:env_logger",
"dep:rust-embed",
"dep:tokio-stream"
] ]
log = ["dep:log"]
env_logger = ["dep:env_logger"]
rust-embed = ["dep:rust-embed"]
tokio-stream = ["dep:tokio-stream"]
# Defines a size-optimized profile for the WASM bundle in release mode # Defines a size-optimized profile for the WASM bundle in release mode
[profile.wasm-release] [profile.wasm-release]

View file

@ -14,7 +14,11 @@ services:
# size: 134217728 # 128*2^20 bytes = 128Mb # size: 134217728 # 128*2^20 bytes = 128Mb
volumes: volumes:
- watch_data:/var/lib/postgres/data - watch_data:/var/lib/postgres/data
ports:
- 5432:5432
environment: environment:
POSTGRES_DB: watch
POSTGRES_USER: watch
POSTGRES_PASSWORD: watch POSTGRES_PASSWORD: watch
adminer: adminer:

View file

@ -0,0 +1,6 @@
create table if not exists "public"."series" (
"id" uuid default gen_random_uuid() not null primary key,
"name" text not null,
"created" timestamptz default now() not null,
"updated" timestamptz default now() not null
);

View file

@ -2,6 +2,10 @@ use crate::error_template::{AppError, ErrorTemplate};
use leptos::*; use leptos::*;
use leptos_meta::*; use leptos_meta::*;
use leptos_router::*; use leptos_router::*;
use serde::{Deserialize, Serialize};
#[cfg(feature = "ssr")]
use sqlx::FromRow;
use uuid::Uuid;
#[component] #[component]
pub fn App() -> impl IntoView { pub fn App() -> impl IntoView {
@ -21,7 +25,7 @@ pub fn App() -> impl IntoView {
/> />
// sets the document title // sets the document title
<Title text="Welcome to Leptos"/> <Title text="Watch"/>
// content for this welcome page // content for this welcome page
<Router fallback=|| { <Router fallback=|| {
@ -41,38 +45,55 @@ pub fn App() -> impl IntoView {
} }
} }
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "ssr", derive(FromRow))]
struct Series {
id: Uuid,
name: String,
}
#[server] #[server]
pub async fn get_all() -> Result<Vec<String>, ServerFnError> { pub async fn get_all() -> Result<Vec<String>, ServerFnError> {
use std::time::Duration; use sqlx::{Pool, Postgres};
use tokio_stream::StreamExt;
std::thread::sleep(Duration::from_millis(250)); let pool: Pool<Postgres> = expect_context();
println!("Loading");
Ok(vec!["data1".to_string(), "data2".to_string()]) let mut r = sqlx::query_as::<_, Series>("select * from series;").fetch(&pool);
let mut found = Vec::new();
while let Some(series) = r.try_next().await? {
found.push(series.name);
}
Ok(found)
} }
/// Renders the home page of your application. /// Renders the home page of your application.
#[component] #[component]
fn HomePage() -> impl IntoView { fn HomePage() -> impl IntoView {
// Creates a reactive value to update the button // Creates a reactive value to update the button
let load = create_local_resource(|| (), |_| async move { get_all().await }); let load = create_resource(|| (), |_| async move { get_all().await });
view! { view! {
<h1>"Welcome to Leptos!"</h1> <h1>"What We're Watching"</h1>
{ <div class="card">
move || match load() { <Suspense fallback=|| view!{ <div>"Loading..."</div> }>
None => view!{ <div>"Loading..."</div> }, <ul>
Some(data) => { {load().map(format_series)}
</ul>
</Suspense>
</div>
}
}
fn format_series(data: Result<Vec<String>, ServerFnError>) -> Vec<impl IntoView> {
data.unwrap_or_default()
.into_iter()
.map(|d| {
view! { view! {
<div><ul>
{
data.unwrap_or_default().into_iter().map(|d| view!{
<li>{d}</li> <li>{d}</li>
}).collect::<Vec<_>>()
}
</ul></div>
}
}
}
}
} }
})
.collect::<Vec<_>>()
} }

View file

@ -1,9 +1,13 @@
#[cfg(feature = "ssr")] #[cfg(feature = "ssr")]
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
use std::env;
use axum::Router; use axum::Router;
use leptos::*; use leptos::*;
use leptos_axum::{generate_route_list, LeptosRoutes}; use leptos_axum::{generate_route_list, LeptosRoutes};
use log::info;
use sqlx::postgres::PgPoolOptions;
use watch::app::*; use watch::app::*;
use watch::fileserv::file_and_error_handler; use watch::fileserv::file_and_error_handler;
@ -17,14 +21,35 @@ async fn main() {
let addr = leptos_options.site_addr; let addr = leptos_options.site_addr;
let routes = generate_route_list(App); let routes = generate_route_list(App);
let pool = PgPoolOptions::new()
.test_before_acquire(true)
.max_lifetime(None)
.connect_lazy(&env::var("DATABASE_URL").expect("DATABASE_URL was not set!"))
.expect("Failed to create database connection pool");
// build our application with a route // build our application with a route
let app = Router::new() let app = Router::new()
.leptos_routes(&leptos_options, routes, App) .leptos_routes_with_context(
&leptos_options,
routes,
{
let pool = pool.clone();
move || provide_context(pool.clone())
},
App,
)
.fallback(file_and_error_handler) .fallback(file_and_error_handler)
.with_state(leptos_options); .with_state(leptos_options)
.with_state(pool);
if env::var("RUST_LOG").is_err() {
env::set_var("RUST_LOG", format!("{}=debug", module_path!()));
}
env_logger::init();
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap(); let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
logging::log!("listening on http://{}", &addr); info!("listening on http://{}", &addr);
axum::serve(listener, app.into_make_service()) axum::serve(listener, app.into_make_service())
.await .await
.unwrap(); .unwrap();